vsrepo 1.0.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 +281 -0
- package/package.json +41 -0
- package/src/index.d.ts +2 -0
- package/src/index.js +2 -0
package/README.md
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# VSRepo
|
|
2
|
+
|
|
3
|
+
Biblioteca de repository para projetos que usam Prisma. Ela permite criar repositories tipados com métodos base (`get`, `save` e `remove`) e métodos dinâmicos mapeados pelo nome, como `findByEmail`, `findManyPaginated`, `updateById` e `deleteManyByIdIn`.
|
|
4
|
+
|
|
5
|
+
## Instalação
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add vsrepo @prisma/client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
O Prisma Client deve estar configurado no projeto:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm prisma generate
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Uso básico
|
|
18
|
+
|
|
19
|
+
Crie uma instância do Prisma Client:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// src/db.ts
|
|
23
|
+
import { PrismaClient } from "@prisma/client";
|
|
24
|
+
|
|
25
|
+
const prisma = new PrismaClient();
|
|
26
|
+
|
|
27
|
+
export default prisma;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Depois crie o repository do modelo:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// src/repositories/usuarioRepository.ts
|
|
34
|
+
import prisma from "../db";
|
|
35
|
+
import { setupVSRepo, type SelectModels, type WhereModel } from "vsrepo";
|
|
36
|
+
import type { Prisma } from "@prisma/client";
|
|
37
|
+
|
|
38
|
+
type Usuario = Prisma.usuarioGetPayload<{
|
|
39
|
+
include: {
|
|
40
|
+
perfil: true;
|
|
41
|
+
postagens: true;
|
|
42
|
+
};
|
|
43
|
+
}>;
|
|
44
|
+
|
|
45
|
+
const usuarioSelectModels = {
|
|
46
|
+
public: {
|
|
47
|
+
id: true,
|
|
48
|
+
nome: true,
|
|
49
|
+
email: true,
|
|
50
|
+
},
|
|
51
|
+
minimal: {
|
|
52
|
+
id: true,
|
|
53
|
+
},
|
|
54
|
+
} satisfies SelectModels<"usuario">;
|
|
55
|
+
|
|
56
|
+
const usuarioRequiredWhere = {
|
|
57
|
+
ativo: true,
|
|
58
|
+
} satisfies WhereModel<"usuario">;
|
|
59
|
+
|
|
60
|
+
const usuarioVSRepo = setupVSRepo<Usuario, "usuario">()({
|
|
61
|
+
tableName: "usuario",
|
|
62
|
+
pkName: "id",
|
|
63
|
+
selectModels: usuarioSelectModels,
|
|
64
|
+
defaultSelectModel: "public",
|
|
65
|
+
requiredWhere: usuarioRequiredWhere,
|
|
66
|
+
methods: {
|
|
67
|
+
findByEmail: { map: true, fbMode: "one" },
|
|
68
|
+
findManyPaginated: { map: true },
|
|
69
|
+
updateById: { map: true },
|
|
70
|
+
deleteManyByIdIn: { map: true, whereType: "overwrite" },
|
|
71
|
+
count: { map: true },
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const usuarioRepository = usuarioVSRepo.build(prisma);
|
|
76
|
+
|
|
77
|
+
export default usuarioRepository;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use o repository na aplicação:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import usuarioRepository from "./repositories/usuarioRepository";
|
|
84
|
+
|
|
85
|
+
const usuario = await usuarioRepository.save({
|
|
86
|
+
nome: "Joao",
|
|
87
|
+
email: "joao@email.com",
|
|
88
|
+
senha: "password",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const usuarioEncontrado = await usuarioRepository.get(usuario.id);
|
|
92
|
+
|
|
93
|
+
const porEmail = await usuarioRepository.findByEmail("joao@email.com");
|
|
94
|
+
|
|
95
|
+
await usuarioRepository.updateById(usuario.id, {
|
|
96
|
+
nome: "Joao Pedro",
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await usuarioRepository.remove(usuario.id);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Métodos base
|
|
103
|
+
|
|
104
|
+
Ao chamar `build(prisma)`, o VSRepo cria três métodos por padrão:
|
|
105
|
+
|
|
106
|
+
| Método | Descrição |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| `get(pk, options?)` | Busca um registro pela primary key definida em `pkName`. |
|
|
109
|
+
| `save(obj, options?)` | Cria ou atualiza um registro. Se `obj` tiver a primary key, usa `upsert`; se não tiver, usa `create`. |
|
|
110
|
+
| `remove(pk, options?)` | Remove um registro pela primary key. |
|
|
111
|
+
|
|
112
|
+
Você pode configurar esses métodos no `build`:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
const usuarioRepository = usuarioVSRepo.build(prisma, {
|
|
116
|
+
freeze: true,
|
|
117
|
+
showWorking: false,
|
|
118
|
+
baseMethods: {
|
|
119
|
+
get: {
|
|
120
|
+
active: true,
|
|
121
|
+
defaultSelect: "public",
|
|
122
|
+
},
|
|
123
|
+
remove: {
|
|
124
|
+
defaultSelect: "minimal",
|
|
125
|
+
},
|
|
126
|
+
save: {
|
|
127
|
+
active: true,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Select models
|
|
134
|
+
|
|
135
|
+
`selectModels` define selects reutilizáveis do Prisma. O `defaultSelectModel` é aplicado quando o método não recebe outro select.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
const usuario = await usuarioRepository.get(id, {
|
|
139
|
+
selectModel: "minimal",
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const usuarioCompleto = await usuarioRepository.get(id, {
|
|
143
|
+
selectModel: false,
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Use `selectModel: false` para não aplicar nenhum select model e deixar o Prisma retornar o payload padrão.
|
|
148
|
+
|
|
149
|
+
## Métodos dinâmicos
|
|
150
|
+
|
|
151
|
+
Métodos dinâmicos são declarados em `methods` com `map: true`. O nome do método informa qual operação do Prisma será usada e quais campos entram no `where`.
|
|
152
|
+
|
|
153
|
+
Exemplos:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
methods: {
|
|
157
|
+
findUniqueByIdAndEmail: { map: true },
|
|
158
|
+
findByNomeContainsInsensitive: { map: true },
|
|
159
|
+
findManyPaginated: { map: true },
|
|
160
|
+
findListWhereOrderedAndPaginated: { map: true, whereType: "overwrite" },
|
|
161
|
+
createManyAndReturn: { map: true },
|
|
162
|
+
updateById: { map: true },
|
|
163
|
+
deleteManyByIdIn: { map: true, whereType: "overwrite" },
|
|
164
|
+
existsByEmail: { map: true },
|
|
165
|
+
countByPerfil: { map: true },
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Uso:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
const usuario = await usuarioRepository.findUniqueByIdAndEmail(id, email);
|
|
173
|
+
|
|
174
|
+
const usuarios = await usuarioRepository.findByNomeContainsInsensitive("joao");
|
|
175
|
+
|
|
176
|
+
const pagina = await usuarioRepository.findManyPaginated({
|
|
177
|
+
take: 10,
|
|
178
|
+
skip: 0,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const ordenado = await usuarioRepository.findListWhereOrderedAndPaginated(
|
|
182
|
+
{ nome: { startsWith: "A" } },
|
|
183
|
+
{ dataCriacao: "desc" },
|
|
184
|
+
{ take: 10 }
|
|
185
|
+
);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Filtros pelo nome do método
|
|
189
|
+
|
|
190
|
+
O VSRepo interpreta sufixos no nome dos métodos para montar o `where`:
|
|
191
|
+
|
|
192
|
+
| Padrão | Exemplo |
|
|
193
|
+
| --- | --- |
|
|
194
|
+
| `And` / `Or` | `findByIdAndEmail`, `findByNomeOrEmail` |
|
|
195
|
+
| `In` / `NotIn` | `deleteManyByIdIn` |
|
|
196
|
+
| `Contains` / `StartsWith` / `EndsWith` | `findByNomeContains` |
|
|
197
|
+
| `Insensitive` | `findByNomeContainsInsensitive` |
|
|
198
|
+
| `GreaterThan` / `GreaterThanEqual` | `findByIdadeGreaterThan` |
|
|
199
|
+
| `LessThan` / `LessThanEqual` | `findByIdadeLessThanEqual` |
|
|
200
|
+
| `IsNull` / `IsNotNull` | `findByPerfilIsNotNull` |
|
|
201
|
+
| `IsTrue` / `IsFalse` | `findByAtivoIsTrue` |
|
|
202
|
+
| `Some` / `Every` / `None` | `findByPostagensSomeTituloContains` |
|
|
203
|
+
| `With` / `Without` | `findByPerfilWith`, `findByPerfilWithout` |
|
|
204
|
+
|
|
205
|
+
## Relações no `save`
|
|
206
|
+
|
|
207
|
+
Configure `relations` para que `save` consiga criar, conectar, atualizar ou remover relações junto com o registro principal.
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
const usuarioVSRepo = setupVSRepo<Usuario, "usuario">()({
|
|
211
|
+
tableName: "usuario",
|
|
212
|
+
pkName: "id",
|
|
213
|
+
relations: {
|
|
214
|
+
perfil: {
|
|
215
|
+
pk: "id",
|
|
216
|
+
mode: "oto",
|
|
217
|
+
restriction: "set",
|
|
218
|
+
},
|
|
219
|
+
postagens: {
|
|
220
|
+
pk: "id",
|
|
221
|
+
mode: "otm",
|
|
222
|
+
restriction: "set",
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Modos disponíveis:
|
|
229
|
+
|
|
230
|
+
| Modo | Relação |
|
|
231
|
+
| --- | --- |
|
|
232
|
+
| `oto` | one-to-one |
|
|
233
|
+
| `otm` | one-to-many |
|
|
234
|
+
| `mto` | many-to-one |
|
|
235
|
+
| `mtm` | many-to-many |
|
|
236
|
+
|
|
237
|
+
`restriction: "set"` substitui o conjunto atual quando aplicável. `restriction: "add"` adiciona/conecta sem limpar os registros já relacionados.
|
|
238
|
+
|
|
239
|
+
## Transações
|
|
240
|
+
|
|
241
|
+
Todos os métodos aceitam `options.db`. Use isso para executar operações dentro de uma transação do Prisma:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
await prisma.$transaction(async (tx) => {
|
|
245
|
+
const usuario = await usuarioRepository.save(
|
|
246
|
+
{
|
|
247
|
+
nome: "Maria",
|
|
248
|
+
email: "maria@email.com",
|
|
249
|
+
senha: "password",
|
|
250
|
+
},
|
|
251
|
+
{ db: tx }
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
await usuarioRepository.updateById(
|
|
255
|
+
usuario.id,
|
|
256
|
+
{ ativo: true },
|
|
257
|
+
{ db: tx }
|
|
258
|
+
);
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Estendendo um repository
|
|
263
|
+
|
|
264
|
+
Use `extend` para adicionar métodos manuais mantendo os métodos gerados:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
const repository = usuarioVSRepo
|
|
268
|
+
.build(prisma)
|
|
269
|
+
.extend((repo) => ({
|
|
270
|
+
async buscarAtivosPorDominio(dominio: string) {
|
|
271
|
+
return repo.findByEmailEndsWith(`@${dominio}`);
|
|
272
|
+
},
|
|
273
|
+
}));
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Scripts do projeto
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
pnpm test:usuario
|
|
280
|
+
pnpm test:base-methods:user
|
|
281
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vsrepo",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Uma biblioteca de repositório virtual para Prisma",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"types": "src/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test:base-methods:user": "tsx ./src/tests/base-methods/user.bm.test.ts",
|
|
10
|
+
"test:usuario": "tsx ./src/tests/usuario.test.ts"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"src/VSRepository.js",
|
|
14
|
+
"src/VSRepository.d.ts",
|
|
15
|
+
"src/VSRepoError.js",
|
|
16
|
+
"src/VSRepoError.d.ts",
|
|
17
|
+
"src/index.js",
|
|
18
|
+
"src/index.d.ts"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"prisma",
|
|
22
|
+
"repository",
|
|
23
|
+
"orm",
|
|
24
|
+
"typescript",
|
|
25
|
+
"vsrepo",
|
|
26
|
+
"vsrepository"
|
|
27
|
+
],
|
|
28
|
+
"author": "João Pedro Azevedo",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@prisma/client": "^7.8.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@prisma/adapter-pg": "^7.8.0",
|
|
35
|
+
"@prisma/client": "^7.8.0",
|
|
36
|
+
"@types/node": "^25.6.0",
|
|
37
|
+
"dotenv": "^17.4.2",
|
|
38
|
+
"prisma": "^7.8.0",
|
|
39
|
+
"typescript": "^6.0.3"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED