vsrepo 1.1.0 → 1.1.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
|
@@ -7,7 +7,7 @@ Biblioteca de repository pattern para projetos que usam **Prisma**, com suporte
|
|
|
7
7
|
|
|
8
8
|
O VSRepository permite criar repositories fortemente tipados com:
|
|
9
9
|
|
|
10
|
-
- **Métodos base** automáticos: `get`, `save`, `remove`
|
|
10
|
+
- **Métodos base** automáticos: `get`, `getOrThrow`, `save`, `remove`
|
|
11
11
|
- **Métodos dinâmicos** inferidos pelo nome: `findByEmail`, `findManyPaginated`, `updateById`, `deleteManyByIdIn`
|
|
12
12
|
- **Select models** reutilizáveis para diferentes projeções de dados
|
|
13
13
|
- **Type safety** em 100% das operações
|
|
@@ -182,7 +182,7 @@ O VSRepository pode ser facilmente integrado em projetos NestJS através de prov
|
|
|
182
182
|
import { Provider } from "@nestjs/common";
|
|
183
183
|
import { PrismaService } from "../../database/prisma.service";
|
|
184
184
|
import { UserGetPayload } from "../../generated/prisma/models";
|
|
185
|
-
import {
|
|
185
|
+
import { setupVSRepo } from "../../generated/vsrepo";
|
|
186
186
|
|
|
187
187
|
const userVSRepo = setupVSRepo<
|
|
188
188
|
UserGetPayload<{ include: { profile: true } }>,
|
|
@@ -220,19 +220,28 @@ const userVSRepo = setupVSRepo<
|
|
|
220
220
|
proxyTo: "findUniqueByEmail",
|
|
221
221
|
selectModel: "auth",
|
|
222
222
|
},
|
|
223
|
+
findByEmailEndsWith: {
|
|
224
|
+
map: true,
|
|
225
|
+
}
|
|
223
226
|
},
|
|
224
227
|
});
|
|
225
228
|
|
|
226
|
-
|
|
229
|
+
const setupUserRepository = (prisma: PrismaService) => {
|
|
230
|
+
return userVSRepo.build(prisma).extend((repo) => ({
|
|
231
|
+
buscarPorDominio: async (dominio: string) => {
|
|
232
|
+
return repo.findByEmailEndsWith(`@${dominio}`);
|
|
233
|
+
},
|
|
234
|
+
}));
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export type UserRepository = ReturnType<typeof setupUserRepository>;
|
|
227
238
|
|
|
228
239
|
export const USER_REPOSITORY = Symbol("USER_REPOSITORY");
|
|
229
240
|
|
|
230
241
|
export const UserRepositoryProvider: Provider = {
|
|
231
242
|
provide: USER_REPOSITORY,
|
|
232
243
|
inject: [PrismaService],
|
|
233
|
-
useFactory:
|
|
234
|
-
return userVSRepo.build(prisma);
|
|
235
|
-
},
|
|
244
|
+
useFactory: setupUserRepository,
|
|
236
245
|
};
|
|
237
246
|
```
|
|
238
247
|
|
|
@@ -323,11 +332,12 @@ export class UserController {
|
|
|
323
332
|
|
|
324
333
|
## Métodos base
|
|
325
334
|
|
|
326
|
-
Ao chamar `.build(prisma)`,
|
|
335
|
+
Ao chamar `.build(prisma)`, quatro métodos são automaticamente disponibilizados:
|
|
327
336
|
|
|
328
337
|
| Método | Descrição |
|
|
329
338
|
| ------------ | ------------------------------------------ |
|
|
330
339
|
| `get(pk)` | Busca um registro pela primary key |
|
|
340
|
+
| `getOrThrow(pk)` | Busca um registro pela primary key e lança erro se não encontrar |
|
|
331
341
|
| `save(obj)` | Cria ou atualiza (upsert pela primary key) |
|
|
332
342
|
| `remove(pk)` | Remove um registro pela primary key |
|
|
333
343
|
|
|
@@ -433,8 +443,11 @@ O prefixo do nome do método determina qual operação Prisma será chamada e qu
|
|
|
433
443
|
| ------------------------- | ------------------------ | ---------------------- | -------------------------------------------------------- |
|
|
434
444
|
| `findBy` | `findMany` / `findFirst` | `T[]` ou `T \| null` | Padrão é lista; use `fbMode: "one"` para retorno único |
|
|
435
445
|
| `findUniqueBy` | `findUnique` | `T \| null` | |
|
|
446
|
+
| `findUniqueOrThrowBy` | `findUniqueOrThrow` | `T` | Lança erro se não encontrar |
|
|
436
447
|
| `findFirstBy` | `findFirst` | `T \| null` | Aceita campos como filtro |
|
|
448
|
+
| `findFirstOrThrowBy` | `findFirstOrThrow` | `T` | Aceita campos como filtro; lança erro se não encontrar |
|
|
437
449
|
| `findFirst` | `findFirst` | `T \| null` | Sem filtros de campo; aplica só `requiredWhere` e `pushWhere` |
|
|
450
|
+
| `findFirstOrThrow` | `findFirstOrThrow` | `T` | Sem filtros de campo; aplica só `requiredWhere` e `pushWhere`; lança erro se não encontrar |
|
|
438
451
|
| `findManyBy` | `findMany` | `T[]` | Aceita campos como filtro |
|
|
439
452
|
| `findMany` | `findMany` | `T[]` | Sem filtros de campo; aplica só `requiredWhere` e `pushWhere` |
|
|
440
453
|
| `findWhere` | `findFirst` | `T \| null` | Recebe um objeto `where` explícito como argumento |
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { VSRepoConfigError } from "./VSRepoError.js";
|
|
2
|
+
|
|
3
|
+
export class FieldValidator {
|
|
4
|
+
constructor(throws) {
|
|
5
|
+
this.throws = throws;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
validate(
|
|
9
|
+
field,
|
|
10
|
+
fieldName,
|
|
11
|
+
mustBe,
|
|
12
|
+
undefinedAble,
|
|
13
|
+
nullAble,
|
|
14
|
+
customReason
|
|
15
|
+
) {
|
|
16
|
+
if (field === undefined) {
|
|
17
|
+
if (undefinedAble) return;
|
|
18
|
+
throw this.instanceError(fieldName, customReason ?? "cannot be undefined");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (field === null) {
|
|
22
|
+
if (nullAble) return;
|
|
23
|
+
throw this.instanceError(fieldName, customReason ?? "cannot be null");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const condition = typeof mustBe === "function" ? !mustBe(field) : typeof field !== mustBe;
|
|
27
|
+
|
|
28
|
+
if(condition) {
|
|
29
|
+
throw this.instanceError(fieldName, customReason ?? `must be a valid ${mustBe}`);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
instanceError(fieldName, reason) {
|
|
34
|
+
const error = new this.throws("");
|
|
35
|
+
error.message = `[VSRepository] (${error.type}) '${fieldName}' ${reason}.`;
|
|
36
|
+
return error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function validateConfig(config) {
|
|
41
|
+
const validator = new FieldValidator(VSRepoConfigError);
|
|
42
|
+
|
|
43
|
+
validator.validate(config, "config", "object");
|
|
44
|
+
validator.validate(config.tableName, "tableName", "string");
|
|
45
|
+
validator.validate(config.pkName, "pkName", "string");
|
|
46
|
+
validator.validate(config.selectModels, "selectModels", "object", true);
|
|
47
|
+
|
|
48
|
+
if(config.defaultSelectModel !== undefined) {
|
|
49
|
+
validator.validate(config.defaultSelectModel, "defaultSelectModel", "string");
|
|
50
|
+
|
|
51
|
+
validator.validate(config.defaultSelectModel, "defaultSelectModel", (field) => {
|
|
52
|
+
return !!(config.selectModels && Object.keys(config.selectModels).includes(config.defaultSelectModel))
|
|
53
|
+
}, false, false, "must be a valid key of 'selectModels'");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if(config.relations !== undefined) {
|
|
57
|
+
validator.validate(config.relations, "relations", "object");
|
|
58
|
+
|
|
59
|
+
for (const key of Object.keys(config.relations)) {
|
|
60
|
+
const relation = config.relations[key];
|
|
61
|
+
|
|
62
|
+
validator.validate(relation.pk, "pk", "string", false, false, `of relation '${key}' must be a string`);
|
|
63
|
+
validator.validate(relation.mode, "mode", (field) => ['otm', 'mtm', 'mto', 'oto'].includes(field), false, false, `of relation '${key}' must be 'otm', 'mtm', 'mto' or 'oto'`);
|
|
64
|
+
validator.validate(relation.restriction, "restriction", (field) => ['set', 'add'].includes(field), false, false, `of relation '${key}' must be 'set' or 'add'`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
validator.validate(config.requiredWhere, "requiredWhere", "object", true);
|
|
69
|
+
|
|
70
|
+
if(config.methods !== undefined) {
|
|
71
|
+
validator.validate(config.methods, "methods", "object");
|
|
72
|
+
|
|
73
|
+
for (const [key, value] of Object.entries(config.methods)) {
|
|
74
|
+
validator.validate(value.map, "map", "boolean", false, false, `on ${key} must be a valid boolean`);
|
|
75
|
+
validator.validate(value.whereType, "whereType", (field) => ['extending', 'overwrite'].includes(field), true, false, `on ${key} must be 'extending' or 'overwrite'`);
|
|
76
|
+
validator.validate(value.selectModel, "selectModel", (field) => !!(field === false || (config.selectModels && Object.keys(config.selectModels).includes(field))), true, false, `on ${key} must be a valid key of 'selectModels'`);
|
|
77
|
+
validator.validate(value.proxyTo, "proxyTo", "string", true, false, `on ${key} must be a valid string`);
|
|
78
|
+
validator.validate(value.fbMode, "fbMode", (field) => ['one', 'list'].includes(field), true, false, `on ${key} must be 'one' or 'list'`);
|
|
79
|
+
validator.validate(value.pushWhere, "pushWhere", "object", true, false, `on ${key} must be a valid object`);
|
|
80
|
+
validator.validate(value.injectOrdenation, "injectOrdenation", "object", true, false, `on ${key} must be a valid object or array`);
|
|
81
|
+
validator.validate(value.injectPagination, "injectPagination", "object", true, false, `on ${key} must be a valid object`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -43,9 +43,12 @@ export type PaginationOptions<TCursor = unknown> = {
|
|
|
43
43
|
export type OrderOptions = OrderPattern | OrderPattern[];
|
|
44
44
|
|
|
45
45
|
type ValidMethodPatterns =
|
|
46
|
-
| `existsBy${string}` | `findBy${string}`
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
46
|
+
| `existsBy${string}` | `findBy${string}`
|
|
47
|
+
| `findUniqueBy${string}` | `findUniqueOrThrowBy${string}`
|
|
48
|
+
| `findFirstBy${string}` | `findFirst${string}`
|
|
49
|
+
| `findFirstOrThrowBy${string}` | `findFirstOrThrow${string}`
|
|
50
|
+
| `findManyBy${string}` | `findMany${string}`
|
|
51
|
+
| `createManyAndReturn${string}` | `createMany${string}`
|
|
49
52
|
| `create${string}` | `updateManyAndReturnBy${string}` | `updateManyBy${string}`
|
|
50
53
|
| `updateBy${string}` | `upsertBy${string}` | `deleteManyBy${string}`
|
|
51
54
|
| `deleteBy${string}` | `countBy${string}` | `count${string}`
|
|
@@ -55,9 +58,6 @@ type StringModifiers =
|
|
|
55
58
|
| 'NotStartsWith' | 'StartsWith' | 'NotEndsWith' | 'EndsWith'
|
|
56
59
|
| 'NotContains' | 'Contains' | 'NotIn' | 'In' | 'Not';
|
|
57
60
|
|
|
58
|
-
type NumberModifiers =
|
|
59
|
-
| 'LessThanEqual' | 'LessThan' | 'GreaterThanEqual' | 'GreaterThan';
|
|
60
|
-
|
|
61
61
|
type StripModifier<S extends string> =
|
|
62
62
|
S extends `${infer Base}${StringModifiers}Insensitive` ? Base :
|
|
63
63
|
S extends `${infer Base}NotStartsWith` ? Base :
|
|
@@ -149,7 +149,7 @@ type ResolveReturnType<M extends string, TSelected> =
|
|
|
149
149
|
M extends 'createMany' | 'updateManyBy' | 'deleteManyBy' ? { count: number } :
|
|
150
150
|
M extends 'findMany' | 'createManyAndReturn' | 'updateManyAndReturnBy' | 'findListWhere' | 'findByList' ? TSelected[] :
|
|
151
151
|
M extends 'findUnique' | 'findFirst' | 'findWhere' | 'findByOne' ? TSelected | null :
|
|
152
|
-
M extends 'create' | 'updateBy' | 'upsertBy' | 'deleteBy' ? TSelected :
|
|
152
|
+
M extends 'findUniqueOrThrow' | 'findFirstOrThrow' | 'create' | 'updateBy' | 'upsertBy' | 'deleteBy' ? TSelected :
|
|
153
153
|
M extends 'count' ? number : never;
|
|
154
154
|
|
|
155
155
|
type ExtraArgs<M extends string, R extends string, I> = [
|
|
@@ -218,7 +218,9 @@ type GetMappedMethod<K extends string, MethodConf> =
|
|
|
218
218
|
K extends `findBy${string}` ? (MethodConf extends { fbMode: 'one' } ? 'findByOne' : 'findByList') :
|
|
219
219
|
K extends `existsBy${string}` ? 'existsBy' :
|
|
220
220
|
K extends `findUniqueBy${string}` ? 'findUnique' :
|
|
221
|
+
K extends `findUniqueOrThrowBy${string}` ? 'findUniqueOrThrow' :
|
|
221
222
|
K extends `findFirstBy${string}` | `findFirst${string}` ? 'findFirst' :
|
|
223
|
+
K extends `findFirstOrThrowBy${string}` | `findFirstOrThrow${string}` ? 'findFirstOrThrow' :
|
|
222
224
|
K extends `findManyBy${string}` | `findMany${string}` ? 'findMany' :
|
|
223
225
|
K extends `createManyAndReturn${string}` ? 'createManyAndReturn' :
|
|
224
226
|
K extends `createMany${string}` ? 'createMany' :
|
|
@@ -236,7 +238,7 @@ type GetMappedMethod<K extends string, MethodConf> =
|
|
|
236
238
|
|
|
237
239
|
type ExtractPatternBase<K extends string> =
|
|
238
240
|
K extends `${string}By${infer R}` ? R :
|
|
239
|
-
K extends `findFirst${infer R}` | `findMany${infer R}` | `createManyAndReturn${infer R}` | `createMany${infer R}` | `create${infer R}` | `count${infer R}` | `findWhere${infer R}` | `findListWhere${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 : '';
|
|
240
242
|
|
|
241
243
|
type MethodFactory<T, M extends Prisma.ModelName, K extends string, SelectModels, DefaultSelect extends keyof SelectModels | false, I, MethodConf> =
|
|
242
244
|
MethodFn<GetMappedMethod<K, MethodConf>, T, M, ExtractPatternBase<K>, SelectModels, DefaultSelect, I>;
|
|
@@ -364,6 +366,8 @@ export type BuildConfig<TSelectKeys extends PropertyKey = string> = {
|
|
|
364
366
|
baseMethods?: {
|
|
365
367
|
/** Configuração do método `get`. */
|
|
366
368
|
get?: BaseMethodConfig<TSelectKeys>;
|
|
369
|
+
/** Configuração do método `getOrThrow`. */
|
|
370
|
+
getOrThrow?: BaseMethodConfig<TSelectKeys>;
|
|
367
371
|
/** Configuração do método `remove`. */
|
|
368
372
|
remove?: BaseMethodConfig<TSelectKeys>;
|
|
369
373
|
/** Configuração do método `save`. */
|
|
@@ -371,7 +375,7 @@ export type BuildConfig<TSelectKeys extends PropertyKey = string> = {
|
|
|
371
375
|
};
|
|
372
376
|
};
|
|
373
377
|
|
|
374
|
-
type ResolveMethodDefaultSelect<Config, C, Method extends 'get' | 'remove' | 'save', TSelects = ExtractSelectModels<Config>> =
|
|
378
|
+
type ResolveMethodDefaultSelect<Config, C, Method extends 'get' | 'getOrThrow' | 'remove' | 'save', TSelects = ExtractSelectModels<Config>> =
|
|
375
379
|
C extends { baseMethods?: { [_ in Method]?: { defaultSelect?: infer D } } }
|
|
376
380
|
? D extends keyof TSelects
|
|
377
381
|
? D
|
|
@@ -406,6 +410,17 @@ type InjectedGet<
|
|
|
406
410
|
): Promise<ResolveCurrentReturn<M, TSelects, S, TDefault> | null>;
|
|
407
411
|
};
|
|
408
412
|
|
|
413
|
+
type InjectedGetOrThrow<
|
|
414
|
+
T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined,
|
|
415
|
+
TSelects = ExtractSelectModels<Config>,
|
|
416
|
+
TDefault = ExtractDefaultSelect<Config>,
|
|
417
|
+
TPk = ExtractPkName<T, Config>
|
|
418
|
+
> = C extends { baseMethods: { getOrThrow: { active: false } } } ? {} : {
|
|
419
|
+
getOrThrow<S extends keyof TSelects | false = ResolveMethodDefaultSelect<Config, C, 'getOrThrow', TSelects>>(
|
|
420
|
+
pk: T[TPk extends keyof T ? TPk : never], options?: MethodOptions<S>
|
|
421
|
+
): Promise<ResolveCurrentReturn<M, TSelects, S, TDefault>>;
|
|
422
|
+
};
|
|
423
|
+
|
|
409
424
|
type InjectedRemove<
|
|
410
425
|
T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined,
|
|
411
426
|
TSelects = ExtractSelectModels<Config>,
|
|
@@ -462,6 +477,7 @@ type InjectedSave<
|
|
|
462
477
|
|
|
463
478
|
type InjectedBaseMethods<T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined> =
|
|
464
479
|
InjectedGet<T, M, Config, C> &
|
|
480
|
+
InjectedGetOrThrow<T, M, Config, C> &
|
|
465
481
|
InjectedRemove<T, M, Config, C> &
|
|
466
482
|
InjectedSave<T, M, Config, C>;
|
|
467
483
|
|
|
@@ -9,6 +9,7 @@ export * from "./VSRepoError.js";
|
|
|
9
9
|
|
|
10
10
|
// * Importando classes de erro
|
|
11
11
|
import { VSRepoConfigError, VSRepoBuildError, VSRepoExtendError, VSRepoRuntimeError } from "./VSRepoError.js";
|
|
12
|
+
import { validateConfig } from "./VSRepoUtils.js";
|
|
12
13
|
|
|
13
14
|
// * Essa é a classe pincipal, é a partir dela que serão criados os objetos dos repositories
|
|
14
15
|
export class VSRepository {
|
|
@@ -28,92 +29,7 @@ export class VSRepository {
|
|
|
28
29
|
methods;
|
|
29
30
|
|
|
30
31
|
constructor(config) {
|
|
31
|
-
|
|
32
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'config' must be a valid object.`);
|
|
33
|
-
}
|
|
34
|
-
if(typeof config.tableName !== 'string') {
|
|
35
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'tableName' must be a valid string.`);
|
|
36
|
-
}
|
|
37
|
-
if(typeof config.pkName !== 'string') {
|
|
38
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'pkName' must be a valid string.`);
|
|
39
|
-
}
|
|
40
|
-
if(config.selectModels !== undefined && typeof config.selectModels !== 'object' || config.selectModels === null) {
|
|
41
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'selectModels' must be a valid object.`);
|
|
42
|
-
}
|
|
43
|
-
if(config.defaultSelectModel !== undefined) {
|
|
44
|
-
if(!config.selectModels) {
|
|
45
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'defaultSelectModel' can only be passed with 'selectModels'.`);
|
|
46
|
-
}
|
|
47
|
-
if(typeof config.defaultSelectModel !== 'string') {
|
|
48
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'defaultSelectModel' must be a valid string.`);
|
|
49
|
-
}
|
|
50
|
-
if(!Object.keys(config.selectModels).includes(config.defaultSelectModel)){
|
|
51
|
-
throw new VSRepoConfigError(`[VSRepository] (config) Invalid 'defaultSelectModel': ${config.defaultSelectModel}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if(config.relations !== undefined) {
|
|
55
|
-
const relations = config.relations;
|
|
56
|
-
if(typeof relations !== 'object' || relations === null){
|
|
57
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'relations' must be a valid object.`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
for (const key of Object.keys(relations)) {
|
|
61
|
-
const relation = relations[key];
|
|
62
|
-
if(typeof relation.pk !== 'string'){
|
|
63
|
-
throw new VSRepoConfigError(`[VSRepository] (config) The property 'pk' of relation '${key}' must be a string.`);
|
|
64
|
-
}
|
|
65
|
-
if(typeof relation.mode !== 'string'){
|
|
66
|
-
throw new VSRepoConfigError(`[VSRepository] (config) The property 'mode' of relation '${key}' must be 'otm', 'mtm', 'mto' or 'oto'.`);
|
|
67
|
-
}
|
|
68
|
-
if(typeof relation.restriction !== 'string'){
|
|
69
|
-
throw new VSRepoConfigError(`[VSRepository] (config) The property 'restriction' of relation '${key}' must be 'set' or 'add'.`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if(config.requiredWhere !== undefined && typeof config.requiredWhere !== 'object' || config.requiredWhere === null) {
|
|
74
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'requiredWhere' must be a valid object.`);
|
|
75
|
-
}
|
|
76
|
-
if(config.methods !== undefined) {
|
|
77
|
-
if(typeof config.methods !== 'object' || config.methods === null){
|
|
78
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'methods' must be a valid object.`);
|
|
79
|
-
} else {
|
|
80
|
-
for (const [key, value] of Object.entries(config.methods)) {
|
|
81
|
-
|
|
82
|
-
if(typeof value.map !== "boolean") {
|
|
83
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'map' on ${key} must be a valid boolean.`);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if(value.whereType !== undefined && !['extending', 'overwrite'].includes(value.whereType)){
|
|
87
|
-
throw new VSRepoConfigError(`[VSRepository] (config) Invalid 'whereType' on ${key}: ${value.whereType}`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if(value.selectModel !== undefined && value.selectModel !== false && (!config.selectModels || !Object.keys(config.selectModels).includes(value.selectModel))){
|
|
91
|
-
throw new VSRepoConfigError(`[VSRepository] (config) Invalid 'selectModel' on ${key}: ${value.selectModel}`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if(value.proxyTo !== undefined && typeof value.proxyTo !== 'string') {
|
|
95
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'proxyTo' on ${key} must be a valid string.`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if(value.fbMode !== undefined && !['one', 'list'].includes(value.fbMode)) {
|
|
99
|
-
throw new VSRepoConfigError(`[VSRepository] (config) Invalid 'fbMode' on ${key}: ${value.whereType}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if(value.pushWhere !== undefined && (typeof value.pushWhere !== 'object' || value.pushWhere === null)) {
|
|
103
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'pushWhere' on ${key} must be a valid object.`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if(value.injectOrdenation !== undefined && (typeof value.injectOrdenation !== 'object' || value.injectOrdenation === null)) {
|
|
107
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'injectOrdenation' on ${key} must be a valid object or array.`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if(value.injectPagination !== undefined && (typeof value.injectPagination !== 'object' || value.injectPagination === null)) {
|
|
111
|
-
throw new VSRepoConfigError(`[VSRepository] (config) 'injectPagination' on ${key} must be a valid object.`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
32
|
+
validateConfig(config);
|
|
117
33
|
|
|
118
34
|
this.vsrepocache = new Map();
|
|
119
35
|
this.tableName = config.tableName;
|
|
@@ -185,9 +101,22 @@ export class VSRepository {
|
|
|
185
101
|
const whereParams = [];
|
|
186
102
|
const otherParams = [];
|
|
187
103
|
|
|
188
|
-
if(keyToMap.startsWith('
|
|
104
|
+
if(keyToMap.startsWith('findUniqueOrThrowBy')) {
|
|
105
|
+
keyToMapReplaced = keyToMap.replace('findUniqueOrThrowBy', '');
|
|
106
|
+
method = 'findUniqueOrThrow';
|
|
107
|
+
} else if(keyToMap.startsWith('findUniqueBy')) {
|
|
189
108
|
keyToMapReplaced = keyToMap.replace('findUniqueBy', '');
|
|
190
109
|
method = 'findUnique';
|
|
110
|
+
} else if(keyToMap.startsWith('findFirstOrThrowBy')) {
|
|
111
|
+
keyToMapReplaced = keyToMap.replace('findFirstOrThrowBy', '')
|
|
112
|
+
ignoreOrderByAndPagination = false;
|
|
113
|
+
method = 'findFirstOrThrow';
|
|
114
|
+
} else if(keyToMap.startsWith('findFirstOrThrow')) {
|
|
115
|
+
keyToMapReplaced = keyToMap.replace('findFirstOrThrow', '');
|
|
116
|
+
ignoreOrderByAndPagination = false;
|
|
117
|
+
ignoreWhere = true;
|
|
118
|
+
onlyBaseWheres = true;
|
|
119
|
+
method = 'findFirstOrThrow';
|
|
191
120
|
} else if(keyToMap.startsWith('findFirstBy')) {
|
|
192
121
|
keyToMapReplaced = keyToMap.replace('findFirstBy', '')
|
|
193
122
|
ignoreOrderByAndPagination = false;
|
|
@@ -851,6 +780,48 @@ export class VSRepository {
|
|
|
851
780
|
}
|
|
852
781
|
}
|
|
853
782
|
}
|
|
783
|
+
if(config.baseMethods?.getOrThrow?.active !== false) {
|
|
784
|
+
buildInstance.getOrThrow = async(pk, options = {}) => {
|
|
785
|
+
let db;
|
|
786
|
+
if(options?.db) db = options.db;
|
|
787
|
+
else db = prisma;
|
|
788
|
+
|
|
789
|
+
const prismaArgs = {};
|
|
790
|
+
|
|
791
|
+
const where = { [buildInstance.pkName]: pk };
|
|
792
|
+
if(buildInstance.requiredWhere && !config.baseMethods?.getOrThrow?.ignoreRequiredWhere) {
|
|
793
|
+
Object.assign(where, buildInstance.requiredWhere);
|
|
794
|
+
}
|
|
795
|
+
prismaArgs.where = where;
|
|
796
|
+
|
|
797
|
+
let selectModelKey = options?.selectModel ?? config.baseMethods?.getOrThrow?.defaultSelect ?? buildInstance.defaultSelectModel;
|
|
798
|
+
if(selectModelKey){
|
|
799
|
+
prismaArgs.select = buildInstance.selectModels[selectModelKey];
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
let start;
|
|
803
|
+
if(showWorking){
|
|
804
|
+
console.log(`[VSRepository] (runtime) Executing getOrThrow on ${buildInstance.tableName}.`);
|
|
805
|
+
console.log(`[VSRepository] (runtime) Built arguments to getOrThrow on ${buildInstance.tableName}:\n`, JSON.stringify(prismaArgs, null, 2));
|
|
806
|
+
start = performance.now();
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
try {
|
|
810
|
+
const result = await db[buildInstance.tableName].findUniqueOrThrow(prismaArgs);
|
|
811
|
+
|
|
812
|
+
if(showWorking) {
|
|
813
|
+
const end = performance.now();
|
|
814
|
+
const duration = (end - start).toFixed(2);
|
|
815
|
+
console.log(`[VSRepository] (runtime) Executed getOrThrow on ${buildInstance.tableName} (took: ${duration}ms).`);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
return result;
|
|
819
|
+
} catch (err) {
|
|
820
|
+
console.log(`[VSRepository] (runtime) Fatal error when trying to getOrThrow on ${buildInstance.tableName}:\n`, JSON.stringify({ prismaArgs }, null, 2));
|
|
821
|
+
throw err;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
854
825
|
if(config.baseMethods?.remove?.active !== false) {
|
|
855
826
|
buildInstance.remove = async (pk, options = {}) => {
|
|
856
827
|
let db;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vsrepo",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Uma biblioteca de repository pattern para Prisma",
|
|
5
5
|
"homepage": "https://github.com/jaobrabo123/VSRepository#readme",
|
|
6
6
|
"repository": {
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"@types/node": "^25.6.0",
|
|
62
62
|
"dotenv": "^17.4.2",
|
|
63
63
|
"prisma": "^7.8.0",
|
|
64
|
+
"tsx": "^4.22.3",
|
|
64
65
|
"typescript": "^6.0.3"
|
|
65
66
|
}
|
|
66
67
|
}
|
|
@@ -10,6 +10,15 @@ const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
|
10
10
|
|
|
11
11
|
const DEFAULT_OUTPUT = 'src/generated/vsrepo';
|
|
12
12
|
const DEFAULT_PRISMA = 'src/generated/prisma';
|
|
13
|
+
const GENERATED_HEADER = `/**
|
|
14
|
+
* ! THIS FILE IS AUTO-GENERATED.
|
|
15
|
+
* ! DO NOT EDIT MANUALLY.
|
|
16
|
+
*/
|
|
17
|
+
/* eslint-disable */
|
|
18
|
+
/* biome-ignore-all lint: generated file */
|
|
19
|
+
// @ts-nocheck
|
|
20
|
+
|
|
21
|
+
`;
|
|
13
22
|
|
|
14
23
|
const command = args[0]?.startsWith('-') ? 'generate' : args[0] ?? 'generate';
|
|
15
24
|
const commandArgs = command === 'generate' && args[0] !== command ? args : args.slice(1);
|
|
@@ -91,7 +100,7 @@ for (const fileName of typeFiles) {
|
|
|
91
100
|
`from '${relativeImportPath(path.dirname(targetFile), prismaTargetPath)}'`
|
|
92
101
|
);
|
|
93
102
|
|
|
94
|
-
fs.writeFileSync(targetFile, contents, 'utf8');
|
|
103
|
+
fs.writeFileSync(targetFile, GENERATED_HEADER + contents, 'utf8');
|
|
95
104
|
|
|
96
105
|
console.log(`Gerado: ${path.relative(workspaceRoot, targetFile)}`);
|
|
97
106
|
}
|
|
@@ -166,7 +175,7 @@ export type * from './VSRepository.types';
|
|
|
166
175
|
for (const file of tsFiles) {
|
|
167
176
|
const targetFile = path.join(outputDir, file.fileName);
|
|
168
177
|
|
|
169
|
-
fs.writeFileSync(targetFile, file.content, 'utf8');
|
|
178
|
+
fs.writeFileSync(targetFile, GENERATED_HEADER + file.content, 'utf8');
|
|
170
179
|
|
|
171
180
|
console.log(`Gerado: ${path.relative(workspaceRoot, targetFile)}`);
|
|
172
181
|
}
|