@vsrepo/prisma7-adapter 1.3.2 → 2.0.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +535 -0
- package/LICENSE +20 -20
- package/README.md +394 -299
- package/README.pt-BR.md +394 -299
- package/dist/constants/provider-capabilities.constant.d.ts +17 -0
- package/dist/constants/provider-capabilities.constant.js +27 -0
- package/dist/index.d.ts +9 -8
- package/dist/index.js +1 -7
- package/dist/parsers/data.parser.d.ts +3 -33
- package/dist/parsers/data.parser.js +70 -69
- package/dist/parsers/include.parser.d.ts +1 -1
- package/dist/parsers/include.parser.js +3 -7
- package/dist/parsers/order-by.parser.d.ts +1 -10
- package/dist/parsers/order-by.parser.js +3 -16
- package/dist/parsers/select.parser.d.ts +1 -1
- package/dist/parsers/select.parser.js +3 -7
- package/dist/parsers/where.parser.d.ts +4 -29
- package/dist/parsers/where.parser.js +49 -148
- package/dist/prisma7.adapter.d.ts +116 -81
- package/dist/prisma7.adapter.js +345 -362
- package/dist/resolvers/map-prisma-error.resolver.d.ts +0 -11
- package/dist/resolvers/map-prisma-error.resolver.js +48 -72
- package/dist/resolvers/merge-entities.resolver.d.ts +11 -15
- package/dist/resolvers/merge-entities.resolver.js +29 -38
- package/dist/resolvers/resolve-provider.resolver.d.ts +2 -22
- package/dist/resolvers/resolve-provider.resolver.js +10 -33
- package/dist/types/adapter-config.type.d.ts +21 -10
- package/dist/types/adapter-config.type.js +1 -3
- package/dist/types/adapter-relations.type.d.ts +14 -8
- package/dist/types/adapter-relations.type.js +1 -3
- package/dist/types/plain-object.type.js +1 -3
- package/dist/types/prisma-arg-like.type.js +1 -3
- package/dist/types/prisma-client-models.type.d.ts +4 -4
- package/dist/types/prisma-client-models.type.js +1 -3
- package/dist/types/prisma-provider.type.d.ts +1 -1
- package/dist/types/prisma-provider.type.js +1 -3
- package/dist/types/prisma-repository-like.type.d.ts +1 -1
- package/dist/types/prisma-repository-like.type.js +1 -3
- package/dist/types/prisma7-client-like.type.d.ts +1 -8
- package/dist/types/prisma7-client-like.type.js +1 -3
- package/dist/types/prisma7-global-config.type.d.ts +17 -0
- package/dist/types/prisma7-global-config.type.js +1 -0
- package/dist/types/prisma7-orm-types.type.d.ts +2 -2
- package/dist/types/prisma7-orm-types.type.js +1 -3
- package/dist/types/relation.type.d.ts +119 -25
- package/dist/types/relation.type.js +1 -3
- package/dist/validators/is-decimal.validator.d.ts +2 -5
- package/dist/validators/is-decimal.validator.js +3 -6
- package/dist/validators/is-plain-object.validator.d.ts +1 -1
- package/dist/validators/is-plain-object.validator.js +3 -7
- package/dist/validators/validate-prisma-client.validator.d.ts +1 -14
- package/dist/validators/validate-prisma-client.validator.js +7 -24
- package/package.json +108 -91
- package/dist/index.js.map +0 -1
- package/dist/parsers/data.parser.js.map +0 -1
- package/dist/parsers/include.parser.js.map +0 -1
- package/dist/parsers/order-by.parser.js.map +0 -1
- package/dist/parsers/select.parser.js.map +0 -1
- package/dist/parsers/where.parser.js.map +0 -1
- package/dist/prisma7.adapter.js.map +0 -1
- package/dist/resolvers/map-prisma-error.resolver.js.map +0 -1
- package/dist/resolvers/merge-entities.resolver.js.map +0 -1
- package/dist/resolvers/resolve-provider.resolver.js.map +0 -1
- package/dist/types/adapter-config.type.js.map +0 -1
- package/dist/types/adapter-relations.type.js.map +0 -1
- package/dist/types/plain-object.type.js.map +0 -1
- package/dist/types/prisma-arg-like.type.js.map +0 -1
- package/dist/types/prisma-client-models.type.js.map +0 -1
- package/dist/types/prisma-provider.type.js.map +0 -1
- package/dist/types/prisma-repository-like.type.js.map +0 -1
- package/dist/types/prisma7-client-like.type.js.map +0 -1
- package/dist/types/prisma7-orm-types.type.js.map +0 -1
- package/dist/types/relation.type.js.map +0 -1
- package/dist/validators/is-decimal.validator.js.map +0 -1
- package/dist/validators/is-plain-object.validator.js.map +0 -1
- package/dist/validators/validate-adapter-config.validator.d.ts +0 -8
- package/dist/validators/validate-adapter-config.validator.js +0 -78
- package/dist/validators/validate-adapter-config.validator.js.map +0 -1
- package/dist/validators/validate-prisma-client.validator.js.map +0 -1
package/README.pt-BR.md
CHANGED
|
@@ -1,299 +1,394 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
<img src="https://res.cloudinary.com/ddbfifdxd/image/upload/w_200,q_auto,f_auto/v1786386427/VS_logo_TextoAbaixo_yev4tq.png" alt="VSRepository Logo" width="200"/>
|
|
3
|
-
|
|
4
|
-
<p style="margin-top: 12px;">
|
|
5
|
-
<img src="https://img.shields.io/npm/v/@vsrepo/prisma7-adapter?style=flat-square" alt="npm version"/>
|
|
6
|
-
<img src="https://img.shields.io/npm/l/@vsrepo/prisma7-adapter?style=flat-square" alt="npm license"/>
|
|
7
|
-
<img src="https://img.shields.io/npm/dt/@vsrepo/prisma7-adapter?style=flat-square" alt="npm downloads"/>
|
|
8
|
-
<img src="https://img.shields.io/badge/inspired%20by-JpaRepository-E73121?style=flat-square" alt="inspired by JpaRepository"/>
|
|
9
|
-
</p>
|
|
10
|
-
</div>
|
|
11
|
-
|
|
12
|
-
# VSRepoPrisma7Adapter
|
|
13
|
-
|
|
14
|
-
🇺🇸 [Read in English](./README.md)
|
|
15
|
-
|
|
16
|
-
>
|
|
17
|
-
|
|
18
|
-
Implementação de `VSRepoAdapter` para o [VSRepository
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Sumário
|
|
23
|
-
|
|
24
|
-
- [Instalação](#instalação)
|
|
25
|
-
- [Uso básico](#uso-básico)
|
|
26
|
-
- [Config do construtor](#config-do-construtor)
|
|
27
|
-
- [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- [`
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- [`
|
|
36
|
-
- [
|
|
37
|
-
- [`
|
|
38
|
-
- [
|
|
39
|
-
- [
|
|
40
|
-
- [
|
|
41
|
-
- [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
| `mode` |
|
|
138
|
-
|
|
|
139
|
-
| `
|
|
140
|
-
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
- `
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://res.cloudinary.com/ddbfifdxd/image/upload/w_200,q_auto,f_auto/v1786386427/VS_logo_TextoAbaixo_yev4tq.png" alt="VSRepository Logo" width="200"/>
|
|
3
|
+
|
|
4
|
+
<p style="margin-top: 12px;">
|
|
5
|
+
<img src="https://img.shields.io/npm/v/@vsrepo/prisma7-adapter?style=flat-square" alt="npm version"/>
|
|
6
|
+
<img src="https://img.shields.io/npm/l/@vsrepo/prisma7-adapter?style=flat-square" alt="npm license"/>
|
|
7
|
+
<img src="https://img.shields.io/npm/dt/@vsrepo/prisma7-adapter?style=flat-square" alt="npm downloads"/>
|
|
8
|
+
<img src="https://img.shields.io/badge/inspired%20by-JpaRepository-E73121?style=flat-square" alt="inspired by JpaRepository"/>
|
|
9
|
+
</p>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
# VSRepoPrisma7Adapter
|
|
13
|
+
|
|
14
|
+
🇺🇸 [Read in English](./README.md)
|
|
15
|
+
|
|
16
|
+
> 🚧 **Em desenvolvimento.** Esta é a `v2` do `@vsrepo/prisma7-adapter`, feita para o [VSRepository v3](https://github.com/VSRepository/VSRepository). É uma versão de desenvolvimento, ainda não uma release estável, então a API pode mudar. Para o VSRepository v2, use a linha 1.x (última: `1.3.2`). Por enquanto é o único adapter oficialmente suportado — adapters para outros ORMs estão planejados, mas ainda não foram publicados (veja [Status dos adapters](https://github.com/VSRepository/VSRepository/blob/main/README.pt-BR.md#status-dos-adapters) no README do VSRepository).
|
|
17
|
+
|
|
18
|
+
Implementação de `VSRepoAdapter` para o [VSRepository v3](https://github.com/VSRepository/VSRepository) usando [Prisma 7](https://www.prisma.io/). Traduz toda operação do `VSRepository` em chamadas do Prisma Client, resolvendo `VSRepoWhere`, `Ordering`, `select`/`relations` através de parsers dedicados e — quando uma config de `relations` é fornecida — resolvendo campos de relação em `create`/`update`/`upsert`/`save`.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Sumário
|
|
23
|
+
|
|
24
|
+
- [Instalação](#instalação)
|
|
25
|
+
- [Uso básico](#uso-básico)
|
|
26
|
+
- [Config do construtor](#config-do-construtor)
|
|
27
|
+
- [Config global](#config-global)
|
|
28
|
+
- [Relations](#relations)
|
|
29
|
+
- [Os dois `relations`](#os-dois-relations)
|
|
30
|
+
- [`relations` no construtor (escrita)](#relations-no-construtor-escrita)
|
|
31
|
+
- [`mode`](#mode)
|
|
32
|
+
- [`pk`](#pk)
|
|
33
|
+
- [`onUpdate`](#onupdate)
|
|
34
|
+
- [`onDisconnect`](#ondisconnect)
|
|
35
|
+
- [Ignorando uma relation (`"ignore"`)](#ignorando-uma-relation-ignore)
|
|
36
|
+
- [`fkHere` e `fkThere`](#fkhere-e-fkthere)
|
|
37
|
+
- [`allowOmitPk`](#allowomitpk)
|
|
38
|
+
- [Como cada método de escrita resolve relations](#como-cada-método-de-escrita-resolve-relations)
|
|
39
|
+
- [`relations` nas options (leitura)](#relations-nas-options-leitura)
|
|
40
|
+
- [Métodos atômicos e de agregação](#métodos-atômicos-e-de-agregação)
|
|
41
|
+
- [`createMany`/`createManyReturning`/`updateMany`/`updateManyReturning` não suportam nested writes](#createmanycreatemanyreturningupdatemanyupdatemanyreturning-não-suportam-nested-writes)
|
|
42
|
+
- [Limitações por provider](#limitações-por-provider)
|
|
43
|
+
- [Transactions](#transactions)
|
|
44
|
+
- [Logging](#logging)
|
|
45
|
+
- [Requisitos](#requisitos)
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Instalação
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install vsrepo@dev @prisma/client @vsrepo/prisma7-adapter@dev
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Enquanto a `2.0.0` está em desenvolvimento, instale o `vsrepo` e o `@vsrepo/prisma7-adapter` com a tag `dev`.
|
|
56
|
+
|
|
57
|
+
## Uso básico
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { VSRepository, VSLogLevel } from "vsrepo";
|
|
61
|
+
import { Prisma7Adapter, Prisma7OrmTypes } from "@vsrepo/prisma7-adapter";
|
|
62
|
+
import { Prisma, PrismaClient } from "./generated/prisma/client";
|
|
63
|
+
import { prisma } from "./prisma";
|
|
64
|
+
|
|
65
|
+
type User = Prisma.UserGetPayload<{ include: { posts: true } }>;
|
|
66
|
+
type MyOrmTypes = Prisma7OrmTypes<PrismaClient>;
|
|
67
|
+
|
|
68
|
+
class UserRepository extends VSRepository<User, string, MyOrmTypes> {
|
|
69
|
+
constructor() {
|
|
70
|
+
super({
|
|
71
|
+
adapter: new Prisma7Adapter(prisma, {
|
|
72
|
+
modelName: "user",
|
|
73
|
+
pkName: "id",
|
|
74
|
+
relations: {
|
|
75
|
+
posts: { mode: "otm", pk: "id" },
|
|
76
|
+
},
|
|
77
|
+
logLevel: VSLogLevel.WARN,
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const userRepository = new UserRepository();
|
|
84
|
+
|
|
85
|
+
const user = await userRepository.get("...", { relations: { posts: true } }); // pela primary key
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Aqui, o `relations` passado no `options` do método — com a forma `{ campo: true }` — é transformado num `include` do Prisma pelo adapter (ver [`relations` nas options (leitura)](#relations-nas-options-leitura)). Se você fornecer `select`, o `relations`/`include` é ignorado. Não confunda com o `relations` da config do construtor (que descreve como campos de relação são resolvidos em escritas) — a diferença é explicada em [Os dois `relations`](#os-dois-relations).
|
|
89
|
+
|
|
90
|
+
`Prisma7OrmTypes<DB>` amarra os tipos de retorno de `getDbClient()`/`transaction()` do `VSRepository` aos seus tipos reais e gerados do Prisma — ver [Transactions](#transactions).
|
|
91
|
+
|
|
92
|
+
## Config do construtor
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
new Prisma7Adapter(prisma, {
|
|
96
|
+
modelName: "user", // obrigatório — nome do model/delegate do Prisma Client, como em `prisma.user`
|
|
97
|
+
pkName: "id", // obrigatório — nome do campo de primary key da entidade
|
|
98
|
+
relations: { ... }, // opcional — ver "relations no construtor (escrita)" abaixo
|
|
99
|
+
provider: "postgresql", // opcional — detectado a partir do Prisma Client quando omitido
|
|
100
|
+
logLevel: VSLogLevel.WARN, // opcional — default: VSLogLevel.WARN
|
|
101
|
+
allowOmitPk: false, // opcional — default: false. Leia "`allowOmitPk`" antes de ativar
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Na construção, o adapter confere o Prisma Client e o model, e resolve o provider do datasource:
|
|
106
|
+
|
|
107
|
+
- um client `null`/`undefined` lança um `VSRepoAdapterError` (code `MISSING_DB_CLIENT`);
|
|
108
|
+
- um `modelName` sem delegate correspondente no client (`prisma.<modelName>`) lança `MODEL_NOT_FOUND`;
|
|
109
|
+
- o provider vem do `provider`, quando informado; senão é detectado a partir do client gerado (`_engineConfig.activeProvider`, com fallback pro `provider` do driver adapter). Se não for detectável, a construção lança `NOT_SUPPORTED` — nesse caso, informe o `provider` explicitamente (um de `postgresql`, `cockroachdb`, `mysql`, `sqlite`, `sqlserver`).
|
|
110
|
+
|
|
111
|
+
Os outros campos (`pkName`, `relations`, `logLevel`, `allowOmitPk`) são checados só pelo TypeScript, não em runtime.
|
|
112
|
+
|
|
113
|
+
## Config global
|
|
114
|
+
|
|
115
|
+
`provider`, `logLevel` e `allowOmitPk` podem ser definidos uma vez pra todos os adapters, pelo `setGlobalConfig` estático:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { VSLogLevel } from "vsrepo";
|
|
119
|
+
import { Prisma7Adapter } from "@vsrepo/prisma7-adapter";
|
|
120
|
+
|
|
121
|
+
Prisma7Adapter.setGlobalConfig({ logLevel: VSLogLevel.WARN });
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- Só afeta adapters criados **depois** da chamada, então chame antes de instanciar seus repositories.
|
|
125
|
+
- Cada chamada **substitui** a config global anterior — não faz merge com ela.
|
|
126
|
+
- Um valor definido na config do próprio adapter sempre vence o global.
|
|
127
|
+
|
|
128
|
+
## Relations
|
|
129
|
+
|
|
130
|
+
### Os dois `relations`
|
|
131
|
+
|
|
132
|
+
O nome `relations` aparece em **dois lugares diferentes** da API, com **formas e propósitos diferentes** — fácil de confundir. Em resumo:
|
|
133
|
+
|
|
134
|
+
| | `relations` no **construtor** | `relations` nas **options** |
|
|
135
|
+
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
136
|
+
| Onde você define | `new Prisma7Adapter(prisma, { relations: ... })` | `repository.get(where, { relations: ... })` — e demais métodos |
|
|
137
|
+
| Formato | Um objeto de **configuração** por campo (`{ mode, pk, onUpdate?, onDisconnect?, fkHere?, fkThere? }`), ou `"ignore"` | Um objeto por campo **só com `true` ou sub-objeto**: `{ posts: true }` |
|
|
138
|
+
| Propósito | **Escrita** — quando um payload de `create`/`update`/`upsert`/`save` tem campo de relação, diz como transformá-lo num nested write do Prisma (`create`/`connect`/`connectOrCreate`/`upsert`/`disconnect`/`deleteMany`/`set`) | **Leitura** — eager loading: quais relations trazer junto no resultado (vira um `include` do Prisma) |
|
|
139
|
+
| Quem consome | `parsePrismaWriteData` (resolver de escrita) | `parsePrismaInclude` (via `resolveReadArg`) |
|
|
140
|
+
| Precisa da outra? | Não — só afeta escritas | Não — funciona mesmo sem `relations` no construtor |
|
|
141
|
+
|
|
142
|
+
Nenhum depende do outro: o `relations` das options faz eager loading de leitura mesmo quando o construtor não tem `relations`; e o `relations` do construtor governa o comportamento de escrita mesmo que você nunca passe `relations` nas options. As duas subseções abaixo detalham cada um.
|
|
143
|
+
|
|
144
|
+
### `relations` no construtor (escrita)
|
|
145
|
+
|
|
146
|
+
A config `relations` do construtor descreve, para cada campo de relação da entidade, **como o adapter deve resolver esse campo quando ele aparecer nos payloads de escrita** (`create`/`update`/`upsert`/`save`).
|
|
147
|
+
|
|
148
|
+
Cada relation é configurada pelo nome do campo:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
relations: {
|
|
152
|
+
posts: { mode: "otm", pk: "id", onUpdate: "set" },
|
|
153
|
+
address: { mode: "oto", pk: "id", onUpdate: "upsert", onDisconnect: "delete" },
|
|
154
|
+
author: { mode: "mto", pk: "id" }, // toda opção tem default
|
|
155
|
+
audit: "ignore", // nunca é escrita por este repository
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Sem `relations`, todo campo — incluindo campos de relação — é repassado direto pro `where`/`data`/`create`/`update` do Prisma, como está. Isso funciona bem pra campos escalares, mas o Prisma espera um formato bem específico de nested write (`create`/`connectOrCreate`/`upsert`/`disconnect`/`deleteMany`/`set`) pra campos de relação — então, se sua entidade tem relations, normalmente você vai querer configurá-las.
|
|
160
|
+
|
|
161
|
+
#### `mode`
|
|
162
|
+
|
|
163
|
+
Cardinalidade da relation, do ponto de vista da entidade dona do campo:
|
|
164
|
+
|
|
165
|
+
| `mode` | Significado | Formato do campo que você envia | Opções |
|
|
166
|
+
| ------ | ------------ | ------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
167
|
+
| `oto` | one-to-one | um único objeto, ou `null` | `pk`, `onUpdate` (to-one), `onDisconnect` (`"disconnect"`/`"delete"`), `fkHere`, `fkThere` |
|
|
168
|
+
| `mto` | many-to-one | um único objeto, ou `null` | `pk`, `onUpdate` (to-one), `onDisconnect` (`"disconnect"`/`"delete"`), `fkHere`, `fkThere` |
|
|
169
|
+
| `otm` | one-to-many | um array de objetos | `pk`, `onUpdate` (to-many), `fkThere` |
|
|
170
|
+
| `mtm` | many-to-many | um array de objetos | `pk`, `onUpdate` (to-many), `fkThere` |
|
|
171
|
+
|
|
172
|
+
#### `pk`
|
|
173
|
+
|
|
174
|
+
O campo usado pra identificar um registro relacionado já existente. Um item **com** `pk` no payload é conectado (ou recebe upsert) ao registro existente; um item **sem** ele é criado.
|
|
175
|
+
|
|
176
|
+
#### `onUpdate`
|
|
177
|
+
|
|
178
|
+
Como `save`/`update`/`upsert` escrevem a relation no update. Os valores aceitos dependem do `mode`.
|
|
179
|
+
|
|
180
|
+
**To-one (`oto`/`mto`)**, quando o objeto relacionado tem a `pk`:
|
|
181
|
+
|
|
182
|
+
- **`"connect"`** nunca cria nem sobrescreve o registro relacionado: só aponta a relation pra ele.
|
|
183
|
+
- **`"connectOrCreate"`** cria o relacionado quando ele não existe.
|
|
184
|
+
- **`"upsert"`** é a que **sobrescreve** o relacionado com os campos enviados.
|
|
185
|
+
- Quando o próprio registro dono está sendo criado (`create`, ou o ramo `create` do `save`), um relacionado com `pk` sempre vira `connectOrCreate`.
|
|
186
|
+
|
|
187
|
+
**To-many (`otm`/`mtm`)**, pros itens que já existem (casados por `pk`) e pros que **não** foram enviados:
|
|
188
|
+
|
|
189
|
+
- **`"add"`** (default) — só cria/conecta/faz upsert dos itens enviados. Itens já existentes que não estão no payload permanecem intocados.
|
|
190
|
+
- **`"set"`** — igual ao `"add"`, mas também remove o que não foi enviado: `otm` roda um `deleteMany` nos itens ausentes do array, e `mtm` zera o join (`set: []`) antes de reconectar.
|
|
191
|
+
|
|
192
|
+
#### `onDisconnect`
|
|
193
|
+
|
|
194
|
+
O que enviar `null` pra uma relation to-one faz no update:
|
|
195
|
+
|
|
196
|
+
- **`"disconnect"`** (default) — desanexa o registro relacionado e o mantém. O Prisma rejeita quando a relation é obrigatória.
|
|
197
|
+
- **`"delete"`** — apaga o registro relacionado. Existe pra `oto` e `mto`, e é sempre opt-in. Cuidado num `mto`: o relacionado é compartilhado com outras linhas, então apagar afeta todas elas, e o Prisma rejeita a escrita enquanto outras linhas ainda o referenciam (a menos que a FK tenha uma ação referencial como `onDelete: Cascade`).
|
|
198
|
+
|
|
199
|
+
#### Ignorando uma relation (`"ignore"`)
|
|
200
|
+
|
|
201
|
+
Definir uma relation como `"ignore"` deixa o campo fora de toda escrita (`create`, `update`, `save`...): o objeto (ou array) é simplesmente descartado. Serve pra uma relation que você **carrega** mas não quer **persistir** por essa entidade.
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
// AddressRepository
|
|
205
|
+
relations: {
|
|
206
|
+
user: "ignore";
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Como o objeto de relation é descartado, uma FK escalar no payload (`userId`) passa como está, junto da primary key. Isso faz dela a forma mais simples de contornar o [problema do `allowOmitPk`](#allowomitpk) quando você só quer manter a FK: `address.save(addressCarregadoComUser)` atualiza o address e nunca toca no user.
|
|
211
|
+
|
|
212
|
+
#### `fkHere` e `fkThere`
|
|
213
|
+
|
|
214
|
+
Numa relation one-to-one ou many-to-one, só um dos lados guarda a coluna da foreign key. Em `Address.user`, é a `Address.userId`. Informe ao adapter em qual lado cada entidade está:
|
|
215
|
+
|
|
216
|
+
| Entidade | Campo da relation | Guarda a FK? | Config |
|
|
217
|
+
| --------------------- | ----------------- | --------------------------------------------------- | ------------------- |
|
|
218
|
+
| `Address` (lado dono) | `user` | **sim** — `userId` é uma coluna de `Address` | `fkHere: "userId"` |
|
|
219
|
+
| `User` (lado inverso) | `address` | não — a FK fica na entidade relacionada (`Address`) | `fkThere: "userId"` |
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// UserRepository
|
|
223
|
+
relations: { address: { mode: "oto", pk: "id", fkThere: "userId" } }
|
|
224
|
+
|
|
225
|
+
// AddressRepository
|
|
226
|
+
relations: { user: { mode: "oto", pk: "id", fkHere: "userId" } }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- **`fkThere`** — nome da FK que fica na entidade **relacionada**. Um objeto relacionado carregado com `get`/`find` normalmente a traz (`address: { id, street, userId }`), mas a relation já define esse valor, e o Prisma não aceita ela dentro do nested write. O adapter remove a FK do(s) payload(s) relacionado(s) antes de montar a escrita.
|
|
230
|
+
- **`fkHere`** — nome da FK que fica **nesta** entidade. O Prisma não aceita a FK escalar junto do objeto de relation, então, quando o campo da relation está no payload, o adapter remove esse campo do `create` e do `update`. Um payload que traz só a FK escalar (sem o objeto de relation) fica como está.
|
|
231
|
+
|
|
232
|
+
O `fkHere` tem mais uma consequência, pra primary key — leia a próxima seção.
|
|
233
|
+
|
|
234
|
+
#### `allowOmitPk`
|
|
235
|
+
|
|
236
|
+
`allowOmitPk` é uma opção do adapter (também dá pra definir na [config global](#config-global)), mas só importa pra relations configuradas com `fkHere`.
|
|
237
|
+
|
|
238
|
+
**O problema.** Pegue um `Address` carregado junto com o `user` (`relations: { user: true }`) e enviado de volta pro `save`. O payload tem a primary key (`id`) **e** uma relation que guarda a FK na própria entidade. Com o objeto de relation presente, o Prisma não aceita nem a FK escalar nem a primary key (`Unknown argument 'id'`), então o adapter não consegue enviar tudo.
|
|
239
|
+
|
|
240
|
+
**O que o adapter faz:**
|
|
241
|
+
|
|
242
|
+
- **`allowOmitPk: false`** (default) — lança um `VSRepoAdapterError` (`INVALID_DATA`) **antes** de tocar no banco, explicando o que fazer. Payloads sem primary key (um `create` simples, por exemplo) ou sem o objeto de relation não são afetados.
|
|
243
|
+
- **`allowOmitPk: true`** — remove a primary key do `create` e do `update`. Registros que já existem continuam sendo achados pelo `where: { id }`, então **os updates funcionam normalmente**. O porém é o ramo `create` do `save`: se o registro ainda não existe, ele é criado com uma **primary key gerada pelo banco, e não a que você enviou** — e nada te avisa disso.
|
|
244
|
+
|
|
245
|
+
**Quando ativar.** Só se for realmente necessário, e só se não for a sua aplicação que gerencia os ids — ou seja, o banco os gera (`autoincrement`, defaults `uuid()`/`cuid()`, sequences...). Se a aplicação atribui os ids (UUIDs gerados no client, ids importados de outro sistema, ids que outros registros já referenciam), mantenha **desativado**: um registro que não existe seria gravado, sem aviso, com outro id.
|
|
246
|
+
|
|
247
|
+
**Formas de contornar, sem `allowOmitPk`:**
|
|
248
|
+
|
|
249
|
+
1. **Ignorar a relation** quando você só quer manter a FK: `relations: { user: "ignore" }` (veja [Ignorando uma relation](#ignorando-uma-relation-ignore)). O payload mantém `id` e `userId`, e a entidade relacionada não é escrita.
|
|
250
|
+
2. **Enviar a FK escalar no lugar do objeto de relation.** `address.save({ id, city, userId })` mantém `id` e `userId`, já que não há objeto de relation. Persista a entidade relacionada por conta própria (pelo repository dela) quando ela também mudou — de preferência numa só `transaction`.
|
|
251
|
+
3. **Enviar só o que mudou.** `address.update({ id }, { city })` não carrega nem a primary key nem a relation.
|
|
252
|
+
4. **Salvar pelo outro lado.** `user.save({ ...user, address })` mantém as primary keys (configure o `fkThere` lá): a FK fica em `Address`, então `User` não tem FK própria pra conflitar.
|
|
253
|
+
5. **Deixar o banco gerar a key dos registros novos.** Não envie `id` ao criar: `address.create({ city, user })`.
|
|
254
|
+
|
|
255
|
+
#### Como cada método de escrita resolve relations
|
|
256
|
+
|
|
257
|
+
| Método | Relations |
|
|
258
|
+
| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
259
|
+
| `create` | Só `create`/`connectOrCreate` (sem upsert — ainda não existe nada pra atualizar) |
|
|
260
|
+
| `update` / `upsert` (metade do `update`) / `save` (branch de upsert) | Resolução completa: `create`/`connect`/`connectOrCreate`/`upsert`/`disconnect`/`delete`/`deleteMany`/`set`, conforme `mode`/`onUpdate`/`onDisconnect` |
|
|
261
|
+
| `createMany` / `createManyReturning` / `updateMany` / `updateManyReturning` | Não suportado — lança um `VSRepoAdapterError` apontando o campo problemático se o payload tiver uma relation configurada |
|
|
262
|
+
|
|
263
|
+
### `relations` nas options (leitura)
|
|
264
|
+
|
|
265
|
+
Esse é o `relations` que você passa no `options` de um método do `VSRepository` (`get`, `find`, `findOne`, etc.). A forma é bem mais simples: um objeto onde cada campo de relação aceita:
|
|
266
|
+
|
|
267
|
+
- `true` — carrega a relation completa;
|
|
268
|
+
- ou outro objeto `relations` — para eager loading aninhado (relations da relation).
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
const user = await userRepository.get(
|
|
272
|
+
"...", // primary key
|
|
273
|
+
{
|
|
274
|
+
relations: {
|
|
275
|
+
posts: true, // carrega os posts junto
|
|
276
|
+
author: { profile: true }, // e, dentro do author, o profile (eager loading aninhado)
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Esse objeto é transformado num `include` do Prisma pelo adapter (`parsePrismaInclude`). Ele **não** usa a config `relations` do construtor: é puramente uma opção de leitura e funciona mesmo sem `relations` na config.
|
|
283
|
+
|
|
284
|
+
O `select` e o `relations` que você passa nas options são transformados num `select` do Prisma (`parsePrismaSelect`) e num `include` (`parsePrismaInclude`), respectivamente. Quando o `select` é fornecido, o `include` derivado do `relations` é descartado (fica `undefined`), porque o Prisma não permite combinar `include` e `select` na mesma query. Ou seja, se você já estiver usando `select`, o objeto `relations` vira redundante — por isso ele é facultativo. Essa é a lógica resolvida (de `resolveReadArg`):
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
const prismaSelect = options.select && parsePrismaSelect(options.select);
|
|
288
|
+
const prismaInclude = prismaSelect ? undefined : options.relations && parsePrismaInclude(options.relations);
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Métodos atômicos e de agregação
|
|
292
|
+
|
|
293
|
+
O adapter implementa os 12 métodos abstratos pros quais `increment`/`decrement`/`multiply`/`divide`, suas versões `*Many` e `sum`/`average`/`min`/`max` do `VSRepository` delegam: `incrementOne`, `decrementOne`, `multiplyOne`, `divideOne`, `incrementMany`, `decrementMany`, `multiplyMany`, `divideMany`, `sum`, `average`, `min`, `max`.
|
|
294
|
+
|
|
295
|
+
- `incrementOne`/`decrementOne`/`multiplyOne`/`divideOne` traduzem direto pra escrita atômica nativa de campo único do Prisma — `data: { [field]: { increment: value } }` (e `decrement`/`multiply`/`divide`) — então a operação é avaliada **server-side** contra o valor _atual_ do registro (`UPDATE ... SET field = field + value`), e não como um fetch-then-save no cliente. O `update()` do Prisma já retorna a linha refletindo o estado _após_ a escrita, então o adapter nunca precisa fazer uma leitura extra.
|
|
296
|
+
- `incrementMany`/`decrementMany`/`multiplyMany`/`divideMany` aplicam essa mesma escrita nativa em **todo** registro que casa com `where`, através do `updateMany` do Prisma, também avaliada server-side. Eles retornam um `{ count }` (`CountResult`) em vez dos registros.
|
|
297
|
+
- `sum`/`average`/`min`/`max` traduzem pro `aggregate()` do Prisma, com `_sum`/`_avg`/`_min`/`_max: { [field]: true }`. O resultado bruto (`number`, `bigint`, uma instância de `Decimal`, ou `null`) é normalizado pra `number | null` — `null` é repassado como está (espelhando o `SUM()`/`AVG()`/`MIN()`/`MAX()` do SQL, que retornam `NULL`, não `0`, sobre um conjunto vazio), e um valor `Decimal`/`DecimalLike` é convertido via seu `.toNumber()`.
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
// Atômico — avaliado server-side, sem read-modify-write:
|
|
301
|
+
await productRepository.increment("stock", 10, { id: productId });
|
|
302
|
+
await accountRepository.decrement("balance", 50, { id: accountId });
|
|
303
|
+
await productRepository.multiply("price", 1.1, { id: productId }); // ex: um reajuste de 10% no preço
|
|
304
|
+
await productRepository.divide("price", 2, { id: productId });
|
|
305
|
+
|
|
306
|
+
// O mesmo, pra todo registro que casa com `where` — retorna `{ count }`:
|
|
307
|
+
await productRepository.incrementMany("stock", 10, { category: "books" });
|
|
308
|
+
|
|
309
|
+
// Agregação — entre todos os registros que casam com um `where` opcional (todos se omitido):
|
|
310
|
+
const total = await productRepository.sum("price"); // number | null
|
|
311
|
+
const avgPrice = await productRepository.average("price", { active: true });
|
|
312
|
+
const cheapest = await productRepository.min("price");
|
|
313
|
+
const mostExpensive = await productRepository.max("price");
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Campos Decimal** (`Prisma.Decimal`) são totalmente suportados como o `field` de todos esses métodos — o `value` dos métodos atômicos é repassado direto pro Prisma (que aceita `number`, `string`, ou `Decimal`/`DecimalJsLike`), e os métodos de agregação sempre normalizam o resultado pra `number`, nunca uma instância de `Decimal`.
|
|
317
|
+
|
|
318
|
+
## `createMany`/`createManyReturning`/`updateMany`/`updateManyReturning` não suportam nested writes
|
|
319
|
+
|
|
320
|
+
`createMany`, `createManyReturning`, `updateMany` e `updateManyReturning` só aceitam campos escalares no `data`. Se seu payload incluir um campo configurado em `relations` (independente do valor), o adapter lança um `VSRepoAdapterError` apontando o campo problemático. Pra um nested write completo, use `create`/`update`/`save` registro por registro, ou envolva várias chamadas de `save` num `saveMany`/`transaction`.
|
|
321
|
+
|
|
322
|
+
> Nota sobre a ordem de retorno: `createManyReturning` e `updateManyReturning` não garantem que os registros devolvidos seguem a ordem do payload de entrada (`objs`). Sem `select`/`relations` eles são uma única chamada `createManyAndReturn`/`updateManyAndReturn`; com eles, o resultado vem de um segundo `findMany` (re-buscando as linhas inseridas/atualizadas pela primary key, dentro de uma transaction). Em qualquer caso, não conte com a ordem batendo com a da entrada.
|
|
323
|
+
|
|
324
|
+
## Limitações por provider
|
|
325
|
+
|
|
326
|
+
Alguns recursos que esse adapter usa não estão disponíveis em todo banco. O adapter checa todos os recursos abaixo antecipadamente e lança um `VSRepoAdapterError` (code `NOT_SUPPORTED`) antes de tocar no banco.
|
|
327
|
+
|
|
328
|
+
| Recurso | Providers com suporte | Sem suporte |
|
|
329
|
+
| ----------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
330
|
+
| `ignoreCase` (traduzido pro `mode: "insensitive"` do Prisma; usado pelos métodos dinâmicos `findByXIgnoreCase`) | PostgreSQL, CockroachDB | MySQL, SQLite, SQL Server — MySQL e SQL Server já são case-insensitive por padrão (conforme a collation), então não precisam de `mode`; SQLite só é case-insensitive pra caracteres ASCII |
|
|
331
|
+
| `createManyReturning` / `updateManyReturning` (baseados no `createManyAndReturn`/`updateManyAndReturn` do Prisma) | PostgreSQL, CockroachDB, SQLite | MySQL, SQL Server |
|
|
332
|
+
| `ignoreConflicts: true` no `createMany` / `createManyReturning` (o `skipDuplicates` do Prisma) | PostgreSQL, MySQL, CockroachDB | SQL Server, SQLite |
|
|
333
|
+
|
|
334
|
+
O operador `like` não é suportado em nenhum provider — o Prisma não tem equivalente, então o adapter lança `NOT_SUPPORTED`. Use `contains`, `startsWith` ou `endsWith`.
|
|
335
|
+
|
|
336
|
+
> Essas limitações vêm do próprio Prisma, não do adapter — veja a [doc de case sensitivity](https://www.prisma.io/docs/orm/v7/reference/prisma-client-reference#mode) e a [referência de CRUD](https://www.prisma.io/docs/orm/v7/prisma-client/queries/crud) do Prisma pra mais detalhes. O adapter é coberto por testes de integração em PostgreSQL, MySQL, SQLite, SQL Server e CockroachDB.
|
|
337
|
+
|
|
338
|
+
## Transactions
|
|
339
|
+
|
|
340
|
+
**Todos** os métodos aceitam `options.db` e executam a operação no client/transaction passado — a diferença está em **como** cada um o trata:
|
|
341
|
+
|
|
342
|
+
- A maioria (operações de uma única chamada Prisma) roda direto em `options?.db`: você passa um transaction client e a chamada participa da transaction, sem iniciar nada novo.
|
|
343
|
+
- `saveMany` e `deleteManyReturning` — e `updateManyReturning`/`createManyReturning` quando você passa `select` ou `relations` — precisam rodar **mais de uma operação** do Prisma atomicamente (`saveMany` salva cada registro individualmente; `updateManyReturning`/`createManyReturning` fazem um `updateManyAndReturn`/`createManyAndReturn` + `findMany`; `deleteManyReturning` faz um `findMany` + `deleteMany`), então passam por `runTransactional`. Sem `select`/`relations`, `updateManyReturning`/`createManyReturning` são uma única chamada do Prisma. Se `options.db` já for um transaction client ativo, ele é reaproveitado — nenhuma transaction aninhada é criada; caso contrário, uma nova transaction é criada (em `options.db`, se fornecido, ou no client raiz caso contrário).
|
|
344
|
+
|
|
345
|
+
Um transaction client é diferenciado do `PrismaClient` raiz pelo método `$on`: o client raiz do Prisma o expõe (pra event listeners), o transaction client interativo não. É isso que `Prisma7ClientLike`/`Prisma7OrmTypes` codificam nos seus tipos.
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
await userRepository.transaction(async tx => {
|
|
349
|
+
// Toda chamada de repository feita com `{ db: tx }` dentro desse
|
|
350
|
+
// callback compartilha a mesma transaction — incluindo chamadas a
|
|
351
|
+
// `saveMany`/`updateManyReturning`/`createManyReturning`/`deleteManyReturning`,
|
|
352
|
+
// que vão detectar que `tx` já é uma transaction e rodar direto nele, sem
|
|
353
|
+
// aninhar uma nova.
|
|
354
|
+
await userRepository.save(user, { db: tx });
|
|
355
|
+
await userRepository.save(outrosUsuarios, { db: tx }); // um array salva vários registros
|
|
356
|
+
});
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Concorrência em `deleteManyReturning`
|
|
360
|
+
|
|
361
|
+
O `deleteManyReturning` roda um `findMany` no `where` informado (pra capturar os registros que vai devolver) e depois deleta **esses mesmos registros pela primary key** num `deleteMany`, tudo dentro de uma transaction. Como o Prisma não tem um `deleteManyAndReturn` nativo, ele precisa dessas duas etapas. Se o seu `select` deixar a primary key de fora, o adapter a adiciona internamente na query e a remove dos registros devolvidos.
|
|
362
|
+
|
|
363
|
+
Como o delete é guiado pelas primary keys que de fato foram buscadas, uma linha **inserida depois** do `findMany` nunca é deletada por engano. Ainda assim, uma alteração concorrente entre as duas etapas pode fazer os dois divergirem:
|
|
364
|
+
|
|
365
|
+
- uma linha devolvida que foi **deletada por outra pessoa** nesse meio-tempo continua sendo devolvida, mesmo não tendo sido deletada por essa chamada;
|
|
366
|
+
- uma linha devolvida que **deixa de bater** com o `where` antes do `deleteMany` rodar ainda é deletada, já que o `where` não é re-aplicado.
|
|
367
|
+
|
|
368
|
+
Se você quiser garantir que não haverá problemas de concorrência, rode esse método **dentro** de um `repository.transaction()` num nível de isolamento mais alto — ex.: `SERIALIZABLE`:
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
import { VSRepoIsolationLevel } from "vsrepo";
|
|
372
|
+
|
|
373
|
+
await userRepository.transaction(async tx => {
|
|
374
|
+
// Atômico: nenhum insert/update concorrente consegue entrar entre o
|
|
375
|
+
// findMany e o deleteMany, pois o SERIALIZABLE isola esta transaction.
|
|
376
|
+
// Sempre passe `{ db: tx }` pra chamada rodar dentro desta transaction.
|
|
377
|
+
const deleted = await userRepository.deleteManyReturning({ id: { in: [...] } }, { db: tx });
|
|
378
|
+
}, {
|
|
379
|
+
isolationLevel: VSRepoIsolationLevel.SERIALIZABLE,
|
|
380
|
+
});
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
> O nível de isolamento só se aplica quando a chamada roda **dentro** daquela transaction (ou seja, com `{ db: tx }`). Quando o `deleteManyReturning` inicia a própria transaction interna (sem `options.db`), ele usa o nível de isolamento padrão.
|
|
384
|
+
|
|
385
|
+
## Logging
|
|
386
|
+
|
|
387
|
+
O adapter usa o `VSLogger` (de `vsrepo`) internamente: todo método loga uma linha `DEBUG` com o arg resolvido do Prisma, além de logs de performance de início/fim. Configure `logLevel` na config do construtor pra controlar a verbosidade; o default é `VSLogLevel.WARN`.
|
|
388
|
+
|
|
389
|
+
## Requisitos
|
|
390
|
+
|
|
391
|
+
- `vsrepo` ^3.0.0-dev.3
|
|
392
|
+
- `@prisma/client` ^7.10.1
|
|
393
|
+
- Node.js `>=22`
|
|
394
|
+
- Um banco PostgreSQL, CockroachDB, MySQL, SQLite ou SQL Server (todos cobertos pelos testes de integração)
|