vsrepo 1.3.4 → 1.3.5

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
@@ -35,6 +35,7 @@ VSRepository lets you create strongly-typed repositories with:
35
35
  - [Merge](#merge)
36
36
  - [Configuring the base methods](#configuring-the-base-methods)
37
37
  - [Select Models](#select-models)
38
+ - [Raw select (options.select)](#raw-select-optionsselect)
38
39
  - [Include Models](#include-models)
39
40
  - [Raw include (options.include)](#raw-include-optionsinclude)
40
41
  - [Required Where](#required-where)
@@ -104,6 +105,8 @@ npx vsrepo generate \
104
105
 
105
106
  ```
106
107
  generated/vsrepo/
108
+ ├── DynamicRepository.ts
109
+ ├── DynamicRepository.types.d.ts
107
110
  ├── VSRepoError.ts
108
111
  ├── VSRepoError.types.d.ts
109
112
  ├── VSRepository.ts
@@ -531,6 +534,35 @@ const user = await userRepository.get(id, { selectModel: "minimal" });
531
534
  const fullUser = await userRepository.get(id, { selectModel: false });
532
535
  ```
533
536
 
537
+ ### Raw `select` (`options.select`)
538
+
539
+ Besides `selectModel` (named, pre-configured in `selectModels`), you can pass a raw Prisma `select` directly in the call, without registering it beforehand on the repository.
540
+
541
+ ```ts
542
+ const user = await userRepository.get(id, {
543
+ select: { id: true, name: true },
544
+ });
545
+ ```
546
+
547
+ `options.select` accepts any valid Prisma `select` for the repository's model — it's fully typed and offers the same autocomplete/validation as calling `prisma.user.findMany({ select: ... })` directly, and the method's return type is narrowed to exactly the fields selected.
548
+
549
+ **Rules and behavior:**
550
+
551
+ - **Mutually exclusive with `selectModel`, `includeModel` and `include`.** Only one of the four can be provided per call; the types enforce this — passing more than one is a compile-time error.
552
+ - **Ad hoc, not reusable.** Unlike `selectModel`, it doesn't need to be declared in `selectModels`. Use it for one-off projections that don't justify a named select model.
553
+ - **No `defaultSelectModel` is applied.** When `select` is provided, the default select (`defaultSelectModel`) is ignored and only the raw `select` is sent to Prisma.
554
+
555
+ ```ts
556
+ // CORRECT ✅ — raw select only
557
+ await userRepository.get(id, { select: { id: true, name: true } });
558
+
559
+ // WRONG ❌ — combining select with selectModel/includeModel/include is not allowed
560
+ await userRepository.get(id, { selectModel: "public", select: { id: true } });
561
+ await userRepository.get(id, { include: { posts: true }, select: { id: true } });
562
+ ```
563
+
564
+ > **When to use `selectModel` vs. `select`:** prefer `selectModel` for projections reused across multiple calls (defined once in `selectModels`); use `select` for specific, occasional projections that don't need a name.
565
+
534
566
  ---
535
567
 
536
568
  ## Include Models
@@ -1367,6 +1399,8 @@ type OptsModel = MethodOptionsModel<typeof userVSRepo>;
1367
1399
  ```
1368
1400
 
1369
1401
  > The second parameter of `MethodOptions` (`IM`) represents the valid keys of `includeModels`. When provided, `selectModel` and `includeModel` become mutually exclusive in the type — it's not possible to pass both in the same call.
1402
+ >
1403
+ > `MethodOptions` also accepts two further generic parameters, `RI` and `RS`, for typing raw `include` and raw `select` respectively (both default to `never`, meaning they're not accepted unless explicitly typed): `MethodOptions<"public", "withPosts", "user", IncludeModel<"user">, SelectModel<"user">>`. `MethodOptionsModel`, derived directly from a configured repository, does not expose `RI`/`RS` — use `MethodOptions` directly if you need to type raw `include`/`select` options.
1370
1404
 
1371
1405
  ### Configuration types
1372
1406
 
package/README.pt-BR.md CHANGED
@@ -35,6 +35,7 @@ O VSRepository permite criar repositórios fortemente tipados com:
35
35
  - [Merge](#merge)
36
36
  - [Configurando os métodos base](#configurando-os-métodos-base)
37
37
  - [Select Models](#select-models)
38
+ - [Select bruto (options.select)](#select-bruto-optionsselect)
38
39
  - [Include Models](#include-models)
39
40
  - [Include bruto (options.include)](#include-bruto-optionsinclude)
40
41
  - [Required Where](#required-where)
@@ -104,6 +105,8 @@ npx vsrepo generate \
104
105
 
105
106
  ```
106
107
  generated/vsrepo/
108
+ ├── DynamicRepository.ts
109
+ ├── DynamicRepository.types.d.ts
107
110
  ├── VSRepoError.ts
108
111
  ├── VSRepoError.types.d.ts
109
112
  ├── VSRepository.ts
@@ -531,6 +534,35 @@ const user = await userRepository.get(id, { selectModel: "minimal" });
531
534
  const fullUser = await userRepository.get(id, { selectModel: false });
532
535
  ```
533
536
 
537
+ ### Select bruto (`options.select`)
538
+
539
+ Além do `selectModel` (nomeado, pré-configurado em `selectModels`), você pode passar um `select` bruto do Prisma diretamente na chamada, sem precisar registrá-lo antecipadamente no repositório.
540
+
541
+ ```ts
542
+ const usuario = await usuarioRepository.get(id, {
543
+ select: { id: true, nome: true },
544
+ });
545
+ ```
546
+
547
+ `options.select` aceita qualquer `select` válido para o modelo Prisma do repositório — é totalmente tipado e oferece o mesmo autocomplete/validação de chamar `prisma.usuario.findMany({ select: ... })` diretamente, e o tipo de retorno do método é restrito exatamente aos campos selecionados.
548
+
549
+ **Regras e comportamento:**
550
+
551
+ - **Mutuamente exclusivo com `selectModel`, `includeModel` e `include`.** Apenas um dos quatro pode ser fornecido por chamada; os tipos garantem isso — passar mais de um é um erro em tempo de compilação.
552
+ - **Ad hoc, não reutilizável.** Diferente do `selectModel`, não precisa ser declarado em `selectModels`. Use-o para projeções pontuais que não justificam um select model nomeado.
553
+ - **Nenhum `defaultSelectModel` é aplicado.** Quando `select` é fornecido, o select padrão (`defaultSelectModel`) é ignorado e apenas o `select` bruto é enviado ao Prisma.
554
+
555
+ ```ts
556
+ // CORRETO ✅ — apenas select bruto
557
+ await usuarioRepository.get(id, { select: { id: true, nome: true } });
558
+
559
+ // ERRADO ❌ — combinar select com selectModel/includeModel/include não é permitido
560
+ await usuarioRepository.get(id, { selectModel: "public", select: { id: true } });
561
+ await usuarioRepository.get(id, { include: { posts: true }, select: { id: true } });
562
+ ```
563
+
564
+ > **Quando usar `selectModel` vs. `select`:** prefira `selectModel` para projeções reutilizadas em várias chamadas (definidas uma vez em `selectModels`); use `select` para projeções específicas e ocasionais que não precisam de um nome.
565
+
534
566
  ---
535
567
 
536
568
  ## Include Models
@@ -1367,6 +1399,8 @@ type OptsModel = MethodOptionsModel<typeof userVSRepo>;
1367
1399
  ```
1368
1400
 
1369
1401
  > O segundo parâmetro de `MethodOptions` (`IM`) representa as chaves válidas de `includeModels`. Quando informado, `selectModel` e `includeModel` se tornam mutuamente exclusivos no tipo — não é possível passar os dois na mesma chamada.
1402
+ >
1403
+ > `MethodOptions` também aceita mais dois parâmetros genéricos, `RI` e `RS`, para tipar `include` e `select` brutos respectivamente (ambos com padrão `never`, ou seja, não são aceitos a menos que tipados explicitamente): `MethodOptions<"public", "withPosts", "usuario", IncludeModel<"usuario">, SelectModel<"usuario">>`. O `MethodOptionsModel`, derivado diretamente de um repositório configurado, não expõe `RI`/`RS` — use `MethodOptions` diretamente se precisar tipar as opções de `include`/`select` brutos.
1370
1404
 
1371
1405
  ### Tipos de configuração
1372
1406
 
@@ -15,6 +15,7 @@ import {
15
15
  PrismaModelName,
16
16
  RepositoryRelations,
17
17
  SeeMode,
18
+ SelectModel,
18
19
  WhereModel,
19
20
  } from "./VSRepository.d";
20
21
 
@@ -157,8 +158,11 @@ export type DynamicMethodOptions<TName extends PrismaModelName = PrismaModelName
157
158
  */
158
159
  see?: SeeMode;
159
160
 
160
- /** Raw Prisma `include`. */
161
+ /** Raw Prisma `include`. Cannot be used together with `select`. */
161
162
  include?: IncludeModel<TName>;
163
+
164
+ /** Raw Prisma `select`. Cannot be used together with `include`. */
165
+ select?: SelectModel<TName>;
162
166
  };
163
167
 
164
168
  /**
@@ -290,7 +294,7 @@ export declare abstract class DynamicRepository<
290
294
  /** Saves an array of objects in a single automatic transaction. */
291
295
  saveList(
292
296
  objs: DynamicSaveInput<TEntity, UName, WRelations>[],
293
- options?: Omit<DynamicMethodOptions<UName>, "include"> & {
297
+ options?: Omit<DynamicMethodOptions<UName>, "include" | "select"> & {
294
298
  /** Transaction client to use for this operation. */
295
299
  db?: DbTransaction;
296
300
  },
@@ -306,7 +310,7 @@ export declare abstract class DynamicRepository<
306
310
  /** Partially updates multiple records via `[pk, obj]` tuples in an automatic transaction. */
307
311
  patchList(
308
312
  tuples: [pk: VPKType, obj: DynamicPatchInput<TEntity, UName, WRelations>][],
309
- options?: Omit<DynamicMethodOptions<UName>, "include"> & {
313
+ options?: Omit<DynamicMethodOptions<UName>, "include" | "select"> & {
310
314
  /** Transaction client to use for this operation. */
311
315
  db?: DbTransaction;
312
316
  },
@@ -322,7 +326,7 @@ export declare abstract class DynamicRepository<
322
326
  /** Deletes multiple records by their primary keys. */
323
327
  removeList(
324
328
  pks: VPKType[],
325
- options?: Omit<DynamicMethodOptions<UName>, "include">,
329
+ options?: Omit<DynamicMethodOptions<UName>, "include" | "select">,
326
330
  ): Promise<{ count: number }>;
327
331
 
328
332
  /** Fetches all records (respects `requiredWhere` when set). */
@@ -340,10 +344,10 @@ export declare abstract class DynamicRepository<
340
344
  ): Promise<TEntity[]>;
341
345
 
342
346
  /** Returns the total number of records. */
343
- total(options?: Omit<DynamicMethodOptions<UName>, "include">): Promise<number>;
347
+ total(options?: Omit<DynamicMethodOptions<UName>, "include" | "select">): Promise<number>;
344
348
 
345
349
  /** Checks whether a record exists by its primary key (PK). */
346
- has(pk: VPKType, options?: Omit<DynamicMethodOptions<UName>, "include">): Promise<boolean>;
350
+ has(pk: VPKType, options?: Omit<DynamicMethodOptions<UName>, "include" | "select">): Promise<boolean>;
347
351
 
348
352
  /** Marks a record as deleted (soft-delete). */
349
353
  softRemove(pk: VPKType, options?: Omit<DynamicMethodOptions<UName>, "see">): Promise<TEntity>;
@@ -351,7 +355,7 @@ export declare abstract class DynamicRepository<
351
355
  /** Marks multiple records as deleted (soft-delete) in batch. */
352
356
  softRemoveList(
353
357
  pks: VPKType[],
354
- options?: Omit<DynamicMethodOptions<UName>, "see" | "include">,
358
+ options?: Omit<DynamicMethodOptions<UName>, "see" | "include" | "select">,
355
359
  ): Promise<{ count: number }>;
356
360
 
357
361
  /** Restores a record previously marked as deleted (soft-delete). */
@@ -360,7 +364,7 @@ export declare abstract class DynamicRepository<
360
364
  /** Restores multiple records previously marked as deleted (soft-delete) in batch. */
361
365
  restoreList(
362
366
  pks: VPKType[],
363
- options?: Omit<DynamicMethodOptions<UName>, "see" | "include">,
367
+ options?: Omit<DynamicMethodOptions<UName>, "see" | "include" | "select">,
364
368
  ): Promise<{ count: number }>;
365
369
  }
366
370
 
@@ -228,20 +228,22 @@ type PrecomputedIncludes<M extends PrismaModelName, IncludeModels> = {
228
228
  [I in keyof IncludeModels]: Prisma.Result<PrismaDelegate<M>, { include: IncludeModels[I] }, 'findMany'> extends Array<infer U> ? U : never
229
229
  };
230
230
 
231
- type SelectedModel<M extends PrismaModelName, S, SelectModels, IM = never, IncludeModels = {}, RI = never> =
232
- [RI] extends [never]
233
- ? [IM] extends [never]
234
- ? [S] extends [false]
235
- ? FullModelType<M>
236
- : [S] extends [never]
237
- ? FullModelType<M>
238
- : [S] extends [keyof SelectModels]
239
- ? PrecomputedSelects<M, SelectModels>[S]
240
- : FullModelType<M>
241
- : IM extends keyof IncludeModels
242
- ? PrecomputedIncludes<M, IncludeModels>[IM]
243
- : FullModelType<M>
244
- : Prisma.Result<PrismaDelegate<M>, { include: RI }, 'findMany'> extends Array<infer U> ? U : never;
231
+ type SelectedModel<M extends PrismaModelName, S, SelectModels, IM = never, IncludeModels = {}, RI = never, RS = never> =
232
+ [RS] extends [never]
233
+ ? [RI] extends [never]
234
+ ? [IM] extends [never]
235
+ ? [S] extends [false]
236
+ ? FullModelType<M>
237
+ : [S] extends [never]
238
+ ? FullModelType<M>
239
+ : [S] extends [keyof SelectModels]
240
+ ? PrecomputedSelects<M, SelectModels>[S]
241
+ : FullModelType<M>
242
+ : IM extends keyof IncludeModels
243
+ ? PrecomputedIncludes<M, IncludeModels>[IM]
244
+ : FullModelType<M>
245
+ : Prisma.Result<PrismaDelegate<M>, { include: RI }, 'findMany'> extends Array<infer U> ? U : never
246
+ : Prisma.Result<PrismaDelegate<M>, { select: RS }, 'findMany'> extends Array<infer U> ? U : never;
245
247
 
246
248
  /**
247
249
  * Strips the `Distinct{Fields}` suffix used by read methods (e.g. `findManyDistinctIdadeAndCargo`).
@@ -265,7 +267,7 @@ type CleanFields<R extends string> =
265
267
  * @template S Available keys in `selectModels`.
266
268
  * @template IM Available keys in `includeModels`.
267
269
  */
268
- export type MethodOptions<S, IM extends PropertyKey = never, M extends PrismaModelName = never, RI = never> = {
270
+ export type MethodOptions<S, IM extends PropertyKey = never, M extends PrismaModelName = never, RI = never, RS = never> = {
269
271
  /** Prisma client or transaction to use for the operation. */
270
272
  db?: ClientOrTransaction;
271
273
  /**
@@ -287,15 +289,25 @@ export type MethodOptions<S, IM extends PropertyKey = never, M extends PrismaMod
287
289
  includeModel?: never;
288
290
  /** Raw Prisma `include`. Cannot be used together with `selectModel` or `includeModel`. */
289
291
  include?: never;
292
+ /** Raw Prisma `select`. Cannot be used together with `selectModel`, `includeModel` or `include`. */
293
+ select?: never;
290
294
  }
291
295
  | {
292
296
  selectModel?: never;
293
297
  includeModel?: never;
294
298
  include: RI;
299
+ select?: never;
295
300
  }
296
- : | { selectModel?: S | false; includeModel?: never; include?: never }
297
- | { selectModel?: never; includeModel: IM; include?: never }
298
- | { selectModel?: never; includeModel?: never; include: RI }
301
+ | {
302
+ selectModel?: never;
303
+ includeModel?: never;
304
+ include?: never;
305
+ select: RS;
306
+ }
307
+ : | { selectModel?: S | false; includeModel?: never; include?: never; select?: never }
308
+ | { selectModel?: never; includeModel: IM; include?: never; select?: never }
309
+ | { selectModel?: never; includeModel?: never; include: RI; select?: never }
310
+ | { selectModel?: never; includeModel?: never; include?: never; select: RS }
299
311
  );
300
312
 
301
313
  /**
@@ -318,14 +330,15 @@ type MethodOptionsWithInclude<
318
330
  TIncludes,
319
331
  M extends PrismaModelName,
320
332
  RI = never,
321
- Excluded extends keyof MethodOptions<S, keyof TIncludes, M, RI> = never
322
- > = DistributiveOmit<MethodOptions<S, keyof TIncludes, M, RI>, Excluded>
333
+ RS = never,
334
+ Excluded extends keyof MethodOptions<S, keyof TIncludes, M, RI, RS> = never
335
+ > = DistributiveOmit<MethodOptions<S, keyof TIncludes, M, RI, RS>, Excluded>
323
336
  & ({ includeModel: IM } | { includeModel?: never });
324
337
 
325
338
  type MethodFn<MethodName extends string, T, M extends PrismaModelName, R extends string, SelectModels, DefaultSelect extends keyof SelectModels | false, I, IncludeModels> =
326
- <S extends keyof SelectModels | false = DefaultSelect, IM extends keyof IncludeModels = never, RI extends IncludeModel<M> = never>(
327
- ...args: [...ExtractFields<T, CleanFields<R>, I>, ...ExtraArgs<MethodName, R, I>, options?: MethodOptionsWithInclude<S, IM, IncludeModels, M, RI>]
328
- ) => Promise<ResolveReturnType<MethodName, SelectedModel<M, S, SelectModels, IM, IncludeModels, RI>>>;
339
+ <S extends keyof SelectModels | false = DefaultSelect, IM extends keyof IncludeModels = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
340
+ ...args: [...ExtractFields<T, CleanFields<R>, I>, ...ExtraArgs<MethodName, R, I>, options?: MethodOptionsWithInclude<S, IM, IncludeModels, M, RI, RS>]
341
+ ) => Promise<ResolveReturnType<MethodName, SelectedModel<M, S, SelectModels, IM, IncludeModels, RI, RS>>>;
329
342
 
330
343
  type GetMappedMethod<K extends string, MethodConf> =
331
344
  K extends `findBy${string}` ? (MethodConf extends { fbMode: 'one' } ? 'findByOne' : 'findByList') :
@@ -380,9 +393,9 @@ type MethodFactory<T, M extends PrismaModelName, K extends string, SelectModels,
380
393
  K extends `findWhere${string}`
381
394
  ? {
382
395
  /** @deprecated Use findOneWhere instead. */
383
- <S extends keyof SelectModels | false = DefaultSelect, IM extends keyof IncludeModels = never, RI extends IncludeModel<M> = never>(
384
- ...args: [...ExtractFields<T, CleanFields<ExtractPatternBase<K>>, I>, ...ExtraArgs<GetMappedMethod<K, MethodConf>, ExtractPatternBase<K>, I>, options?: MethodOptionsWithInclude<S, IM, IncludeModels, M, RI>]
385
- ): Promise<ResolveReturnType<GetMappedMethod<K, MethodConf>, SelectedModel<M, S, SelectModels, IM, IncludeModels, RI>>>;
396
+ <S extends keyof SelectModels | false = DefaultSelect, IM extends keyof IncludeModels = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
397
+ ...args: [...ExtractFields<T, CleanFields<ExtractPatternBase<K>>, I>, ...ExtraArgs<GetMappedMethod<K, MethodConf>, ExtractPatternBase<K>, I>, options?: MethodOptionsWithInclude<S, IM, IncludeModels, M, RI, RS>]
398
+ ): Promise<ResolveReturnType<GetMappedMethod<K, MethodConf>, SelectedModel<M, S, SelectModels, IM, IncludeModels, RI, RS>>>;
386
399
  }
387
400
  : MethodFn<GetMappedMethod<K, MethodConf>, T, M, ExtractPatternBase<K>, SelectModels, DefaultSelect, I, IncludeModels>;
388
401
 
@@ -674,18 +687,20 @@ type ResolveMethodDefaultSelect<Config, C, Method extends keyof NonNullable<Buil
674
687
  ? ExtractDefaultSelect<Config>
675
688
  : false;
676
689
 
677
- type ResolveCurrentReturn<M extends PrismaModelName, Models, S, D, IM = never, Includes = {}, RI = never> =
678
- [RI] extends [never]
679
- ? [IM] extends [never]
680
- ? [S] extends [false]
681
- ? FullModelType<M>
682
- : [S] extends [never]
683
- ? ([D] extends [never] ? FullModelType<M> : SelectedModel<M, D, Models>)
684
- : SelectedModel<M, S, Models>
685
- : IM extends keyof Includes
686
- ? PrecomputedIncludes<M, Includes>[IM]
687
- : FullModelType<M>
688
- : Prisma.Result<PrismaDelegate<M>, { include: RI }, 'findMany'> extends Array<infer U> ? U : never;
690
+ type ResolveCurrentReturn<M extends PrismaModelName, Models, S, D, IM = never, Includes = {}, RI = never, RS = never> =
691
+ [RS] extends [never]
692
+ ? [RI] extends [never]
693
+ ? [IM] extends [never]
694
+ ? [S] extends [false]
695
+ ? FullModelType<M>
696
+ : [S] extends [never]
697
+ ? ([D] extends [never] ? FullModelType<M> : SelectedModel<M, D, Models>)
698
+ : SelectedModel<M, S, Models>
699
+ : IM extends keyof Includes
700
+ ? PrecomputedIncludes<M, Includes>[IM]
701
+ : FullModelType<M>
702
+ : Prisma.Result<PrismaDelegate<M>, { include: RI }, 'findMany'> extends Array<infer U> ? U : never
703
+ : Prisma.Result<PrismaDelegate<M>, { select: RS }, 'findMany'> extends Array<infer U> ? U : never;
689
704
 
690
705
  // ─── helpers reused within the mapped type ───────────────────────────────────
691
706
  // ─── relation payload types ──────────────────────────────────────────────────
@@ -791,7 +806,7 @@ export type PatchObject<TInput, TRepo> =
791
806
 
792
807
 
793
808
  type _Pk<T, Config> = WidenField<T[ExtractPkName<T, Config> extends keyof T ? ExtractPkName<T, Config> : never]>;
794
- type _Ret<M extends PrismaModelName, TSelects, S, TDefault, IM = never, TIncludes = {}, RI = never> = ResolveCurrentReturn<M, TSelects, S, TDefault, IM, TIncludes, RI>;
809
+ type _Ret<M extends PrismaModelName, TSelects, S, TDefault, IM = never, TIncludes = {}, RI = never, RS = never> = ResolveCurrentReturn<M, TSelects, S, TDefault, IM, TIncludes, RI, RS>;
795
810
  type _DS<Config, C, Method extends keyof NonNullable<BuildConfig['baseMethods']>, TSelects = ExtractSelectModels<Config>> =
796
811
  ResolveMethodDefaultSelect<Config, C, Method, TSelects>;
797
812
  type _Sel<Config> = ExtractSelectModels<Config>;
@@ -818,56 +833,56 @@ type AllBaseMethods<
818
833
  TIncludes = _Inc<Config>
819
834
  > = {
820
835
  /** Fetches a record by its primary key (PK). */
821
- get: <S extends keyof TSelects | false = _DS<Config, C, 'get', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
822
- pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI>
823
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI> | null>;
836
+ get: <S extends keyof TSelects | false = _DS<Config, C, 'get', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
837
+ pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS>
838
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS> | null>;
824
839
 
825
840
  /** Fetches a record by PK and throws `VSRepoRuntimeError` if not found. */
826
- getOrThrow: <S extends keyof TSelects | false = _DS<Config, C, 'getOrThrow', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
827
- pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI>
828
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI>>;
841
+ getOrThrow: <S extends keyof TSelects | false = _DS<Config, C, 'getOrThrow', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
842
+ pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS>
843
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS>>;
829
844
 
830
845
  /** Fetches multiple records by a list of primary keys (PKs). */
831
- getList: <S extends keyof TSelects | false = _DS<Config, C, 'getList', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
832
- pks: _Pk<T, Config>[], options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI>
833
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI>[]>;
846
+ getList: <S extends keyof TSelects | false = _DS<Config, C, 'getList', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
847
+ pks: _Pk<T, Config>[], options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS>
848
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS>[]>;
834
849
 
835
850
  /** Deletes a record identified by its primary key (PK). */
836
- remove: <S extends keyof TSelects | false = _DS<Config, C, 'remove', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
837
- pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI>
838
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI>>;
851
+ remove: <S extends keyof TSelects | false = _DS<Config, C, 'remove', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
852
+ pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS>
853
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS>>;
839
854
 
840
855
  /** Inserts or updates (upsert) a record. */
841
- save: <S extends keyof TSelects | false = _DS<Config, C, 'save', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
842
- obj: UpsertWithRelations<T, M, TRelations>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI>
843
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI>>;
856
+ save: <S extends keyof TSelects | false = _DS<Config, C, 'save', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
857
+ obj: UpsertWithRelations<T, M, TRelations>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS>
858
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS>>;
844
859
 
845
860
  /** Saves an array of objects in a single automatic transaction. */
846
- saveList: <S extends keyof TSelects | false = _DS<Config, C, 'saveList', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
847
- objs: UpsertWithRelations<T, M, TRelations>[], options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, 'db'> & { db?: DbTransaction }
848
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI>[]>;
861
+ saveList: <S extends keyof TSelects | false = _DS<Config, C, 'saveList', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
862
+ objs: UpsertWithRelations<T, M, TRelations>[], options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS, 'db'> & { db?: DbTransaction }
863
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS>[]>;
849
864
 
850
865
  /** Partially updates (patch) an existing record by its primary key (PK). */
851
- patch: <S extends keyof TSelects | false = _DS<Config, C, 'patch', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
852
- pk: _Pk<T, Config>, obj: PatchWithRelations<T, M, TRelations>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI>
853
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI>>;
866
+ patch: <S extends keyof TSelects | false = _DS<Config, C, 'patch', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
867
+ pk: _Pk<T, Config>, obj: PatchWithRelations<T, M, TRelations>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS>
868
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS>>;
854
869
 
855
870
  /** Partially updates multiple records via `[pk, obj]` tuples in an automatic transaction. */
856
- patchList: <S extends keyof TSelects | false = _DS<Config, C, 'patchList', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
857
- tuples: [pk: _Pk<T, Config>, obj: PatchWithRelations<T, M, TRelations>][], options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, 'db'> & { db?: DbTransaction }
858
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI>[]>;
871
+ patchList: <S extends keyof TSelects | false = _DS<Config, C, 'patchList', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
872
+ tuples: [pk: _Pk<T, Config>, obj: PatchWithRelations<T, M, TRelations>][], options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS, 'db'> & { db?: DbTransaction }
873
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS>[]>;
859
874
 
860
875
  /** Fetches a record by PK and deep-merges it with the provided object **in memory**. */
861
- merge: <S extends keyof TSelects | false = _DS<Config, C, 'merge', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
862
- pk: _Pk<T, Config>, obj: UpdateWithRelations<T, M, TRelations>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI>
863
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI> | null>;
876
+ merge: <S extends keyof TSelects | false = _DS<Config, C, 'merge', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
877
+ pk: _Pk<T, Config>, obj: UpdateWithRelations<T, M, TRelations>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS>
878
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS> | null>;
864
879
 
865
880
  /** Deletes multiple records by their primary keys. */
866
881
  removeList: (pks: _Pk<T, Config>[], options?: { db?: ClientOrTransaction }) => Promise<{ count: number }>;
867
882
 
868
883
  /** Fetches all records (respects `requiredWhere` when set). */
869
- getAll: <S extends keyof TSelects | false = _DS<Config, C, 'getAll', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
870
- options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI> & {
884
+ getAll: <S extends keyof TSelects | false = _DS<Config, C, 'getAll', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
885
+ options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS> & {
871
886
  pagination?: PaginationOptions<I extends { cursorInput: infer Curs } ? Curs : unknown>;
872
887
  /**
873
888
  * Ordering to apply to the query.
@@ -876,7 +891,7 @@ type AllBaseMethods<
876
891
  */
877
892
  order?: I extends { orderByInput: infer OB } ? OB : OrderOptions;
878
893
  }
879
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI>[]>;
894
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS>[]>;
880
895
 
881
896
  /** Returns the total number of records. */
882
897
  total: (options?: { db?: ClientOrTransaction; see?: SeeMode }) => Promise<number>;
@@ -885,17 +900,17 @@ type AllBaseMethods<
885
900
  has: (pk: _Pk<T, Config>, options?: { db?: ClientOrTransaction; see?: SeeMode }) => Promise<boolean>;
886
901
 
887
902
  /** Marks a record as deleted (soft-delete). */
888
- softRemove: <S extends keyof TSelects | false = _DS<Config, C, 'softRemove', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
889
- pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, 'see'>
890
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI>>;
903
+ softRemove: <S extends keyof TSelects | false = _DS<Config, C, 'softRemove', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
904
+ pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS, 'see'>
905
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS>>;
891
906
 
892
907
  /** Marks multiple records as deleted (soft-delete) in batch. */
893
908
  softRemoveList: (pks: _Pk<T, Config>[], options?: { db?: ClientOrTransaction }) => Promise<{ count: number }>;
894
909
 
895
910
  /** Restores a record previously marked as deleted (soft-delete). */
896
- restore: <S extends keyof TSelects | false = _DS<Config, C, 'restore', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never>(
897
- pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, 'see'>
898
- ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI>>;
911
+ restore: <S extends keyof TSelects | false = _DS<Config, C, 'restore', TSelects>, IM extends keyof TIncludes = never, RI extends IncludeModel<M> = never, RS extends SelectModel<M> = never>(
912
+ pk: _Pk<T, Config>, options?: MethodOptionsWithInclude<S, IM, TIncludes, M, RI, RS, 'see'>
913
+ ) => Promise<_Ret<M, TSelects, S, TDefault, IM, TIncludes, RI, RS>>;
899
914
 
900
915
  /** Restores multiple records previously marked as deleted (soft-delete) in batch. */
901
916
  restoreList: (pks: _Pk<T, Config>[], options?: { db?: ClientOrTransaction }) => Promise<{ count: number }>;
@@ -3,8 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.QueryMethod = QueryMethod;
4
4
  const dinamic_methods_key_constant_1 = require("../constants/dinamic-methods-key.constant");
5
5
  const dynamic_method_metadata_entity_1 = require("../entities/dynamic-method-metadata.entity");
6
+ const vs_repo_error_1 = require("../errors/vs-repo.error");
6
7
  const query_method_options_validate_1 = require("../validation/query-method-options.validate");
7
8
  function QueryMethod(value, options) {
9
+ if (typeof value !== "string") {
10
+ throw new vs_repo_error_1.VSRepoDecoratorError(`[VSRepository] (unknown: decorator) 'value' must be a valid string`);
11
+ }
8
12
  const validatedConfig = options
9
13
  ? (0, query_method_options_validate_1.validateQueryMethodOptions)(options)
10
14
  : { modifying: false };
@@ -21,6 +21,9 @@ function resolveDbAndPrismaArgs(data) {
21
21
  if (specificSelect) {
22
22
  prismaArgs.select = specificSelect;
23
23
  }
24
+ else if (validatedOptions.select) {
25
+ prismaArgs.select = validatedOptions.select;
26
+ }
24
27
  else if (validatedOptions.includeModel) {
25
28
  prismaArgs.include = instance.includeModels?.[validatedOptions.includeModel];
26
29
  }
@@ -18,12 +18,19 @@ function validateMethodOptions(options, instance) {
18
18
  includeModel: zod_1.default.enum(Object.keys(instance.includeModels ?? {})).optional(),
19
19
  see: zod_1.default.enum(["active", "removed", "all"]).default("active"),
20
20
  include: schemas_util_1.objectSchema.optional(),
21
+ select: schemas_util_1.objectSchema.optional(),
21
22
  })
22
23
  .refine(data => {
23
- return !(data.selectModel !== undefined && data.includeModel !== undefined);
24
+ const provided = [
25
+ data.select !== undefined,
26
+ data.selectModel !== undefined,
27
+ data.include !== undefined,
28
+ data.includeModel !== undefined,
29
+ ].filter(Boolean).length;
30
+ return provided <= 1;
24
31
  }, {
25
- message: "cannot be provided with 'selectModel'.",
26
- path: ["includeModel"],
32
+ message: "'select', 'selectModel', 'include' and 'includeModel' are mutually exclusive — only one can be provided.",
33
+ path: ["options"],
27
34
  });
28
35
  const optionsParsed = optionsSchema.safeParse(options ?? {});
29
36
  if (!optionsParsed.success) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vsrepo",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "description": "A repository pattern library for Prisma",
5
5
  "homepage": "https://github.com/jaobrabo123/VSRepository#readme",
6
6
  "repository": {
@@ -45,7 +45,11 @@
45
45
  "scripts": {
46
46
  "vsrepo": "node ./scripts/configure-prisma-import.mjs",
47
47
  "build": "tsc -p tsconfig.build.json && node ./scripts/copy-types.mjs",
48
- "prepack": "pnpm build"
48
+ "prepack": "pnpm build",
49
+ "test": "npm run test:typing && npm run test:implementation",
50
+ "test:implementation": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
51
+ "test:implementation:watch": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --watch",
52
+ "test:typing": "tsc --noEmit -p test/typing/tsconfig.json"
49
53
  },
50
54
  "keywords": [
51
55
  "prisma",
@@ -62,11 +66,16 @@
62
66
  "@prisma/client": "^7.8.0"
63
67
  },
64
68
  "devDependencies": {
69
+ "@jest/globals": "^30.4.1",
65
70
  "@prisma/adapter-pg": "^7.8.0",
66
71
  "@prisma/client": "^7.8.0",
72
+ "@types/jest": "^30.0.0",
67
73
  "@types/node": "^25.6.0",
74
+ "cross-env": "^10.1.0",
68
75
  "dotenv": "^17.4.2",
76
+ "jest": "^30.4.2",
69
77
  "prisma": "^7.8.0",
78
+ "ts-jest": "^29.4.12",
70
79
  "tsx": "^4.22.3",
71
80
  "typescript": "^6.0.3"
72
81
  },
@@ -137,7 +137,8 @@ const tsFiles = [
137
137
  VSRepoConfigError as VSRepoConfigErrorRuntime,
138
138
  VSRepoBuildError as VSRepoBuildErrorRuntime,
139
139
  VSRepoExtendError as VSRepoExtendErrorRuntime,
140
- VSRepoRuntimeError as VSRepoRuntimeErrorRuntime
140
+ VSRepoRuntimeError as VSRepoRuntimeErrorRuntime,
141
+ VSRepoDecoratorError as VSRepoDecoratorErrorRuntime,
141
142
  } from 'vsrepo/VSRepoError';
142
143
 
143
144
  import type {
@@ -145,14 +146,16 @@ import type {
145
146
  VSRepoConfigError as VSRepoConfigErrorType,
146
147
  VSRepoBuildError as VSRepoBuildErrorType,
147
148
  VSRepoExtendError as VSRepoExtendErrorType,
148
- VSRepoRuntimeError as VSRepoRuntimeErrorType
149
+ VSRepoRuntimeError as VSRepoRuntimeErrorType,
150
+ VSRepoDecoratorError as VSRepoDecoratorErrorType,
149
151
  } from './VSRepoError.types';
150
152
 
151
- export const VSRepoError: typeof VSRepoErrorType = VSRepoErrorRuntime;
152
- export const VSRepoConfigError: typeof VSRepoConfigErrorType = VSRepoConfigErrorRuntime;
153
- export const VSRepoBuildError: typeof VSRepoBuildErrorType = VSRepoBuildErrorRuntime;
154
- export const VSRepoExtendError: typeof VSRepoExtendErrorType = VSRepoExtendErrorRuntime;
155
- export const VSRepoRuntimeError: typeof VSRepoRuntimeErrorType = VSRepoRuntimeErrorRuntime;
153
+ export const VSRepoError = VSRepoErrorRuntime as typeof VSRepoErrorType;
154
+ export const VSRepoConfigError = VSRepoConfigErrorRuntime as typeof VSRepoConfigErrorType;
155
+ export const VSRepoBuildError = VSRepoBuildErrorRuntime as typeof VSRepoBuildErrorType;
156
+ export const VSRepoExtendError = VSRepoExtendErrorRuntime as typeof VSRepoExtendErrorType;
157
+ export const VSRepoRuntimeError = VSRepoRuntimeErrorRuntime as typeof VSRepoRuntimeErrorType;
158
+ export const VSRepoDecoratorError = VSRepoDecoratorErrorRuntime as typeof VSRepoDecoratorErrorType;
156
159
 
157
160
  export type * from './VSRepoError.types';
158
161
  `,