vsrepo 1.4.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +827 -1322
  2. package/README.pt-BR.md +833 -1325
  3. package/dist/VSRepoAdapter.d.ts +109 -0
  4. package/dist/VSRepoAdapter.js +18 -0
  5. package/dist/VSRepository.d.ts +166 -1201
  6. package/dist/VSRepository.js +327 -237
  7. package/dist/decorators/dynamic-method.decorator.d.ts +25 -0
  8. package/dist/decorators/dynamic-method.decorator.js +38 -0
  9. package/dist/decorators/query-method.decorator.d.ts +27 -0
  10. package/dist/decorators/query-method.decorator.js +45 -0
  11. package/dist/errors/VSRepoAdapterError.d.ts +22 -0
  12. package/dist/errors/VSRepoAdapterError.js +31 -0
  13. package/dist/errors/VSRepoError.d.ts +15 -0
  14. package/dist/errors/VSRepoError.js +21 -0
  15. package/dist/index.d.ts +38 -1
  16. package/dist/index.js +28 -15
  17. package/dist/internal/constants/debug-arg-symbol.constant.d.ts +1 -0
  18. package/dist/internal/constants/debug-arg-symbol.constant.js +4 -0
  19. package/dist/internal/constants/dynamic-methods-key.constant.d.ts +1 -0
  20. package/dist/internal/constants/query-methods-key.constant.d.ts +1 -0
  21. package/dist/internal/constants/query-methods-key.constant.js +4 -0
  22. package/dist/internal/enums/adapter-error-code.enum.d.ts +125 -0
  23. package/dist/internal/enums/adapter-error-code.enum.js +129 -0
  24. package/dist/internal/enums/transaction-isolation-level.enum.d.ts +16 -0
  25. package/dist/internal/enums/transaction-isolation-level.enum.js +20 -0
  26. package/dist/internal/enums/vs-log-level.enum.d.ts +18 -0
  27. package/dist/internal/enums/vs-log-level.enum.js +22 -0
  28. package/dist/internal/enums/vsrepo-error-type.enum.d.ts +19 -0
  29. package/dist/internal/enums/vsrepo-error-type.enum.js +23 -0
  30. package/dist/internal/resolvers/dynamic-methods.resolver.d.ts +23 -0
  31. package/dist/internal/resolvers/dynamic-methods.resolver.js +910 -0
  32. package/dist/internal/resolvers/merge-wheres.resolver.d.ts +7 -0
  33. package/dist/internal/resolvers/merge-wheres.resolver.js +26 -0
  34. package/dist/internal/utils/uncapitalize.util.d.ts +1 -0
  35. package/dist/internal/utils/vs-logger.util.d.ts +25 -0
  36. package/dist/internal/utils/vs-logger.util.js +139 -0
  37. package/dist/internal/validators/decorators.validator.d.ts +8 -0
  38. package/dist/internal/validators/decorators.validator.js +75 -0
  39. package/dist/internal/validators/schemas/ordering.schema.d.ts +3 -0
  40. package/dist/internal/validators/schemas/ordering.schema.js +38 -0
  41. package/dist/internal/validators/schemas/pagination.schema.d.ts +6 -0
  42. package/dist/internal/validators/schemas/pagination.schema.js +40 -0
  43. package/dist/internal/validators/schemas/where.schema.d.ts +7 -0
  44. package/dist/internal/validators/schemas/where.schema.js +42 -0
  45. package/dist/internal/validators/vsrepo.validator.d.ts +40 -0
  46. package/dist/internal/validators/vsrepo.validator.js +182 -0
  47. package/dist/types/adapter/adapter-method-options.type.d.ts +24 -0
  48. package/dist/types/adapter/adapter-query-options.type.d.ts +5 -0
  49. package/dist/types/decorators/dynamic-method-options.type.d.ts +14 -0
  50. package/dist/types/decorators/query-method-options.type.d.ts +15 -0
  51. package/dist/types/dynamic-methods/dynamic-method-customization.type.d.ts +7 -0
  52. package/dist/types/dynamic-methods/dynamic-method-info.type.d.ts +18 -0
  53. package/dist/types/dynamic-methods/dynamic-method-where-ops.type.d.ts +6 -0
  54. package/dist/types/utils/count-result.type.d.ts +9 -0
  55. package/dist/types/utils/decimal-like.type.d.ts +23 -0
  56. package/dist/types/utils/deep-partial.type.d.ts +14 -0
  57. package/dist/types/utils/keys-of-type.type.d.ts +20 -0
  58. package/dist/types/utils/methods-options.type.d.ts +23 -0
  59. package/dist/types/utils/numeric-keys.type.d.ts +24 -0
  60. package/dist/types/utils/numeric-like.type.d.ts +10 -0
  61. package/dist/types/utils/ordering.type.d.ts +39 -0
  62. package/dist/types/utils/pagination.type.d.ts +11 -0
  63. package/dist/types/utils/perform-data.type.d.ts +4 -0
  64. package/dist/types/utils/primitive.type.d.ts +7 -0
  65. package/dist/types/utils/query-method-arg.type.d.ts +27 -0
  66. package/dist/types/utils/restrict-method-options.type.d.ts +14 -0
  67. package/dist/types/utils/see-mode.type.d.ts +12 -0
  68. package/dist/types/vsrepo/vsrepo-args.type.d.ts +9 -0
  69. package/dist/types/vsrepo/vsrepo-method.type.d.ts +4 -0
  70. package/dist/types/vsrepo/vsrepo-method.type.js +2 -0
  71. package/dist/types/vsrepo/vsrepo-options.type.d.ts +34 -0
  72. package/dist/types/vsrepo/vsrepo-options.type.js +2 -0
  73. package/dist/types/vsrepo/vsrepo-orm-types.type.d.ts +17 -0
  74. package/dist/types/vsrepo/vsrepo-orm-types.type.js +2 -0
  75. package/dist/types/vsrepo/vsrepo-pretty-where.type.d.ts +7 -0
  76. package/dist/types/vsrepo/vsrepo-pretty-where.type.js +2 -0
  77. package/dist/types/vsrepo/vsrepo-query-options.type.d.ts +17 -0
  78. package/dist/types/vsrepo/vsrepo-query-options.type.js +2 -0
  79. package/dist/types/vsrepo/vsrepo-query.type.d.ts +5 -0
  80. package/dist/types/vsrepo/vsrepo-query.type.js +2 -0
  81. package/dist/types/vsrepo/vsrepo-relations.type.d.ts +26 -0
  82. package/dist/types/vsrepo/vsrepo-relations.type.js +2 -0
  83. package/dist/types/vsrepo/vsrepo-resolve-args-data.type.d.ts +19 -0
  84. package/dist/types/vsrepo/vsrepo-resolve-args-data.type.js +2 -0
  85. package/dist/types/vsrepo/vsrepo-select.type.d.ts +15 -0
  86. package/dist/types/vsrepo/vsrepo-select.type.js +2 -0
  87. package/dist/types/vsrepo/vsrepo-transaction-options.type.d.ts +12 -0
  88. package/dist/types/vsrepo/vsrepo-transaction-options.type.js +2 -0
  89. package/dist/types/vsrepo/vsrepo-ugly-where.type.d.ts +9 -0
  90. package/dist/types/vsrepo/vsrepo-ugly-where.type.js +2 -0
  91. package/dist/types/vsrepo/vsrepo-where.type.d.ts +99 -0
  92. package/dist/types/vsrepo/vsrepo-where.type.js +2 -0
  93. package/package.json +16 -37
  94. package/README-DynamicRepo.md +0 -625
  95. package/README-DynamicRepo.pt-BR.md +0 -625
  96. package/dist/DynamicRepository.d.ts +0 -497
  97. package/dist/DynamicRepository.js +0 -26
  98. package/dist/VSRepoError.d.ts +0 -83
  99. package/dist/VSRepoError.js +0 -17
  100. package/dist/internal/decorators/dynamic-method.decorator.js +0 -14
  101. package/dist/internal/decorators/query-method.decorator.js +0 -20
  102. package/dist/internal/entities/dynamic-method-metadata.entity.js +0 -26
  103. package/dist/internal/errors/vs-repo.error.js +0 -31
  104. package/dist/internal/resolvers/base-methods.resolve.js +0 -541
  105. package/dist/internal/resolvers/create-update-payloads-with-relations.resolve.js +0 -143
  106. package/dist/internal/resolvers/data-payload-with-relations.resolve.js +0 -60
  107. package/dist/internal/resolvers/dbAndPrismaArgs.resolve.js +0 -63
  108. package/dist/internal/resolvers/dynamic-method-customization.resolve.js +0 -57
  109. package/dist/internal/resolvers/dynamic-method-info.resolve.js +0 -279
  110. package/dist/internal/resolvers/dynamic-methods-metadata.resolve.js +0 -15
  111. package/dist/internal/resolvers/merge-wheres.resolve.js +0 -22
  112. package/dist/internal/resolvers/pretty-wheres.resolve.js +0 -87
  113. package/dist/internal/resolvers/select.resolve.js +0 -7
  114. package/dist/internal/resolvers/specific-where.resolve.js +0 -84
  115. package/dist/internal/resolvers/ugly-where.resolve.js +0 -178
  116. package/dist/internal/utils/logger.util.js +0 -21
  117. package/dist/internal/utils/schemas.util.js +0 -31
  118. package/dist/internal/validation/build-config.validate.js +0 -84
  119. package/dist/internal/validation/constructor-config.validate.js +0 -64
  120. package/dist/internal/validation/dynamic-method-config.validate.js +0 -19
  121. package/dist/internal/validation/extension.validate.js +0 -15
  122. package/dist/internal/validation/is-object.validate.js +0 -6
  123. package/dist/internal/validation/method-options.validate.js +0 -42
  124. package/dist/internal/validation/obj-with-relations.validate.js +0 -37
  125. package/dist/internal/validation/prisma-client.validate.js +0 -10
  126. package/dist/internal/validation/query-method-arg.validate.js +0 -22
  127. package/dist/internal/validation/query-method-options.validate.js +0 -24
  128. package/scripts/configure-prisma-import.mjs +0 -283
  129. package/scripts/copy-types.mjs +0 -24
  130. /package/dist/{internal/decorators/types/dynamic-method-config.type.js → types/adapter/adapter-method-options.type.js} +0 -0
  131. /package/dist/{internal/errors/types/vs-repo-error-type.type.js → types/adapter/adapter-query-options.type.js} +0 -0
  132. /package/dist/{internal/errors/types/vs-repo-runtime-error-code.type.js → types/decorators/dynamic-method-options.type.js} +0 -0
  133. /package/dist/{internal/validation/types → types/decorators}/query-method-options.type.js +0 -0
  134. /package/dist/{internal/resolvers/types → types/dynamic-methods}/dynamic-method-customization.type.js +0 -0
  135. /package/dist/{internal/resolvers/types → types/dynamic-methods}/dynamic-method-info.type.js +0 -0
  136. /package/dist/{internal/resolvers/types → types/dynamic-methods}/dynamic-method-where-ops.type.js +0 -0
  137. /package/dist/{internal/resolvers/types/base-method-function.type.js → types/utils/count-result.type.js} +0 -0
  138. /package/dist/{internal/resolvers/types/pretty-where.type.js → types/utils/decimal-like.type.js} +0 -0
  139. /package/dist/{internal/resolvers/types/prisma-args.type.js → types/utils/deep-partial.type.js} +0 -0
  140. /package/dist/{internal/resolvers/types/repository-build-instance.type.js → types/utils/keys-of-type.type.js} +0 -0
  141. /package/dist/{internal/resolvers/types/resolve-db-and-prisma-args-data.type.js → types/utils/methods-options.type.js} +0 -0
  142. /package/dist/{internal/resolvers/types/ugly-where.type.js → types/utils/numeric-keys.type.js} +0 -0
  143. /package/dist/{internal/validation/types/base-methods.type.js → types/utils/numeric-like.type.js} +0 -0
  144. /package/dist/{internal/validation/types/build-config.type.js → types/utils/ordering.type.js} +0 -0
  145. /package/dist/{internal/validation/types → types/utils}/pagination.type.js +0 -0
  146. /package/dist/{internal/validation/types/constructor-config.type.js → types/utils/perform-data.type.js} +0 -0
  147. /package/dist/{internal/validation/types/method-options.type.js → types/utils/primitive.type.js} +0 -0
  148. /package/dist/{internal/validation/types → types/utils}/query-method-arg.type.js +0 -0
  149. /package/dist/{internal/validation/types/method.type.js → types/utils/restrict-method-options.type.js} +0 -0
  150. /package/dist/{internal/validation/types → types/utils}/see-mode.type.js +0 -0
  151. /package/dist/{internal/validation/types/relation.type.js → types/vsrepo/vsrepo-args.type.js} +0 -0
