vsrepo 1.1.0 → 1.1.2
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 +79 -42
- package/VSRepository/VSRepoUtils.js +84 -0
- package/VSRepository/VSRepository.d.ts +83 -11
- package/VSRepository/VSRepository.js +190 -100
- package/package.json +2 -1
- package/scripts/configure-prisma-import.mjs +11 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Biblioteca de repository pattern para projetos que usam **Prisma**, com suporte
|
|
|
7
7
|
|
|
8
8
|
O VSRepository permite criar repositories fortemente tipados com:
|
|
9
9
|
|
|
10
|
-
- **Métodos base** automáticos: `get`, `save`, `remove`
|
|
10
|
+
- **Métodos base** automáticos: `get`, `getOrThrow`, `save`, `remove`, `removeList`, `getAll`, `total`, `has`
|
|
11
11
|
- **Métodos dinâmicos** inferidos pelo nome: `findByEmail`, `findManyPaginated`, `updateById`, `deleteManyByIdIn`
|
|
12
12
|
- **Select models** reutilizáveis para diferentes projeções de dados
|
|
13
13
|
- **Type safety** em 100% das operações
|
|
@@ -24,7 +24,7 @@ O VSRepository permite criar repositories fortemente tipados com:
|
|
|
24
24
|
- [Integração com NestJS](#integração-com-nestjs)
|
|
25
25
|
- [Métodos base](#métodos-base)
|
|
26
26
|
- [Select Models](#select-models)
|
|
27
|
-
- [
|
|
27
|
+
- [Required Where](#requiredwhere)
|
|
28
28
|
- [Métodos dinâmicos](#métodos-dinâmicos)
|
|
29
29
|
- [Prefixos disponíveis](#prefixos-disponíveis)
|
|
30
30
|
- [Filtros de campo](#filtros-de-campo)
|
|
@@ -38,6 +38,7 @@ O VSRepository permite criar repositories fortemente tipados com:
|
|
|
38
38
|
- [Tratamento de erros](#tratamento-de-erros)
|
|
39
39
|
- [Tipos utilitários](#tipos-utilitários)
|
|
40
40
|
- [API Reference](#api-reference)
|
|
41
|
+
- [Contribuindo](#contribuindo)
|
|
41
42
|
- [Requisitos](#requisitos)
|
|
42
43
|
- [Troubleshooting](#troubleshooting)
|
|
43
44
|
|
|
@@ -94,9 +95,11 @@ src/generated/vsrepo/
|
|
|
94
95
|
Após gerar, importe sempre a partir da pasta gerada:
|
|
95
96
|
|
|
96
97
|
```ts
|
|
98
|
+
// CORRETO ✅
|
|
97
99
|
import { setupVSRepo } from "../generated/vsrepo";
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
|
|
101
|
+
// ERRADO ❌
|
|
102
|
+
import { setupVSRepo } from "vsrepo";
|
|
100
103
|
```
|
|
101
104
|
|
|
102
105
|
---
|
|
@@ -128,22 +131,11 @@ import type { Usuario } from "../generated/prisma/client";
|
|
|
128
131
|
const usuarioRepository = setupVSRepo<Usuario, "usuario">()({
|
|
129
132
|
tableName: "usuario",
|
|
130
133
|
pkName: "id",
|
|
131
|
-
|
|
132
134
|
selectModels: {
|
|
133
135
|
public: { id: true, nome: true, email: true },
|
|
134
|
-
minimal: { id: true },
|
|
135
136
|
},
|
|
136
137
|
defaultSelectModel: "public",
|
|
137
|
-
|
|
138
138
|
requiredWhere: { ativo: true },
|
|
139
|
-
|
|
140
|
-
methods: {
|
|
141
|
-
findByEmail: { map: true, fbMode: "one" },
|
|
142
|
-
findManyPaginated: { map: true },
|
|
143
|
-
updateById: { map: true },
|
|
144
|
-
deleteManyByIdIn: { map: true, whereType: "overwrite" },
|
|
145
|
-
count: { map: true },
|
|
146
|
-
},
|
|
147
139
|
}).build(prisma);
|
|
148
140
|
|
|
149
141
|
export default usuarioRepository;
|
|
@@ -163,9 +155,11 @@ const usuario = await usuarioRepository.save({
|
|
|
163
155
|
});
|
|
164
156
|
|
|
165
157
|
const encontrado = await usuarioRepository.get(usuario.id);
|
|
166
|
-
const
|
|
158
|
+
const todos = await usuarioRepository.getAll();
|
|
159
|
+
|
|
160
|
+
usuario.nome = "Joao Pedro";
|
|
167
161
|
|
|
168
|
-
await usuarioRepository.
|
|
162
|
+
await usuarioRepository.save(usuario);
|
|
169
163
|
await usuarioRepository.remove(usuario.id);
|
|
170
164
|
```
|
|
171
165
|
|
|
@@ -182,7 +176,7 @@ O VSRepository pode ser facilmente integrado em projetos NestJS através de prov
|
|
|
182
176
|
import { Provider } from "@nestjs/common";
|
|
183
177
|
import { PrismaService } from "../../database/prisma.service";
|
|
184
178
|
import { UserGetPayload } from "../../generated/prisma/models";
|
|
185
|
-
import {
|
|
179
|
+
import { setupVSRepo } from "../../generated/vsrepo";
|
|
186
180
|
|
|
187
181
|
const userVSRepo = setupVSRepo<
|
|
188
182
|
UserGetPayload<{ include: { profile: true } }>,
|
|
@@ -220,19 +214,28 @@ const userVSRepo = setupVSRepo<
|
|
|
220
214
|
proxyTo: "findUniqueByEmail",
|
|
221
215
|
selectModel: "auth",
|
|
222
216
|
},
|
|
217
|
+
findByEmailEndsWith: {
|
|
218
|
+
map: true,
|
|
219
|
+
}
|
|
223
220
|
},
|
|
224
221
|
});
|
|
225
222
|
|
|
226
|
-
|
|
223
|
+
const setupUserRepository = (prisma: PrismaService) => {
|
|
224
|
+
return userVSRepo.build(prisma).extend((repo) => ({
|
|
225
|
+
buscarPorDominio: async (dominio: string) => {
|
|
226
|
+
return repo.findByEmailEndsWith(`@${dominio}`);
|
|
227
|
+
},
|
|
228
|
+
}));
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export type UserRepository = ReturnType<typeof setupUserRepository>;
|
|
227
232
|
|
|
228
233
|
export const USER_REPOSITORY = Symbol("USER_REPOSITORY");
|
|
229
234
|
|
|
230
235
|
export const UserRepositoryProvider: Provider = {
|
|
231
236
|
provide: USER_REPOSITORY,
|
|
232
237
|
inject: [PrismaService],
|
|
233
|
-
useFactory:
|
|
234
|
-
return userVSRepo.build(prisma);
|
|
235
|
-
},
|
|
238
|
+
useFactory: setupUserRepository,
|
|
236
239
|
};
|
|
237
240
|
```
|
|
238
241
|
|
|
@@ -323,22 +326,27 @@ export class UserController {
|
|
|
323
326
|
|
|
324
327
|
## Métodos base
|
|
325
328
|
|
|
326
|
-
Ao chamar `.build(prisma)
|
|
329
|
+
Ao chamar `.build(prisma)` os métodos base abaixo são automaticamente disponibilizados:
|
|
327
330
|
|
|
328
331
|
| Método | Descrição |
|
|
329
332
|
| ------------ | ------------------------------------------ |
|
|
330
333
|
| `get(pk)` | Busca um registro pela primary key |
|
|
331
|
-
| `
|
|
334
|
+
| `getOrThrow(pk)` | Busca um registro pela primary key e lança erro se não encontrar |
|
|
335
|
+
| `save(obj)` | Cria ou atualiza (se o objeto passado tiver a `pk` faz `upsert`, se não faz `create`) |
|
|
332
336
|
| `remove(pk)` | Remove um registro pela primary key |
|
|
337
|
+
| `removeList(pks)` | Remove vários registros pela lista de primary keys — retorna `{ count }` |
|
|
338
|
+
| `getAll()` | Retorna todos os registros (aceita `pagination` e `order` no `options`) |
|
|
339
|
+
| `total()` | Retorna o total de registros |
|
|
340
|
+
| `has(pk)` | Verifica existência de um registro pela primary key — retorna `boolean` |
|
|
333
341
|
|
|
334
|
-
Todos aceitam `options
|
|
342
|
+
Todos aceitam `options` como último argumento.
|
|
335
343
|
|
|
336
344
|
### Configurando os métodos base
|
|
337
345
|
|
|
338
346
|
```ts
|
|
339
347
|
usuarioVSRepo.build(prisma, {
|
|
340
|
-
freeze: true, // Congela o objeto (padrão
|
|
341
|
-
showWorking:
|
|
348
|
+
freeze: true, // Congela o objeto (padrão = true)
|
|
349
|
+
showWorking: true, // Exibe logs do VSRepository no console, ótimo para debugar as queries criadas e os objetos passados para o prisma
|
|
342
350
|
|
|
343
351
|
baseMethods: {
|
|
344
352
|
get: {
|
|
@@ -350,9 +358,11 @@ usuarioVSRepo.build(prisma, {
|
|
|
350
358
|
defaultSelect: "minimal",
|
|
351
359
|
},
|
|
352
360
|
save: {
|
|
353
|
-
|
|
354
|
-
ignoreRequiredWhere: true, // Não aplica requiredWhere no upsert
|
|
361
|
+
ignoreRequiredWhere: true, // Não aplica requiredWhere no save
|
|
355
362
|
},
|
|
363
|
+
has: {
|
|
364
|
+
active: false, // Desativa o 'has' (padrão = true)
|
|
365
|
+
}
|
|
356
366
|
},
|
|
357
367
|
});
|
|
358
368
|
```
|
|
@@ -388,7 +398,7 @@ const usuarioCompleto = await usuarioRepository.get(id, { selectModel: false });
|
|
|
388
398
|
|
|
389
399
|
---
|
|
390
400
|
|
|
391
|
-
##
|
|
401
|
+
## Required Where
|
|
392
402
|
|
|
393
403
|
`requiredWhere` define filtros aplicados automaticamente em todas as queries do repository.
|
|
394
404
|
|
|
@@ -433,8 +443,11 @@ O prefixo do nome do método determina qual operação Prisma será chamada e qu
|
|
|
433
443
|
| ------------------------- | ------------------------ | ---------------------- | -------------------------------------------------------- |
|
|
434
444
|
| `findBy` | `findMany` / `findFirst` | `T[]` ou `T \| null` | Padrão é lista; use `fbMode: "one"` para retorno único |
|
|
435
445
|
| `findUniqueBy` | `findUnique` | `T \| null` | |
|
|
446
|
+
| `findUniqueOrThrowBy` | `findUniqueOrThrow` | `T` | Lança erro se não encontrar |
|
|
436
447
|
| `findFirstBy` | `findFirst` | `T \| null` | Aceita campos como filtro |
|
|
448
|
+
| `findFirstOrThrowBy` | `findFirstOrThrow` | `T` | Aceita campos como filtro; lança erro se não encontrar |
|
|
437
449
|
| `findFirst` | `findFirst` | `T \| null` | Sem filtros de campo; aplica só `requiredWhere` e `pushWhere` |
|
|
450
|
+
| `findFirstOrThrow` | `findFirstOrThrow` | `T` | Sem filtros de campo; aplica só `requiredWhere` e `pushWhere`; lança erro se não encontrar |
|
|
438
451
|
| `findManyBy` | `findMany` | `T[]` | Aceita campos como filtro |
|
|
439
452
|
| `findMany` | `findMany` | `T[]` | Sem filtros de campo; aplica só `requiredWhere` e `pushWhere` |
|
|
440
453
|
| `findWhere` | `findFirst` | `T \| null` | Recebe um objeto `where` explícito como argumento |
|
|
@@ -474,10 +487,10 @@ Os filtros são sufixos aplicados ao nome do campo dentro do método. O campo em
|
|
|
474
487
|
| `GreaterThanEqual` | `gte` | sim |
|
|
475
488
|
| `LessThan` | `lt` | sim |
|
|
476
489
|
| `LessThanEqual` | `lte` | sim |
|
|
477
|
-
| `IsNull` | `null` | não
|
|
478
|
-
| `IsNotNull` | `not: null` | não
|
|
479
|
-
| `IsTrue` | `true` | não
|
|
480
|
-
| `IsFalse` | `false` | não
|
|
490
|
+
| `IsNull` | `null` | não |
|
|
491
|
+
| `IsNotNull` | `not: null` | não |
|
|
492
|
+
| `IsTrue` | `true` | não |
|
|
493
|
+
| `IsFalse` | `false` | não |
|
|
481
494
|
| `Insensitive` | `mode: 'insensitive'` | combinador |
|
|
482
495
|
|
|
483
496
|
`Insensitive` é um combinador e pode ser usado junto com outro filtro de texto:
|
|
@@ -820,7 +833,7 @@ try {
|
|
|
820
833
|
|
|
821
834
|
| Classe | Quando é lançada |
|
|
822
835
|
| -------------------- | ------------------------------------------------------- |
|
|
823
|
-
| `VSRepoConfigError` | Configuração inválida em `setupVSRepo`
|
|
836
|
+
| `VSRepoConfigError` | Configuração inválida em `setupVSRepo` |
|
|
824
837
|
| `VSRepoBuildError` | Nome de método inválido ou desconhecido no `build` |
|
|
825
838
|
| `VSRepoExtendError` | Argumento inválido em `extend` |
|
|
826
839
|
| `VSRepoRuntimeError` | Erro em tempo de execução durante uma operação |
|
|
@@ -985,13 +998,24 @@ setupVSRepo<TPayload, TTableName>()({
|
|
|
985
998
|
|
|
986
999
|
```ts
|
|
987
1000
|
vsRepo.build(prisma, {
|
|
988
|
-
freeze?: boolean; // Congela o objeto (
|
|
989
|
-
showWorking?: boolean; // Exibe logs internos no console
|
|
1001
|
+
freeze?: boolean; // Congela o objeto (default = true)
|
|
1002
|
+
showWorking?: boolean; // Exibe logs internos no console (default = false)
|
|
990
1003
|
|
|
1004
|
+
// `active` serve para definir se o método vai existir depois do build (default = true)
|
|
1005
|
+
// `defaultSelect` serve para definir qual `selectModel` esse método vai usar por default (default = `defaultSelectModel` definido no `setupVSRepo`)
|
|
1006
|
+
// `ignoreRequiredWhere` serve para definir se o método vai ignorar o `requiredWhere` definido no `setupVSRepo` (default = false)
|
|
991
1007
|
baseMethods?: {
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1008
|
+
// Métodos que podem utilizar um `defaultSelect`
|
|
1009
|
+
get?: { active?: boolean; defaultSelect?: string; ignoreRequiredWhere?: boolean };
|
|
1010
|
+
getOrThrow?:{ active?: boolean; defaultSelect?: string; ignoreRequiredWhere?: boolean };
|
|
1011
|
+
remove?: { active?: boolean; defaultSelect?: string; ignoreRequiredWhere?: boolean };
|
|
1012
|
+
save?: { active?: boolean; defaultSelect?: string; ignoreRequiredWhere?: boolean };
|
|
1013
|
+
getAll?: { active?: boolean; defaultSelect?: string; ignoreRequiredWhere?: boolean };
|
|
1014
|
+
|
|
1015
|
+
// Métodos que NÃO aceitam `defaultSelect`
|
|
1016
|
+
removeList?: { active?: boolean; ignoreRequiredWhere?: boolean };
|
|
1017
|
+
total?: { active?: boolean; ignoreRequiredWhere?: boolean };
|
|
1018
|
+
has?: { active?: boolean; ignoreRequiredWhere?: boolean };
|
|
995
1019
|
};
|
|
996
1020
|
});
|
|
997
1021
|
```
|
|
@@ -1000,12 +1024,25 @@ vsRepo.build(prisma, {
|
|
|
1000
1024
|
|
|
1001
1025
|
```ts
|
|
1002
1026
|
repo.extend((repo) => ({
|
|
1003
|
-
meuMetodo() { ... }
|
|
1027
|
+
meuMetodo: () => { ... }
|
|
1004
1028
|
}));
|
|
1005
1029
|
```
|
|
1006
1030
|
|
|
1007
1031
|
---
|
|
1008
1032
|
|
|
1033
|
+
## Contribuindo
|
|
1034
|
+
|
|
1035
|
+
Contribuições são bem-vindas! Se você encontrou um bug, tem uma ideia de melhoria ou quer ajudar com a documentação, sinta-se à vontade para participar (**[Repositório do GitHub](https://github.com/jaobrabo123/VSRepository)**):
|
|
1036
|
+
|
|
1037
|
+
1. Faça um **Fork** do projeto.
|
|
1038
|
+
2. Crie uma nova branch com a sua alteração: `git checkout -b corrigindo-bug`.
|
|
1039
|
+
3. Faça o push para a sua branch: `git push origin corrigindo-bug`.
|
|
1040
|
+
4. Abra um **Pull Request**.
|
|
1041
|
+
|
|
1042
|
+
Para reportar problemas ou sugerir novas funcionalidades, abra uma **Issue**.
|
|
1043
|
+
|
|
1044
|
+
---
|
|
1045
|
+
|
|
1009
1046
|
## Requisitos
|
|
1010
1047
|
|
|
1011
1048
|
- Node.js 16+ (ESM)
|
|
@@ -1038,4 +1075,4 @@ repo.extend((repo) => ({
|
|
|
1038
1075
|
|
|
1039
1076
|
**`proxyTo` obrigatório** — Nomes fora dos moldes (ex.: `buscarPorEmail`) não são parseados diretamente. Use `proxyTo: "findByEmail"` nesses casos.
|
|
1040
1077
|
|
|
1041
|
-
**Select model retorna campos inesperados** — Verifique se o select model define exatamente os campos que o seu tipo TypeScript espera. Campos com `false` não serão retornados pelo Prisma.
|
|
1078
|
+
**Select model retorna campos inesperados** — Verifique se o select model define exatamente os campos que o seu tipo TypeScript espera. Campos com `false` não serão retornados pelo Prisma.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { VSRepoConfigError } from "./VSRepoError.js";
|
|
2
|
+
|
|
3
|
+
export class FieldValidator {
|
|
4
|
+
constructor(throws) {
|
|
5
|
+
this.throws = throws;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
validate(
|
|
9
|
+
field,
|
|
10
|
+
fieldName,
|
|
11
|
+
mustBe,
|
|
12
|
+
undefinedAble,
|
|
13
|
+
nullAble,
|
|
14
|
+
customReason
|
|
15
|
+
) {
|
|
16
|
+
if (field === undefined) {
|
|
17
|
+
if (undefinedAble) return;
|
|
18
|
+
throw this.instanceError(fieldName, customReason ?? "cannot be undefined");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (field === null) {
|
|
22
|
+
if (nullAble) return;
|
|
23
|
+
throw this.instanceError(fieldName, customReason ?? "cannot be null");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const condition = typeof mustBe === "function" ? !mustBe(field) : typeof field !== mustBe;
|
|
27
|
+
|
|
28
|
+
if(condition) {
|
|
29
|
+
throw this.instanceError(fieldName, customReason ?? `must be a valid ${mustBe}`);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
instanceError(fieldName, reason) {
|
|
34
|
+
const error = new this.throws("");
|
|
35
|
+
error.message = `[VSRepository] (${error.type}) '${fieldName}' ${reason}.`;
|
|
36
|
+
return error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function validateConfig(config) {
|
|
41
|
+
const validator = new FieldValidator(VSRepoConfigError);
|
|
42
|
+
|
|
43
|
+
validator.validate(config, "config", "object");
|
|
44
|
+
validator.validate(config.tableName, "tableName", "string");
|
|
45
|
+
validator.validate(config.pkName, "pkName", "string");
|
|
46
|
+
validator.validate(config.selectModels, "selectModels", "object", true);
|
|
47
|
+
|
|
48
|
+
if(config.defaultSelectModel !== undefined) {
|
|
49
|
+
validator.validate(config.defaultSelectModel, "defaultSelectModel", "string");
|
|
50
|
+
|
|
51
|
+
validator.validate(config.defaultSelectModel, "defaultSelectModel", (field) => {
|
|
52
|
+
return !!(config.selectModels && Object.keys(config.selectModels).includes(config.defaultSelectModel))
|
|
53
|
+
}, false, false, "must be a valid key of 'selectModels'");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if(config.relations !== undefined) {
|
|
57
|
+
validator.validate(config.relations, "relations", "object");
|
|
58
|
+
|
|
59
|
+
for (const key of Object.keys(config.relations)) {
|
|
60
|
+
const relation = config.relations[key];
|
|
61
|
+
|
|
62
|
+
validator.validate(relation.pk, "pk", "string", false, false, `of relation '${key}' must be a string`);
|
|
63
|
+
validator.validate(relation.mode, "mode", (field) => ['otm', 'mtm', 'mto', 'oto'].includes(field), false, false, `of relation '${key}' must be 'otm', 'mtm', 'mto' or 'oto'`);
|
|
64
|
+
validator.validate(relation.restriction, "restriction", (field) => ['set', 'add'].includes(field), false, false, `of relation '${key}' must be 'set' or 'add'`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
validator.validate(config.requiredWhere, "requiredWhere", "object", true);
|
|
69
|
+
|
|
70
|
+
if(config.methods !== undefined) {
|
|
71
|
+
validator.validate(config.methods, "methods", "object");
|
|
72
|
+
|
|
73
|
+
for (const [key, value] of Object.entries(config.methods)) {
|
|
74
|
+
validator.validate(value.map, "map", "boolean", false, false, `on ${key} must be a valid boolean`);
|
|
75
|
+
validator.validate(value.whereType, "whereType", (field) => ['extending', 'overwrite'].includes(field), true, false, `on ${key} must be 'extending' or 'overwrite'`);
|
|
76
|
+
validator.validate(value.selectModel, "selectModel", (field) => !!(field === false || (config.selectModels && Object.keys(config.selectModels).includes(field))), true, false, `on ${key} must be a valid key of 'selectModels'`);
|
|
77
|
+
validator.validate(value.proxyTo, "proxyTo", "string", true, false, `on ${key} must be a valid string`);
|
|
78
|
+
validator.validate(value.fbMode, "fbMode", (field) => ['one', 'list'].includes(field), true, false, `on ${key} must be 'one' or 'list'`);
|
|
79
|
+
validator.validate(value.pushWhere, "pushWhere", "object", true, false, `on ${key} must be a valid object`);
|
|
80
|
+
validator.validate(value.injectOrdenation, "injectOrdenation", "object", true, false, `on ${key} must be a valid object or array`);
|
|
81
|
+
validator.validate(value.injectPagination, "injectPagination", "object", true, false, `on ${key} must be a valid object`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -43,9 +43,12 @@ export type PaginationOptions<TCursor = unknown> = {
|
|
|
43
43
|
export type OrderOptions = OrderPattern | OrderPattern[];
|
|
44
44
|
|
|
45
45
|
type ValidMethodPatterns =
|
|
46
|
-
| `existsBy${string}` | `findBy${string}`
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
46
|
+
| `existsBy${string}` | `findBy${string}`
|
|
47
|
+
| `findUniqueBy${string}` | `findUniqueOrThrowBy${string}`
|
|
48
|
+
| `findFirstBy${string}` | `findFirst${string}`
|
|
49
|
+
| `findFirstOrThrowBy${string}` | `findFirstOrThrow${string}`
|
|
50
|
+
| `findManyBy${string}` | `findMany${string}`
|
|
51
|
+
| `createManyAndReturn${string}` | `createMany${string}`
|
|
49
52
|
| `create${string}` | `updateManyAndReturnBy${string}` | `updateManyBy${string}`
|
|
50
53
|
| `updateBy${string}` | `upsertBy${string}` | `deleteManyBy${string}`
|
|
51
54
|
| `deleteBy${string}` | `countBy${string}` | `count${string}`
|
|
@@ -55,9 +58,6 @@ type StringModifiers =
|
|
|
55
58
|
| 'NotStartsWith' | 'StartsWith' | 'NotEndsWith' | 'EndsWith'
|
|
56
59
|
| 'NotContains' | 'Contains' | 'NotIn' | 'In' | 'Not';
|
|
57
60
|
|
|
58
|
-
type NumberModifiers =
|
|
59
|
-
| 'LessThanEqual' | 'LessThan' | 'GreaterThanEqual' | 'GreaterThan';
|
|
60
|
-
|
|
61
61
|
type StripModifier<S extends string> =
|
|
62
62
|
S extends `${infer Base}${StringModifiers}Insensitive` ? Base :
|
|
63
63
|
S extends `${infer Base}NotStartsWith` ? Base :
|
|
@@ -149,7 +149,7 @@ type ResolveReturnType<M extends string, TSelected> =
|
|
|
149
149
|
M extends 'createMany' | 'updateManyBy' | 'deleteManyBy' ? { count: number } :
|
|
150
150
|
M extends 'findMany' | 'createManyAndReturn' | 'updateManyAndReturnBy' | 'findListWhere' | 'findByList' ? TSelected[] :
|
|
151
151
|
M extends 'findUnique' | 'findFirst' | 'findWhere' | 'findByOne' ? TSelected | null :
|
|
152
|
-
M extends 'create' | 'updateBy' | 'upsertBy' | 'deleteBy' ? TSelected :
|
|
152
|
+
M extends 'findUniqueOrThrow' | 'findFirstOrThrow' | 'create' | 'updateBy' | 'upsertBy' | 'deleteBy' ? TSelected :
|
|
153
153
|
M extends 'count' ? number : never;
|
|
154
154
|
|
|
155
155
|
type ExtraArgs<M extends string, R extends string, I> = [
|
|
@@ -218,7 +218,9 @@ type GetMappedMethod<K extends string, MethodConf> =
|
|
|
218
218
|
K extends `findBy${string}` ? (MethodConf extends { fbMode: 'one' } ? 'findByOne' : 'findByList') :
|
|
219
219
|
K extends `existsBy${string}` ? 'existsBy' :
|
|
220
220
|
K extends `findUniqueBy${string}` ? 'findUnique' :
|
|
221
|
+
K extends `findUniqueOrThrowBy${string}` ? 'findUniqueOrThrow' :
|
|
221
222
|
K extends `findFirstBy${string}` | `findFirst${string}` ? 'findFirst' :
|
|
223
|
+
K extends `findFirstOrThrowBy${string}` | `findFirstOrThrow${string}` ? 'findFirstOrThrow' :
|
|
222
224
|
K extends `findManyBy${string}` | `findMany${string}` ? 'findMany' :
|
|
223
225
|
K extends `createManyAndReturn${string}` ? 'createManyAndReturn' :
|
|
224
226
|
K extends `createMany${string}` ? 'createMany' :
|
|
@@ -236,7 +238,7 @@ type GetMappedMethod<K extends string, MethodConf> =
|
|
|
236
238
|
|
|
237
239
|
type ExtractPatternBase<K extends string> =
|
|
238
240
|
K extends `${string}By${infer R}` ? R :
|
|
239
|
-
K extends `findFirst${infer R}` | `findMany${infer R}` | `createManyAndReturn${infer R}` | `createMany${infer R}` | `create${infer R}` | `count${infer R}` | `findWhere${infer R}` | `findListWhere${infer R}` ? R : '';
|
|
241
|
+
K extends `findFirst${infer R}` | `findFirstOrThrow${infer R}` | `findMany${infer R}` | `createManyAndReturn${infer R}` | `createMany${infer R}` | `create${infer R}` | `count${infer R}` | `findWhere${infer R}` | `findListWhere${infer R}` ? R : '';
|
|
240
242
|
|
|
241
243
|
type MethodFactory<T, M extends Prisma.ModelName, K extends string, SelectModels, DefaultSelect extends keyof SelectModels | false, I, MethodConf> =
|
|
242
244
|
MethodFn<GetMappedMethod<K, MethodConf>, T, M, ExtractPatternBase<K>, SelectModels, DefaultSelect, I>;
|
|
@@ -346,7 +348,7 @@ type BaseMethodConfig<TSelectKeys extends PropertyKey = string> = {
|
|
|
346
348
|
active?: boolean;
|
|
347
349
|
/** Select model padrão usado pelo método base. */
|
|
348
350
|
defaultSelect?: TSelectKeys;
|
|
349
|
-
/** Ignora o `requiredWhere
|
|
351
|
+
/** Ignora o `requiredWhere`. */
|
|
350
352
|
ignoreRequiredWhere?: boolean;
|
|
351
353
|
};
|
|
352
354
|
|
|
@@ -364,14 +366,24 @@ export type BuildConfig<TSelectKeys extends PropertyKey = string> = {
|
|
|
364
366
|
baseMethods?: {
|
|
365
367
|
/** Configuração do método `get`. */
|
|
366
368
|
get?: BaseMethodConfig<TSelectKeys>;
|
|
369
|
+
/** Configuração do método `getOrThrow`. */
|
|
370
|
+
getOrThrow?: BaseMethodConfig<TSelectKeys>;
|
|
367
371
|
/** Configuração do método `remove`. */
|
|
368
372
|
remove?: BaseMethodConfig<TSelectKeys>;
|
|
369
373
|
/** Configuração do método `save`. */
|
|
370
374
|
save?: BaseMethodConfig<TSelectKeys>;
|
|
375
|
+
/** Configuração do método `removeList` (exclusão em lote). Não aceita select. */
|
|
376
|
+
removeList?: Omit<BaseMethodConfig<TSelectKeys>, 'defaultSelect'>;
|
|
377
|
+
/** Configuração do método `getAll` (listagem total). */
|
|
378
|
+
getAll?: BaseMethodConfig<TSelectKeys>;
|
|
379
|
+
/** Configuração do método `total` (contagem). Não aceita select. */
|
|
380
|
+
total?: Omit<BaseMethodConfig<TSelectKeys>, 'defaultSelect'>;
|
|
381
|
+
/** Configuração do método `has` (verificação de existência). Não aceita select. */
|
|
382
|
+
has?: Omit<BaseMethodConfig<TSelectKeys>, 'defaultSelect'>;
|
|
371
383
|
};
|
|
372
384
|
};
|
|
373
385
|
|
|
374
|
-
type ResolveMethodDefaultSelect<Config, C, Method extends 'get' | 'remove' | 'save', TSelects = ExtractSelectModels<Config>> =
|
|
386
|
+
type ResolveMethodDefaultSelect<Config, C, Method extends 'get' | 'getOrThrow' | 'remove' | 'save' | 'getAll', TSelects = ExtractSelectModels<Config>> =
|
|
375
387
|
C extends { baseMethods?: { [_ in Method]?: { defaultSelect?: infer D } } }
|
|
376
388
|
? D extends keyof TSelects
|
|
377
389
|
? D
|
|
@@ -406,6 +418,17 @@ type InjectedGet<
|
|
|
406
418
|
): Promise<ResolveCurrentReturn<M, TSelects, S, TDefault> | null>;
|
|
407
419
|
};
|
|
408
420
|
|
|
421
|
+
type InjectedGetOrThrow<
|
|
422
|
+
T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined,
|
|
423
|
+
TSelects = ExtractSelectModels<Config>,
|
|
424
|
+
TDefault = ExtractDefaultSelect<Config>,
|
|
425
|
+
TPk = ExtractPkName<T, Config>
|
|
426
|
+
> = C extends { baseMethods: { getOrThrow: { active: false } } } ? {} : {
|
|
427
|
+
getOrThrow<S extends keyof TSelects | false = ResolveMethodDefaultSelect<Config, C, 'getOrThrow', TSelects>>(
|
|
428
|
+
pk: T[TPk extends keyof T ? TPk : never], options?: MethodOptions<S>
|
|
429
|
+
): Promise<ResolveCurrentReturn<M, TSelects, S, TDefault>>;
|
|
430
|
+
};
|
|
431
|
+
|
|
409
432
|
type InjectedRemove<
|
|
410
433
|
T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined,
|
|
411
434
|
TSelects = ExtractSelectModels<Config>,
|
|
@@ -460,10 +483,59 @@ type InjectedSave<
|
|
|
460
483
|
): Promise<RefineSaveResult<ResolveCurrentReturn<M, TSelects, S, TDefault>, O>>;
|
|
461
484
|
};
|
|
462
485
|
|
|
486
|
+
type InjectedRemoveList<
|
|
487
|
+
T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined,
|
|
488
|
+
TPk = ExtractPkName<T, Config>
|
|
489
|
+
> = C extends { baseMethods: { removeList: { active: false } } } ? {} : {
|
|
490
|
+
/** Remove múltiplos registros de uma vez pelas suas chaves primárias. */
|
|
491
|
+
removeList(
|
|
492
|
+
pks: T[TPk extends keyof T ? TPk : never][],
|
|
493
|
+
options?: { db?: ClientOrTransaction }
|
|
494
|
+
): Promise<{ count: number }>;
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
type InjectedGetAll<
|
|
498
|
+
T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined,
|
|
499
|
+
TSelects = ExtractSelectModels<Config>,
|
|
500
|
+
TDefault = ExtractDefaultSelect<Config>,
|
|
501
|
+
I = PrismaModelInputs<M>
|
|
502
|
+
> = C extends { baseMethods: { getAll: { active: false } } } ? {} : {
|
|
503
|
+
/** Busca todos os registros, respeitando o `requiredWhere` se houver. */
|
|
504
|
+
getAll<S extends keyof TSelects | false = ResolveMethodDefaultSelect<Config, C, 'getAll', TSelects>>(
|
|
505
|
+
options?: MethodOptions<S> & {
|
|
506
|
+
pagination?: PaginationOptions<I extends { cursorInput: infer Curs } ? Curs : unknown>;
|
|
507
|
+
order?: I extends { orderByInput: infer OB } ? OB : OrderOptions;
|
|
508
|
+
}
|
|
509
|
+
): Promise<ResolveCurrentReturn<M, TSelects, S, TDefault>[]>;
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
type InjectedTotal<
|
|
513
|
+
T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined
|
|
514
|
+
> = C extends { baseMethods: { total: { active: false } } } ? {} : {
|
|
515
|
+
/** Retorna a quantidade total de registros, respeitando o `requiredWhere` se houver. */
|
|
516
|
+
total(options?: { db?: ClientOrTransaction }): Promise<number>;
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
type InjectedHas<
|
|
520
|
+
T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined,
|
|
521
|
+
TPk = ExtractPkName<T, Config>
|
|
522
|
+
> = C extends { baseMethods: { has: { active: false } } } ? {} : {
|
|
523
|
+
/** Verifica de forma otimizada se um registro existe pela chave primária. */
|
|
524
|
+
has(
|
|
525
|
+
pk: T[TPk extends keyof T ? TPk : never],
|
|
526
|
+
options?: { db?: ClientOrTransaction }
|
|
527
|
+
): Promise<boolean>;
|
|
528
|
+
};
|
|
529
|
+
|
|
463
530
|
type InjectedBaseMethods<T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined> =
|
|
464
531
|
InjectedGet<T, M, Config, C> &
|
|
532
|
+
InjectedGetOrThrow<T, M, Config, C> &
|
|
465
533
|
InjectedRemove<T, M, Config, C> &
|
|
466
|
-
InjectedSave<T, M, Config, C
|
|
534
|
+
InjectedSave<T, M, Config, C> &
|
|
535
|
+
InjectedRemoveList<T, M, Config, C> &
|
|
536
|
+
InjectedGetAll<T, M, Config, C> &
|
|
537
|
+
InjectedTotal<T, M, Config, C> &
|
|
538
|
+
InjectedHas<T, M, Config, C>;
|
|
467
539
|
|
|
468
540
|
/**
|
|
469
541
|
* Infere automaticamente a configuração de relação possível a partir de um campo.
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
export * from "./VSRepoError.js";
|
|
9
9
|
|
|
10
10
|
// * Importando classes de erro
|
|
11
|
-
import {
|
|
11
|
+
import { VSRepoBuildError, VSRepoExtendError, VSRepoRuntimeError } from "./VSRepoError.js";
|
|
12
|
+
import { validateConfig } from "./VSRepoUtils.js";
|
|
12
13
|
|
|
13
14
|
// * Essa é a classe pincipal, é a partir dela que serão criados os objetos dos repositories
|
|
14
15
|
export class VSRepository {
|
|
@@ -28,92 +29,7 @@ export class VSRepository {
|
|
|
28
29
|
methods;
|
|
29
30
|
|
|
30
31
|
constructor(config) {
|
|
31
|
-
|
|
32
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'config' must be a valid object.`);
|
|
33
|
-
}
|
|
34
|
-
if(typeof config.tableName !== 'string') {
|
|
35
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'tableName' must be a valid string.`);
|
|
36
|
-
}
|
|
37
|
-
if(typeof config.pkName !== 'string') {
|
|
38
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'pkName' must be a valid string.`);
|
|
39
|
-
}
|
|
40
|
-
if(config.selectModels !== undefined && typeof config.selectModels !== 'object' || config.selectModels === null) {
|
|
41
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'selectModels' must be a valid object.`);
|
|
42
|
-
}
|
|
43
|
-
if(config.defaultSelectModel !== undefined) {
|
|
44
|
-
if(!config.selectModels) {
|
|
45
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'defaultSelectModel' can only be passed with 'selectModels'.`);
|
|
46
|
-
}
|
|
47
|
-
if(typeof config.defaultSelectModel !== 'string') {
|
|
48
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'defaultSelectModel' must be a valid string.`);
|
|
49
|
-
}
|
|
50
|
-
if(!Object.keys(config.selectModels).includes(config.defaultSelectModel)){
|
|
51
|
-
throw new VSRepoConfigError(`[VSRepository] (config) Invalid 'defaultSelectModel': ${config.defaultSelectModel}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if(config.relations !== undefined) {
|
|
55
|
-
const relations = config.relations;
|
|
56
|
-
if(typeof relations !== 'object' || relations === null){
|
|
57
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'relations' must be a valid object.`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
for (const key of Object.keys(relations)) {
|
|
61
|
-
const relation = relations[key];
|
|
62
|
-
if(typeof relation.pk !== 'string'){
|
|
63
|
-
throw new VSRepoConfigError(`[VSRepository] (config) The property 'pk' of relation '${key}' must be a string.`);
|
|
64
|
-
}
|
|
65
|
-
if(typeof relation.mode !== 'string'){
|
|
66
|
-
throw new VSRepoConfigError(`[VSRepository] (config) The property 'mode' of relation '${key}' must be 'otm', 'mtm', 'mto' or 'oto'.`);
|
|
67
|
-
}
|
|
68
|
-
if(typeof relation.restriction !== 'string'){
|
|
69
|
-
throw new VSRepoConfigError(`[VSRepository] (config) The property 'restriction' of relation '${key}' must be 'set' or 'add'.`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if(config.requiredWhere !== undefined && typeof config.requiredWhere !== 'object' || config.requiredWhere === null) {
|
|
74
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'requiredWhere' must be a valid object.`);
|
|
75
|
-
}
|
|
76
|
-
if(config.methods !== undefined) {
|
|
77
|
-
if(typeof config.methods !== 'object' || config.methods === null){
|
|
78
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'methods' must be a valid object.`);
|
|
79
|
-
} else {
|
|
80
|
-
for (const [key, value] of Object.entries(config.methods)) {
|
|
81
|
-
|
|
82
|
-
if(typeof value.map !== "boolean") {
|
|
83
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'map' on ${key} must be a valid boolean.`);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if(value.whereType !== undefined && !['extending', 'overwrite'].includes(value.whereType)){
|
|
87
|
-
throw new VSRepoConfigError(`[VSRepository] (config) Invalid 'whereType' on ${key}: ${value.whereType}`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if(value.selectModel !== undefined && value.selectModel !== false && (!config.selectModels || !Object.keys(config.selectModels).includes(value.selectModel))){
|
|
91
|
-
throw new VSRepoConfigError(`[VSRepository] (config) Invalid 'selectModel' on ${key}: ${value.selectModel}`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if(value.proxyTo !== undefined && typeof value.proxyTo !== 'string') {
|
|
95
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'proxyTo' on ${key} must be a valid string.`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if(value.fbMode !== undefined && !['one', 'list'].includes(value.fbMode)) {
|
|
99
|
-
throw new VSRepoConfigError(`[VSRepository] (config) Invalid 'fbMode' on ${key}: ${value.whereType}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if(value.pushWhere !== undefined && (typeof value.pushWhere !== 'object' || value.pushWhere === null)) {
|
|
103
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'pushWhere' on ${key} must be a valid object.`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if(value.injectOrdenation !== undefined && (typeof value.injectOrdenation !== 'object' || value.injectOrdenation === null)) {
|
|
107
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'injectOrdenation' on ${key} must be a valid object or array.`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if(value.injectPagination !== undefined && (typeof value.injectPagination !== 'object' || value.injectPagination === null)) {
|
|
111
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'injectPagination' on ${key} must be a valid object.`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
32
|
+
validateConfig(config);
|
|
117
33
|
|
|
118
34
|
this.vsrepocache = new Map();
|
|
119
35
|
this.tableName = config.tableName;
|
|
@@ -185,9 +101,22 @@ export class VSRepository {
|
|
|
185
101
|
const whereParams = [];
|
|
186
102
|
const otherParams = [];
|
|
187
103
|
|
|
188
|
-
if(keyToMap.startsWith('
|
|
104
|
+
if(keyToMap.startsWith('findUniqueOrThrowBy')) {
|
|
105
|
+
keyToMapReplaced = keyToMap.replace('findUniqueOrThrowBy', '');
|
|
106
|
+
method = 'findUniqueOrThrow';
|
|
107
|
+
} else if(keyToMap.startsWith('findUniqueBy')) {
|
|
189
108
|
keyToMapReplaced = keyToMap.replace('findUniqueBy', '');
|
|
190
109
|
method = 'findUnique';
|
|
110
|
+
} else if(keyToMap.startsWith('findFirstOrThrowBy')) {
|
|
111
|
+
keyToMapReplaced = keyToMap.replace('findFirstOrThrowBy', '')
|
|
112
|
+
ignoreOrderByAndPagination = false;
|
|
113
|
+
method = 'findFirstOrThrow';
|
|
114
|
+
} else if(keyToMap.startsWith('findFirstOrThrow')) {
|
|
115
|
+
keyToMapReplaced = keyToMap.replace('findFirstOrThrow', '');
|
|
116
|
+
ignoreOrderByAndPagination = false;
|
|
117
|
+
ignoreWhere = true;
|
|
118
|
+
onlyBaseWheres = true;
|
|
119
|
+
method = 'findFirstOrThrow';
|
|
191
120
|
} else if(keyToMap.startsWith('findFirstBy')) {
|
|
192
121
|
keyToMapReplaced = keyToMap.replace('findFirstBy', '')
|
|
193
122
|
ignoreOrderByAndPagination = false;
|
|
@@ -811,10 +740,7 @@ export class VSRepository {
|
|
|
811
740
|
|
|
812
741
|
if(config.baseMethods?.get?.active !== false) {
|
|
813
742
|
buildInstance.get = async(pk, options = {}) => {
|
|
814
|
-
let db;
|
|
815
|
-
if(options?.db) db = options.db;
|
|
816
|
-
else db = prisma;
|
|
817
|
-
|
|
743
|
+
let db = options?.db ?? prisma;
|
|
818
744
|
const prismaArgs = {};
|
|
819
745
|
|
|
820
746
|
const where = { [buildInstance.pkName]: pk };
|
|
@@ -851,12 +777,48 @@ export class VSRepository {
|
|
|
851
777
|
}
|
|
852
778
|
}
|
|
853
779
|
}
|
|
780
|
+
if(config.baseMethods?.getOrThrow?.active !== false) {
|
|
781
|
+
buildInstance.getOrThrow = async(pk, options = {}) => {
|
|
782
|
+
let db = options?.db ?? prisma;
|
|
783
|
+
const prismaArgs = {};
|
|
784
|
+
|
|
785
|
+
const where = { [buildInstance.pkName]: pk };
|
|
786
|
+
if(buildInstance.requiredWhere && !config.baseMethods?.getOrThrow?.ignoreRequiredWhere) {
|
|
787
|
+
Object.assign(where, buildInstance.requiredWhere);
|
|
788
|
+
}
|
|
789
|
+
prismaArgs.where = where;
|
|
790
|
+
|
|
791
|
+
let selectModelKey = options?.selectModel ?? config.baseMethods?.getOrThrow?.defaultSelect ?? buildInstance.defaultSelectModel;
|
|
792
|
+
if(selectModelKey){
|
|
793
|
+
prismaArgs.select = buildInstance.selectModels[selectModelKey];
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
let start;
|
|
797
|
+
if(showWorking){
|
|
798
|
+
console.log(`[VSRepository] (runtime) Executing getOrThrow on ${buildInstance.tableName}.`);
|
|
799
|
+
console.log(`[VSRepository] (runtime) Built arguments to getOrThrow on ${buildInstance.tableName}:\n`, JSON.stringify(prismaArgs, null, 2));
|
|
800
|
+
start = performance.now();
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
try {
|
|
804
|
+
const result = await db[buildInstance.tableName].findUniqueOrThrow(prismaArgs);
|
|
805
|
+
|
|
806
|
+
if(showWorking) {
|
|
807
|
+
const end = performance.now();
|
|
808
|
+
const duration = (end - start).toFixed(2);
|
|
809
|
+
console.log(`[VSRepository] (runtime) Executed getOrThrow on ${buildInstance.tableName} (took: ${duration}ms).`);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
return result;
|
|
813
|
+
} catch (err) {
|
|
814
|
+
console.log(`[VSRepository] (runtime) Fatal error when trying to getOrThrow on ${buildInstance.tableName}:\n`, JSON.stringify({ prismaArgs }, null, 2));
|
|
815
|
+
throw err;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
854
819
|
if(config.baseMethods?.remove?.active !== false) {
|
|
855
820
|
buildInstance.remove = async (pk, options = {}) => {
|
|
856
|
-
let db;
|
|
857
|
-
if(options?.db) db = options.db;
|
|
858
|
-
else db = prisma;
|
|
859
|
-
|
|
821
|
+
let db = options?.db ?? prisma;
|
|
860
822
|
const prismaArgs = {};
|
|
861
823
|
|
|
862
824
|
const where = { [buildInstance.pkName]: pk };
|
|
@@ -894,10 +856,7 @@ export class VSRepository {
|
|
|
894
856
|
}
|
|
895
857
|
if(config.baseMethods?.save?.active !== false) {
|
|
896
858
|
buildInstance.save = async (obj, options = {}) => {
|
|
897
|
-
let db;
|
|
898
|
-
if(options?.db) db = options.db;
|
|
899
|
-
else db = prisma;
|
|
900
|
-
|
|
859
|
+
let db = options?.db ?? prisma;
|
|
901
860
|
const prismaArgs = {};
|
|
902
861
|
|
|
903
862
|
let selectModelKey = options?.selectModel ?? config.baseMethods?.save?.defaultSelect ?? buildInstance.defaultSelectModel;
|
|
@@ -1106,6 +1065,137 @@ export class VSRepository {
|
|
|
1106
1065
|
throw err;
|
|
1107
1066
|
}
|
|
1108
1067
|
}
|
|
1068
|
+
if(config.baseMethods?.removeList?.active !== false) {
|
|
1069
|
+
buildInstance.removeList = async (pks, options = {}) => {
|
|
1070
|
+
let db = options?.db ?? prisma;
|
|
1071
|
+
const prismaArgs = {};
|
|
1072
|
+
|
|
1073
|
+
const where = { [buildInstance.pkName]: { in: pks } };
|
|
1074
|
+
if(buildInstance.requiredWhere && !config.baseMethods?.removeList?.ignoreRequiredWhere) {
|
|
1075
|
+
Object.assign(where, buildInstance.requiredWhere);
|
|
1076
|
+
}
|
|
1077
|
+
prismaArgs.where = where;
|
|
1078
|
+
|
|
1079
|
+
let start;
|
|
1080
|
+
if(showWorking){
|
|
1081
|
+
console.log(`[VSRepository] (runtime) Executing removeList on ${buildInstance.tableName}.`);
|
|
1082
|
+
console.log(`[VSRepository] (runtime) Built arguments to removeList on ${buildInstance.tableName}:\n`, JSON.stringify(prismaArgs, null, 2));
|
|
1083
|
+
start = performance.now();
|
|
1084
|
+
}
|
|
1085
|
+
try {
|
|
1086
|
+
const result = await db[buildInstance.tableName].deleteMany(prismaArgs);
|
|
1087
|
+
if(showWorking) {
|
|
1088
|
+
const end = performance.now();
|
|
1089
|
+
console.log(`[VSRepository] (runtime) Executed removeList on ${buildInstance.tableName} (took: ${(end - start).toFixed(2)}ms).`);
|
|
1090
|
+
}
|
|
1091
|
+
return result;
|
|
1092
|
+
} catch (err) {
|
|
1093
|
+
console.log(`[VSRepository] (runtime) Fatal error when trying to removeList on ${buildInstance.tableName}:\n`, JSON.stringify({ prismaArgs }, null, 2));
|
|
1094
|
+
throw err;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
if(config.baseMethods?.getAll?.active !== false) {
|
|
1099
|
+
buildInstance.getAll = async (options = {}) => {
|
|
1100
|
+
let db = options?.db ?? prisma;
|
|
1101
|
+
const prismaArgs = {};
|
|
1102
|
+
|
|
1103
|
+
if(buildInstance.requiredWhere && !config.baseMethods?.getAll?.ignoreRequiredWhere) {
|
|
1104
|
+
prismaArgs.where = { ...buildInstance.requiredWhere };
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
let selectModelKey = options?.selectModel ?? config.baseMethods?.getAll?.defaultSelect ?? buildInstance.defaultSelectModel;
|
|
1108
|
+
if(selectModelKey){
|
|
1109
|
+
prismaArgs.select = buildInstance.selectModels[selectModelKey];
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
if(options.pagination) {
|
|
1113
|
+
prismaArgs.skip = options.pagination.skip;
|
|
1114
|
+
prismaArgs.take = options.pagination.take;
|
|
1115
|
+
prismaArgs.cursor = options.pagination.cursor;
|
|
1116
|
+
}
|
|
1117
|
+
if(options.order) {
|
|
1118
|
+
prismaArgs.orderBy = options.order;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
let start;
|
|
1122
|
+
if(showWorking){
|
|
1123
|
+
console.log(`[VSRepository] (runtime) Executing getAll on ${buildInstance.tableName}.`);
|
|
1124
|
+
console.log(`[VSRepository] (runtime) Built arguments to getAll on ${buildInstance.tableName}:\n`, JSON.stringify(prismaArgs, null, 2));
|
|
1125
|
+
start = performance.now();
|
|
1126
|
+
}
|
|
1127
|
+
try {
|
|
1128
|
+
const result = await db[buildInstance.tableName].findMany(prismaArgs);
|
|
1129
|
+
if(showWorking) {
|
|
1130
|
+
const end = performance.now();
|
|
1131
|
+
console.log(`[VSRepository] (runtime) Executed getAll on ${buildInstance.tableName} (took: ${(end - start).toFixed(2)}ms).`);
|
|
1132
|
+
}
|
|
1133
|
+
return result;
|
|
1134
|
+
} catch (err) {
|
|
1135
|
+
console.log(`[VSRepository] (runtime) Fatal error when trying to getAll on ${buildInstance.tableName}:\n`, JSON.stringify({ prismaArgs }, null, 2));
|
|
1136
|
+
throw err;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
if(config.baseMethods?.total?.active !== false) {
|
|
1141
|
+
buildInstance.total = async (options = {}) => {
|
|
1142
|
+
let db = options?.db ?? prisma;
|
|
1143
|
+
const prismaArgs = {};
|
|
1144
|
+
|
|
1145
|
+
if(buildInstance.requiredWhere && !config.baseMethods?.total?.ignoreRequiredWhere) {
|
|
1146
|
+
prismaArgs.where = { ...buildInstance.requiredWhere };
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
let start;
|
|
1150
|
+
if(showWorking){
|
|
1151
|
+
console.log(`[VSRepository] (runtime) Executing total on ${buildInstance.tableName}.`);
|
|
1152
|
+
console.log(`[VSRepository] (runtime) Built arguments to total on ${buildInstance.tableName}:\n`, JSON.stringify(prismaArgs, null, 2));
|
|
1153
|
+
start = performance.now();
|
|
1154
|
+
}
|
|
1155
|
+
try {
|
|
1156
|
+
const result = await db[buildInstance.tableName].count(prismaArgs);
|
|
1157
|
+
if(showWorking) {
|
|
1158
|
+
const end = performance.now();
|
|
1159
|
+
console.log(`[VSRepository] (runtime) Executed total on ${buildInstance.tableName} (took: ${(end - start).toFixed(2)}ms).`);
|
|
1160
|
+
}
|
|
1161
|
+
return result;
|
|
1162
|
+
} catch (err) {
|
|
1163
|
+
console.log(`[VSRepository] (runtime) Fatal error when trying to total on ${buildInstance.tableName}:\n`, JSON.stringify({ prismaArgs }, null, 2));
|
|
1164
|
+
throw err;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
if(config.baseMethods?.has?.active !== false) {
|
|
1169
|
+
buildInstance.has = async (pk, options = {}) => {
|
|
1170
|
+
let db = options?.db ?? prisma;
|
|
1171
|
+
const prismaArgs = {};
|
|
1172
|
+
|
|
1173
|
+
const where = { [buildInstance.pkName]: pk };
|
|
1174
|
+
if(buildInstance.requiredWhere && !config.baseMethods?.has?.ignoreRequiredWhere) {
|
|
1175
|
+
Object.assign(where, buildInstance.requiredWhere);
|
|
1176
|
+
}
|
|
1177
|
+
prismaArgs.where = where;
|
|
1178
|
+
prismaArgs.select = { [buildInstance.pkName]: true };
|
|
1179
|
+
|
|
1180
|
+
let start;
|
|
1181
|
+
if(showWorking){
|
|
1182
|
+
console.log(`[VSRepository] (runtime) Executing has on ${buildInstance.tableName}.`);
|
|
1183
|
+
console.log(`[VSRepository] (runtime) Built arguments to has on ${buildInstance.tableName}:\n`, JSON.stringify(prismaArgs, null, 2));
|
|
1184
|
+
start = performance.now();
|
|
1185
|
+
}
|
|
1186
|
+
try {
|
|
1187
|
+
const result = await db[buildInstance.tableName].findUnique(prismaArgs);
|
|
1188
|
+
if(showWorking) {
|
|
1189
|
+
const end = performance.now();
|
|
1190
|
+
console.log(`[VSRepository] (runtime) Executed has on ${buildInstance.tableName} (took: ${(end - start).toFixed(2)}ms).`);
|
|
1191
|
+
}
|
|
1192
|
+
return !!result;
|
|
1193
|
+
} catch (err) {
|
|
1194
|
+
console.log(`[VSRepository] (runtime) Fatal error when trying to has on ${buildInstance.tableName}:\n`, JSON.stringify({ prismaArgs }, null, 2));
|
|
1195
|
+
throw err;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1109
1199
|
}
|
|
1110
1200
|
|
|
1111
1201
|
if(config.freeze) Object.freeze(buildInstance)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vsrepo",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Uma biblioteca de repository pattern para Prisma",
|
|
5
5
|
"homepage": "https://github.com/jaobrabo123/VSRepository#readme",
|
|
6
6
|
"repository": {
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"@types/node": "^25.6.0",
|
|
62
62
|
"dotenv": "^17.4.2",
|
|
63
63
|
"prisma": "^7.8.0",
|
|
64
|
+
"tsx": "^4.22.3",
|
|
64
65
|
"typescript": "^6.0.3"
|
|
65
66
|
}
|
|
66
67
|
}
|
|
@@ -10,6 +10,15 @@ const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
|
10
10
|
|
|
11
11
|
const DEFAULT_OUTPUT = 'src/generated/vsrepo';
|
|
12
12
|
const DEFAULT_PRISMA = 'src/generated/prisma';
|
|
13
|
+
const GENERATED_HEADER = `/**
|
|
14
|
+
* ! THIS FILE IS AUTO-GENERATED.
|
|
15
|
+
* ! DO NOT EDIT MANUALLY.
|
|
16
|
+
*/
|
|
17
|
+
/* eslint-disable */
|
|
18
|
+
/* biome-ignore-all lint: generated file */
|
|
19
|
+
// @ts-nocheck
|
|
20
|
+
|
|
21
|
+
`;
|
|
13
22
|
|
|
14
23
|
const command = args[0]?.startsWith('-') ? 'generate' : args[0] ?? 'generate';
|
|
15
24
|
const commandArgs = command === 'generate' && args[0] !== command ? args : args.slice(1);
|
|
@@ -91,7 +100,7 @@ for (const fileName of typeFiles) {
|
|
|
91
100
|
`from '${relativeImportPath(path.dirname(targetFile), prismaTargetPath)}'`
|
|
92
101
|
);
|
|
93
102
|
|
|
94
|
-
fs.writeFileSync(targetFile, contents, 'utf8');
|
|
103
|
+
fs.writeFileSync(targetFile, GENERATED_HEADER + contents, 'utf8');
|
|
95
104
|
|
|
96
105
|
console.log(`Gerado: ${path.relative(workspaceRoot, targetFile)}`);
|
|
97
106
|
}
|
|
@@ -166,7 +175,7 @@ export type * from './VSRepository.types';
|
|
|
166
175
|
for (const file of tsFiles) {
|
|
167
176
|
const targetFile = path.join(outputDir, file.fileName);
|
|
168
177
|
|
|
169
|
-
fs.writeFileSync(targetFile, file.content, 'utf8');
|
|
178
|
+
fs.writeFileSync(targetFile, GENERATED_HEADER + file.content, 'utf8');
|
|
170
179
|
|
|
171
180
|
console.log(`Gerado: ${path.relative(workspaceRoot, targetFile)}`);
|
|
172
181
|
}
|