vsrepo 1.2.0 → 1.2.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 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`:
@@ -51,8 +51,9 @@ 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}`;
54
+ | `deleteBy${string}` | `countBy${string}` | `count${string}` | `countWhere${string}`
55
+ | `findWhere${string}` | `findListWhere${string}`
56
+ | 'aggregate' | 'groupBy';
56
57
 
57
58
  type StringModifiers =
58
59
  | 'NotStartsWith' | 'StartsWith' | 'NotEndsWith' | 'EndsWith'
@@ -150,7 +151,7 @@ type ResolveReturnType<M extends string, TSelected> =
150
151
  M extends 'findMany' | 'createManyAndReturn' | 'updateManyAndReturnBy' | 'findListWhere' | 'findByList' ? TSelected[] :
151
152
  M extends 'findUnique' | 'findFirst' | 'findWhere' | 'findByOne' ? TSelected | null :
152
153
  M extends 'findUniqueOrThrow' | 'findFirstOrThrow' | 'create' | 'updateBy' | 'upsertBy' | 'deleteBy' ? TSelected :
153
- M extends 'count' ? number : never;
154
+ M extends 'count' | 'countWhere' ? number : never;
154
155
 
155
156
  type ExtraArgs<M extends string, R extends string, I> = [
156
157
  ...(M extends 'upsertBy' ? [update: I extends { updateInput: infer U } ? U : unknown, create: I extends { createInput: infer C } ? C : unknown]
@@ -158,7 +159,7 @@ type ExtraArgs<M extends string, R extends string, I> = [
158
159
  : M extends 'updateBy' ? [data: I extends { updateInput: infer U } ? U : unknown]
159
160
  : M extends 'createMany' | 'createManyAndReturn' ? [data: I extends { createManyInput: infer CM } ? CM : unknown]
160
161
  : 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]
162
+ : M extends 'findWhere' | 'findListWhere' | 'countWhere' ? [where: I extends { whereInput: infer W } ? NonNullable<W> : unknown]
162
163
  : []),
163
164
  ...(R extends `${string}PaginatedAndOrdered` ? [pagination: PaginationOptions<I extends { cursorInput: infer C } ? C : unknown>, order: I extends { orderByInput: infer OB } ? OB : OrderOptions]
164
165
  : R extends `${string}OrderedAndPaginated` ? [order: I extends { orderByInput: infer OB } ? OB : OrderOptions, pagination: PaginationOptions<I extends { cursorInput: infer C } ? C : unknown>]
@@ -217,10 +218,10 @@ type MethodFn<MethodName extends string, T, M extends Prisma.ModelName, R extend
217
218
  type GetMappedMethod<K extends string, MethodConf> =
218
219
  K extends `findBy${string}` ? (MethodConf extends { fbMode: 'one' } ? 'findByOne' : 'findByList') :
219
220
  K extends `existsBy${string}` ? 'existsBy' :
220
- K extends `findUniqueBy${string}` ? 'findUnique' :
221
221
  K extends `findUniqueOrThrowBy${string}` ? 'findUniqueOrThrow' :
222
- K extends `findFirstBy${string}` | `findFirst${string}` ? 'findFirst' :
222
+ K extends `findUniqueBy${string}` ? 'findUnique' :
223
223
  K extends `findFirstOrThrowBy${string}` | `findFirstOrThrow${string}` ? 'findFirstOrThrow' :
224
+ K extends `findFirstBy${string}` | `findFirst${string}` ? 'findFirst' :
224
225
  K extends `findManyBy${string}` | `findMany${string}` ? 'findMany' :
225
226
  K extends `createManyAndReturn${string}` ? 'createManyAndReturn' :
226
227
  K extends `createMany${string}` ? 'createMany' :
@@ -231,14 +232,26 @@ type GetMappedMethod<K extends string, MethodConf> =
231
232
  K extends `upsertBy${string}` ? 'upsertBy' :
232
233
  K extends `deleteManyBy${string}` ? 'deleteManyBy' :
233
234
  K extends `deleteBy${string}` ? 'deleteBy' :
235
+ K extends `countWhere${string}` ? 'countWhere' :
234
236
  K extends `countBy${string}` | `count${string}` ? 'count' :
235
237
  K extends `findListWhere${string}` ? 'findListWhere' :
236
238
  K extends `findWhere${string}` ? 'findWhere' :
239
+ K extends 'aggregate' ? 'aggregate' :
240
+ K extends 'groupBy' ? 'groupBy' :
237
241
  never;
238
242
 
239
243
  type ExtractPatternBase<K extends string> =
240
244
  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 : '';
245
+ K extends `findFirstOrThrow${infer R}` ? R :
246
+ K extends `findFirst${infer R}` ? R :
247
+ K extends `findMany${infer R}` ? R :
248
+ K extends `createManyAndReturn${infer R}` ? R :
249
+ K extends `createMany${infer R}` ? R :
250
+ K extends `create${infer R}` ? R :
251
+ K extends `countWhere${infer R}` ? R :
252
+ K extends `count${infer R}` ? R :
253
+ K extends `findListWhere${infer R}` ? R :
254
+ K extends `findWhere${infer R}` ? R : '';
242
255
 
243
256
  type MethodFactory<T, M extends Prisma.ModelName, K extends string, SelectModels, DefaultSelect extends keyof SelectModels | false, I, MethodConf> =
244
257
  MethodFn<GetMappedMethod<K, MethodConf>, T, M, ExtractPatternBase<K>, SelectModels, DefaultSelect, I>;
@@ -252,10 +265,28 @@ type ExtractSelectModels<Config> = Config extends { selectModels: infer SM } ? S
252
265
  type ExtractDefaultSelect<Config> = Config extends { defaultSelectModel: infer D } ? D : never;
253
266
  type ExtractRelations<Config> = Config extends { relations: infer R } ? (R extends object ? R : {}) : {};
254
267
 
268
+ type AggregateMethod<M extends Prisma.ModelName> = <A extends Prisma.TypeMap['model'][M]['operations']['aggregate']['args']>(
269
+ prismaArgs: A,
270
+ options?: { db?: ClientOrTransaction }
271
+ ) => Promise<Prisma.Result<PrismaDelegate<M>, A, 'aggregate'>>;
272
+
273
+ type GroupByMethod<M extends Prisma.ModelName> = <A extends Prisma.TypeMap['model'][M]['operations']['groupBy']['args']>(
274
+ prismaArgs: A,
275
+ options?: { db?: ClientOrTransaction }
276
+ ) => Promise<Prisma.Result<PrismaDelegate<M>, A, 'groupBy'>>;
277
+
255
278
  type DynamicMethods<T, M extends Prisma.ModelName, Config, I> = Config extends { methods: infer Methods }
256
279
  ? {
257
280
  [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]>
281
+ ? (Methods[K] extends { proxyTo: infer P extends string } ? P : K) extends infer ResolvedKey
282
+ ? ResolvedKey extends 'aggregate'
283
+ ? AggregateMethod<M>
284
+ : ResolvedKey extends 'groupBy'
285
+ ? GroupByMethod<M>
286
+ : ResolvedKey extends string
287
+ ? MethodFactory<T, M, ResolvedKey, ExtractSelectModels<Config>, ResolveSelectModel<Methods[K], Config, ExtractSelectModels<Config>>, I, Methods[K]>
288
+ : never
289
+ : never
259
290
  : never;
260
291
  }
261
292
  : {};
@@ -775,8 +806,8 @@ export type ValidateRepoConfig<T extends object, M extends Prisma.ModelName, Con
775
806
  pkName: keyof T;
776
807
  /** Mapa de select models disponíveis. */
777
808
  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;
809
+ /** Select model padrão. Deve ser uma chave dos `selectModels`. */
810
+ defaultSelectModel?: string;
780
811
  /** Filtros globais aplicados às queries do repository. */
781
812
  requiredWhere?: WhereModel<M>;
782
813
  /** Relações gerenciadas automaticamente pelo `save` e pelo `patch`. */
@@ -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.1",
4
4
  "description": "Uma biblioteca de repository pattern para Prisma",
5
5
  "homepage": "https://github.com/jaobrabo123/VSRepository#readme",
6
6
  "repository": {