vsrepo 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,6 +33,7 @@ O VSRepository permite criar repositories fortemente tipados com:
33
33
  - [Filtros de relação](#filtros-de-relação)
34
34
  - [Sufixos de paginação e ordenação](#sufixos-de-paginação-e-ordenação)
35
35
  - [Configuração de métodos](#configuração-de-métodos)
36
+ - [Aggregate e GroupBy](#aggregate-e-groupby)
36
37
  - [Relações no save](#relações-no-save)
37
38
  - [Transações](#transações)
38
39
  - [Estendendo um repository](#estendendo-um-repository)
@@ -460,6 +461,7 @@ O prefixo do nome do método determina qual operação Prisma será chamada e qu
460
461
  | `findListWhere` | `findMany` | `T[]` | Recebe um objeto `where` explícito como argumento |
461
462
  | `existsBy` | `findFirst` | `boolean` | Retorna `true` se encontrar, `false` caso contrário |
462
463
  | `countBy` | `count` | `number` | Aceita campos como filtro |
464
+ | `countWhere` | `count` | `number` | Recebe um objeto `where` explícito como argumento |
463
465
  | `count` | `count` | `number` | Sem filtros de campo; aplica só `requiredWhere` e `pushWhere` |
464
466
  | `create` | `create` | `T` | Recebe `data` como argumento |
465
467
  | `createMany` | `createMany` | `{ count: number }` | Recebe `data` como argumento; suporta `SkipDuplicates` |
@@ -470,6 +472,8 @@ O prefixo do nome do método determina qual operação Prisma será chamada e qu
470
472
  | `upsertBy` | `upsert` | `T` | Recebe `update` e `create` como argumentos |
471
473
  | `deleteBy` | `delete` | `T` | |
472
474
  | `deleteManyBy` | `deleteMany` | `{ count: number }` | |
475
+ | `aggregate` | `aggregate` | `Dinâmico` | Nome deve ser exato; recebe args nativos do Prisma; ignora `selectModels`, `pushWhere` e `requiredWhere` |
476
+ | `groupBy` | `groupBy` | `Dinâmico[]` | Nome deve ser exato; recebe args nativos do Prisma; ignora `selectModels`, `pushWhere` e `requiredWhere` |
473
477
 
474
478
  ---
475
479
 
@@ -724,6 +728,81 @@ methods: {
724
728
 
725
729
  ---
726
730
 
731
+ ### Aggregate e GroupBy
732
+
733
+ Para utilizar operações de agrupamento e agregação do Prisma (`aggregate` e `groupBy`), você deve expô-las explicitamente na configuração dos métodos dinâmicos (`methods`):
734
+
735
+ ```ts
736
+ const usuarioRepository = setupVSRepo<Usuario, "usuario">()({
737
+ tableName: "usuario",
738
+ pkName: "id",
739
+ methods: {
740
+ aggregate: { map: true },
741
+ groupBy: { map: true },
742
+ },
743
+ }).build(prisma);
744
+ ```
745
+
746
+ > [!NOTE]
747
+ > Estes métodos devem ter exatamente esses nomes (`aggregate` e `groupBy`).
748
+ > Ao contrário dos outros métodos dinâmicos, eles recebem argumentos nativos do Prisma e **ignoram** as configurações de `selectModels`, `pushWhere` e `requiredWhere`.
749
+
750
+ #### Exemplo de uso do `aggregate`
751
+
752
+ O método `aggregate` permite calcular valores agregados (como média, soma, mínimo, máximo, contagem) sobre os registros:
753
+
754
+ ```ts
755
+ const resultado = await usuarioRepository.aggregate({
756
+ _count: {
757
+ _all: true,
758
+ },
759
+ _avg: {
760
+ idade: true,
761
+ },
762
+ _sum: {
763
+ saldo: true,
764
+ },
765
+ where: {
766
+ ativo: true,
767
+ },
768
+ });
769
+
770
+ console.log(resultado._count._all); // Total de usuários ativos
771
+ console.log(resultado._avg.idade); // Média de idade dos usuários ativos
772
+ console.log(resultado._sum.saldo); // Soma dos saldos dos usuários ativos
773
+ ```
774
+
775
+ #### Exemplo de uso do `groupBy`
776
+
777
+ O método `groupBy` permite agrupar registros por um ou mais campos para realizar operações de agregação em cada grupo:
778
+
779
+ ```ts
780
+ const grupos = await usuarioRepository.groupBy({
781
+ by: ["status"],
782
+ _count: {
783
+ status: true,
784
+ },
785
+ _avg: {
786
+ idade: true,
787
+ },
788
+ having: {
789
+ idade: {
790
+ _avg: {
791
+ gt: 18,
792
+ },
793
+ },
794
+ },
795
+ });
796
+
797
+ for (const grupo of grupos) {
798
+ console.log(`Status: ${grupo.status}`);
799
+ console.log(`Quantidade: ${grupo._count.status}`);
800
+ console.log(`Média de Idade: ${grupo._avg.idade}`);
801
+ }
802
+ ```
803
+
804
+ ---
805
+
727
806
  ## Relações no save
728
807
 
729
808
  Configure relações para que o `save` as gerencie automaticamente. Essas mesmas relações também são usadas no `patch`. Para que as relações apareçam no autocomplete, o tipo genérico deve incluir as relações usando `GetPayload`:
@@ -939,8 +1018,9 @@ import type { MethodOptions, MethodOptionsModel } from "../../generated/vsrepo";
939
1018
  // S = chave do select model ou false
940
1019
  type Opts = MethodOptions<"public" | "minimal">;
941
1020
 
942
- // MethodOptionsModel<T> — versão derivada do tipo do repository
943
- type OptsModel = MethodOptionsModel<typeof usuarioSelectModels>;
1021
+ // MethodOptionsModel<TRepo> — derivado diretamente de uma instância VSRepository configurada
1022
+ const usuarioVSRepo = setupVSRepo<Usuario, "usuario">()(config);
1023
+ type OptsModel = MethodOptionsModel<typeof usuarioVSRepo>;
944
1024
  ```
945
1025
 
946
1026
  ### Tipos de configuração
@@ -952,7 +1032,6 @@ import type {
952
1032
  BuildConfig,
953
1033
  RepositoryRelations,
954
1034
  ExtractRelationConfig,
955
- UpsertWithRelations,
956
1035
  } from "../../generated/vsrepo";
957
1036
 
958
1037
  // Configuração de um método dinâmico
@@ -969,18 +1048,12 @@ type UsuarioRelations = RepositoryRelations<Usuario>;
969
1048
 
970
1049
  // Configuração de relação inferida a partir de um campo
971
1050
  type PerfilRelationConfig = ExtractRelationConfig<Usuario["perfil"]>;
972
-
973
- // Payload do save com relações
974
- type UsuarioComRelacoes = UpsertWithRelations<Usuario, "usuario", typeof relations>;
975
1051
  ```
976
1052
 
977
1053
  ### Tipo do repository construído
978
1054
 
979
1055
  ```ts
980
- import type { BuiltRepository, RepositoryOf } from "../../generated/vsrepo";
981
-
982
- // Tipo completo de um repository construído
983
- type MeuRepo = BuiltRepository<Usuario, "usuario", typeof config, typeof buildConfig>;
1056
+ import type { RepositoryOf } from "../../generated/vsrepo";
984
1057
 
985
1058
  // Inferência a partir de uma instância VSRepository (útil para injeção de dependência)
986
1059
  const usuarioVSRepo = setupVSRepo<Usuario, "usuario">()({ ... });
@@ -1001,15 +1074,31 @@ const extension = { buscarPorDominio: (dominio: string) => Promise<Usuario[]> };
1001
1074
  type UsuarioRepositoryExtended = RepositoryOf<typeof usuarioVSRepo, undefined, typeof extension>;
1002
1075
  ```
1003
1076
 
1004
- ### Tipo auxiliar
1077
+ ### Tipos do payload dos métodos `save` e `patch`
1078
+
1079
+ Use `SaveObject` e `PatchObject` para extrair o tipo do payload esperado pelos métodos `save` e `patch` diretamente a partir de uma instância `VSRepository` configurada. Esses tipos já levam em conta as relações configuradas.
1005
1080
 
1006
1081
  ```ts
1007
- import type { DistributiveOmit } from "../../generated/vsrepo";
1082
+ import type { SaveObject, PacthObject } from "../../generated/vsrepo";
1083
+
1084
+ const usuarioVSRepo = setupVSRepo<Usuario, "usuario">()({
1085
+ tableName: "usuario",
1086
+ pkName: "id",
1087
+ relations: {
1088
+ perfil: { pk: "id", mode: "oto", restriction: "set" },
1089
+ postagens: { pk: "id", mode: "otm", restriction: "add" },
1090
+ },
1091
+ });
1092
+
1093
+ // Tipo do objeto aceito pelo .save()
1094
+ type UsuarioSavePayload = SaveObject<typeof usuarioVSRepo>;
1008
1095
 
1009
- // Omit distributivo — preserva unions ao omitir propriedades
1010
- type SemEmail = DistributiveOmit<Usuario | Perfil, "email">;
1096
+ // Tipo do objeto aceito pelo .patch()
1097
+ type UsuarioPatchPayload = PacthObject<typeof usuarioVSRepo>;
1011
1098
  ```
1012
1099
 
1100
+ > Útil para tipar DTOs, funções auxiliares ou serviços que chamam `save`/`patch` e precisam do tipo correto do payload sem referenciar diretamente os tipos internos do Prisma.
1101
+
1013
1102
  ---
1014
1103
 
1015
1104
  ## API Reference
@@ -51,15 +51,22 @@ type ValidMethodPatterns =
51
51
  | `createManyAndReturn${string}` | `createMany${string}`
52
52
  | `create${string}` | `updateManyAndReturnBy${string}` | `updateManyBy${string}`
53
53
  | `updateBy${string}` | `upsertBy${string}` | `deleteManyBy${string}`
54
- | `deleteBy${string}` | `countBy${string}` | `count${string}`
55
- | `findWhere${string}` | `findListWhere${string}`;
56
-
57
- type StringModifiers =
58
- | 'NotStartsWith' | 'StartsWith' | 'NotEndsWith' | 'EndsWith'
59
- | 'NotContains' | 'Contains' | 'NotIn' | 'In' | 'Not';
54
+ | `deleteBy${string}` | `countBy${string}` | `count${string}` | `countWhere${string}`
55
+ | `findWhere${string}` | `findListWhere${string}`
56
+ | 'aggregate' | 'groupBy';
60
57
 
61
58
  type StripModifier<S extends string> =
62
- S extends `${infer Base}${StringModifiers}Insensitive` ? Base :
59
+ // Expansão explícita para resolver o problema do 'Insensitive'
60
+ S extends `${infer Base}NotStartsWithInsensitive` ? Base :
61
+ S extends `${infer Base}StartsWithInsensitive` ? Base :
62
+ S extends `${infer Base}NotEndsWithInsensitive` ? Base :
63
+ S extends `${infer Base}EndsWithInsensitive` ? Base :
64
+ S extends `${infer Base}NotContainsInsensitive` ? Base :
65
+ S extends `${infer Base}ContainsInsensitive` ? Base :
66
+ S extends `${infer Base}NotInInsensitive` ? Base :
67
+ S extends `${infer Base}InInsensitive` ? Base :
68
+ S extends `${infer Base}NotInsensitive` ? Base :
69
+ // Modificadores normais
63
70
  S extends `${infer Base}NotStartsWith` ? Base :
64
71
  S extends `${infer Base}StartsWith` ? Base :
65
72
  S extends `${infer Base}NotEndsWith` ? Base :
@@ -93,7 +100,7 @@ type GetFieldType<T, S extends string, I> = ExtractFieldName<S> extends infer Fi
93
100
  ? T[FieldName][]
94
101
  : NonNullable<T[FieldName]> extends object | any[]
95
102
  ? I extends { whereInput: infer W }
96
- ? FieldName extends keyof NonNullable<W> ? NonNullable<W>[FieldName] : T[FieldName]
103
+ ? FieldName extends keyof NonNullable<W> ? Exclude<NonNullable<W>[FieldName], undefined> : T[FieldName]
97
104
  : T[FieldName]
98
105
  : T[FieldName]
99
106
  : ParseRelation<S> extends [infer Rel extends keyof T, any, infer Rest extends string]
@@ -124,8 +131,6 @@ type MapToContractTypes<T, Arr extends string[], I> =
124
131
  : never
125
132
  : [];
126
133
 
127
- type HasMultipleANDs<S extends string> = S extends `${string}AND${string}AND${string}` ? true : false;
128
-
129
134
  type SplitAllTokens<S extends string> =
130
135
  S extends `${infer L}AND${infer R}` ? [...SplitAllTokens<L>, ...SplitAllTokens<R>] :
131
136
  S extends `${infer L}And${infer R}` ? [...SplitAllTokens<L>, ...SplitAllTokens<R>] :
@@ -150,20 +155,28 @@ type ResolveReturnType<M extends string, TSelected> =
150
155
  M extends 'findMany' | 'createManyAndReturn' | 'updateManyAndReturnBy' | 'findListWhere' | 'findByList' ? TSelected[] :
151
156
  M extends 'findUnique' | 'findFirst' | 'findWhere' | 'findByOne' ? TSelected | null :
152
157
  M extends 'findUniqueOrThrow' | 'findFirstOrThrow' | 'create' | 'updateBy' | 'upsertBy' | 'deleteBy' ? TSelected :
153
- M extends 'count' ? number : never;
158
+ M extends 'count' | 'countWhere' ? number : never;
159
+
160
+ type GetUpdateInput<I> = I extends { updateInput: infer U } ? U : unknown;
161
+ type GetCreateInput<I> = I extends { createInput: infer C } ? C : unknown;
162
+ type GetCreateManyInput<I> = I extends { createManyInput: infer CM } ? CM : unknown;
163
+ type GetUpdateManyInput<I> = I extends { updateManyInput: infer UM } ? UM : unknown;
164
+ type GetWhereInput<I> = I extends { whereInput: infer W } ? NonNullable<W> : unknown;
165
+ type GetCursorInput<I> = I extends { cursorInput: infer C } ? C : unknown;
166
+ type GetOrderByInput<I> = I extends { orderByInput: infer OB } ? OB : OrderOptions;
154
167
 
155
168
  type ExtraArgs<M extends string, R extends string, I> = [
156
- ...(M extends 'upsertBy' ? [update: I extends { updateInput: infer U } ? U : unknown, create: I extends { createInput: infer C } ? C : unknown]
157
- : M extends 'create' ? [data: I extends { createInput: infer C } ? C : unknown]
158
- : M extends 'updateBy' ? [data: I extends { updateInput: infer U } ? U : unknown]
159
- : M extends 'createMany' | 'createManyAndReturn' ? [data: I extends { createManyInput: infer CM } ? CM : unknown]
160
- : M extends 'updateManyBy' | 'updateManyAndReturnBy' ? [data: I extends { updateManyInput: infer UM } ? UM : unknown]
161
- : M extends 'findWhere' | 'findListWhere' ? [where: I extends { whereInput: infer W } ? NonNullable<W> : unknown]
169
+ ...(M extends 'upsertBy' ? [update: GetUpdateInput<I>, create: GetCreateInput<I>]
170
+ : M extends 'create' ? [data: GetCreateInput<I>]
171
+ : M extends 'updateBy' ? [data: GetUpdateInput<I>]
172
+ : M extends 'createMany' | 'createManyAndReturn' ? [data: GetCreateManyInput<I>]
173
+ : M extends 'updateManyBy' | 'updateManyAndReturnBy' ? [data: GetUpdateManyInput<I>]
174
+ : M extends 'findWhere' | 'findListWhere' | 'countWhere' ? [where: GetWhereInput<I>]
162
175
  : []),
163
- ...(R extends `${string}PaginatedAndOrdered` ? [pagination: PaginationOptions<I extends { cursorInput: infer C } ? C : unknown>, order: I extends { orderByInput: infer OB } ? OB : OrderOptions]
164
- : R extends `${string}OrderedAndPaginated` ? [order: I extends { orderByInput: infer OB } ? OB : OrderOptions, pagination: PaginationOptions<I extends { cursorInput: infer C } ? C : unknown>]
165
- : R extends `${string}Paginated` ? [pagination: PaginationOptions<I extends { cursorInput: infer C } ? C : unknown>]
166
- : R extends `${string}Ordered` ? [order: I extends { orderByInput: infer OB } ? OB : OrderOptions]
176
+ ...(R extends `${string}PaginatedAndOrdered` ? [pagination: PaginationOptions<GetCursorInput<I>>, order: GetOrderByInput<I>]
177
+ : R extends `${string}OrderedAndPaginated` ? [order: GetOrderByInput<I>, pagination: PaginationOptions<GetCursorInput<I>>]
178
+ : R extends `${string}Paginated` ? [pagination: PaginationOptions<GetCursorInput<I>>]
179
+ : R extends `${string}Ordered` ? [order: GetOrderByInput<I>]
167
180
  : [])
168
181
  ];
169
182
 
@@ -172,13 +185,17 @@ type PrismaDelegate<M extends Prisma.ModelName> = Uncapitalize<M> extends keyof
172
185
  type FullModelType<M extends Prisma.ModelName> =
173
186
  Prisma.Result<PrismaDelegate<M>, {}, 'findMany'> extends Array<infer U> ? U : never;
174
187
 
188
+ type PrecomputedSelects<M extends Prisma.ModelName, SelectModels> = {
189
+ [S in keyof SelectModels]: Prisma.Result<PrismaDelegate<M>, { select: SelectModels[S] }, 'findMany'> extends Array<infer U> ? U : never
190
+ };
191
+
175
192
  type SelectedModel<M extends Prisma.ModelName, S, SelectModels> =
176
193
  [S] extends [false]
177
194
  ? FullModelType<M>
178
195
  : [S] extends [never]
179
196
  ? FullModelType<M>
180
197
  : [S] extends [keyof SelectModels]
181
- ? (Prisma.Result<PrismaDelegate<M>, { select: SelectModels[S] }, 'findMany'> extends Array<infer U> ? U : never)
198
+ ? PrecomputedSelects<M, SelectModels>[S]
182
199
  : FullModelType<M>;
183
200
 
184
201
  type CleanFields<R extends string> =
@@ -207,9 +224,18 @@ export type MethodOptions<S> = {
207
224
  };
208
225
 
209
226
  /**
210
- * Versão de `MethodOptions` derivada de um mapa de select models.
227
+ * Versão de `MethodOptions` derivada diretamente de uma instância configurada de `VSRepository`.
228
+ *
229
+ * @template TRepo Instância de `VSRepository` configurada (use `typeof meuVSRepo`).
230
+ *
231
+ * @example
232
+ * const usuarioVSRepo = setupVSRepo<Usuario, "usuario">()(config);
233
+ * type Opts = MethodOptionsModel<typeof usuarioVSRepo>;
211
234
  */
212
- export type MethodOptionsModel<T> = MethodOptions<keyof T | false>;
235
+ export type MethodOptionsModel<TRepo> =
236
+ TRepo extends VSRepository<any, any, infer Config>
237
+ ? MethodOptions<keyof ExtractSelectModels<Config> | false>
238
+ : never;
213
239
 
214
240
  type MethodFn<MethodName extends string, T, M extends Prisma.ModelName, R extends string, SelectModels, DefaultSelect extends keyof SelectModels | false, I> =
215
241
  <S extends keyof SelectModels | false = DefaultSelect>(...args: [...ExtractFields<T, CleanFields<R>, I>, ...ExtraArgs<MethodName, R, I>, options?: MethodOptions<S>]) => Promise<ResolveReturnType<MethodName, SelectedModel<M, S, SelectModels>>>;
@@ -217,10 +243,10 @@ type MethodFn<MethodName extends string, T, M extends Prisma.ModelName, R extend
217
243
  type GetMappedMethod<K extends string, MethodConf> =
218
244
  K extends `findBy${string}` ? (MethodConf extends { fbMode: 'one' } ? 'findByOne' : 'findByList') :
219
245
  K extends `existsBy${string}` ? 'existsBy' :
220
- K extends `findUniqueBy${string}` ? 'findUnique' :
221
246
  K extends `findUniqueOrThrowBy${string}` ? 'findUniqueOrThrow' :
222
- K extends `findFirstBy${string}` | `findFirst${string}` ? 'findFirst' :
247
+ K extends `findUniqueBy${string}` ? 'findUnique' :
223
248
  K extends `findFirstOrThrowBy${string}` | `findFirstOrThrow${string}` ? 'findFirstOrThrow' :
249
+ K extends `findFirstBy${string}` | `findFirst${string}` ? 'findFirst' :
224
250
  K extends `findManyBy${string}` | `findMany${string}` ? 'findMany' :
225
251
  K extends `createManyAndReturn${string}` ? 'createManyAndReturn' :
226
252
  K extends `createMany${string}` ? 'createMany' :
@@ -231,14 +257,26 @@ type GetMappedMethod<K extends string, MethodConf> =
231
257
  K extends `upsertBy${string}` ? 'upsertBy' :
232
258
  K extends `deleteManyBy${string}` ? 'deleteManyBy' :
233
259
  K extends `deleteBy${string}` ? 'deleteBy' :
260
+ K extends `countWhere${string}` ? 'countWhere' :
234
261
  K extends `countBy${string}` | `count${string}` ? 'count' :
235
262
  K extends `findListWhere${string}` ? 'findListWhere' :
236
263
  K extends `findWhere${string}` ? 'findWhere' :
264
+ K extends 'aggregate' ? 'aggregate' :
265
+ K extends 'groupBy' ? 'groupBy' :
237
266
  never;
238
267
 
239
268
  type ExtractPatternBase<K extends string> =
240
269
  K extends `${string}By${infer R}` ? R :
241
- K extends `findFirst${infer R}` | `findFirstOrThrow${infer R}` | `findMany${infer R}` | `createManyAndReturn${infer R}` | `createMany${infer R}` | `create${infer R}` | `count${infer R}` | `findWhere${infer R}` | `findListWhere${infer R}` ? R : '';
270
+ K extends `findFirstOrThrow${infer R}` ? R :
271
+ K extends `findFirst${infer R}` ? R :
272
+ K extends `findMany${infer R}` ? R :
273
+ K extends `createManyAndReturn${infer R}` ? R :
274
+ K extends `createMany${infer R}` ? R :
275
+ K extends `create${infer R}` ? R :
276
+ K extends `countWhere${infer R}` ? R :
277
+ K extends `count${infer R}` ? R :
278
+ K extends `findListWhere${infer R}` ? R :
279
+ K extends `findWhere${infer R}` ? R : '';
242
280
 
243
281
  type MethodFactory<T, M extends Prisma.ModelName, K extends string, SelectModels, DefaultSelect extends keyof SelectModels | false, I, MethodConf> =
244
282
  MethodFn<GetMappedMethod<K, MethodConf>, T, M, ExtractPatternBase<K>, SelectModels, DefaultSelect, I>;
@@ -252,10 +290,28 @@ type ExtractSelectModels<Config> = Config extends { selectModels: infer SM } ? S
252
290
  type ExtractDefaultSelect<Config> = Config extends { defaultSelectModel: infer D } ? D : never;
253
291
  type ExtractRelations<Config> = Config extends { relations: infer R } ? (R extends object ? R : {}) : {};
254
292
 
293
+ type AggregateMethod<M extends Prisma.ModelName> = <A extends Prisma.TypeMap['model'][M]['operations']['aggregate']['args']>(
294
+ prismaArgs: A,
295
+ options?: { db?: ClientOrTransaction }
296
+ ) => Promise<Prisma.Result<PrismaDelegate<M>, A, 'aggregate'>>;
297
+
298
+ type GroupByMethod<M extends Prisma.ModelName> = <A extends Prisma.TypeMap['model'][M]['operations']['groupBy']['args']>(
299
+ prismaArgs: A,
300
+ options?: { db?: ClientOrTransaction }
301
+ ) => Promise<Prisma.Result<PrismaDelegate<M>, A, 'groupBy'>>;
302
+
255
303
  type DynamicMethods<T, M extends Prisma.ModelName, Config, I> = Config extends { methods: infer Methods }
256
304
  ? {
257
305
  [K in keyof Methods as Methods[K] extends { map: true } ? K : never]: K extends string
258
- ? MethodFactory<T, M, Methods[K] extends { proxyTo: infer P extends string } ? P : K, ExtractSelectModels<Config>, ResolveSelectModel<Methods[K], Config, ExtractSelectModels<Config>>, I, Methods[K]>
306
+ ? (Methods[K] extends { proxyTo: infer P extends string } ? P : K) extends infer ResolvedKey
307
+ ? ResolvedKey extends 'aggregate'
308
+ ? AggregateMethod<M>
309
+ : ResolvedKey extends 'groupBy'
310
+ ? GroupByMethod<M>
311
+ : ResolvedKey extends string
312
+ ? MethodFactory<T, M, ResolvedKey, ExtractSelectModels<Config>, ResolveSelectModel<Methods[K], Config, ExtractSelectModels<Config>>, I, Methods[K]>
313
+ : never
314
+ : never
259
315
  : never;
260
316
  }
261
317
  : {};
@@ -463,7 +519,7 @@ type InjectedRemove<
463
519
  /**
464
520
  * Versão distributiva de `Omit`, preservando unions ao remover propriedades.
465
521
  */
466
- export type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
522
+ type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
467
523
  type ExtractUnionProp<T, K extends PropertyKey> = T extends any ? (K extends keyof T ? T[K] : never) : never;
468
524
 
469
525
  type ExtractNestedCreateInput<M extends Prisma.ModelName, K extends PropertyKey> =
@@ -483,7 +539,7 @@ type RelationPayload<TField, TRelationConfig, M extends Prisma.ModelName, K exte
483
539
  * Substitui os campos relacionais pelo formato compatível com os modos
484
540
  * configurados em `relations`.
485
541
  */
486
- export type UpsertWithRelations<T, M extends Prisma.ModelName, TRelations> =
542
+ type UpsertWithRelations<T, M extends Prisma.ModelName, TRelations> =
487
543
  DistributiveOmit<ModelUpsertInput<M>, keyof TRelations> & {
488
544
  [K in Extract<keyof TRelations, keyof T>]?: RelationPayload<T[K], TRelations[K], M, K>;
489
545
  };
@@ -511,11 +567,27 @@ type RelationUpdatePayload<TField, TRelationConfig, M extends Prisma.ModelName,
511
567
  * Substitui os campos relacionais pelo formato compatível com os modos
512
568
  * configurados em `relations` para fluxos de Update.
513
569
  */
514
- export type UpdateWithRelations<T, M extends Prisma.ModelName, TRelations> =
570
+ type UpdateWithRelations<T, M extends Prisma.ModelName, TRelations> =
515
571
  DistributiveOmit<PrismaModelInputs<M>['updateInput'], keyof TRelations> & {
516
572
  [K in Extract<keyof TRelations, keyof T>]?: RelationUpdatePayload<T[K], TRelations[K], M, K>;
517
573
  };
518
574
 
575
+ /**
576
+ * Extrai o tipo de payload do método `save` a partir de uma instância de VSRepository configurada.
577
+ */
578
+ export type SaveObject<TRepo> =
579
+ TRepo extends VSRepository<infer T, infer M, infer Config>
580
+ ? UpsertWithRelations<T, M, Config extends { relations: infer R } ? (R extends object ? R : {}) : {}>
581
+ : never;
582
+
583
+ /**
584
+ * Extrai o tipo de payload do método `patch` a partir de uma instância de VSRepository configurada.
585
+ */
586
+ export type PacthObject<TRepo> =
587
+ TRepo extends VSRepository<infer T, infer M, infer Config>
588
+ ? UpdateWithRelations<T, M, Config extends { relations: infer R } ? (R extends object ? R : {}) : {}>
589
+ : never;
590
+
519
591
  type InjectedSave<
520
592
  T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined,
521
593
  TSelects = ExtractSelectModels<Config>,
@@ -529,7 +601,7 @@ type InjectedSave<
529
601
  * @returns O registro salvo com tipos refinados conforme a entrada.
530
602
  */
531
603
  save<
532
- O extends UpsertWithRelations<T, M, TRelations>,
604
+ O,
533
605
  S extends keyof TSelects | false = ResolveMethodDefaultSelect<Config, C, 'save', TSelects>
534
606
  >(
535
607
  obj: O & UpsertWithRelations<T, M, TRelations>,
@@ -552,7 +624,7 @@ type InjectedPatch<
552
624
  * @returns O registro atualizado refinado conforme a entrada.
553
625
  */
554
626
  patch<
555
- O extends UpdateWithRelations<T, M, TRelations>,
627
+ O,
556
628
  S extends keyof TSelects | false = ResolveMethodDefaultSelect<Config, C, 'patch', TSelects>
557
629
  >(
558
630
  pk: T[TPk extends keyof T ? TPk : never],
@@ -726,7 +798,7 @@ export type RepoConfig<T, M extends Prisma.ModelName, SM extends Record<string,
726
798
  *
727
799
  * Combina métodos dinâmicos, métodos base e extensões personalizadas.
728
800
  */
729
- export type BuiltRepository<T extends object, M extends Prisma.ModelName, Config extends RepoConfig<T, M, any>, C extends BuildConfig<any> | undefined> = {
801
+ type BuiltRepository<T extends object, M extends Prisma.ModelName, Config extends RepoConfig<T, M, any>, C extends BuildConfig<any> | undefined> = {
730
802
  /**
731
803
  * Estende o repository com métodos personalizados sem perder a tipagem.
732
804
  */
@@ -775,8 +847,8 @@ export type ValidateRepoConfig<T extends object, M extends Prisma.ModelName, Con
775
847
  pkName: keyof T;
776
848
  /** Mapa de select models disponíveis. */
777
849
  selectModels?: SelectModels<M>;
778
- /** Select model padrão. */
779
- defaultSelectModel?: Config extends { selectModels: infer SM } ? (keyof SM extends never ? string : Extract<keyof SM, string>) : string;
850
+ /** Select model padrão. Deve ser uma chave dos `selectModels`. */
851
+ defaultSelectModel?: string;
780
852
  /** Filtros globais aplicados às queries do repository. */
781
853
  requiredWhere?: WhereModel<M>;
782
854
  /** Relações gerenciadas automaticamente pelo `save` e pelo `patch`. */
@@ -784,9 +856,7 @@ export type ValidateRepoConfig<T extends object, M extends Prisma.ModelName, Con
784
856
  /** Mapa dos métodos dinâmicos e suas regras de validação. */
785
857
  methods?: {
786
858
  [K in keyof (Config extends { methods: infer Meth } ? Meth : {})]: K extends string
787
- ? HasMultipleANDs<K> extends true
788
- ? MethodConfig<M, Config extends { selectModels: infer SM } ? SM : any> & { proxyTo: ValidMethodPatterns }
789
- : MethodConfig<M, Config extends { selectModels: infer SM } ? SM : any> & (K extends ValidMethodPatterns ? {} : { proxyTo: ValidMethodPatterns })
859
+ ? MethodConfig<M, Config extends { selectModels: infer SM } ? SM : any> & (K extends ValidMethodPatterns ? {} : { proxyTo: ValidMethodPatterns })
790
860
  : never;
791
861
  };
792
862
  };
@@ -96,7 +96,8 @@ export class VSRepository {
96
96
  let existsMode = false;
97
97
  let skipDuplicates;
98
98
  let ignoreSkipDuplicates = true;
99
- let whereIndex
99
+ let whereIndex;
100
+ let prismaArgsIndex;
100
101
  const whereParams = [];
101
102
  const otherParams = [];
102
103
 
@@ -131,6 +132,16 @@ export class VSRepository {
131
132
  ignoreOrderByAndPagination = false;
132
133
  ignoreSelect = true;
133
134
  method = 'count';
135
+ } else if(keyToMap.startsWith('countWhere')) {
136
+ keyToMapReplaced = keyToMap.replace('countWhere', '');
137
+ ignoreOrderByAndPagination = false;
138
+ ignoreWhere = true;
139
+ onlyBaseWheres = true;
140
+ ignoreSelect = true;
141
+ method = 'count';
142
+ whereIndex = 0;
143
+ otherParams.push('where');
144
+ argsCount += 1;
134
145
  } else if(keyToMap.startsWith('count')) {
135
146
  keyToMapReplaced = keyToMap.replace('count', '');
136
147
  ignoreOrderByAndPagination = false;
@@ -233,6 +244,22 @@ export class VSRepository {
233
244
  keyToMapReplaced = keyToMap.replace('findBy', '');
234
245
  ignoreOrderByAndPagination = false;
235
246
  method = methods[originalKey].fbMode === 'one' ? 'findFirst' : 'findMany';
247
+ } else if(keyToMap === 'aggregate') {
248
+ keyToMapReplaced = keyToMap.replace('aggregate', '')
249
+ ignoreSelect = true;
250
+ ignoreWhere = true;
251
+ method = 'aggregate';
252
+ prismaArgsIndex = 0;
253
+ otherParams.push('prismaArgs');
254
+ argsCount++;
255
+ } else if(keyToMap === 'groupBy') {
256
+ keyToMapReplaced = keyToMap.replace('groupBy', '')
257
+ ignoreSelect = true;
258
+ ignoreWhere = true;
259
+ method = 'groupBy';
260
+ prismaArgsIndex = 0;
261
+ otherParams.push('prismaArgs');
262
+ argsCount++;
236
263
  } else {
237
264
  throw new VSRepoBuildError(`[VSRepository] (build) Unknown method: ${keyToMap}.`);
238
265
  }
@@ -559,6 +586,8 @@ export class VSRepository {
559
586
  }
560
587
 
561
588
  buildInstance.vsrepocache.set(originalKey, (args, optionsSelectModel) => {
589
+ if (prismaArgsIndex !== undefined) return args.at(prismaArgsIndex);
590
+
562
591
  const prismaArgs = {};
563
592
 
564
593
  if(!ignoreSelect) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vsrepo",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Uma biblioteca de repository pattern para Prisma",
5
5
  "homepage": "https://github.com/jaobrabo123/VSRepository#readme",
6
6
  "repository": {
@@ -38,8 +38,7 @@
38
38
  "node": ">=18"
39
39
  },
40
40
  "scripts": {
41
- "vsrepo": "node ./scripts/configure-prisma-import.mjs",
42
- "configure:prisma-import": "node ./scripts/configure-prisma-import.mjs"
41
+ "vsrepo": "node ./scripts/configure-prisma-import.mjs"
43
42
  },
44
43
  "keywords": [
45
44
  "prisma",
@@ -47,7 +46,8 @@
47
46
  "orm",
48
47
  "typescript",
49
48
  "vsrepo",
50
- "vsrepository"
49
+ "vsrepository",
50
+ "jparepository"
51
51
  ],
52
52
  "author": "João Pedro Azevedo Freire Mecenas",
53
53
  "license": "MIT",