@@ -1,625 +0,0 @@
1
- # DynamicRepository (Abordagem baseada em classes)
2
-
3
- 🇧🇷 Você está lendo a versão em português. [🇺🇸 Read in English](./README-DynamicRepo.md)
4
-
5
- O VSRepository oferece duas formas de criar repositórios: a abordagem funcional `setupVSRepo` e a abordagem OOP baseada em classes `DynamicRepository`. Este documento cobre a abordagem baseada em classes usando `DynamicRepository` e o decorator `@DynamicMethod`.
6
-
7
- > Para a abordagem funcional, veja o [README.pt-BR.md](./README.pt-BR.md) principal.
8
-
9
- ## Sumário
10
-
11
- - [Quando usar o DynamicRepository](#quando-usar-o-dynamicrepository)
12
- - [Requisitos](#requisitos)
13
- - [Criando uma classe](#criando-uma-classe)
14
- - [O decorator @DynamicMethod](#o-decorator-dynamicmethod)
15
- - [Opções de configuração do decorator](#opções-de-configuração-do-decorator)
16
- - [O decorator @QueryMethod](#o-decorator-querymethod)
17
- - [Métodos base](#métodos-base)
18
- - [Trabalhando com relações](#trabalhando-com-relações)
19
- - [Transações](#transações)
20
- - [Trabalhando com includes](#trabalhando-com-includes)
21
- - [DynamicMethodOptions](#dynamicmethodoptions)
22
- - [Integração com NestJS](#integração-com-nestjs)
23
- - [Referência da API](#referência-da-api)
24
- - [Diferenças em relação ao setupVSRepo](#diferenças-em-relação-ao-setupvsrepo)
25
-
26
- ---
27
-
28
- ## Quando usar o DynamicRepository
29
-
30
- Use `DynamicRepository` quando preferir um **estilo OOP com decorators** em vez da abordagem funcional `setupVSRepo`. Características principais:
31
-
32
- - Métodos são definidos como campos `declare` com decorators `@DynamicMethod()`
33
- - O repositório é uma classe que você pode estender e injetar via injeção de dependência
34
- - `selectModels` e `includeModels` **não são suportados** (use `select`/`include` brutos via `DynamicMethodOptions`)
35
- - Os métodos base estão sempre ativos (sem toggle `active` por método)
36
- - O repositório é construído automaticamente no construtor (sem chamada explícita a `.build()`)
37
-
38
- ---
39
-
40
- ## Requisitos
41
-
42
- `DynamicRepository` depende dos decorators legados do TypeScript, então o `tsconfig.json` do seu projeto precisa ter:
43
-
44
- ```json
45
- {
46
- "compilerOptions": {
47
- "experimentalDecorators": true
48
- }
49
- }
50
- ```
51
-
52
- `reflect-metadata` já é uma dependência do `vsrepo` e é importado internamente — você não precisa importá-lo manualmente.
53
-
54
- Sem `experimentalDecorators: true`, o `@DynamicMethod()` não vai compilar (ou vai falhar silenciosamente ao registrar o método em tempo de execução, dependendo da sua ferramenta de build).
55
-
56
- ---
57
-
58
- ## Criando uma classe
59
-
60
- Estenda `DynamicRepository` com quatro parâmetros genéricos:
61
-
62
- ```typescript
63
- import {
64
- DynamicRepository,
65
- DynamicMethod,
66
- DynamicMethodOptions,
67
- PaginationModel,
68
- } from "../../generated/vsrepo";
69
- import type { Prisma } from "../../generated/prisma/client";
70
- import { PrismaClient } from "../../generated/prisma/client";
71
-
72
- type User = Prisma.UserGetPayload<{
73
- include: { address: true; posts: true };
74
- }>;
75
-
76
- class UserRepository extends DynamicRepository<
77
- User, // Tipo da entidade (com relações incluídas)
78
- "User", // Nome do modelo no Prisma
79
- string, // Tipo da chave primária
80
- { address: true; posts: true } // Quais campos são relações (flags)
81
- > {
82
- constructor(prisma: PrismaClient) {
83
- super(prisma, {
84
- tableName: "user",
85
- pkName: "id",
86
- relations: {
87
- address: { mode: "oto", pk: "id", restriction: "set" },
88
- posts: { mode: "otm", pk: "id", restriction: "add" },
89
- },
90
- requiredWhere: { active: true },
91
- build: {
92
- showWorking: false,
93
- baseMethods: {
94
- save: { ignoreRequiredWhere: true },
95
- },
96
- },
97
- });
98
- }
99
- }
100
- ```
101
-
102
- **Parâmetros genéricos:**
103
-
104
- | Parâmetro | Descrição |
105
- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
106
- | `TEntity` | O tipo completo da entidade, incluindo as relações que você quer disponíveis |
107
- | `UName` | O nome do modelo no Prisma como string literal (capitalizado, ex.: `"User"`) |
108
- | `VPKType` | O tipo da chave primária (`string`, `number`, etc.) |
109
- | `WRelations` *(opcional)* | Um objeto de flags indicando quais campos são relações (ex.: `{ address: true }`). Só é necessário se você for configurar as relações do repository — caso contrário, pode ser omitido |
110
-
111
- ---
112
-
113
- ## O decorator @DynamicMethod
114
-
115
- Declare métodos dinâmicos como campos de classe usando `declare` e decore-os com `@DynamicMethod()`:
116
-
117
- ```typescript
118
- class UserRepository extends DynamicRepository<User, "User", string> {
119
- // Método dinâmico simples - o nome determina o comportamento
120
- @DynamicMethod()
121
- declare findByEmail: (email: string) => Promise<User | null>;
122
-
123
- // Com configuração do decorator
124
- @DynamicMethod<"User">({ proxyTo: "findMany", pushWhere: { active: false } })
125
- declare findDisabled: () => Promise<User[]>;
126
-
127
- // Proxy para outro método
128
- @DynamicMethod<"User">({ proxyTo: "findOneByEmail", whereType: "overwrite" })
129
- declare findInternalByEmail: (email: string) => Promise<User | null>;
130
- }
131
- ```
132
-
133
- O **nome** do método determina o comportamento (mesmas regras da abordagem funcional). O objeto de configuração do decorator ajusta esse comportamento.
134
-
135
- > **Importante:** Sempre use `declare` (não uma propriedade comum) para campos decorados. O decorator fornece metadados em tempo de execução; a palavra-chave `declare` informa ao TypeScript que a propriedade existe sem emitir código de inicialização.
136
-
137
- ---
138
-
139
- ## Opções de configuração do decorator
140
-
141
- O decorator `@DynamicMethod<M>()` aceita um objeto de configuração opcional:
142
-
143
- ```typescript
144
- @DynamicMethod<"User">({
145
- proxyTo: "findByEmail", // Delega para outro padrão de método
146
- whereType: "overwrite", // "extending" (padrão) ou "overwrite"
147
- pushWhere: { active: false }, // Cláusula where extra
148
- injectOrdering: [{ name: "asc" }], // Ordenação fixa
149
- injectPagination: { skip: 0, take: 10 }, // Paginação fixa
150
- })
151
- ```
152
-
153
- | Opção | Tipo | Descrição |
154
- | ------------------ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
155
- | `proxyTo` | `string` | Delega para outro padrão de método válido (ex.: `"findOneByEmail"`) |
156
- | `whereType` | `"extending" \| "overwrite"` | `extending` combina com `requiredWhere`; `overwrite` o ignora |
157
- | `pushWhere` | `WhereModel<M>` | Cláusula `where` extra adicionada além do `requiredWhere` |
158
- | `injectOrdering` | `OrderingModel<M>` | Ordenação fixa injetada na query |
159
- | `injectPagination` | `PaginationModel<M>` | Paginação fixa injetada na query |
160
- | `fbMode` | `"one" \| "list"` | **Deprecated.** Relevante apenas para métodos com prefixo `findBy`. Use `findOneBy` em vez disso se quiser um resultado único. |
161
-
162
- ---
163
-
164
- ## O decorator @QueryMethod
165
-
166
- `@QueryMethod` declara um **método de SQL bruto** em um campo de classe com `declare`, contornando totalmente o parser de nomes usado por `@DynamicMethod`. É útil para consultas complexas (joins pesados, CTEs, funções específicas do banco) que não são práticas de expressar com os prefixos/sufixos padrão.
167
-
168
- Por baixo dos panos, a SQL é executada via Prisma usando `$queryRawUnsafe` (leitura) ou `$executeRawUnsafe` (escrita), e os valores passados em `args` são injetados como **parâmetros posicionais** (`$1`, `$2`, ...) — a mesma técnica de *prepared statements* usada pelo próprio Prisma. Os valores nunca são concatenados na string SQL, o que é o que efetivamente previne SQL Injection.
169
-
170
- ```typescript
171
- class UserRepository extends DynamicRepository<User, "User", string> {
172
- // Query method de leitura (não-modifying) — o tipo de retorno vem da declaração do campo
173
- @QueryMethod('SELECT * FROM "user" WHERE email = $1')
174
- declare findByEmailRaw: (arg: QueryMethodArg<[email: string]>) => Promise<User[]>;
175
-
176
- // Query method de escrita — deve sempre resolver para 'number'
177
- @QueryMethod('UPDATE "user" SET active = true WHERE id = $1', { modifying: true })
178
- declare activateUser: (arg: QueryMethodArg<[id: string]>) => Promise<number>;
179
- }
180
-
181
- const userRepository = new UserRepository(prisma, { tableName: "user", pkName: "id" });
182
-
183
- const usuarios = await userRepository.findByEmailRaw({ args: ["joao@email.com"] });
184
- const afetados = await userRepository.activateUser({ args: ["1"] });
185
- ```
186
-
187
- > [!WARNING]
188
- > `$1`, `$2`, ... devem sempre representar **valores**, nunca nomes de colunas/tabelas ou trechos de SQL dinâmicos. Nomes de identificadores não podem ser passados como parâmetro posicional — se um método precisar variar isso, monte a SQL a partir de um conjunto fixo e conhecido de opções no seu próprio código, nunca a partir de entrada não confiável.
189
-
190
- Como `@QueryMethod` pula o parsing de nome, não há inferência automática de tipos para o campo: os tipos de parâmetro e retorno do método vêm inteiramente de como você declara o campo com `declare`. Use `QueryMethodArg<T>` para tipar o único argumento `{ args, db? }` recebido pelo método.
191
-
192
- | Opção | Tipo | Padrão | Descrição |
193
- | ---------------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
194
- | `value` (1º argumento) | `string` | — | **Obrigatório.** SQL bruto a ser executado. Use `$1`, `$2`, ... para os placeholders de `args`. |
195
- | `options.modifying` | `boolean` | `false` | Quando `true`, executa via `$executeRawUnsafe`; o campo deve ser declarado retornando `Promise<number>`. Quando `false`, executa via `$queryRawUnsafe`. |
196
-
197
- > [!NOTE]
198
- > `@QueryMethod` ignora todos os outros conceitos de método dinâmico — `requiredWhere`, `pushWhere`, `whereType`, `selectModels`/`includeModels`, `injectOrdering`, `injectPagination`. Nada disso se aplica aqui.
199
-
200
- ---
201
-
202
- ## Métodos base
203
-
204
- Todas as instâncias de `DynamicRepository` incluem automaticamente estes métodos:
205
-
206
- | Método | Descrição |
207
- | --------------------- | ------------------------------------------------------------------------------------------------------------- |
208
- | `get(pk)` | Busca um registro pela chave primária |
209
- | `getOrThrow(pk)` | Busca pela PK, lança erro se não encontrado |
210
- | `getList(pks)` | Busca múltiplos registros pelas PKs |
211
- | `save(obj)` | Cria ou faz upsert de um registro |
212
- | `saveList(objs)` | Salva em lote em uma transação automática |
213
- | `patch(pk, obj)` | Atualização parcial pela PK |
214
- | `patchList(tuples)` | Atualização parcial em lote via tuplas `[pk, obj]` |
215
- | `merge(pk, obj)` | Busca e faz deep-merge em memória (não persiste) |
216
- | `remove(pk)` | Apaga um registro pela PK |
217
- | `removeList(pks)` | Apaga em lote pelas PKs |
218
- | `getAll()` | Busca todos os registros (respeita `requiredWhere`). Aceita `pagination` e `order` em `options` — veja abaixo |
219
- | `total()` | Conta todos os registros |
220
- | `has(pk)` | Verifica se um registro existe |
221
- | `softRemove(pk)` | Soft-delete (requer configuração de `softRemovekName`) |
222
- | `softRemoveList(pks)` | Soft-delete em lote |
223
- | `restore(pk)` | Restaura registro com soft-delete |
224
- | `restoreList(pks)` | Restauração em lote |
225
-
226
- Todos os métodos aceitam um argumento opcional `options` baseado em `DynamicMethodOptions` (`db`, `see`, `include`, `select`), mas alguns métodos restringem esse tipo:
227
-
228
- - **`getAll`** aceita adicionalmente `pagination?: PaginationOptions` e `order?: OrderingModel<UName>` (usa `defaultOrdering` quando omitido).
229
- - **`saveList` / `patchList`** omitem `include` e `select`, e `db` só aceita uma `DbTransaction` (o retorno de `prisma.$transaction`) — não o client Prisma comum.
230
- - **`removeList`, `total`, `has`** omitem `include` e `select`.
231
- - **`softRemove`, `restore`** omitem `see` (a visibilidade de soft-delete não se aplica ao registro sendo alterado).
232
- - **`softRemoveList`, `restoreList`** omitem `see`, `include` e `select`.
233
-
234
- ```typescript
235
- // getAll com paginação e ordenação
236
- const page = await userRepository.getAll({
237
- pagination: { skip: 0, take: 20 },
238
- order: { createdAt: "desc" },
239
- });
240
- ```
241
-
242
- ---
243
-
244
- ## Trabalhando com relações
245
-
246
- Configure as relações no construtor para que `save` e `patch` as gerenciem automaticamente:
247
-
248
- ```typescript
249
- class UserRepository extends DynamicRepository<
250
- User, "User", string,
251
- { address: true; posts: true }
252
- > {
253
- constructor(prisma: PrismaClient) {
254
- super(prisma, {
255
- tableName: "user",
256
- pkName: "id",
257
- relations: {
258
- address: { mode: "oto", pk: "id", restriction: "set" },
259
- posts: { mode: "otm", pk: "id", restriction: "add" },
260
- },
261
- });
262
- }
263
- }
264
- ```
265
-
266
- O quarto parâmetro genérico (`WRelations`) é **opcional** — só é necessário quando você for configurar as relações do repository. Quando fornecido, ele deve sinalizar quais campos são relações. Isso garante que `DynamicSaveInput` e `DynamicPatchInput` resolvam esses campos para o formato de payload aninhado de create/update do Prisma. Se o seu repository não gerenciar nenhuma relação, você pode simplesmente omitir esse parâmetro.
267
-
268
- **Modos de relação:** `oto` (um-para-um), `otm` (um-para-muitos), `mto` (muitos-para-um), `mtm` (muitos-para-muitos).
269
-
270
- **Restrições:** `set` (substitui tudo) ou `add` (adiciona/atualiza sem remover).
271
-
272
- Veja o [README.pt-BR.md](./README.pt-BR.md#relações-no-save) principal para todos os detalhes sobre o comportamento das relações.
273
-
274
- ---
275
-
276
- ## Transações
277
-
278
- Todos os métodos aceitam `{ db: tx }` para participar de uma transação:
279
-
280
- ```typescript
281
- await userRepository.prisma.$transaction(async (tx) => {
282
- const user = await userRepository.save(
283
- { name: "Mary", email: "mary@email.com", password: "password" },
284
- { db: tx }
285
- );
286
-
287
- await postRepository.save(
288
- { title: "First Post", authorId: user.id },
289
- { db: tx }
290
- );
291
- });
292
- ```
293
-
294
- Acesse o client do Prisma via `repository.prisma`.
295
-
296
- ---
297
-
298
- ## Trabalhando com includes
299
-
300
- `DynamicRepository` não suporta `includeModels` (presets nomeados), mas você pode usar um `include` bruto do Prisma via `DynamicMethodOptions`:
301
-
302
- ```typescript
303
- // Incluir apenas address
304
- const user = await userRepository.get(id, {
305
- include: { address: true },
306
- });
307
-
308
- // Include aninhado
309
- const userFull = await userRepository.get(id, {
310
- include: {
311
- address: true,
312
- posts: { include: { tags: true } },
313
- },
314
- });
315
-
316
- // Funciona em qualquer método que aceite options
317
- const all = await userRepository.getAll({
318
- include: { posts: true },
319
- });
320
- ```
321
-
322
- ---
323
-
324
- ## DynamicMethodOptions
325
-
326
- Todo método base e todo método dinâmico decorado aceita um segundo argumento opcional do tipo `DynamicMethodOptions`. Esse objeto tem quatro campos opcionais:
327
-
328
- ```typescript
329
- type DynamicMethodOptions<TName extends PrismaModelName> = {
330
- db?: ClientOrTransaction; // Client ou transação do Prisma
331
- see?: "active" | "removed" | "all"; // Visibilidade do soft-delete
332
- include?: IncludeModel<TName>; // Include bruto do Prisma
333
- select?: SelectModel<TName>; // Select bruto do Prisma
334
- };
335
- ```
336
-
337
- > `include` e `select` são mutuamente exclusivos. Diferente da API funcional `setupVSRepo`, o tipo de options mais simples do `DynamicRepository` não garante isso em tempo de compilação — passar os dois gera um `VSRepoRuntimeError` em tempo de execução.
338
-
339
- ### `db` — Usando uma transação
340
-
341
- Passe `{ db: tx }` para direcionar uma única chamada de método para dentro de uma transação existente:
342
-
343
- ```typescript
344
- await userRepository.prisma.$transaction(async (tx) => {
345
- const user = await userRepository.save(
346
- { name: "Mary", email: "mary@email.com", password: "password" },
347
- { db: tx },
348
- );
349
-
350
- await postRepository.save(
351
- { title: "First Post", authorId: user.id },
352
- { db: tx },
353
- );
354
- });
355
- ```
356
-
357
- Você também pode usar a transação para leituras:
358
-
359
- ```typescript
360
- await userRepository.prisma.$transaction(async (tx) => {
361
- const user = await userRepository.get(id, { db: tx });
362
- const total = await userRepository.total({ db: tx });
363
- });
364
- ```
365
-
366
- ### `see` — Visibilidade do soft-delete
367
-
368
- Se `softRemovekName` estiver configurado, o campo `see` controla quais registros ficam visíveis:
369
-
370
- | Valor | Comportamento |
371
- | ------------------- | -------------------------------- |
372
- | `"active"` (padrão) | Apenas registros não removidos |
373
- | `"removed"` | Apenas registros com soft-delete |
374
- | `"all"` | Registros ativos e removidos |
375
-
376
- ```typescript
377
- // Busca apenas usuários com soft-delete
378
- const deleted = await userRepository.getAll({ see: "removed" });
379
-
380
- // Busca todos os usuários, incluindo os com soft-delete
381
- const all = await userRepository.getAll({ see: "all" });
382
-
383
- // Restaura um usuário com soft-delete buscando-o primeiro
384
- const [removed] = await userRepository.getAll({ see: "removed", include: { address: true } });
385
- ```
386
-
387
- ### `include` — Include bruto do Prisma
388
-
389
- Use `include` para carregar relações de forma antecipada (eager loading) em qualquer chamada de método. É o equivalente ao `includeModels` da abordagem funcional, mas com a sintaxe bruta de include do Prisma:
390
-
391
- ```typescript
392
- // Include simples
393
- const user = await userRepository.get(id, {
394
- include: { address: true },
395
- });
396
-
397
- // Include aninhado
398
- const userFull = await userRepository.get(id, {
399
- include: {
400
- address: true,
401
- posts: { include: { author: true } },
402
- },
403
- });
404
-
405
- // Também funciona em métodos dinâmicos
406
- const admin = await userRepository.findAdminByEmail(email, {
407
- include: { address: true, posts: true },
408
- });
409
- ```
410
-
411
- ### `select` — Select bruto do Prisma
412
-
413
- Use `select` para projetar um conjunto específico de campos em qualquer chamada de método. É o equivalente ao `selectModels` da abordagem funcional, mas com a sintaxe bruta de select do Prisma:
414
-
415
- ```typescript
416
- // Select simples
417
- const user = await userRepository.get(id, {
418
- select: { id: true, email: true },
419
- });
420
-
421
- // Também funciona em métodos dinâmicos
422
- const admin = await userRepository.findAdminByEmail(email, {
423
- select: { id: true, email: true },
424
- });
425
- ```
426
-
427
- > Como o `DynamicRepository` não tem estreitamento de tipo orientado por `selectModels`/`selectedModel`, o tipo de retorno permanece `TEntity` independentemente do `select` passado — o resultado em tempo de execução conterá apenas os campos selecionados, mas o TypeScript não vai estreitar isso para você. Faça cast ou desestruture conforme necessário.
428
-
429
- ### Combinando opções
430
-
431
- `db` e `see` podem ser combinados livremente com `include` ou `select` (mas não com os dois juntos):
432
-
433
- ```typescript
434
- // Dentro de uma transação, busca um usuário com relações, incluindo os com soft-delete
435
- await userRepository.prisma.$transaction(async (tx) => {
436
- const user = await userRepository.get(id, {
437
- db: tx,
438
- see: "all",
439
- include: { address: true, posts: true },
440
- });
441
- });
442
- ```
443
-
444
- ---
445
-
446
- ## Integração com NestJS
447
-
448
- `DynamicRepository` funciona naturalmente com a injeção de dependência do NestJS.
449
-
450
- ### Provider do repositório
451
-
452
- ```typescript
453
- // src/modules/user/user.repository.ts
454
- import { Injectable } from "@nestjs/common";
455
- import { PrismaService } from "../../database/prisma.service";
456
- import { DynamicRepository, DynamicMethod, DynamicMethodOptions } from "../../../generated/vsrepo";
457
-
458
- type User = /* Prisma UserGetPayload com relações */;
459
-
460
- @Injectable()
461
- class UserRepository extends DynamicRepository<User, "User", string, { profile: true }> {
462
- constructor(prisma: PrismaService) {
463
- super(prisma, {
464
- tableName: "user",
465
- pkName: "id",
466
- relations: {
467
- profile: { mode: "oto", pk: "id", restriction: "add" },
468
- },
469
- build: {
470
- baseMethods: {
471
- save: { ignoreRequiredWhere: true },
472
- },
473
- },
474
- });
475
- }
476
-
477
- @DynamicMethod()
478
- declare findByEmail: (email: string, options?: DynamicMethodOptions<"User">) => Promise<User | null>;
479
- }
480
-
481
- ```
482
-
483
- ### Registrando o módulo
484
-
485
- ```typescript
486
- // src/modules/user/user.module.ts
487
- import { Module } from "@nestjs/common";
488
- import { UserRepository } from "./user.repository";
489
- import { UserService } from "./user.service";
490
- import { UserController } from "./user.controller";
491
-
492
- @Module({
493
- providers: [UserRepository, UserService],
494
- controllers: [UserController],
495
- exports: [UserService],
496
- })
497
- export class UserModule {}
498
- ```
499
-
500
- ### Usando em um service
501
-
502
- ```typescript
503
- // src/modules/user/user.service.ts
504
- import { Injectable, Inject } from "@nestjs/common";
505
- import { UserRepository } from "./user.repository";
506
-
507
- @Injectable()
508
- export class UserService {
509
- constructor(
510
- private readonly userRepository: UserRepository,
511
- ) {}
512
-
513
- async getUserById(id: string) {
514
- return this.userRepository.get(id);
515
- }
516
-
517
- async getUserAuthByEmailWithProfile(email: string) {
518
- return this.userRepository.findByEmail(email, { include: { profile: true } });
519
- }
520
-
521
- async createUser(data: { email: string; password: string; name: string }) {
522
- return this.userRepository.save({
523
- email: data.email,
524
- password: data.password,
525
- name: data.name,
526
- });
527
- }
528
- }
529
- ```
530
-
531
- ---
532
-
533
- ## Referência da API
534
-
535
- ### `DynamicRepository<TEntity, UName, VPKType, WRelations>`
536
-
537
- ```typescript
538
- abstract class DynamicRepository<
539
- TEntity extends object,
540
- UName extends PrismaModelName,
541
- VPKType,
542
- WRelations extends Partial<Record<keyof TEntity, true>> | undefined = undefined,
543
- >
544
- ```
545
-
546
- > `WRelations` é opcional (o padrão é `undefined`) e só precisa ser informado quando você for configurar as relações do repository.
547
-
548
- **Construtor:**
549
-
550
- ```typescript
551
- constructor(prisma: DbClient, config: DynamicRepositoryConstructorConfig<TEntity, UName>)
552
- ```
553
-
554
- ### DynamicRepositoryConstructorConfig
555
-
556
- | Propriedade | Tipo | Descrição |
557
- | -------------------- | ------------------------------ | ------------------------------- |
558
- | `tableName` | `Uncapitalize<UName>` | Nome da tabela no Prisma |
559
- | `pkName` | `keyof TEntity` | Campo da chave primária |
560
- | `softRemovekName?` | `keyof TEntity` | Campo DateTime para soft-delete |
561
- | `requiredWhere?` | `WhereModel<UName>` | Filtros globais |
562
- | `defaultOrdering?` | `OrderingModel<UName>` | Ordenação padrão |
563
- | `relations?` | `RepositoryRelations<TEntity>` | Configuração de relações |
564
- | `build?` | `DynamicRepositoryBuildConfig` | Opções de build |
565
-
566
- ### DynamicRepositoryBuildConfig
567
-
568
- | Propriedade | Tipo | Descrição |
569
- | -------------- | --------------------------------------------------- | ------------------------------------- |
570
- | `showWorking?` | `boolean` | Exibe logs internos (padrão: `false`) |
571
- | `baseMethods?` | `Record<string, { ignoreRequiredWhere?: boolean }>` | Configuração por método |
572
-
573
- ### @DynamicMethod\<M>(config?)
574
-
575
- ```typescript
576
- function DynamicMethod<M extends PrismaModelName>(
577
- config?: DynamicMethodConfig<M>,
578
- ): PropertyDecorator;
579
- ```
580
-
581
- ### DynamicMethodOptions\<TName>
582
-
583
- | Propriedade | Tipo | Descrição |
584
- | ----------- | -------------------------------- | ------------------------------------- |
585
- | `db?` | `ClientOrTransaction` | Client ou transação do banco de dados |
586
- | `see?` | `"active" \| "removed" \| "all"` | Visibilidade do soft-delete |
587
- | `include?` | `IncludeModel<TName>` | Include bruto do Prisma |
588
- | `select?` | `SelectModel<TName>` | Select bruto do Prisma |
589
-
590
- ### @QueryMethod(value, options?)
591
-
592
- ```typescript
593
- function QueryMethod(value: string, options?: QueryMethodOptions): PropertyDecorator;
594
- ```
595
-
596
- ### QueryMethodArg\<T>
597
-
598
- | Propriedade | Tipo | Descrição |
599
- | ----------- | --------------------- | -------------------------------------------------------------------------- |
600
- | `args` | `T` (tupla) | Parâmetros posicionais injetados nos placeholders da SQL (`$1`, `$2`, ...) |
601
- | `db?` | `ClientOrTransaction` | Client de transação para executar essa query |
602
-
603
- ### QueryMethodOptions
604
-
605
- | Propriedade | Tipo | Padrão | Descrição |
606
- |------------- | --------- | ------- | ----------------------------------------------------------------------------------------------------------------------- |
607
- | `modifying?` | `boolean` | `false` | `true` executa via `$executeRawUnsafe` (o campo deve retornar `Promise<number>`); `false` executa via `$queryRawUnsafe` |
608
-
609
- ---
610
-
611
- ## Diferenças em relação ao setupVSRepo
612
-
613
- | Aspecto | `setupVSRepo` | `DynamicRepository` |
614
- | -------------------------- | ------------------------------------------------ | ------------------------------------------------------------- |
615
- | **Estilo** | Funcional / curried | OOP / baseado em classes |
616
- | **Métodos definidos via** | Objeto de configuração `methods` | Decorators `@DynamicMethod()` |
617
- | **selectModels** | Suportado | Não suportado |
618
- | **includeModels** | Suportado | Não suportado |
619
- | **Select padrão** | Configuração `defaultSelectModel` | Não disponível |
620
- | **Etapa de build** | `.build(prisma)` explícito | Automático no construtor |
621
- | **Toggles de método base** | `active`, `defaultSelect` por método | Sempre ativo, sem defaultSelect |
622
- | **Instância do Prisma** | Passada no `.build()` | Passada para `super()` no construtor |
623
- | **Extensibilidade** | Método `.extend()` | Herança de classe |
624
- | **Includes brutos** | Via `options.include` | Via `DynamicMethodOptions.include` |
625
- | **Selects brutos** | Via `options.select` (com estreitamento de tipo) | Via `DynamicMethodOptions.select` (sem estreitamento de tipo) |