vsrepo 1.2.9 → 1.3.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/README.md +287 -36
- package/dist/VSRepoError.d.ts +1 -1
- package/dist/VSRepository.d.ts +135 -80
- package/dist/VSRepository.js +3 -1
- package/dist/internal/resolvers/base-methods.resolve.js +18 -18
- package/dist/internal/resolvers/dbAndPrismaArgs.resolve.js +10 -4
- package/dist/internal/validation/constructor-config.validate.js +1 -0
- package/dist/internal/validation/method-options.validate.js +10 -2
- package/dist/internal/validation/obj-with-relations.validate.js +5 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# VSRepository
|
|
2
2
|
|
|
3
3
|

|
|
4
|
-

|
|
4
|
+

|
|
5
5
|

|
|
6
6
|
|
|
7
7
|
Biblioteca de repository pattern para projetos que usam **Prisma**, com suporte completo a **TypeScript** e **type inference** automático.
|
|
@@ -10,12 +10,14 @@ O VSRepository permite criar repositories fortemente tipados com:
|
|
|
10
10
|
|
|
11
11
|
- **Métodos base** automáticos: `get`, `getOrThrow`, `getList`, `save`, `saveList`, `remove`, `removeList`, `patch`, `patchList`, `merge`, `getAll`, `total`, `has`
|
|
12
12
|
- **Soft-delete nativo**: `softRemove`, `softRemoveList`, `restore`, `restoreList`
|
|
13
|
-
- **Métodos dinâmicos** inferidos pelo nome: `
|
|
13
|
+
- **Métodos dinâmicos** inferidos pelo nome: `findOneByEmail`, `findManyPaginated`, `updateById`, `deleteManyByNameStartsWith`
|
|
14
14
|
- **Select models** reutilizáveis para diferentes projeções de dados
|
|
15
15
|
- **Type safety** em 100% das operações
|
|
16
16
|
- **Transações** nativas do Prisma (automáticas em `saveList` e `patchList`)
|
|
17
17
|
- **Extensibilidade** com métodos personalizados
|
|
18
18
|
|
|
19
|
+
> 💡 Quer ver tudo isso funcionando na prática? A pasta [`examples/`](https://github.com/jaobrabo123/VSRepository/tree/main/examples) do repositório tem exemplos comentados e executáveis para cada funcionalidade — veja a seção [Exemplos práticos](#exemplos-práticos) abaixo.
|
|
20
|
+
|
|
19
21
|
---
|
|
20
22
|
|
|
21
23
|
## Sumário
|
|
@@ -30,6 +32,7 @@ O VSRepository permite criar repositories fortemente tipados com:
|
|
|
30
32
|
- [Merge](#merge)
|
|
31
33
|
- [Configurando os métodos base](#configurando-os-métodos-base)
|
|
32
34
|
- [Select Models](#select-models)
|
|
35
|
+
- [Include Models](#include-models)
|
|
33
36
|
- [Required Where](#requiredwhere)
|
|
34
37
|
- [Default Ordenation](#default-ordenation)
|
|
35
38
|
- [Opção `see`](#opção-see)
|
|
@@ -47,6 +50,7 @@ O VSRepository permite criar repositories fortemente tipados com:
|
|
|
47
50
|
- [Tratamento de erros](#tratamento-de-erros)
|
|
48
51
|
- [Tipos utilitários](#tipos-utilitários)
|
|
49
52
|
- [API Reference](#api-reference)
|
|
53
|
+
- [Exemplos práticos](#exemplos-práticos)
|
|
50
54
|
- [Contribuindo](#contribuindo)
|
|
51
55
|
- [Requisitos](#requisitos)
|
|
52
56
|
- [Troubleshooting](#troubleshooting)
|
|
@@ -137,21 +141,18 @@ import prisma from "../configs/db";
|
|
|
137
141
|
import { setupVSRepo } from "../../generated/vsrepo";
|
|
138
142
|
import type { Usuario } from "../../generated/prisma/client";
|
|
139
143
|
|
|
140
|
-
const usuarioRepository = setupVSRepo<Usuario, "
|
|
144
|
+
const usuarioRepository = setupVSRepo<Usuario, "Usuario">()(({
|
|
141
145
|
tableName: "usuario",
|
|
142
146
|
pkName: "id",
|
|
143
147
|
selectModels: {
|
|
144
148
|
public: { id: true, nome: true, email: true },
|
|
145
149
|
},
|
|
146
150
|
defaultSelectModel: "public",
|
|
147
|
-
requiredWhere: { ativo: true },
|
|
148
151
|
}).build(prisma);
|
|
149
152
|
|
|
150
153
|
export default usuarioRepository;
|
|
151
154
|
```
|
|
152
155
|
|
|
153
|
-
> `selectModels` e `requiredWhere` podem ser declarados fora do `setupVSRepo` se você precisar exportá-los para uso em outros arquivos.
|
|
154
|
-
|
|
155
156
|
### Usando o repository
|
|
156
157
|
|
|
157
158
|
```ts
|
|
@@ -230,14 +231,18 @@ const userVSRepo = setupVSRepo<
|
|
|
230
231
|
});
|
|
231
232
|
|
|
232
233
|
const setupUserRepository = (prisma: PrismaService) => {
|
|
233
|
-
return userVSRepo.build(prisma)
|
|
234
|
-
buscarPorDominio: async (dominio: string) => {
|
|
235
|
-
return repo.findByEmailEndsWith(`@${dominio}`);
|
|
236
|
-
},
|
|
237
|
-
}));
|
|
234
|
+
return userVSRepo.build(prisma);
|
|
238
235
|
};
|
|
239
236
|
|
|
240
237
|
export type UserRepository = ReturnType<typeof setupUserRepository>;
|
|
238
|
+
/*
|
|
239
|
+
A tipagem também pode ser inferida usando o `RepositoryOf` do VSRepository, passando o tipo do `userVSRepo`:
|
|
240
|
+
|
|
241
|
+
export type UserRepository = RepositoryOf<typeof userVSRepo>;
|
|
242
|
+
|
|
243
|
+
OBS: Caso você use o `.extend` para estender o repository ou configure os métodos base, recomenda-se
|
|
244
|
+
usar o `ReturnType` por ser mais simples de inferir a tipagem
|
|
245
|
+
*/
|
|
241
246
|
|
|
242
247
|
export const USER_REPOSITORY = Symbol("USER_REPOSITORY");
|
|
243
248
|
|
|
@@ -362,7 +367,7 @@ await usuarioRepository.restore(1);
|
|
|
362
367
|
// saveList — cria ou atualiza múltiplos objetos em transação automática
|
|
363
368
|
const usuarios = await usuarioRepository.saveList([
|
|
364
369
|
{ nome: "Maria", email: "maria@email.com" },
|
|
365
|
-
{ id: 2, nome: "João Atualizado" },
|
|
370
|
+
{ id: 2, nome: "João Atualizado", email: "joao@email.com" },
|
|
366
371
|
]);
|
|
367
372
|
|
|
368
373
|
// patchList — atualiza parcialmente múltiplos registros via tuplas [pk, obj]
|
|
@@ -400,32 +405,80 @@ await usuarioRepository.save(mesclado);
|
|
|
400
405
|
|
|
401
406
|
Retorna `null` se o registro não for encontrado.
|
|
402
407
|
|
|
408
|
+
**Merge de relações to-many (`otm`/`mtm`) é feito por PK, não por concatenação simples.** Para relações to-one (`oto`/`mto`), o `merge` faz um deepmerge comum do objeto. Já para relações to-many, cada item do array enviado é casado com o item existente que tem a mesma PK (definida em `relations[chave].pk`): se a PK bate, os dois objetos são mesclados entre si; se não bate (item novo, sem correspondente), ele é apenas adicionado à lista. Itens existentes que não aparecem no array enviado são mantidos.
|
|
409
|
+
|
|
410
|
+
```ts
|
|
411
|
+
const existente = await usuarioRepository.get(1);
|
|
412
|
+
// existente: {
|
|
413
|
+
// id: 1,
|
|
414
|
+
// postagens: [
|
|
415
|
+
// { id: 10, titulo: "Post A", publicada: false },
|
|
416
|
+
// { id: 11, titulo: "Post B", publicada: true },
|
|
417
|
+
// ],
|
|
418
|
+
// }
|
|
419
|
+
|
|
420
|
+
const mesclado = await usuarioRepository.merge(1, {
|
|
421
|
+
postagens: [
|
|
422
|
+
{ id: 10, publicada: true }, // mesma PK (id: 10) → mescla com o item existente
|
|
423
|
+
{ titulo: "Post C" }, // sem PK → é adicionado como um novo item
|
|
424
|
+
],
|
|
425
|
+
});
|
|
426
|
+
// mesclado: {
|
|
427
|
+
// id: 1,
|
|
428
|
+
// postagens: [
|
|
429
|
+
// { id: 10, titulo: "Post A", publicada: true }, // mesclado
|
|
430
|
+
// { id: 11, titulo: "Post B", publicada: true }, // mantido, não veio no array enviado
|
|
431
|
+
// { titulo: "Post C" }, // adicionado
|
|
432
|
+
// ],
|
|
433
|
+
// }
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
> Note que o `merge` nunca remove itens de uma relação to-many — ele só mescla os que casam por PK e adiciona os que não casam. Para remover itens de uma relação, use `save`/`patch` com `restriction: "set"` na configuração da relação.
|
|
437
|
+
|
|
403
438
|
### Configurando os métodos base
|
|
404
439
|
|
|
440
|
+
O segundo argumento de `.build(prisma, config)` permite ajustar o comportamento global do repository e customizar cada método base individualmente através de `baseMethods`.
|
|
441
|
+
|
|
405
442
|
```ts
|
|
406
443
|
usuarioVSRepo.build(prisma, {
|
|
407
|
-
|
|
444
|
+
// Exibe logs internos do VSRepository no console (queries montadas, prefixo detectado,
|
|
445
|
+
// filtros aplicados etc). Ótimo para debugar métodos dinâmicos. Padrão = false.
|
|
446
|
+
showWorking: true,
|
|
408
447
|
|
|
409
448
|
baseMethods: {
|
|
410
449
|
get: {
|
|
450
|
+
// Habilita/desabilita o método no repository final. Se `false`, o método
|
|
451
|
+
// sequer aparece no tipo do repository (não é só um erro em runtime). Padrão = true.
|
|
411
452
|
active: true,
|
|
453
|
+
|
|
454
|
+
// Select model aplicado por padrão quando o método é chamado sem `options.selectModel`.
|
|
455
|
+
// Sobrescreve o `defaultSelectModel` do setupVSRepo apenas para este método.
|
|
412
456
|
defaultSelect: "public",
|
|
413
457
|
},
|
|
414
458
|
remove: {
|
|
415
459
|
active: true,
|
|
416
460
|
defaultSelect: "minimal",
|
|
461
|
+
|
|
462
|
+
// Quando `true`, ignora o `requiredWhere` configurado no setupVSRepo para
|
|
463
|
+
// este método específico — útil quando um método precisa "furar" um filtro
|
|
464
|
+
// global (ex.: multi-tenancy) em um caso pontual. Padrão = false.
|
|
417
465
|
ignoreRequiredWhere: false,
|
|
418
466
|
},
|
|
419
467
|
save: {
|
|
468
|
+
// Aqui só `ignoreRequiredWhere` é definido — `active` e `defaultSelect`
|
|
469
|
+
// continuam com seus padrões (true e o `defaultSelectModel` global).
|
|
420
470
|
ignoreRequiredWhere: true,
|
|
421
471
|
},
|
|
422
472
|
patch: {
|
|
473
|
+
// Somente o select é sobrescrito; o método continua ativo normalmente.
|
|
423
474
|
defaultSelect: "minimal",
|
|
424
475
|
},
|
|
425
476
|
has: {
|
|
426
|
-
active: false, // Desativa o 'has' (padrão = true)
|
|
477
|
+
active: false, // Desativa o 'has' (padrão = true) — o método some do repository
|
|
427
478
|
},
|
|
428
479
|
softRemove: {
|
|
480
|
+
// Métodos de soft-delete seguem as mesmas opções (`active`, `defaultSelect`,
|
|
481
|
+
// `ignoreRequiredWhere`). Só ficam disponíveis se `softRemovekName` estiver configurado.
|
|
429
482
|
active: true,
|
|
430
483
|
defaultSelect: "minimal",
|
|
431
484
|
},
|
|
@@ -433,6 +486,8 @@ usuarioVSRepo.build(prisma, {
|
|
|
433
486
|
});
|
|
434
487
|
```
|
|
435
488
|
|
|
489
|
+
> Métodos em lote/agregados como `removeList`, `softRemoveList`, `restoreList`, `total` e `has` **não** aceitam `defaultSelect` (não retornam um registro selecionável — retornam `{ count }` ou `boolean`). Nesses casos `BaseMethodConfig` fica restrito a `active` e `ignoreRequiredWhere`.
|
|
490
|
+
|
|
436
491
|
---
|
|
437
492
|
|
|
438
493
|
## Select Models
|
|
@@ -464,6 +519,51 @@ const usuarioCompleto = await usuarioRepository.get(id, { selectModel: false });
|
|
|
464
519
|
|
|
465
520
|
---
|
|
466
521
|
|
|
522
|
+
## Include Models
|
|
523
|
+
|
|
524
|
+
`includeModels` funciona de forma parecida com o `selectModels`, mas em vez de receber um `select`, ele recebe um `include` válido do Prisma.
|
|
525
|
+
|
|
526
|
+
```ts
|
|
527
|
+
const usuarioRepository = setupVSRepo<Usuario, "usuario">()(({
|
|
528
|
+
tableName: "usuario",
|
|
529
|
+
pkName: "id",
|
|
530
|
+
selectModels: {
|
|
531
|
+
public: { id: true, nome: true, email: true },
|
|
532
|
+
},
|
|
533
|
+
defaultSelectModel: "public",
|
|
534
|
+
includeModels: {
|
|
535
|
+
comPosts: { posts: true },
|
|
536
|
+
comPostsEPerfil: { posts: true, perfil: true },
|
|
537
|
+
},
|
|
538
|
+
}).build(prisma);
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
**Usando um `includeModel` na chamada:**
|
|
542
|
+
|
|
543
|
+
```ts
|
|
544
|
+
const usuario = await usuarioRepository.get(id, { includeModel: "comPosts" });
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Nesse caso, o `select` padrão (`selectModels`/`defaultSelectModel`) é ignorado e apenas o `include` é enviado ao Prisma.
|
|
548
|
+
|
|
549
|
+
### Diferenças em relação ao `selectModels`
|
|
550
|
+
|
|
551
|
+
- **Só pode ser passado na chamada do método**, via `options.includeModel`. Não existe `defaultIncludeModel` nem `defaultInclude` — não há como configurar um `includeModel` padrão no repository, diferente do que ocorre com `defaultSelectModel`.
|
|
552
|
+
- **`includeModel` e `selectModel` não podem ser passados juntos** na mesma chamada. Se um `includeModel` for informado, qualquer `selectModel` (incluindo o padrão) é ignorado.
|
|
553
|
+
|
|
554
|
+
```ts
|
|
555
|
+
// CORRETO ✅ — apenas includeModel
|
|
556
|
+
await usuarioRepository.get(id, { includeModel: "comPosts" });
|
|
557
|
+
|
|
558
|
+
// CORRETO ✅ — apenas selectModel
|
|
559
|
+
await usuarioRepository.get(id, { selectModel: "public" });
|
|
560
|
+
|
|
561
|
+
// ERRADO ❌ — não é permitido combinar os dois
|
|
562
|
+
await usuarioRepository.get(id, { selectModel: "public", includeModel: "comPosts" });
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
467
567
|
## Required Where
|
|
468
568
|
|
|
469
569
|
`requiredWhere` define filtros aplicados automaticamente em todas as queries do repository.
|
|
@@ -528,7 +628,9 @@ methods: {
|
|
|
528
628
|
|
|
529
629
|
---
|
|
530
630
|
|
|
531
|
-
|
|
631
|
+
## Opção `see`
|
|
632
|
+
|
|
633
|
+
Quando `softRemovekName` está configurado, todos os métodos aceitam a opção `see` para controlar a visibilidade de registros soft-deletados:
|
|
532
634
|
|
|
533
635
|
| Valor | Comportamento |
|
|
534
636
|
| ----------- | ------------------------------------------------------------- |
|
|
@@ -545,10 +647,6 @@ const removidos = await usuarioRepository.getAll({ see: "removed" });
|
|
|
545
647
|
|
|
546
648
|
// Retorna todos
|
|
547
649
|
const todos = await usuarioRepository.getAll({ see: "all" });
|
|
548
|
-
|
|
549
|
-
// Funciona em qualquer método base
|
|
550
|
-
const usuario = await usuarioRepository.get(id, { see: "all" });
|
|
551
|
-
const existe = await usuarioRepository.has(id, { see: "removed" });
|
|
552
650
|
```
|
|
553
651
|
|
|
554
652
|
> A opção `see` funciona independentemente do `requiredWhere` — ela é aplicada em cima do filtro de soft-delete, não o substitui.
|
|
@@ -689,16 +787,50 @@ Exemplo:
|
|
|
689
787
|
methods: {
|
|
690
788
|
findOneByIdAndEmail: { map: true },
|
|
691
789
|
findByNomeOrEmail: { map: true },
|
|
692
|
-
|
|
790
|
+
findFirstByIdOrEmailAndNome: { map: true },
|
|
693
791
|
findByEmailOrNameANDActiveStatusAndIdadeGreaterThan: { map: true }
|
|
694
792
|
}
|
|
695
793
|
|
|
696
794
|
await usuarioRepository.findOneByIdAndEmail(1, "joao@email.com");
|
|
697
795
|
await usuarioRepository.findByNomeOrEmail("Joao", "joao@email.com");
|
|
698
|
-
await usuarioRepository.
|
|
796
|
+
await usuarioRepository.findFirstByIdOrEmailAndNome(1, "joao@email.com", "Joao");
|
|
699
797
|
await usuarioRepository.findByEmailOrNameANDActiveStatusAndIdadeGreaterThan("joao@email.com", "Joao", true, 17)
|
|
700
798
|
```
|
|
701
799
|
|
|
800
|
+
Gera (`findOneByIdAndEmail`):
|
|
801
|
+
|
|
802
|
+
```ts
|
|
803
|
+
{
|
|
804
|
+
id: 1,
|
|
805
|
+
email: "joao@email.com"
|
|
806
|
+
}
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
Gera (`findByNomeOrEmail`):
|
|
810
|
+
|
|
811
|
+
```ts
|
|
812
|
+
{
|
|
813
|
+
OR: [
|
|
814
|
+
{ nome: "Joao" },
|
|
815
|
+
{ email: "joao@email.com" }
|
|
816
|
+
]
|
|
817
|
+
}
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
Gera (`findFirstByIdOrEmailAndNome`):
|
|
821
|
+
|
|
822
|
+
```ts
|
|
823
|
+
{
|
|
824
|
+
OR: [
|
|
825
|
+
{ id: 1 },
|
|
826
|
+
{
|
|
827
|
+
email: "joao@email.com",
|
|
828
|
+
nome: "Joao"
|
|
829
|
+
}
|
|
830
|
+
]
|
|
831
|
+
}
|
|
832
|
+
```
|
|
833
|
+
|
|
702
834
|
Gera (`findByEmailOrNameANDActiveStatusAndIdadeGreaterThan`):
|
|
703
835
|
|
|
704
836
|
```ts
|
|
@@ -738,6 +870,78 @@ Permitem filtrar por campos de modelos relacionados.
|
|
|
738
870
|
| `Without` | `isNot: {}` | Relação não existe (é null) |
|
|
739
871
|
| `WithoutField` | `isNot.field` | Filtra campo dentro da relação com negação |
|
|
740
872
|
|
|
873
|
+
Considerando `usuario` com uma relação to-one `perfil` e uma relação to-many `postagens`:
|
|
874
|
+
|
|
875
|
+
```ts
|
|
876
|
+
methods: {
|
|
877
|
+
// to-many (postagens)
|
|
878
|
+
findByPostagensSome: { map: true }, // tem ao menos uma postagem
|
|
879
|
+
findByPostagensSomeTitulo: { map: true }, // tem ao menos uma postagem com esse título
|
|
880
|
+
findByPostagensEveryPublicada:{ map: true }, // todas as postagens estão publicadas
|
|
881
|
+
findByPostagensNone: { map: true }, // não tem nenhuma postagem
|
|
882
|
+
findByPostagensNoneTitulo: { map: true }, // nenhuma postagem tem esse título
|
|
883
|
+
|
|
884
|
+
// to-one (perfil)
|
|
885
|
+
findByPerfilWith: { map: true }, // possui perfil (não é null)
|
|
886
|
+
findByPerfilWithBio: { map: true }, // possui perfil com essa bio
|
|
887
|
+
findByPerfilWithout: { map: true }, // não possui perfil (é null)
|
|
888
|
+
findByPerfilWithoutBio: { map: true }, // possui perfil, mas com bio diferente da informada
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
await usuarioRepository.findByPostagensSome();
|
|
892
|
+
await usuarioRepository.findByPostagensSomeTitulo("Meu primeiro post");
|
|
893
|
+
await usuarioRepository.findByPostagensEveryPublicada(true);
|
|
894
|
+
await usuarioRepository.findByPostagensNone();
|
|
895
|
+
await usuarioRepository.findByPostagensNoneTitulo("Rascunho");
|
|
896
|
+
|
|
897
|
+
await usuarioRepository.findByPerfilWith();
|
|
898
|
+
await usuarioRepository.findByPerfilWithBio("Olá, mundo!");
|
|
899
|
+
await usuarioRepository.findByPerfilWithout();
|
|
900
|
+
await usuarioRepository.findByPerfilWithoutBio("Bio antiga");
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
Gera (`findByPostagensSomeTitulo`):
|
|
904
|
+
|
|
905
|
+
```ts
|
|
906
|
+
{
|
|
907
|
+
postagens: {
|
|
908
|
+
some: { titulo: "Meu primeiro post" }
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
Gera (`findByPostagensEveryPublicada`):
|
|
914
|
+
|
|
915
|
+
```ts
|
|
916
|
+
{
|
|
917
|
+
postagens: {
|
|
918
|
+
every: { publicada: true }
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
Gera (`findByPerfilWithBio`):
|
|
924
|
+
|
|
925
|
+
```ts
|
|
926
|
+
{
|
|
927
|
+
perfil: {
|
|
928
|
+
is: { bio: "Olá, mundo!" }
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
Gera (`findByPerfilWithout`):
|
|
934
|
+
|
|
935
|
+
```ts
|
|
936
|
+
{
|
|
937
|
+
perfil: {
|
|
938
|
+
isNot: {}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
> `Some`, `None`, `With` e `Without` (sem campo) não recebem argumento — a relação inteira é testada quanto à existência de registros (`some`/`none`) ou a ser `null`/não-`null` (`is`/`isNot`). Já as variantes `SomeField`, `EveryField`, `NoneField`, `WithField` e `WithoutField` recebem o valor do campo filtrado como argumento.
|
|
944
|
+
|
|
741
945
|
---
|
|
742
946
|
|
|
743
947
|
### Sufixos de paginação e ordenação
|
|
@@ -841,6 +1045,20 @@ const usuarioRepository = setupVSRepo<Usuario, "usuario">()(({
|
|
|
841
1045
|
| `set` | Substitui completamente (remove os que não foram enviados) |
|
|
842
1046
|
| `add` | Adiciona/atualiza sem remover os existentes |
|
|
843
1047
|
|
|
1048
|
+
> [!WARNING]
|
|
1049
|
+
> **`set` significa coisas diferentes dependendo do `mode` da relação — e isso pode causar perda de dados se você não prestar atenção.**
|
|
1050
|
+
>
|
|
1051
|
+
> Em relações onde o registro relacionado **pertence** ao registro pai (`oto` e `otm`), "remover os que não foram enviados" significa **deletar o registro do banco** (`delete`/`deleteMany`). Já em relações onde o registro relacionado é **independente** (`mto` e `mtm`), "remover" significa apenas **desvincular** (`disconnect`/`set: []`) — o registro relacionado continua existindo no banco, só deixa de apontar para o pai (ou de estar na tabela de junção).
|
|
1052
|
+
>
|
|
1053
|
+
> | Modo | `restriction: "set"` ao omitir um item | O item continua existindo no banco? |
|
|
1054
|
+
> | ----- | ---------------------------------------- | ------------------------------------ |
|
|
1055
|
+
> | `oto` | Passar `null` no campo → **deleta** o registro relacionado (`delete: true`) | Não |
|
|
1056
|
+
> | `otm` | Itens fora da lista enviada → **deletados** (`deleteMany` com `notIn`) | Não |
|
|
1057
|
+
> | `mto` | Passar `null` no campo (com `nullable: true`) → **desvincula** (`disconnect: true`) | Sim |
|
|
1058
|
+
> | `mtm` | Itens fora da lista enviada → **desvinculados** da tabela de junção (`set: []`) | Sim |
|
|
1059
|
+
>
|
|
1060
|
+
> Exemplo prático: se `postagens` é `otm` com `restriction: "set"`, um `save`/`patch` que envie o usuário com apenas 2 das 5 postagens existentes vai **apagar as outras 3 postagens do banco**, não apenas desvinculá-las do usuário. Se o comportamento esperado é só desvincular sem apagar, use `restriction: "add"` (que nunca remove nada) e gerencie a remoção manualmente.
|
|
1061
|
+
|
|
844
1062
|
**Relação `mto` com nullable:**
|
|
845
1063
|
|
|
846
1064
|
Use `nullable` (letra minúscula) para permitir a desvinculação de uma relação many-to-one:
|
|
@@ -856,8 +1074,6 @@ relations: {
|
|
|
856
1074
|
}
|
|
857
1075
|
```
|
|
858
1076
|
|
|
859
|
-
> **Nota:** `nullAble` (com A maiúsculo) ainda é aceito por compatibilidade, mas está **obsoleto**. Prefira `nullable`.
|
|
860
|
-
|
|
861
1077
|
---
|
|
862
1078
|
|
|
863
1079
|
## Transações
|
|
@@ -878,15 +1094,17 @@ await usuarioRepository.prisma.$transaction(async (tx) => {
|
|
|
878
1094
|
});
|
|
879
1095
|
```
|
|
880
1096
|
|
|
881
|
-
Para `saveList` e `patchList`, o campo `db` deve ser um `DbTransaction
|
|
1097
|
+
Para `saveList` e `patchList`, o campo `db` deve ser um `DbTransaction`:
|
|
882
1098
|
|
|
883
1099
|
```ts
|
|
884
1100
|
await prisma.$transaction(async (tx) => {
|
|
885
1101
|
// CORRETO: tx é uma DbTransaction
|
|
886
|
-
await usuarioRepository.saveList([{ nome: "Maria" }], { db: tx });
|
|
1102
|
+
const usuariosCadastrados = await usuarioRepository.saveList([{ nome: "Maria" }, { nome: "Lucas" }], { db: tx });
|
|
887
1103
|
|
|
888
|
-
|
|
889
|
-
|
|
1104
|
+
await usuarioLogsRepository.save(
|
|
1105
|
+
{ acao: "Cadastro de usuários", data: { usuariosCadastrados: usuariosCadastrados.map(u => u.id) } },
|
|
1106
|
+
{ db: tx }
|
|
1107
|
+
);
|
|
890
1108
|
});
|
|
891
1109
|
```
|
|
892
1110
|
|
|
@@ -924,12 +1142,14 @@ O VSRepository lança `VSRepoError` e suas subclasses em situações específica
|
|
|
924
1142
|
import { VSRepoError, VSRepoRuntimeError } from "../../generated/vsrepo";
|
|
925
1143
|
|
|
926
1144
|
try {
|
|
927
|
-
const usuario = await usuarioRepository.getOrThrow(id);
|
|
1145
|
+
const usuario = await usuarioRepository.getOrThrow("id-que-nao-existe");
|
|
928
1146
|
} catch (error) {
|
|
929
1147
|
if (error instanceof VSRepoRuntimeError && error.code === "20727") {
|
|
930
|
-
|
|
1148
|
+
console.error("Registro não encontrado");
|
|
931
1149
|
} else if (error instanceof VSRepoError) {
|
|
932
1150
|
console.error("Erro no repository:", error.message);
|
|
1151
|
+
} else {
|
|
1152
|
+
console.error("Erro:", error.message)
|
|
933
1153
|
}
|
|
934
1154
|
}
|
|
935
1155
|
```
|
|
@@ -943,7 +1163,7 @@ try {
|
|
|
943
1163
|
| `VSRepoExtendError` | Argumento inválido em `extend` |
|
|
944
1164
|
| `VSRepoRuntimeError` | Erro em tempo de execução durante uma operação |
|
|
945
1165
|
|
|
946
|
-
`VSRepoRuntimeError` possui a propriedade `code` para identificação programática. O código `"20727"` é lançado pelo `getOrThrow` quando o registro não é encontrado.
|
|
1166
|
+
`VSRepoRuntimeError` possui a propriedade `code` para identificação programática. O código `"20727"` é lançado pelo `getOrThrow` quando o registro não é encontrado, por exemplo.
|
|
947
1167
|
|
|
948
1168
|
---
|
|
949
1169
|
|
|
@@ -973,6 +1193,8 @@ type SeeMode = "active" | "removed" | "all";
|
|
|
973
1193
|
import type {
|
|
974
1194
|
SelectModel,
|
|
975
1195
|
SelectModels,
|
|
1196
|
+
IncludeModel,
|
|
1197
|
+
IncludeModels,
|
|
976
1198
|
WhereModel,
|
|
977
1199
|
OrdenationModel,
|
|
978
1200
|
PaginationModel,
|
|
@@ -986,14 +1208,16 @@ import type {
|
|
|
986
1208
|
```ts
|
|
987
1209
|
import type { MethodOptions, MethodOptionsModel } from "../../generated/vsrepo";
|
|
988
1210
|
|
|
989
|
-
// MethodOptions<S> — opções passadas nos métodos do repository
|
|
990
|
-
type Opts = MethodOptions<"public" | "minimal">;
|
|
1211
|
+
// MethodOptions<S, IM> — opções passadas nos métodos do repository
|
|
1212
|
+
type Opts = MethodOptions<"public" | "minimal", "comPosts">;
|
|
991
1213
|
|
|
992
1214
|
// MethodOptionsModel<TRepo> — derivado de uma instância VSRepository configurada
|
|
993
1215
|
const usuarioVSRepo = setupVSRepo<Usuario, "usuario">()(config);
|
|
994
1216
|
type OptsModel = MethodOptionsModel<typeof usuarioVSRepo>;
|
|
995
1217
|
```
|
|
996
1218
|
|
|
1219
|
+
> O segundo parâmetro de `MethodOptions` (`IM`) representa as chaves válidas de `includeModels`. Quando informado, `selectModel` e `includeModel` tornam-se mutuamente exclusivos no tipo — não é possível passar os dois na mesma chamada.
|
|
1220
|
+
|
|
997
1221
|
### Tipos de configuração
|
|
998
1222
|
|
|
999
1223
|
```ts
|
|
@@ -1048,9 +1272,10 @@ type UsuarioPatchPayload = PatchObject<Prisma.UsuarioUpdateInput, typeof usuario
|
|
|
1048
1272
|
setupVSRepo<TPayload, TTableName>()({
|
|
1049
1273
|
tableName: Uncapitalize<M>; // Nome da tabela no Prisma
|
|
1050
1274
|
pkName: keyof T; // Nome da primary key
|
|
1051
|
-
softRemovekName?: keyof T & string; // Campo DateTime para soft-delete
|
|
1052
|
-
selectModels?: SelectModels<M>; // Projeções de dados nomeadas
|
|
1275
|
+
softRemovekName?: keyof T & string; // Campo DateTime para soft-delete
|
|
1276
|
+
selectModels?: SelectModels<M>; // Projeções de dados nomeadas (select)
|
|
1053
1277
|
defaultSelectModel?: keyof SM; // Select aplicado por padrão
|
|
1278
|
+
includeModels?: IncludeModels<M>; // Projeções de dados nomeadas (include) — sem default, só na chamada
|
|
1054
1279
|
requiredWhere?: WhereModel<M>; // Filtros sempre aplicados
|
|
1055
1280
|
defaultOrdenation?: OrdenationModel<M>; // Ordenação padrão para queries sem Ordered/injectOrdenation
|
|
1056
1281
|
relations?: RepositoryRelations<T>; // Configuração de relações
|
|
@@ -1099,6 +1324,28 @@ repo.extend((repo) => ({
|
|
|
1099
1324
|
|
|
1100
1325
|
---
|
|
1101
1326
|
|
|
1327
|
+
## Exemplos práticos
|
|
1328
|
+
|
|
1329
|
+
Além deste README, o repositório tem uma pasta **[`examples/`](https://github.com/jaobrabo123/VSRepository/tree/main/examples)** com exemplos práticos e comentados, prontos para rodar — é o melhor lugar para ver o VSRepository sendo usado em cenários reais.
|
|
1330
|
+
|
|
1331
|
+
```
|
|
1332
|
+
examples/
|
|
1333
|
+
├── prisma.ts # Instância do PrismaClient usada pelos exemplos
|
|
1334
|
+
├── repositories.ts # Configuração dos repositories (User, Address, Product) com setupVSRepo
|
|
1335
|
+
└── tests/
|
|
1336
|
+
├── base-methods.test.ts # Métodos base: get, save, patch, remove, getAll, total, has...
|
|
1337
|
+
├── relations.test.ts # Como configurar e usar relations no save/patch e em filtros
|
|
1338
|
+
├── required-where.test.ts # Como o requiredWhere é aplicado automaticamente nas queries
|
|
1339
|
+
├── dynamic-methods.test.ts # Prefixos, filtros de campo, operadores lógicos e paginação/ordenação
|
|
1340
|
+
├── transactions.test.ts # Transactions com options.db e acesso à instância via repository.prisma
|
|
1341
|
+
├── soft-delete.test.ts # Soft-delete: softRemove, softRemoveList, restore, restoreList e SeeMode
|
|
1342
|
+
└── batch-methods.test.ts # Operações em lote: getList, saveList, patchList e merge
|
|
1343
|
+
```
|
|
1344
|
+
|
|
1345
|
+
Cada arquivo em `tests/` é um script independente e executável (via `tsx`) que demonstra um conjunto específico de funcionalidades, com `console.log` em cada passo para você acompanhar o resultado no terminal. A própria pasta tem um [README](https://github.com/jaobrabo123/VSRepository/blob/main/examples/README.md) explicando a ordem sugerida de leitura, como configurar o ambiente e como rodar os testes.
|
|
1346
|
+
|
|
1347
|
+
---
|
|
1348
|
+
|
|
1102
1349
|
## Contribuindo
|
|
1103
1350
|
|
|
1104
1351
|
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)**):
|
|
@@ -1144,10 +1391,14 @@ Para reportar problemas ou sugerir novas funcionalidades, abra uma **Issue**.
|
|
|
1144
1391
|
|
|
1145
1392
|
**`proxyTo` obrigatório** — Nomes fora dos moldes (ex.: `buscarPorEmail`) não são parseados diretamente. Use `proxyTo: "findByEmail"` nesses casos.
|
|
1146
1393
|
|
|
1147
|
-
**Select model retorna campos inesperados** — Verifique se o select model define exatamente os campos que o seu tipo TypeScript espera.
|
|
1394
|
+
**Select model retorna campos inesperados** — Verifique se o select model define exatamente os campos que o seu tipo TypeScript espera.
|
|
1395
|
+
|
|
1396
|
+
**`selectModel` e `includeModel` juntos na mesma chamada** — Não é permitido. Escolha um ou outro: se `includeModel` for informado, o `select` (incluindo o `defaultSelectModel`) é ignorado e apenas o `include` é enviado ao Prisma.
|
|
1397
|
+
|
|
1398
|
+
**`includeModel` não aparece como opção padrão do repository** — Isso é esperado. Diferente de `defaultSelectModel`, não existe `defaultIncludeModel`/`defaultInclude`. Um `includeModel` só pode ser definido na chamada do método, via `options.includeModel`.
|
|
1148
1399
|
|
|
1149
1400
|
**`softRemovekName` lança erro no build** — O campo informado deve ser do tipo `DateTime` no schema do Prisma. Tipos como `Boolean` ou `String` não são aceitos.
|
|
1150
1401
|
|
|
1151
1402
|
**`defaultOrdenation` não está sendo aplicada** — Verifique se o método não usa o sufixo `Ordered`, `OrderedAndPaginated` ou `PaginatedAndOrdered`, e se não possui `injectOrdenation` configurado. Ambos têm prioridade sobre a ordenação padrão.
|
|
1152
1403
|
|
|
1153
|
-
**`saveList`/`patchList` com `db` inválido** — O campo `db` nestes métodos aceita apenas `DbTransaction` (retorno de `prisma.$transaction`), não o cliente principal. Passar o `PrismaClient` diretamente causará comportamento inesperado.
|
|
1404
|
+
**`saveList`/`patchList` com `db` inválido** — O campo `db` nestes métodos aceita apenas `DbTransaction` (retorno de `prisma.$transaction`), não o cliente principal. Passar o `PrismaClient` diretamente causará comportamento inesperado.
|
package/dist/VSRepoError.d.ts
CHANGED
|
@@ -50,5 +50,5 @@ export declare class VSRepoExtendError extends VSRepoError {
|
|
|
50
50
|
*/
|
|
51
51
|
export declare class VSRepoRuntimeError extends VSRepoError {
|
|
52
52
|
readonly type: 'VSREPO_RUNTIME';
|
|
53
|
-
constructor(message: string, code
|
|
53
|
+
constructor(message: string, code: string);
|
|
54
54
|
}
|
package/dist/VSRepository.d.ts
CHANGED
|
@@ -222,14 +222,22 @@ type PrecomputedSelects<M extends Prisma.ModelName, SelectModels> = {
|
|
|
222
222
|
[S in keyof SelectModels]: Prisma.Result<PrismaDelegate<M>, { select: SelectModels[S] }, 'findMany'> extends Array<infer U> ? U : never
|
|
223
223
|
};
|
|
224
224
|
|
|
225
|
-
type
|
|
226
|
-
[
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
225
|
+
type PrecomputedIncludes<M extends Prisma.ModelName, IncludeModels> = {
|
|
226
|
+
[I in keyof IncludeModels]: Prisma.Result<PrismaDelegate<M>, { include: IncludeModels[I] }, 'findMany'> extends Array<infer U> ? U : never
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
type SelectedModel<M extends Prisma.ModelName, S, SelectModels, IM = never, IncludeModels = {}> =
|
|
230
|
+
[IM] extends [never]
|
|
231
|
+
? [S] extends [false]
|
|
232
|
+
? FullModelType<M>
|
|
233
|
+
: [S] extends [never]
|
|
234
|
+
? FullModelType<M>
|
|
235
|
+
: [S] extends [keyof SelectModels]
|
|
236
|
+
? PrecomputedSelects<M, SelectModels>[S]
|
|
237
|
+
: FullModelType<M>
|
|
238
|
+
: IM extends keyof IncludeModels
|
|
239
|
+
? PrecomputedIncludes<M, IncludeModels>[IM]
|
|
240
|
+
: FullModelType<M>;
|
|
233
241
|
|
|
234
242
|
type CleanFields<R extends string> =
|
|
235
243
|
R extends `${infer F}PaginatedAndOrdered` ? F :
|
|
@@ -242,17 +250,10 @@ type CleanFields<R extends string> =
|
|
|
242
250
|
* Additional options accepted by repository methods.
|
|
243
251
|
*
|
|
244
252
|
* @template S Available keys in `selectModels`.
|
|
253
|
+
* @template IM Available keys in `includeModels`.
|
|
245
254
|
*/
|
|
246
|
-
export type MethodOptions<S> = {
|
|
247
|
-
/**
|
|
248
|
-
* Select model to apply to the operation.
|
|
249
|
-
*
|
|
250
|
-
* @note Use `false` to return the full Prisma payload without a select.
|
|
251
|
-
*/
|
|
252
|
-
selectModel?: S | false;
|
|
253
|
-
/**
|
|
254
|
-
* Prisma client or transaction to use for the operation.
|
|
255
|
-
*/
|
|
255
|
+
export type MethodOptions<S, IM extends PropertyKey = never> = {
|
|
256
|
+
/** Prisma client or transaction to use for the operation. */
|
|
256
257
|
db?: ClientOrTransaction;
|
|
257
258
|
/**
|
|
258
259
|
* Visibility mode for records with soft-delete.
|
|
@@ -264,7 +265,17 @@ export type MethodOptions<S> = {
|
|
|
264
265
|
* - `"all"` — returns all records, ignoring deletion status.
|
|
265
266
|
*/
|
|
266
267
|
see?: SeeMode;
|
|
267
|
-
}
|
|
268
|
+
} & (
|
|
269
|
+
[IM] extends [never]
|
|
270
|
+
? {
|
|
271
|
+
/** Select model to apply to the operation. Use `false` to return the full payload. */
|
|
272
|
+
selectModel?: S | false;
|
|
273
|
+
/** Include model to apply to the operation. Cannot be used together with `selectModel`. */
|
|
274
|
+
includeModel?: never;
|
|
275
|
+
}
|
|
276
|
+
: | { selectModel?: S | false; includeModel?: never }
|
|
277
|
+
| { selectModel?: never; includeModel: IM }
|
|
278
|
+
);
|
|
268
279
|
|
|
269
280
|
/**
|
|
270
281
|
* Version of `MethodOptions` derived directly from a configured `VSRepository` instance.
|
|
@@ -277,11 +288,21 @@ export type MethodOptions<S> = {
|
|
|
277
288
|
*/
|
|
278
289
|
export type MethodOptionsModel<TRepo> =
|
|
279
290
|
TRepo extends VSRepository<any, any, infer Config>
|
|
280
|
-
? MethodOptions<keyof ExtractSelectModels<Config> | false
|
|
291
|
+
? MethodOptions<keyof ExtractSelectModels<Config> | false, keyof ExtractIncludeModels<Config>>
|
|
281
292
|
: never;
|
|
282
293
|
|
|
283
|
-
type
|
|
284
|
-
|
|
294
|
+
type MethodOptionsWithInclude<
|
|
295
|
+
S,
|
|
296
|
+
IM extends PropertyKey,
|
|
297
|
+
TIncludes,
|
|
298
|
+
Excluded extends keyof MethodOptions<S, keyof TIncludes> = never
|
|
299
|
+
> = Omit<MethodOptions<S, keyof TIncludes>, Excluded>
|
|
300
|
+
& ({ includeModel: IM } | { includeModel?: never });
|
|
301
|
+
|
|
302
|
+
type MethodFn<MethodName extends string, T, M extends Prisma.ModelName, R extends string, SelectModels, DefaultSelect extends keyof SelectModels | false, I, IncludeModels> =
|
|
303
|
+
<S extends keyof SelectModels | false = DefaultSelect, IM extends keyof IncludeModels = never>(
|
|
304
|
+
...args: [...ExtractFields<T, CleanFields<R>, I>, ...ExtraArgs<MethodName, R, I>, options?: MethodOptionsWithInclude<S, IM, IncludeModels>]
|
|
305
|
+
) => Promise<ResolveReturnType<MethodName, SelectedModel<M, S, SelectModels, IM, IncludeModels>>>;
|
|
285
306
|
|
|
286
307
|
type GetMappedMethod<K extends string, MethodConf> =
|
|
287
308
|
K extends `findBy${string}` ? (MethodConf extends { fbMode: 'one' } ? 'findByOne' : 'findByList') :
|
|
@@ -332,13 +353,13 @@ type ExtractPatternBase<K extends string> =
|
|
|
332
353
|
K extends `updateManyAndReturnWhere${infer R}` ? R :
|
|
333
354
|
K extends `deleteManyWhere${infer R}` ? R : '';
|
|
334
355
|
|
|
335
|
-
type MethodFactory<T, M extends Prisma.ModelName, K extends string, SelectModels, DefaultSelect extends keyof SelectModels | false, I, MethodConf> =
|
|
356
|
+
type MethodFactory<T, M extends Prisma.ModelName, K extends string, SelectModels, DefaultSelect extends keyof SelectModels | false, I, MethodConf, IncludeModels> =
|
|
336
357
|
K extends `findWhere${string}`
|
|
337
358
|
? {
|
|
338
359
|
/** @deprecated Use findOneWhere instead. */
|
|
339
|
-
<S extends keyof SelectModels | false = DefaultSelect>(...args: [...ExtractFields<T, CleanFields<ExtractPatternBase<K>>, I>, ...ExtraArgs<GetMappedMethod<K, MethodConf>, ExtractPatternBase<K>, I>, options?:
|
|
360
|
+
<S extends keyof SelectModels | false = DefaultSelect, IM extends keyof IncludeModels = never>(...args: [...ExtractFields<T, CleanFields<ExtractPatternBase<K>>, I>, ...ExtraArgs<GetMappedMethod<K, MethodConf>, ExtractPatternBase<K>, I>, options?: MethodOptionsWithInclude<S, IM, IncludeModels>]): Promise<ResolveReturnType<GetMappedMethod<K, MethodConf>, SelectedModel<M, S, SelectModels, IM, IncludeModels>>>;
|
|
340
361
|
}
|
|
341
|
-
: MethodFn<GetMappedMethod<K, MethodConf>, T, M, ExtractPatternBase<K>, SelectModels, DefaultSelect, I>;
|
|
362
|
+
: MethodFn<GetMappedMethod<K, MethodConf>, T, M, ExtractPatternBase<K>, SelectModels, DefaultSelect, I, IncludeModels>;
|
|
342
363
|
|
|
343
364
|
type ResolveSelectModel<MethodConf, GlobalConf, SelectModels> =
|
|
344
365
|
MethodConf extends { selectModel: infer S } ? (S extends false ? false : S extends keyof SelectModels ? S : never) :
|
|
@@ -346,6 +367,7 @@ type ResolveSelectModel<MethodConf, GlobalConf, SelectModels> =
|
|
|
346
367
|
|
|
347
368
|
type ExtractPkName<T, Config> = Config extends { pkName: infer PK } ? (PK extends keyof T ? PK : never) : never;
|
|
348
369
|
type ExtractSelectModels<Config> = Config extends { selectModels: infer SM } ? SM : {};
|
|
370
|
+
type ExtractIncludeModels<Config> = Config extends { includeModels: infer IM } ? IM : {};
|
|
349
371
|
type ExtractDefaultSelect<Config> = Config extends { defaultSelectModel: infer D } ? D : never;
|
|
350
372
|
type ExtractRelations<Config> = Config extends { relations: infer R } ? (R extends object ? R : {}) : {};
|
|
351
373
|
type ExtractSoftRemovekName<Config> = Config extends { softRemovekName: infer S } ? S : never;
|
|
@@ -370,7 +392,7 @@ type DynamicMethods<T, M extends Prisma.ModelName, Config, I> = Config extends {
|
|
|
370
392
|
: ResolvedKey extends 'groupBy'
|
|
371
393
|
? GroupByMethod<M>
|
|
372
394
|
: ResolvedKey extends string
|
|
373
|
-
? MethodFactory<T, M, ResolvedKey, ExtractSelectModels<Config>, ResolveSelectModel<Methods[K], Config, ExtractSelectModels<Config>>, I, Methods[K]
|
|
395
|
+
? MethodFactory<T, M, ResolvedKey, ExtractSelectModels<Config>, ResolveSelectModel<Methods[K], Config, ExtractSelectModels<Config>>, I, Methods[K], ExtractIncludeModels<Config>>
|
|
374
396
|
: never
|
|
375
397
|
: never
|
|
376
398
|
: never;
|
|
@@ -385,6 +407,8 @@ type DynamicMethods<T, M extends Prisma.ModelName, Config, I> = Config extends {
|
|
|
385
407
|
export type PrismaModelInputs<M extends Prisma.ModelName> = {
|
|
386
408
|
/** Type of the `select` argument used in model queries. */
|
|
387
409
|
select: Prisma.TypeMap['model'][M]['operations']['findMany']['args']['select'];
|
|
410
|
+
/** Type of the `include` argument used in model queries. */
|
|
411
|
+
include: Prisma.TypeMap['model'][M]['operations']['findMany']['args']['include'];
|
|
388
412
|
/** Type of the `data` used in `create`. */
|
|
389
413
|
createInput: Prisma.TypeMap['model'][M]['operations']['create']['args']['data'];
|
|
390
414
|
/** Type of the `data` used in `createMany`. */
|
|
@@ -415,6 +439,16 @@ export type SelectModel<M extends Prisma.ModelName> = PrismaModelInputs<M>['sele
|
|
|
415
439
|
*/
|
|
416
440
|
export type SelectModels<M extends Prisma.ModelName> = Record<string, SelectModel<M>>;
|
|
417
441
|
|
|
442
|
+
/**
|
|
443
|
+
* Type of the `include` object of a Prisma model.
|
|
444
|
+
*/
|
|
445
|
+
export type IncludeModel<M extends Prisma.ModelName> = PrismaModelInputs<M>['include'];
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Map of named, reusable includes for a Prisma model.
|
|
449
|
+
*/
|
|
450
|
+
export type IncludeModels<M extends Prisma.ModelName> = Record<string, IncludeModel<M>>;
|
|
451
|
+
|
|
418
452
|
/**
|
|
419
453
|
* Type of the `where` object of a Prisma model.
|
|
420
454
|
*/
|
|
@@ -452,8 +486,7 @@ export type MethodConfig<M extends Prisma.ModelName, SelectModels = any> = {
|
|
|
452
486
|
readonly proxyTo?: ValidMethodPatterns;
|
|
453
487
|
/** Adds an extra `where` on top of `requiredWhere`. */
|
|
454
488
|
readonly pushWhere?: WhereModel<M>;
|
|
455
|
-
/**
|
|
456
|
-
* Defines whether `findBy` returns a single item (`one`) or a list (`list`).
|
|
489
|
+
/** * Defines whether `findBy` returns a single item (`one`) or a list (`list`).
|
|
457
490
|
* @deprecated Use `findOneBy` if you want to return a single result.
|
|
458
491
|
*/
|
|
459
492
|
readonly fbMode?: 'one' | 'list';
|
|
@@ -497,8 +530,12 @@ export type BuildConfig<TSelectKeys extends PropertyKey = string> = {
|
|
|
497
530
|
remove?: BaseMethodConfig<TSelectKeys>;
|
|
498
531
|
/** Configuration for the `save` method. */
|
|
499
532
|
save?: BaseMethodConfig<TSelectKeys>;
|
|
533
|
+
/** Configuration for the `saveList` method (batch save via transaction). */
|
|
534
|
+
saveList?: BaseMethodConfig<TSelectKeys>;
|
|
500
535
|
/** Configuration for the `patch` method. */
|
|
501
536
|
patch?: BaseMethodConfig<TSelectKeys>;
|
|
537
|
+
/** Configuration for the `patchList` method (batch update via transaction). */
|
|
538
|
+
patchList?: BaseMethodConfig<TSelectKeys>;
|
|
502
539
|
/** Configuration for the `merge` method. */
|
|
503
540
|
merge?: BaseMethodConfig<TSelectKeys>;
|
|
504
541
|
/** Configuration for the `removeList` method (batch deletion). Does not accept select. */
|
|
@@ -509,10 +546,6 @@ export type BuildConfig<TSelectKeys extends PropertyKey = string> = {
|
|
|
509
546
|
total?: Omit<BaseMethodConfig<TSelectKeys>, 'defaultSelect'>;
|
|
510
547
|
/** Configuration for the `has` method (existence check). Does not accept select. */
|
|
511
548
|
has?: Omit<BaseMethodConfig<TSelectKeys>, 'defaultSelect'>;
|
|
512
|
-
/** Configuration for the `saveList` method (batch save via transaction). */
|
|
513
|
-
saveList?: BaseMethodConfig<TSelectKeys>;
|
|
514
|
-
/** Configuration for the `patchList` method (batch update via transaction). */
|
|
515
|
-
patchList?: BaseMethodConfig<TSelectKeys>;
|
|
516
549
|
/** Configuration for the `softRemove` method. Only available if `softRemovekName` is configured. */
|
|
517
550
|
softRemove?: BaseMethodConfig<TSelectKeys>;
|
|
518
551
|
/** Configuration for the `softRemoveList` method. Does not accept select. Only available if `softRemovekName` is configured. */
|
|
@@ -535,12 +568,16 @@ type ResolveMethodDefaultSelect<Config, C, Method extends keyof NonNullable<Buil
|
|
|
535
568
|
? ExtractDefaultSelect<Config>
|
|
536
569
|
: false;
|
|
537
570
|
|
|
538
|
-
type ResolveCurrentReturn<M extends Prisma.ModelName, Models, S, D> =
|
|
539
|
-
[
|
|
540
|
-
?
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
571
|
+
type ResolveCurrentReturn<M extends Prisma.ModelName, Models, S, D, IM = never, Includes = {}> =
|
|
572
|
+
[IM] extends [never]
|
|
573
|
+
? [S] extends [false]
|
|
574
|
+
? FullModelType<M>
|
|
575
|
+
: [S] extends [never]
|
|
576
|
+
? ([D] extends [never] ? FullModelType<M> : SelectedModel<M, D, Models>)
|
|
577
|
+
: SelectedModel<M, S, Models>
|
|
578
|
+
: IM extends keyof Includes
|
|
579
|
+
? PrecomputedIncludes<M, Includes>[IM]
|
|
580
|
+
: FullModelType<M>;
|
|
544
581
|
|
|
545
582
|
// ─── helpers reused within the mapped type ───────────────────────────────────
|
|
546
583
|
// ─── relation payload types ──────────────────────────────────────────────────
|
|
@@ -603,13 +640,21 @@ type RelationUpdatePayload<TField, TRelationConfig, M extends Prisma.ModelName,
|
|
|
603
640
|
: never;
|
|
604
641
|
|
|
605
642
|
/**
|
|
606
|
-
* Payload accepted by `
|
|
643
|
+
* Payload accepted by `merge` when the repository has configured relations (uses update input).
|
|
607
644
|
*/
|
|
608
645
|
type UpdateWithRelations<T, M extends Prisma.ModelName, TRelations> =
|
|
609
646
|
DistributiveOmit<PrismaModelInputs<M>['updateInput'], keyof TRelations> & {
|
|
610
647
|
[K in Extract<keyof TRelations, keyof T>]?: RelationUpdatePayload<T[K], TRelations[K], M, K>;
|
|
611
648
|
};
|
|
612
649
|
|
|
650
|
+
/**
|
|
651
|
+
* Payload accepted by `patch` and `patchList` when the repository has configured relations (uses create input).
|
|
652
|
+
*/
|
|
653
|
+
type PatchWithRelations<T, M extends Prisma.ModelName, TRelations> =
|
|
654
|
+
DistributiveOmit<PrismaModelInputs<M>['updateInput'], keyof TRelations> & {
|
|
655
|
+
[K in Extract<keyof TRelations, keyof T>]?: RelationPayload<T[K], TRelations[K], M, K>;
|
|
656
|
+
};
|
|
657
|
+
|
|
613
658
|
/**
|
|
614
659
|
* Extracts the payload type of the `save` method from a configured VSRepository instance.
|
|
615
660
|
*/
|
|
@@ -631,17 +676,18 @@ export type PatchObject<TInput, TRepo> =
|
|
|
631
676
|
TRepo extends VSRepository<infer T, infer M, infer Config>
|
|
632
677
|
? (Config extends { relations: infer R } ? (R extends object ? R : {}) : {}) extends infer TRelations
|
|
633
678
|
? DistributiveOmit<TInput, keyof TRelations> & {
|
|
634
|
-
[K in Extract<keyof TRelations, keyof T>]?:
|
|
679
|
+
[K in Extract<keyof TRelations, keyof T>]?: RelationPayload<T[K], TRelations[K], M, K>;
|
|
635
680
|
}
|
|
636
681
|
: never
|
|
637
682
|
: never;
|
|
638
683
|
|
|
639
684
|
|
|
640
685
|
type _Pk<T, Config> = WidenField<T[ExtractPkName<T, Config> extends keyof T ? ExtractPkName<T, Config> : never]>;
|
|
641
|
-
type _Ret<M extends Prisma.ModelName, TSelects, S, TDefault> = ResolveCurrentReturn<M, TSelects, S, TDefault>;
|
|
686
|
+
type _Ret<M extends Prisma.ModelName, TSelects, S, TDefault, IM = never, TIncludes = {}> = ResolveCurrentReturn<M, TSelects, S, TDefault, IM, TIncludes>;
|
|
642
687
|
type _DS<Config, C, Method extends keyof NonNullable<BuildConfig['baseMethods']>, TSelects = ExtractSelectModels<Config>> =
|
|
643
688
|
ResolveMethodDefaultSelect<Config, C, Method, TSelects>;
|
|
644
689
|
type _Sel<Config> = ExtractSelectModels<Config>;
|
|
690
|
+
type _Inc<Config> = ExtractIncludeModels<Config>;
|
|
645
691
|
type _Def<Config> = ExtractDefaultSelect<Config>;
|
|
646
692
|
type _Rel<Config> = ExtractRelations<Config>;
|
|
647
693
|
type _Soft<Config> = ExtractSoftRemovekName<Config>;
|
|
@@ -660,59 +706,60 @@ type AllBaseMethods<
|
|
|
660
706
|
TRelations = _Rel<Config>,
|
|
661
707
|
TSoftKey = _Soft<Config>,
|
|
662
708
|
I = PrismaModelInputs<M>,
|
|
663
|
-
TDefaultOrdenation = _DOrd<Config
|
|
709
|
+
TDefaultOrdenation = _DOrd<Config>,
|
|
710
|
+
TIncludes = _Inc<Config>
|
|
664
711
|
> = {
|
|
665
712
|
/** Fetches a record by its primary key (PK). */
|
|
666
|
-
get: <S extends keyof TSelects | false = _DS<Config, C, 'get', TSelects
|
|
667
|
-
pk: _Pk<T, Config>, options?:
|
|
668
|
-
) => Promise<_Ret<M, TSelects, S, TDefault> | null>;
|
|
713
|
+
get: <S extends keyof TSelects | false = _DS<Config, C, 'get', TSelects>, IM extends keyof TIncludes = never>(
|
|
714
|
+
pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes>
|
|
715
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes> | null>;
|
|
669
716
|
|
|
670
717
|
/** Fetches a record by PK and throws `VSRepoRuntimeError` if not found. */
|
|
671
|
-
getOrThrow: <S extends keyof TSelects | false = _DS<Config, C, 'getOrThrow', TSelects
|
|
672
|
-
pk: _Pk<T, Config>, options?:
|
|
673
|
-
) => Promise<_Ret<M, TSelects, S, TDefault>>;
|
|
718
|
+
getOrThrow: <S extends keyof TSelects | false = _DS<Config, C, 'getOrThrow', TSelects>, IM extends keyof TIncludes = never>(
|
|
719
|
+
pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes>
|
|
720
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes>>;
|
|
674
721
|
|
|
675
722
|
/** Fetches multiple records by a list of primary keys (PKs). */
|
|
676
|
-
getList: <S extends keyof TSelects | false = _DS<Config, C, 'getList', TSelects
|
|
677
|
-
pks: _Pk<T, Config>[], options?:
|
|
678
|
-
) => Promise<_Ret<M, TSelects, S, TDefault>[]>;
|
|
723
|
+
getList: <S extends keyof TSelects | false = _DS<Config, C, 'getList', TSelects>, IM extends keyof TIncludes = never>(
|
|
724
|
+
pks: _Pk<T, Config>[], options?: MethodOptionsWithInclude<S, IM, TIncludes>
|
|
725
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes>[]>;
|
|
679
726
|
|
|
680
727
|
/** Deletes a record identified by its primary key (PK). */
|
|
681
|
-
remove: <S extends keyof TSelects | false = _DS<Config, C, 'remove', TSelects
|
|
682
|
-
pk: _Pk<T, Config>, options?:
|
|
683
|
-
) => Promise<_Ret<M, TSelects, S, TDefault>>;
|
|
728
|
+
remove: <S extends keyof TSelects | false = _DS<Config, C, 'remove', TSelects>, IM extends keyof TIncludes = never>(
|
|
729
|
+
pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes>
|
|
730
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes>>;
|
|
684
731
|
|
|
685
732
|
/** Inserts or updates (upsert) a record. */
|
|
686
|
-
save: <S extends keyof TSelects | false = _DS<Config, C, 'save', TSelects
|
|
687
|
-
obj: UpsertWithRelations<T, M, TRelations>, options?:
|
|
688
|
-
) => Promise<_Ret<M, TSelects, S, TDefault>>;
|
|
733
|
+
save: <S extends keyof TSelects | false = _DS<Config, C, 'save', TSelects>, IM extends keyof TIncludes = never>(
|
|
734
|
+
obj: UpsertWithRelations<T, M, TRelations>, options?: MethodOptionsWithInclude<S, IM, TIncludes>
|
|
735
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes>>;
|
|
689
736
|
|
|
690
737
|
/** Saves an array of objects in a single automatic transaction. */
|
|
691
|
-
saveList: <S extends keyof TSelects | false = _DS<Config, C, 'saveList', TSelects
|
|
692
|
-
objs: UpsertWithRelations<T, M, TRelations>[], options?:
|
|
693
|
-
) => Promise<_Ret<M, TSelects, S, TDefault>[]>;
|
|
738
|
+
saveList: <S extends keyof TSelects | false = _DS<Config, C, 'saveList', TSelects>, IM extends keyof TIncludes = never>(
|
|
739
|
+
objs: UpsertWithRelations<T, M, TRelations>[], options?: MethodOptionsWithInclude<S, IM, TIncludes, 'db'> & { db?: DbTransaction }
|
|
740
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes>[]>;
|
|
694
741
|
|
|
695
742
|
/** Partially updates (patch) an existing record by its primary key (PK). */
|
|
696
|
-
patch: <S extends keyof TSelects | false = _DS<Config, C, 'patch', TSelects
|
|
697
|
-
pk: _Pk<T, Config>, obj:
|
|
698
|
-
) => Promise<_Ret<M, TSelects, S, TDefault>>;
|
|
743
|
+
patch: <S extends keyof TSelects | false = _DS<Config, C, 'patch', TSelects>, IM extends keyof TIncludes = never>(
|
|
744
|
+
pk: _Pk<T, Config>, obj: PatchWithRelations<T, M, TRelations>, options?: MethodOptionsWithInclude<S, IM, TIncludes>
|
|
745
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes>>;
|
|
699
746
|
|
|
700
747
|
/** Partially updates multiple records via `[pk, obj]` tuples in an automatic transaction. */
|
|
701
|
-
patchList: <S extends keyof TSelects | false = _DS<Config, C, 'patchList', TSelects
|
|
702
|
-
tuples: [pk: _Pk<T, Config>, obj:
|
|
703
|
-
) => Promise<_Ret<M, TSelects, S, TDefault>[]>;
|
|
748
|
+
patchList: <S extends keyof TSelects | false = _DS<Config, C, 'patchList', TSelects>, IM extends keyof TIncludes = never>(
|
|
749
|
+
tuples: [pk: _Pk<T, Config>, obj: PatchWithRelations<T, M, TRelations>][], options?: MethodOptionsWithInclude<S, IM, TIncludes, 'db'> & { db?: DbTransaction }
|
|
750
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes>[]>;
|
|
704
751
|
|
|
705
752
|
/** Fetches a record by PK and deep-merges it with the provided object **in memory**. */
|
|
706
|
-
merge: <S extends keyof TSelects | false = _DS<Config, C, 'merge', TSelects
|
|
707
|
-
pk: _Pk<T, Config>, obj: UpdateWithRelations<T, M, TRelations>, options?:
|
|
708
|
-
) => Promise<_Ret<M, TSelects, S, TDefault> | null>;
|
|
753
|
+
merge: <S extends keyof TSelects | false = _DS<Config, C, 'merge', TSelects>, IM extends keyof TIncludes = never>(
|
|
754
|
+
pk: _Pk<T, Config>, obj: UpdateWithRelations<T, M, TRelations>, options?: MethodOptionsWithInclude<S, IM, TIncludes>
|
|
755
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes> | null>;
|
|
709
756
|
|
|
710
757
|
/** Deletes multiple records by their primary keys. */
|
|
711
758
|
removeList: (pks: _Pk<T, Config>[], options?: { db?: ClientOrTransaction }) => Promise<{ count: number }>;
|
|
712
759
|
|
|
713
760
|
/** Fetches all records (respects `requiredWhere` when set). */
|
|
714
|
-
getAll: <S extends keyof TSelects | false = _DS<Config, C, 'getAll', TSelects
|
|
715
|
-
options?:
|
|
761
|
+
getAll: <S extends keyof TSelects | false = _DS<Config, C, 'getAll', TSelects>, IM extends keyof TIncludes = never>(
|
|
762
|
+
options?: MethodOptionsWithInclude<S, IM, TIncludes> & {
|
|
716
763
|
pagination?: PaginationOptions<I extends { cursorInput: infer Curs } ? Curs : unknown>;
|
|
717
764
|
/**
|
|
718
765
|
* Ordering to apply to the query.
|
|
@@ -721,7 +768,7 @@ type AllBaseMethods<
|
|
|
721
768
|
*/
|
|
722
769
|
order?: I extends { orderByInput: infer OB } ? OB : OrderOptions;
|
|
723
770
|
}
|
|
724
|
-
) => Promise<_Ret<M, TSelects, S, TDefault>[]>;
|
|
771
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes>[]>;
|
|
725
772
|
|
|
726
773
|
/** Returns the total number of records. */
|
|
727
774
|
total: (options?: { db?: ClientOrTransaction; see?: SeeMode }) => Promise<number>;
|
|
@@ -730,17 +777,17 @@ type AllBaseMethods<
|
|
|
730
777
|
has: (pk: _Pk<T, Config>, options?: { db?: ClientOrTransaction; see?: SeeMode }) => Promise<boolean>;
|
|
731
778
|
|
|
732
779
|
/** Marks a record as deleted (soft-delete). */
|
|
733
|
-
softRemove: <S extends keyof TSelects | false = _DS<Config, C, 'softRemove', TSelects
|
|
734
|
-
pk: _Pk<T, Config>, options?:
|
|
735
|
-
) => Promise<_Ret<M, TSelects, S, TDefault>>;
|
|
780
|
+
softRemove: <S extends keyof TSelects | false = _DS<Config, C, 'softRemove', TSelects>, IM extends keyof TIncludes = never>(
|
|
781
|
+
pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, 'see'>
|
|
782
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes>>;
|
|
736
783
|
|
|
737
784
|
/** Marks multiple records as deleted (soft-delete) in batch. */
|
|
738
785
|
softRemoveList: (pks: _Pk<T, Config>[], options?: { db?: ClientOrTransaction }) => Promise<{ count: number }>;
|
|
739
786
|
|
|
740
787
|
/** Restores a record previously marked as deleted (soft-delete). */
|
|
741
|
-
restore: <S extends keyof TSelects | false = _DS<Config, C, 'restore', TSelects
|
|
742
|
-
pk: _Pk<T, Config>, options?:
|
|
743
|
-
) => Promise<_Ret<M, TSelects, S, TDefault>>;
|
|
788
|
+
restore: <S extends keyof TSelects | false = _DS<Config, C, 'restore', TSelects>, IM extends keyof TIncludes = never>(
|
|
789
|
+
pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, 'see'>
|
|
790
|
+
) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes>>;
|
|
744
791
|
|
|
745
792
|
/** Restores multiple records previously marked as deleted (soft-delete) in batch. */
|
|
746
793
|
restoreList: (pks: _Pk<T, Config>[], options?: { db?: ClientOrTransaction }) => Promise<{ count: number }>;
|
|
@@ -757,9 +804,10 @@ type InjectedBaseMethods<
|
|
|
757
804
|
TRelations = _Rel<Config>,
|
|
758
805
|
TSoftKey = _Soft<Config>,
|
|
759
806
|
I = PrismaModelInputs<M>,
|
|
760
|
-
TDefaultOrdenation = _DOrd<Config
|
|
807
|
+
TDefaultOrdenation = _DOrd<Config>,
|
|
808
|
+
TIncludes = _Inc<Config>
|
|
761
809
|
> = Pick<
|
|
762
|
-
AllBaseMethods<T, M, Config, C, TSelects, TDefault, TPk, TRelations, TSoftKey, I, TDefaultOrdenation>,
|
|
810
|
+
AllBaseMethods<T, M, Config, C, TSelects, TDefault, TPk, TRelations, TSoftKey, I, TDefaultOrdenation, TIncludes>,
|
|
763
811
|
| (C extends { baseMethods: { get: { active: false } } } ? never : 'get')
|
|
764
812
|
| (C extends { baseMethods: { getOrThrow: { active: false } } } ? never : 'getOrThrow')
|
|
765
813
|
| (C extends { baseMethods: { getList: { active: false } } } ? never : 'getList')
|
|
@@ -861,6 +909,7 @@ export type RepoConfig<T, M extends Prisma.ModelName, SM extends Record<string,
|
|
|
861
909
|
softRemovekName?: keyof T & string;
|
|
862
910
|
selectModels?: SM;
|
|
863
911
|
defaultSelectModel?: Extract<keyof SM, string>;
|
|
912
|
+
includeModels?: IncludeModels<M>;
|
|
864
913
|
requiredWhere?: WhereModel<M>;
|
|
865
914
|
defaultOrdenation?: OrdenationModel<M>;
|
|
866
915
|
relations?: RepositoryRelations<T>;
|
|
@@ -946,6 +995,12 @@ export type ValidateRepoConfig<T extends object, M extends Prisma.ModelName, Con
|
|
|
946
995
|
*/
|
|
947
996
|
defaultSelectModel?: string;
|
|
948
997
|
|
|
998
|
+
/**
|
|
999
|
+
* Defines named and reusable include projections.
|
|
1000
|
+
* Similar to selectModels, but resolves Prisma `include` clauses.
|
|
1001
|
+
*/
|
|
1002
|
+
includeModels?: IncludeModels<M>;
|
|
1003
|
+
|
|
949
1004
|
/**
|
|
950
1005
|
* Defines global filters that will be automatically applied to all repository queries.
|
|
951
1006
|
* Useful for tenant isolation (multi-tenancy) or base restrictions (e.g., `isActive: true`).
|
package/dist/VSRepository.js
CHANGED
|
@@ -35,6 +35,7 @@ class VSRepository {
|
|
|
35
35
|
pkName;
|
|
36
36
|
softRemovekName;
|
|
37
37
|
selectModels;
|
|
38
|
+
includeModels;
|
|
38
39
|
defaultSelectModel;
|
|
39
40
|
requiredWhere;
|
|
40
41
|
relations;
|
|
@@ -47,6 +48,7 @@ class VSRepository {
|
|
|
47
48
|
this.pkName = validatedConfig.pkName;
|
|
48
49
|
this.softRemovekName = validatedConfig.softRemovekName;
|
|
49
50
|
this.selectModels = validatedConfig.selectModels;
|
|
51
|
+
this.includeModels = validatedConfig.includeModels;
|
|
50
52
|
this.defaultSelectModel = validatedConfig.defaultSelectModel;
|
|
51
53
|
this.relations = validatedConfig.relations;
|
|
52
54
|
this.requiredWhere = validatedConfig.requiredWhere;
|
|
@@ -162,7 +164,7 @@ class VSRepository {
|
|
|
162
164
|
const missingParams = dinamicMethodInfo.whereParams
|
|
163
165
|
.concat(dinamicMethodInfo.otherParams)
|
|
164
166
|
.slice(args.length);
|
|
165
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${buildInstance.tableName}: runtime) Missing parameters: ${missingParams.join(", ")}
|
|
167
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${buildInstance.tableName}: runtime) Missing parameters: ${missingParams.join(", ")}`, "48670");
|
|
166
168
|
}
|
|
167
169
|
else if (args.length > dinamicMethodInfo.argsCount) {
|
|
168
170
|
const optionsArg = args[args.length - 1];
|
|
@@ -21,7 +21,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
21
21
|
if (baseMethods.get.active) {
|
|
22
22
|
instance.get = async (pk, options) => {
|
|
23
23
|
if (pk == undefined)
|
|
24
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided
|
|
24
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided.`, "65706");
|
|
25
25
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
26
26
|
instance,
|
|
27
27
|
baseConfig: baseMethods.get,
|
|
@@ -41,7 +41,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
41
41
|
if (baseMethods.getOrThrow.active) {
|
|
42
42
|
instance.getOrThrow = async (pk, options) => {
|
|
43
43
|
if (pk == undefined)
|
|
44
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided
|
|
44
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided.`, "65706");
|
|
45
45
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
46
46
|
instance,
|
|
47
47
|
baseConfig: baseMethods.getOrThrow,
|
|
@@ -64,7 +64,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
64
64
|
if (baseMethods.getList.active) {
|
|
65
65
|
instance.getList = async (pks, options) => {
|
|
66
66
|
if (!Array.isArray(pks) || pks.some(pk => pk == undefined))
|
|
67
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pks' must an array of primary keys
|
|
67
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pks' must an array of primary keys.`, "65706");
|
|
68
68
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
69
69
|
instance,
|
|
70
70
|
baseConfig: baseMethods.getList,
|
|
@@ -84,7 +84,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
84
84
|
if (baseMethods.getAll.active) {
|
|
85
85
|
instance.getAll = async (options) => {
|
|
86
86
|
if (options !== undefined && !(0, is_object_validate_1.isObject)(options))
|
|
87
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'options' must be a valid object
|
|
87
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'options' must be a valid object.`, "65706");
|
|
88
88
|
const { pagination, order, ...restOptions } = options ?? {};
|
|
89
89
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
90
90
|
instance,
|
|
@@ -107,7 +107,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
107
107
|
if (baseMethods.remove.active) {
|
|
108
108
|
instance.remove = async (pk, options) => {
|
|
109
109
|
if (pk == undefined)
|
|
110
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided
|
|
110
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided.`, "65706");
|
|
111
111
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
112
112
|
instance,
|
|
113
113
|
baseConfig: baseMethods.remove,
|
|
@@ -127,7 +127,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
127
127
|
if (baseMethods.removeList.active) {
|
|
128
128
|
instance.removeList = async (pks, options) => {
|
|
129
129
|
if (!Array.isArray(pks) || pks.some(pk => pk == undefined))
|
|
130
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pks' must an array of primary keys
|
|
130
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pks' must an array of primary keys.`, "65706");
|
|
131
131
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
132
132
|
instance,
|
|
133
133
|
baseConfig: baseMethods.removeList,
|
|
@@ -166,7 +166,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
166
166
|
if (baseMethods.has.active) {
|
|
167
167
|
instance.has = async (pk, options) => {
|
|
168
168
|
if (pk == undefined)
|
|
169
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided
|
|
169
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided.`, "65706");
|
|
170
170
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
171
171
|
instance,
|
|
172
172
|
baseConfig: baseMethods.has,
|
|
@@ -187,9 +187,9 @@ function resolveBaseMethods(instance, config) {
|
|
|
187
187
|
if (baseMethods.merge.active) {
|
|
188
188
|
instance.merge = async (pk, obj, options) => {
|
|
189
189
|
if (pk == undefined)
|
|
190
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided
|
|
190
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided.`, "65706");
|
|
191
191
|
if (!(0, is_object_validate_1.isObject)(obj)) {
|
|
192
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'obj' must be a valid object
|
|
192
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'obj' must be a valid object.`, "65706");
|
|
193
193
|
}
|
|
194
194
|
(0, obj_with_relations_validate_1.validateObjWithRelations)(instance, obj, relationsKeys);
|
|
195
195
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
@@ -273,7 +273,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
273
273
|
if (baseMethods.softRemove.active && softRemovekName) {
|
|
274
274
|
instance.softRemove = async (pk, options) => {
|
|
275
275
|
if (pk == undefined)
|
|
276
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided
|
|
276
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided.`, "65706");
|
|
277
277
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
278
278
|
instance,
|
|
279
279
|
baseConfig: baseMethods.softRemove,
|
|
@@ -295,7 +295,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
295
295
|
if (baseMethods.softRemoveList.active && softRemovekName) {
|
|
296
296
|
instance.softRemoveList = async (pks, options) => {
|
|
297
297
|
if (!Array.isArray(pks) || pks.some(pk => pk == undefined))
|
|
298
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pks' must an array of primary keys
|
|
298
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pks' must an array of primary keys.`, "65706");
|
|
299
299
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
300
300
|
instance,
|
|
301
301
|
baseConfig: baseMethods.softRemoveList,
|
|
@@ -318,7 +318,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
318
318
|
if (baseMethods.restore.active && softRemovekName) {
|
|
319
319
|
instance.restore = async (pk, options) => {
|
|
320
320
|
if (pk == undefined)
|
|
321
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided
|
|
321
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided.`, "65706");
|
|
322
322
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
323
323
|
instance,
|
|
324
324
|
baseConfig: baseMethods.restore,
|
|
@@ -340,7 +340,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
340
340
|
if (baseMethods.restoreList.active && softRemovekName) {
|
|
341
341
|
instance.restoreList = async (pks, options) => {
|
|
342
342
|
if (!Array.isArray(pks) || pks.some(pk => pk == undefined))
|
|
343
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pks' must an array of primary keys
|
|
343
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pks' must an array of primary keys.`, "65706");
|
|
344
344
|
const { db, prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)({
|
|
345
345
|
instance,
|
|
346
346
|
baseConfig: baseMethods.restoreList,
|
|
@@ -363,7 +363,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
363
363
|
if (baseMethods.save.active) {
|
|
364
364
|
instance.save = async (obj, options) => {
|
|
365
365
|
if (!(0, is_object_validate_1.isObject)(obj)) {
|
|
366
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'obj' must be a valid object
|
|
366
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'obj' must be a valid object.`, "65706");
|
|
367
367
|
}
|
|
368
368
|
(0, obj_with_relations_validate_1.validateObjWithRelations)(instance, obj, relationsKeys);
|
|
369
369
|
const objAny = obj;
|
|
@@ -407,7 +407,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
407
407
|
if (baseMethods.saveList.active) {
|
|
408
408
|
instance.saveList = async (objs, options) => {
|
|
409
409
|
if (!Array.isArray(objs) || objs.some(ob => !(0, is_object_validate_1.isObject)(ob))) {
|
|
410
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'objs' must be an array of valid objects
|
|
410
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'objs' must be an array of valid objects.`, "65706");
|
|
411
411
|
}
|
|
412
412
|
const validatedOptions = (0, method_options_validate_1.validateMethodOptions)(options, instance);
|
|
413
413
|
const argsList = [];
|
|
@@ -466,9 +466,9 @@ function resolveBaseMethods(instance, config) {
|
|
|
466
466
|
if (baseMethods.patch.active) {
|
|
467
467
|
instance.patch = async (pk, obj, options) => {
|
|
468
468
|
if (pk == undefined)
|
|
469
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided
|
|
469
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'pk' must be provided.`, "65706");
|
|
470
470
|
if (!(0, is_object_validate_1.isObject)(obj)) {
|
|
471
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'obj' must be a valid object
|
|
471
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'obj' must be a valid object.`, "65706");
|
|
472
472
|
}
|
|
473
473
|
(0, obj_with_relations_validate_1.validateObjWithRelations)(instance, obj, relationsKeys);
|
|
474
474
|
const { updatePayload } = (0, create_update_payloads_with_relations_resolve_1.resolveCreateUpdatePayloadsWithRelations)(instance, obj, relationsKeys);
|
|
@@ -493,7 +493,7 @@ function resolveBaseMethods(instance, config) {
|
|
|
493
493
|
instance.patchList = async (tuples, options) => {
|
|
494
494
|
if (!Array.isArray(tuples) ||
|
|
495
495
|
tuples.some(tuple => tuple[0] == undefined || !(0, is_object_validate_1.isObject)(tuple[1]))) {
|
|
496
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'tuples' must be a valid array of tuples [pk, obj]
|
|
496
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${tableName}: runtime) 'tuples' must be a valid array of tuples [pk, obj].`, "65706");
|
|
497
497
|
}
|
|
498
498
|
const validatedOptions = (0, method_options_validate_1.validateMethodOptions)(options, instance);
|
|
499
499
|
const argsList = [];
|
|
@@ -6,7 +6,7 @@ const merge_wheres_resolve_1 = require("./merge-wheres.resolve");
|
|
|
6
6
|
const select_resolve_1 = require("./select.resolve");
|
|
7
7
|
function resolveDbAndPrismaArgs(data) {
|
|
8
8
|
const { baseConfig, instance, options, wherePkValue, withoutSelect, withoutWhere, specificSelect, dataPayload, createPayload, updatePayload, alreadyValidatedOptions, specificWhere, pushWhere, ordenation, pagination, skipDuplicates, forceSeeMode, withOrdenationAndPagination, } = data;
|
|
9
|
-
const validatedOptions =
|
|
9
|
+
const validatedOptions = alreadyValidatedOptions && options
|
|
10
10
|
? options
|
|
11
11
|
: (0, method_options_validate_1.validateMethodOptions)(options, instance);
|
|
12
12
|
const db = validatedOptions.db ?? instance.prisma;
|
|
@@ -18,9 +18,15 @@ function resolveDbAndPrismaArgs(data) {
|
|
|
18
18
|
}, wherePkValue != undefined ? { [instance.pkName]: wherePkValue } : specificWhere, pushWhere, instance.requiredWhere, instance.softRemovekName);
|
|
19
19
|
}
|
|
20
20
|
if (!withoutSelect) {
|
|
21
|
-
|
|
22
|
-
specificSelect
|
|
23
|
-
|
|
21
|
+
if (specificSelect) {
|
|
22
|
+
prismaArgs.select = specificSelect;
|
|
23
|
+
}
|
|
24
|
+
else if (validatedOptions.includeModel) {
|
|
25
|
+
prismaArgs.include = instance.includeModels?.[validatedOptions.includeModel];
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
prismaArgs.select = (0, select_resolve_1.resolveSelect)(instance, validatedOptions.selectModel, baseConfig.defaultSelect);
|
|
29
|
+
}
|
|
24
30
|
}
|
|
25
31
|
if (dataPayload) {
|
|
26
32
|
prismaArgs.data = dataPayload;
|
|
@@ -14,6 +14,7 @@ function validateConstructorConfig(config) {
|
|
|
14
14
|
pkName: schemas_util_1.stringSchema,
|
|
15
15
|
softRemovekName: schemas_util_1.stringSchema.optional(),
|
|
16
16
|
selectModels: zod_1.default.record(schemas_util_1.stringSchema, schemas_util_1.objectSchema).optional(),
|
|
17
|
+
includeModels: zod_1.default.record(schemas_util_1.stringSchema, schemas_util_1.objectSchema).optional(),
|
|
17
18
|
defaultSelectModel: schemas_util_1.stringSchema.optional(),
|
|
18
19
|
requiredWhere: schemas_util_1.objectSchema.optional(),
|
|
19
20
|
relations: zod_1.default
|
|
@@ -8,19 +8,27 @@ const zod_1 = __importDefault(require("zod"));
|
|
|
8
8
|
const schemas_util_1 = require("../utils/schemas.util");
|
|
9
9
|
const vs_repo_error_1 = require("../errors/vs-repo.error");
|
|
10
10
|
function validateMethodOptions(options, instance) {
|
|
11
|
-
const optionsSchema = zod_1.default
|
|
11
|
+
const optionsSchema = zod_1.default
|
|
12
|
+
.strictObject({
|
|
12
13
|
db: schemas_util_1.objectSchema.optional(),
|
|
13
14
|
selectModel: zod_1.default
|
|
14
15
|
.literal(false)
|
|
15
16
|
.or(zod_1.default.enum(Object.keys(instance.selectModels ?? {})))
|
|
16
17
|
.optional(),
|
|
18
|
+
includeModel: zod_1.default.enum(Object.keys(instance.includeModels ?? {})).optional(),
|
|
17
19
|
see: zod_1.default.enum(["active", "removed", "all"]).default("active"),
|
|
20
|
+
})
|
|
21
|
+
.refine(data => {
|
|
22
|
+
return !(data.selectModel !== undefined && data.includeModel !== undefined);
|
|
23
|
+
}, {
|
|
24
|
+
message: "cannot be provided with 'selectModel'.",
|
|
25
|
+
path: ["includeModel"],
|
|
18
26
|
});
|
|
19
27
|
const optionsParsed = optionsSchema.safeParse(options ?? {});
|
|
20
28
|
if (!optionsParsed.success) {
|
|
21
29
|
const firstIssue = optionsParsed.error.issues[0];
|
|
22
30
|
const path = firstIssue?.path.length ? firstIssue.path.join(".") : "options";
|
|
23
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) ${path}: ${firstIssue?.message}
|
|
31
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) ${path}: ${firstIssue?.message}`, "67542");
|
|
24
32
|
}
|
|
25
33
|
return optionsParsed.data;
|
|
26
34
|
}
|
|
@@ -14,23 +14,23 @@ function validateObjWithRelations(instance, obj, relationsKeys) {
|
|
|
14
14
|
const relation = relations[key];
|
|
15
15
|
if (relation.mode === "mtm" || relation.mode === "otm") {
|
|
16
16
|
if (obj[key] === null) {
|
|
17
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) '${key}' cannot be 'null' when relation mode is "mtm" or "otm", use an empty array instead
|
|
17
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) '${key}' cannot be 'null' when relation mode is "mtm" or "otm", use an empty array instead.`, "91868");
|
|
18
18
|
}
|
|
19
19
|
else if (!Array.isArray(obj[key]) || obj[key].some(val => !(0, is_object_validate_1.isObject)(val))) {
|
|
20
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) '${key}' must be a valid array of objects when relation mode is "mtm" or "otm"
|
|
20
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) '${key}' must be a valid array of objects when relation mode is "mtm" or "otm".`, "91868");
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
else {
|
|
24
24
|
if (obj[key] === null) {
|
|
25
25
|
if (relation.mode === "oto" && relation.restriction !== "set") {
|
|
26
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) '${key}' can be 'null' only if the relation restriction is "set" and mode is "oto"
|
|
26
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) '${key}' can be 'null' only if the relation restriction is "set" and mode is "oto".`, "91868");
|
|
27
27
|
}
|
|
28
28
|
else if (relation.mode === "mto" && !relation.nullable && !relation.nullAble) {
|
|
29
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) '${key}' can be 'null' only if the relation 'nullable' is 'true' and mode is "mto"
|
|
29
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) '${key}' can be 'null' only if the relation 'nullable' is 'true' and mode is "mto".`, "91868");
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
else if (!(0, is_object_validate_1.isObject)(obj[key])) {
|
|
33
|
-
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) '${key}' must be a valid object when relation mode is "oto" or "mto"
|
|
33
|
+
throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${instance.tableName}: runtime) '${key}' must be a valid object when relation mode is "oto" or "mto".`, "91868");
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|