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 +79 -0
- package/VSRepository/VSRepository.d.ts +41 -10
- package/VSRepository/VSRepository.js +30 -1
- package/package.json +1 -1
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 `
|
|
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 `
|
|
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
|
-
?
|
|
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?:
|
|
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) {
|