vsrepo 2.3.0 → 2.5.0-beta
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/CHANGELOG.md +519 -0
- package/README.md +204 -121
- package/README.pt-BR.md +206 -123
- package/dist/VSRepoAdapter.d.ts +2 -0
- package/dist/VSRepoAdapter.js.map +1 -1
- package/dist/VSRepository.js +9 -1
- package/dist/VSRepository.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/utils/vs-logger.util.d.ts +2 -2
- package/dist/internal/utils/vs-logger.util.js +7 -4
- package/dist/internal/utils/vs-logger.util.js.map +1 -1
- package/dist/internal/validators/vsrepo.validator.js +2 -2
- package/dist/internal/validators/vsrepo.validator.js.map +1 -1
- package/dist/types/utils/infer-method-return.type.d.ts +108 -0
- package/dist/types/utils/infer-method-return.type.js +3 -0
- package/dist/types/utils/infer-method-return.type.js.map +1 -0
- package/dist/types/utils/infer-method-type.type.d.ts +66 -0
- package/dist/types/utils/infer-method-type.type.js +3 -0
- package/dist/types/utils/infer-method-type.type.js.map +1 -0
- package/dist/types/utils/ordering.type.d.ts +1 -5
- package/dist/types/vsrepo/vsrepo-options.type.d.ts +5 -2
- package/package.json +14 -4
package/README.pt-BR.md
CHANGED
|
@@ -13,16 +13,14 @@
|
|
|
13
13
|
|
|
14
14
|
🇧🇷 Você está lendo a versão em português. [🇺🇸 Read in English](./README.md)
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Biblioteca de repository pattern **agnóstica de ORM**, com suporte completo a **TypeScript** e **type inference** automático. O VSRepository v2 é uma reescrita da biblioteca [v1](https://github.com/jaobrabo123/VSRepository/tree/v1): em vez de falar diretamente com o Prisma, o núcleo agora delega toda operação a um **adapter** plugável, permitindo que a mesma API de repository funcione com Prisma, TypeORM ou qualquer outro ORM/banco que implemente o contrato de adapter.
|
|
16
|
+
Biblioteca de repository pattern **agnóstica de ORM**, com suporte completo a **TypeScript** e **type inference** automático. O VSRepository v2 é uma reescrita da biblioteca [v1](https://github.com/jaobrabo123/VSRepository/tree/v1): em vez de falar diretamente com o Prisma, o núcleo agora delega toda operação a um **adapter** plugável, permitindo que a mesma API de repository funcione com Prisma, Drizzle ou qualquer outro ORM/banco que implemente o contrato de adapter.
|
|
19
17
|
|
|
20
18
|
O VSRepository permite criar repositories fortemente tipados com:
|
|
21
19
|
|
|
22
20
|
- **Métodos base** automáticos: `get`, `getOrThrow`, `getList`, `save`, `saveList`, `remove`, `removeList`, `patch`, `merge`, `getAll`, `total`, `has`
|
|
23
21
|
- **Soft-delete nativo**: `softRemove`, `softRemoveList`, `restore`, `restoreList`
|
|
24
22
|
- **Métodos dinâmicos** inferidos a partir do nome de um campo `declare` via o decorador `@DynamicMethod`: `findByEmail`, `findManyByStatusPaginated`, `updateById`
|
|
25
|
-
- **Métodos de query SQL raw** através do
|
|
23
|
+
- **Métodos de query SQL raw** através do decorador `@QueryMethod`, ignorando totalmente o engine de parsing por nome
|
|
26
24
|
- **`select`/`relations`** ad-hoc em cada chamada — sem mais projeções nomeadas pré-declaradas
|
|
27
25
|
- **Type safety** em 100% das operações
|
|
28
26
|
- **Transações** nativas do ORM, compartilhadas entre repositories
|
|
@@ -43,6 +41,7 @@ O VSRepository permite criar repositories fortemente tipados com:
|
|
|
43
41
|
- [Quais campos são elegíveis](#quais-campos-são-elegíveis)
|
|
44
42
|
- [Escrevendo um adapter](#escrevendo-um-adapter)
|
|
45
43
|
- [`select` e `relations`](#select-e-relations)
|
|
44
|
+
- [Tipagem de retorno restrita com `InferMethodReturn` (BETA)](#tipagem-de-retorno-restrita-com-infermethodreturn-beta)
|
|
46
45
|
- [Métodos dinâmicos](#métodos-dinâmicos)
|
|
47
46
|
- [Prefixos disponíveis](#prefixos-disponíveis)
|
|
48
47
|
- [Filtros de campo](#filtros-de-campo)
|
|
@@ -50,6 +49,7 @@ O VSRepository permite criar repositories fortemente tipados com:
|
|
|
50
49
|
- [Filtros de relação](#filtros-de-relação)
|
|
51
50
|
- [Ordenação, paginação e distinct](#ordenação-paginação-e-distinct)
|
|
52
51
|
- [Options do decorador](#options-do-decorador)
|
|
52
|
+
- [Tipagem de retorno restrita com `InferMethodType` (BETA)](#tipagem-de-retorno-restrita-com-infermethodtype-beta)
|
|
53
53
|
- [Query methods (SQL raw)](#query-methods-sql-raw)
|
|
54
54
|
- [Argumentos via spread com `spreadArgs`](#argumentos-via-spread-com-spreadargs)
|
|
55
55
|
- [Queries raw pontuais com `query()`](#queries-raw-pontuais-com-query)
|
|
@@ -69,22 +69,22 @@ O VSRepository permite criar repositories fortemente tipados com:
|
|
|
69
69
|
|
|
70
70
|
Se você vem do código/docs da [v1](https://github.com/jaobrabo123/VSRepository/tree/v1), aqui está o resumo. Veja cada seção linkada para detalhes.
|
|
71
71
|
|
|
72
|
-
| Área | v1 | v2
|
|
73
|
-
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
74
|
-
| Acesso ao banco | Fala diretamente com o **Prisma**, embutido no pacote core | Fala com um **`VSRepoAdapter`**; o suporte a cada ORM é distribuído em pacotes separados (`@vsrepo/prisma7-adapter`, `@vsrepo/
|
|
75
|
-
| Definindo um repository | `setupVSRepo<T, M>()({...}).build(prisma)` funcional, **ou** uma classe `DynamicRepository` | Uma única API **baseada em classes**: `extends VSRepository<Entity, PKType, OrmTypes>`
|
|
76
|
-
| Métodos dinâmicos | Objeto de config `methods: { findByEmail: { map: true } }` | Decorador `@DynamicMethod()` em um campo `declare`
|
|
77
|
-
| Projeções de dados | `selectModels` + `defaultSelectModel` nomeados e reutilizáveis | `select`/`relations` ad-hoc passados em cada chamada (sem modelos nomeados)
|
|
78
|
-
| Eager loading | `include`/`includeModels` (específico do Prisma) | Option `relations` agnóstica de ORM
|
|
79
|
-
| Filtros globais | `requiredWhere`
|
|
80
|
-
| Sufixo de filtro case-insensitive | `Insensitive` | `IgnoreCase`
|
|
81
|
-
| Ordenação inline no nome do método | Não suportado (`order` tinha que ser passado como argumento via `Ordered`/`Paginated`) | Cadeias `OrderBy<Campo>Asc`/`OrderBy<Campo>Desc` embutidas diretamente no nome do método
|
|
82
|
-
| Tratamento de duplicatas no `createMany` | Sufixo `SkipDuplicates` | Sufixo `IgnoreConflicts`
|
|
83
|
-
| `aggregate` / `groupBy` | Suportado (passthrough nativo do Prisma) | **
|
|
84
|
-
| Tipos de erro | `VSRepoError` + subclasses (`VSRepoConfigError`, `VSRepoBuildError`, `VSRepoExtendError`, `VSRepoRuntimeError`) | Uma classe base `VSRepoError` com um campo `type: VSRepoErrorType` (`DECORATOR`, `RESOLVER`, `DYNAMIC`, `VALIDATOR`, `BASE`, `ADAPTER`), além de uma subclasse `VSRepoAdapterError` que carrega um `AdapterErrorCode` e o erro original do ORM
|
|
85
|
-
| Log de debug | Boolean `showWorking: true` | `logLevel: VSLogLevel` (`DEBUG`/`INFO`/`WARN`/`ERROR`) + `logSlowThresholdMs` para avisos de queries lentas
|
|
86
|
-
| CLI `vsrepo generate` (etapa de geração de tipos) | Obrigatória antes de usar | Não faz parte do núcleo da v2 — os tipos vêm diretamente das suas entidades/tipos do ORM
|
|
87
|
-
| Extras de CRUD | `patchList`, `options.select`/`options.include` raw | `select`/`relations` já são o padrão (sempre "raw"); `patch`/`merge` mantêm a mesma semântica. **`patchList` foi removido** — para uma atualização parcial em lote, use um dynamic method `updateManyBy`/`updateManyWhere`
|
|
72
|
+
| Área | v1 | v2 |
|
|
73
|
+
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
74
|
+
| Acesso ao banco | Fala diretamente com o **Prisma**, embutido no pacote core | Fala com um **`VSRepoAdapter`**; o suporte a cada ORM é distribuído em pacotes separados (`@vsrepo/prisma7-adapter`, `@vsrepo/drizzle-adapter`, ...) em vez de vir embutido no pacote core `vsrepo` |
|
|
75
|
+
| Definindo um repository | `setupVSRepo<T, M>()({...}).build(prisma)` funcional, **ou** uma classe `DynamicRepository` | Uma única API **baseada em classes**: `extends VSRepository<Entity, PKType, OrmTypes>` |
|
|
76
|
+
| Métodos dinâmicos | Objeto de config `methods: { findByEmail: { map: true } }` | Decorador `@DynamicMethod()` em um campo `declare` |
|
|
77
|
+
| Projeções de dados | `selectModels` + `defaultSelectModel` nomeados e reutilizáveis | `select`/`relations` ad-hoc passados em cada chamada (sem modelos nomeados) |
|
|
78
|
+
| Eager loading | `include`/`includeModels` (específico do Prisma) | Option `relations` agnóstica de ORM |
|
|
79
|
+
| Filtros globais | `requiredWhere` e `pushWhere` | **Removidos**; Agora aceita apenas `softRemoveKey` + `see: "active" \| "removed" \| "all"` |
|
|
80
|
+
| Sufixo de filtro case-insensitive | `Insensitive` | `IgnoreCase` |
|
|
81
|
+
| Ordenação inline no nome do método | Não suportado (`order` tinha que ser passado como argumento via `Ordered`/`Paginated`) | Cadeias `OrderBy<Campo>Asc`/`OrderBy<Campo>Desc` embutidas diretamente no nome do método |
|
|
82
|
+
| Tratamento de duplicatas no `createMany` | Sufixo `SkipDuplicates` | Sufixo `IgnoreConflicts` |
|
|
83
|
+
| `aggregate` / `groupBy` | Suportado (passthrough nativo do Prisma) | `groupBy` **não está planejado** para a v2. Um prefixo `aggregate` separado também dificilmente será implementado: as operações mais comuns já são cobertas por métodos base dedicados (`sum`, `average`, `min`, `max`, `increment`, `decrement`, `multiply`, `divide`) — veja [Métodos atômicos e de agregação](#métodos-atômicos-e-de-agregação). Para qualquer coisa mais complexa, use `@QueryMethod`. |
|
|
84
|
+
| Tipos de erro | `VSRepoError` + subclasses (`VSRepoConfigError`, `VSRepoBuildError`, `VSRepoExtendError`, `VSRepoRuntimeError`) | Uma classe base `VSRepoError` com um campo `type: VSRepoErrorType` (`DECORATOR`, `RESOLVER`, `DYNAMIC`, `VALIDATOR`, `BASE`, `ADAPTER`), além de uma subclasse `VSRepoAdapterError` que carrega um `AdapterErrorCode` e o erro original do ORM |
|
|
85
|
+
| Log de debug | Boolean `showWorking: true` | `logLevel: VSLogLevel` (`DEBUG`/`INFO`/`WARN`/`ERROR`) + `logSlowThresholdMs` para avisos de queries lentas |
|
|
86
|
+
| CLI `vsrepo generate` (etapa de geração de tipos) | Obrigatória antes de usar | Não faz parte do núcleo da v2 — os tipos vêm diretamente das suas entidades/tipos do ORM |
|
|
87
|
+
| Extras de CRUD | `patchList`, `options.select`/`options.include` raw | `select`/`relations` já são o padrão (sempre "raw"); `patch`/`merge` mantêm a mesma semântica. **`patchList` foi removido** — para uma atualização parcial em lote, use um dynamic method `updateManyBy`/`updateManyWhere` |
|
|
88
88
|
|
|
89
89
|
---
|
|
90
90
|
|
|
@@ -97,17 +97,16 @@ O VSRepository v2 é **agnóstico de ORM por design**. O pacote core (`vsrepo`)
|
|
|
97
97
|
- `@vsrepo/typeorm-adapter`
|
|
98
98
|
- `@vsrepo/drizzle-adapter`
|
|
99
99
|
|
|
100
|
-
O adapter do Prisma 7 já foi publicado no npm como `@vsrepo/prisma7-adapter
|
|
100
|
+
O adapter do Prisma 7 já foi publicado no npm como `@vsrepo/prisma7-adapter`. O adapter do Drizzle está disponível em versão **alpha** — instale com `npm i @vsrepo/drizzle-adapter@alpha`. Os adapters para outros ORMs estão **planejados**, mas ainda não foram publicados. Até que exista um pacote `@vsrepo/*-adapter` oficial para o seu ORM, você pode escrever o seu próprio para o seu projeto e, se quiser, publicá-lo e abrir um PR para ajudar a fazer o ecossistema crescer — contribuições nesse sentido são muito bem-vindas.
|
|
101
101
|
|
|
102
|
-
| Adapter
|
|
103
|
-
|
|
|
104
|
-
| Prisma 7 (`@vsrepo/prisma7-adapter`)
|
|
105
|
-
| Drizzle (`@vsrepo/drizzle-adapter`)
|
|
106
|
-
| TypeORM
|
|
107
|
-
|
|
|
108
|
-
| Adapters customizados | 🟢 Totalmente suportados hoje — implemente você mesmo a classe abstrata [`VSRepoAdapter`](#escrevendo-seu-próprio-adapter) para qualquer ORM/banco que precisar, no seu próprio projeto ou pacote, seguindo o mesmo formato esperado dos `@vsrepo/*-adapter`. |
|
|
102
|
+
| Adapter | Status |
|
|
103
|
+
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
104
|
+
| Prisma 7 (`@vsrepo/prisma7-adapter`) | 🟢 **Lançado** — publicado no npm, implementa o contrato de `VSRepoAdapter` (CRUD, relations, transactions, `merge`, logging, etc.) com testes; veja o [`VSRepoPrisma7Adapter`](https://github.com/jaobrabo123/VSRepoPrisma7Adapter) para o código-fonte e docs. |
|
|
105
|
+
| Drizzle (`@vsrepo/drizzle-adapter`) | 🔵 **Alpha** — uma versão inicial já está disponível no npm; instale com `npm i @vsrepo/drizzle-adapter@alpha`. A API ainda pode mudar antes do release estável. Veja o repositório do [`DrizzleAdapter`](https://github.com/jaobrabo123/VSRepoDrizzleAdapter) para o estado atual e limitações conhecidas, e sinta-se à vontade para contribuir. |
|
|
106
|
+
| Outros ORMs (Prisma 8, TypeORM, etc.) | 🟡 **Planejados, ainda não publicados.** Nenhum pacote oficial existe ainda — por enquanto, escreva o seu próprio adapter (veja [Escrevendo seu próprio adapter](#escrevendo-seu-próprio-adapter)) e considere publicá-lo/contribuir de volta com o projeto. |
|
|
107
|
+
| Adapters customizados | 🟢 Totalmente suportados hoje — implemente você mesmo a classe abstrata [`VSRepoAdapter`](#escrevendo-seu-próprio-adapter) para qualquer ORM/banco que precisar, no seu próprio projeto ou pacote. |
|
|
109
108
|
|
|
110
|
-
Resumindo: a classe de repository, os decoradores `@DynamicMethod`/`@QueryMethod`, o engine de parsing de nomes, o tratamento de erros e o logging já funcionam de ponta a ponta, e o suporte ao Prisma 7 agora é um adapter lançado e publicado. Adapters oficiais para os demais ORMs estão no roadmap e serão distribuídos como pacotes `@vsrepo/*-adapter` separados, e não como parte do pacote core `vsrepo` — mas você não precisa esperar por isso: escrever (e opcionalmente publicar) o seu próprio adapter enquanto isso é uma forma totalmente suportada de usar a v2 hoje e de contribuir de volta com o projeto.
|
|
109
|
+
Resumindo: a classe de repository, os decoradores `@DynamicMethod`/`@QueryMethod`, o engine de parsing de nomes, o tratamento de erros e o logging já funcionam de ponta a ponta, e o suporte ao Prisma 7 agora é um adapter lançado e publicado. O adapter do Drizzle está disponível em alpha. Adapters oficiais para os demais ORMs estão no roadmap e serão distribuídos como pacotes `@vsrepo/*-adapter` separados, e não como parte do pacote core `vsrepo` — mas você não precisa esperar por isso: escrever (e opcionalmente publicar) o seu próprio adapter enquanto isso é uma forma totalmente suportada de usar a v2 hoje e de contribuir de volta com o projeto.
|
|
111
110
|
|
|
112
111
|
---
|
|
113
112
|
|
|
@@ -119,8 +118,6 @@ A v2 é instalada como o pacote core mais um pacote de adapter para o seu ORM, p
|
|
|
119
118
|
npm i vsrepo @vsrepo/prisma7-adapter
|
|
120
119
|
```
|
|
121
120
|
|
|
122
|
-
> O `vsrepo` v2.0.0 e o `@vsrepo/prisma7-adapter` já foram publicados no npm e estão prontos para uso. Para qualquer ORM além do Prisma 7, ainda não existe um pacote de adapter — instale o core e escreva o seu próprio (veja [Escrevendo seu próprio adapter](#escrevendo-seu-próprio-adapter)).
|
|
123
|
-
|
|
124
121
|
---
|
|
125
122
|
|
|
126
123
|
## Uso básico
|
|
@@ -170,14 +167,16 @@ class UserRepository extends VSRepository<User, string> {
|
|
|
170
167
|
export default new UserRepository();
|
|
171
168
|
```
|
|
172
169
|
|
|
173
|
-
> A API do core (`VSRepository`, `VSRepoAdapter`, `DynamicMethod`, `QueryMethod`, `VSRepoError`, enums e tipos) é importada do entry point único `vsrepo`. O adapter concreto vem de um pacote **separado** (`@vsrepo/*-adapter`). No Prisma 7, instale o [`@vsrepo/prisma7-adapter`](https://github.com/jaobrabo123/VSRepoPrisma7Adapter)
|
|
170
|
+
> A API do core (`VSRepository`, `VSRepoAdapter`, `DynamicMethod`, `QueryMethod`, `VSRepoError`, enums e tipos) é importada do entry point único `vsrepo`. O adapter concreto vem de um pacote **separado** (`@vsrepo/*-adapter`). No Prisma 7, instale o [`@vsrepo/prisma7-adapter`](https://github.com/jaobrabo123/VSRepoPrisma7Adapter).
|
|
174
171
|
|
|
175
172
|
> **O terceiro generic (`OrmTypes`):** `VSRepository<Entity, PKType, OrmTypes>` aceita um terceiro type parameter opcional descrevendo os tipos de client/transaction do seu ORM, via `VSRepoOrmTypes` (`{ dbClient; dbTransaction }`). Ao fornecê-lo, `getDbClient()`, o callback de `transaction()` e a option `db` de todo método passam a ser tipados corretamente, em vez de `any`:
|
|
176
173
|
>
|
|
177
174
|
> ```typescript
|
|
178
|
-
>
|
|
175
|
+
> import { Prisma7OrmTypes } from "@vsrepo/prisma7-adapter";
|
|
176
|
+
>
|
|
177
|
+
> type MyOrmTypes = Prisma7OrmTypes<PrismaClient>;
|
|
179
178
|
>
|
|
180
|
-
> class UserRepository extends VSRepository<User, string,
|
|
179
|
+
> class UserRepository extends VSRepository<User, string, MyOrmTypes> {
|
|
181
180
|
> // getDbClient() agora retorna PrismaClient, e transaction(fn) tipa `tx` como Prisma.TransactionClient
|
|
182
181
|
> }
|
|
183
182
|
> ```
|
|
@@ -209,14 +208,14 @@ await userRepository.remove(usuario.id);
|
|
|
209
208
|
|
|
210
209
|
`VSRepoOptions<T, K>`, passado para o `super(...)` dentro do construtor do seu repository:
|
|
211
210
|
|
|
212
|
-
| Option | Tipo
|
|
213
|
-
| -------------------- |
|
|
214
|
-
| `adapter` | `VSRepoAdapter<T>`
|
|
215
|
-
| `pkName` | `keyof T`
|
|
216
|
-
| `softRemoveKey` | `keyof T`
|
|
217
|
-
| `defaultOrdering` | `Ordering<T>`
|
|
218
|
-
| `logLevel` | `VSLogLevel`
|
|
219
|
-
| `logSlowThresholdMs` | `number`
|
|
211
|
+
| Option | Tipo | Descrição |
|
|
212
|
+
| -------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
213
|
+
| `adapter` | `VSRepoAdapter<T>` | **Obrigatório.** A instância do adapter que traduz as chamadas do repository em chamadas contra o ORM/banco por trás dele. |
|
|
214
|
+
| `pkName` | `keyof T` | Opcional. Nome do campo que representa a primary key da entidade. Quando omitido, o repository usa o `getPkName()` do adapter. Se o adapter também não implementar, o construtor lança um `VSRepoError`. |
|
|
215
|
+
| `softRemoveKey` | `keyof T` | Opcional. Quando definido, habilita `softRemove`, `softRemoveList`, `restore` e `restoreList`. |
|
|
216
|
+
| `defaultOrdering` | `Ordering<T>` | Opcional. Ordenação padrão aplicada automaticamente em queries que aceitam `order`, a menos que seja sobrescrita em uma chamada específica. |
|
|
217
|
+
| `logLevel` | `VSLogLevel` | Opcional. Severidade mínima impressa pelo logger interno. Padrão: `VSLogLevel.WARN`. |
|
|
218
|
+
| `logSlowThresholdMs` | `number \| boolean` | Opcional. Duração (ms) acima da qual uma operação concluída é logada como `WARN`. Padrão: 300ms. Passe `false` para desabilitar completamente os avisos de operação lenta; passe `true` para usar explicitamente o threshold padrão de 300ms. |
|
|
220
219
|
|
|
221
220
|
---
|
|
222
221
|
|
|
@@ -247,7 +246,7 @@ Disponíveis automaticamente em toda subclasse de `VSRepository`:
|
|
|
247
246
|
| `min(field, where?, options?)` | Valor mínimo de um campo numérico em todos os registros que baterem no filtro; `null` se nenhum bater. |
|
|
248
247
|
| `max(field, where?, options?)` | Valor máximo de um campo numérico em todos os registros que baterem no filtro; `null` se nenhum bater. |
|
|
249
248
|
| `transaction(fn, options?)` | Executa `fn` dentro de uma transação nativa do ORM. |
|
|
250
|
-
| `getDbClient()` | Retorna a instância do client do ORM
|
|
249
|
+
| `getDbClient()` | Retorna a instância do client do ORM. |
|
|
251
250
|
| `query<T>(query, options?)` | Executa uma instrução SQL raw diretamente contra o banco. Veja [Queries raw pontuais com `query()`](#queries-raw-pontuais-com-query). |
|
|
252
251
|
|
|
253
252
|
A maioria dos métodos acima aceita um objeto `MethodOptions<Entity, OrmTypes>` como último argumento (`select`, `relations`, `see`, `db`). Alguns — `total`, `has`, `removeList`, `sum`, `average`, `min`, `max`, e os métodos em lote de soft-delete (`softRemoveList`/`restoreList`) — não retornam/moldam uma `Entity`, então aceitam o tipo mais restrito `RestrictMethodOptions<Entity, OrmTypes>` (só `see`, `db`; sem `select`/`relations`). `transaction`, `query` e `getDbClient` recebem options próprias ou nenhuma.
|
|
@@ -256,7 +255,7 @@ A maioria dos métodos acima aceita um objeto `MethodOptions<Entity, OrmTypes>`
|
|
|
256
255
|
|
|
257
256
|
## Soft-delete
|
|
258
257
|
|
|
259
|
-
O soft-delete
|
|
258
|
+
O soft-delete é um **conceito nativo de primeira classe**. Configure `softRemoveKey` uma vez no repository:
|
|
260
259
|
|
|
261
260
|
```typescript
|
|
262
261
|
super({
|
|
@@ -287,7 +286,7 @@ await userRepository.getAll({ see: "all" }); // todos, ignorando o soft-delete
|
|
|
287
286
|
|
|
288
287
|
## Métodos atômicos e de agregação
|
|
289
288
|
|
|
290
|
-
Toda subclasse de `VSRepository` ganha 8 métodos
|
|
289
|
+
Toda subclasse de `VSRepository` ganha 8 métodos para trabalhar com campos numéricos, divididos em dois grupos:
|
|
291
290
|
|
|
292
291
|
**Updates atômicos** — avaliados no servidor contra o valor _atual_ da linha (`UPDATE ... SET field = field + value`), não um read-modify-write feito no client:
|
|
293
292
|
|
|
@@ -363,14 +362,6 @@ const usuarioComEndereco = await userRepository.get(id, {
|
|
|
363
362
|
>
|
|
364
363
|
> O core apenas repassa `MethodOptions.select` e `MethodOptions.relations` ao adapter — cada adapter decide como traduzi-los para o ORM subjacente:
|
|
365
364
|
>
|
|
366
|
-
> - **TypeORM (`@vsrepo/typeorm-adapter`)** — `relations` é **obrigatório** para carregar qualquer relação, mesmo quando você quer apenas uma projeção aninhada via `select`. O TypeORM não fará JOIN/carregar a relação a menos que ela esteja listada em `relations`:
|
|
367
|
-
> ```typescript
|
|
368
|
-
> // TypeORM: apenas select NÃO é suficiente
|
|
369
|
-
> await userRepository.get(id, {
|
|
370
|
-
> select: { id: true, address: { city: true } },
|
|
371
|
-
> relations: { address: true }, // ← obrigatório no TypeORM
|
|
372
|
-
> });
|
|
373
|
-
> ```
|
|
374
365
|
> - **Prisma 7 (`@vsrepo/prisma7-adapter` / `VSRepoPrisma7Adapter`)** — `relations` é convertido para `include` do Prisma (`parsePrismaInclude`). **Se `select` estiver presente, `relations` é ignorado** porque o Prisma não permite `select` + `include` na mesma query:
|
|
375
366
|
> ```typescript
|
|
376
367
|
> // Prisma7: relations é ignorado quando select existe
|
|
@@ -382,16 +373,56 @@ const usuarioComEndereco = await userRepository.get(id, {
|
|
|
382
373
|
>
|
|
383
374
|
> Adapters customizados podem mapear `relations` de forma diferente — consulte a documentação do adapter para a semântica exata.
|
|
384
375
|
|
|
376
|
+
### Tipagem de retorno restrita com `InferMethodReturn` [BETA]
|
|
377
|
+
|
|
378
|
+
Por padrão, os métodos são tipados como se retornassem a **entidade inteira**, ignorando o `select` e o `relations` que você passa (a mesma abordagem do TypeORM). Se você prefere uma tipagem mais restrita, `InferMethodReturn<T, Options>` a estreita para o que foi realmente pedido. É opt-in e puramente em nível de tipos — nada muda em runtime.
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import type { InferMethodReturn, MethodOptions } from "vsrepo";
|
|
382
|
+
|
|
383
|
+
type Address = { id: string; city: string };
|
|
384
|
+
type Product = { id: string; name: string };
|
|
385
|
+
type User = {
|
|
386
|
+
id: string;
|
|
387
|
+
name: string;
|
|
388
|
+
email: string;
|
|
389
|
+
address: Address | null;
|
|
390
|
+
products: Product[];
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const options = {
|
|
394
|
+
select: { id: true, name: true, products: { id: true } },
|
|
395
|
+
} satisfies MethodOptions<User>;
|
|
396
|
+
|
|
397
|
+
const users: InferMethodReturn<User[], typeof options> = await userRepository.getAll(options);
|
|
398
|
+
// { id: string; name: string; products: { id: string }[] }[]
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
- O **primeiro** argumento de tipo é o que o método retorna: `User`, `User | null` ou `User[]`. `null` e array são preservados — inclusive nos campos de relação (`address: Address | null`).
|
|
402
|
+
- O **segundo** é o objeto de options passado ao método (`typeof options`). Pode ser omitido, o que equivale a não passar options.
|
|
403
|
+
|
|
404
|
+
| Options passadas | Resultado inferido |
|
|
405
|
+
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
406
|
+
| Nenhuma (ou `{}`) | Apenas os campos escalares da entidade — sem relações. |
|
|
407
|
+
| `relations` | Os campos escalares mais as relações pedidas (inclusive as aninhadas); cada relação traz todos os seus campos escalares. |
|
|
408
|
+
| `select` | Apenas os campos selecionados. Uma relação com `true` traz todos os seus campos escalares; um `select` aninhado a restringe ainda mais. Relações selecionadas assim são carregadas mesmo sem `relations`. |
|
|
409
|
+
| `select` + `relations` | `select` vence e `relations` é ignorado. |
|
|
410
|
+
|
|
411
|
+
- `see` e `db` não afetam o resultado.
|
|
412
|
+
- Mantenha os tipos literais das options, usando `satisfies MethodOptions<T>` (como acima) ou passando-as inline. Se estiverem tipadas como um `MethodOptions<T>` genérico (ex.: `const options: MethodOptions<User> = ...`), nada é conhecido em tempo de compilação e `T` é retornado sem alterações.
|
|
413
|
+
- Campos e relações opcionais (`?`) da entidade continuam opcionais.
|
|
414
|
+
- Para ter essa inferência direto nos métodos dinâmicos, veja [Tipagem de retorno restrita com `InferMethodType`](#tipagem-de-retorno-restrita-com-infermethodtype-beta).
|
|
415
|
+
|
|
385
416
|
---
|
|
386
417
|
|
|
387
418
|
## Métodos dinâmicos
|
|
388
419
|
|
|
389
|
-
Métodos dinâmicos são declarados como um campo `declare` anotado com `@DynamicMethod()`. O comportamento deles — qual método do adapter chamar, quais filtros aplicar e como os argumentos se mapeiam para eles — é inferido inteiramente a partir do **nome** do campo
|
|
420
|
+
Métodos dinâmicos são declarados como um campo `declare` anotado com `@DynamicMethod()`. O comportamento deles — qual método do adapter chamar, quais filtros aplicar e como os argumentos se mapeiam para eles — é inferido inteiramente a partir do **nome** do campo.
|
|
390
421
|
|
|
391
422
|
```typescript
|
|
392
423
|
class UserRepository extends VSRepository<User, string> {
|
|
393
424
|
@DynamicMethod()
|
|
394
|
-
declare findByEmail: (email: string) => Promise<User[]>;
|
|
425
|
+
declare findByEmail: (email: string, options?: MethodOptions<User>) => Promise<User[]>;
|
|
395
426
|
|
|
396
427
|
@DynamicMethod()
|
|
397
428
|
declare findOneByEmail: (email: string) => Promise<User | null>;
|
|
@@ -407,54 +438,55 @@ class UserRepository extends VSRepository<User, string> {
|
|
|
407
438
|
options?: MethodOptions<User>,
|
|
408
439
|
) => Promise<User[]>;
|
|
409
440
|
|
|
410
|
-
//
|
|
441
|
+
// filtros de campo, depois pagination, depois MethodOptions
|
|
411
442
|
@DynamicMethod()
|
|
412
443
|
declare findByNameIgnoreCaseOrAgeBetweenOrderByCreatedAtAscPaginated: (
|
|
413
444
|
name: string,
|
|
414
445
|
age: [number, number],
|
|
415
|
-
order: Ordering<User>,
|
|
416
446
|
pagination: Pagination,
|
|
417
447
|
options?: MethodOptions<User>,
|
|
418
448
|
) => Promise<User[]>;
|
|
419
449
|
}
|
|
420
450
|
```
|
|
421
451
|
|
|
452
|
+
> Quer que o tipo de retorno acompanhe o `select`/`relations` passados, em vez de ser sempre a entidade inteira? Declare o método com [`InferMethodType`](#tipagem-de-retorno-restrita-com-infermethodtype-beta).
|
|
453
|
+
|
|
422
454
|
### Prefixos disponíveis
|
|
423
455
|
|
|
424
|
-
| Prefixo | Método do adapter | Observações
|
|
425
|
-
| -------------------------- | --------------------- |
|
|
426
|
-
| `findBy` | `findMany` | Filtros de campo seguem o prefixo.
|
|
427
|
-
| `findOneBy` | `findOne` | Filtros de campo seguem o prefixo; resultado único.
|
|
428
|
-
| `findOneOrThrowBy` | `findOneOrThrow` | Lança erro se não encontrar.
|
|
429
|
-
| `findOneOrThrow` | `findOneOrThrow` | Sem filtros de campo; aplica só soft-delete/`see`.
|
|
430
|
-
| `findOneOrThrowWhere` | `findOneOrThrow` | Recebe um `VSRepoWhere<T>` como primeiro argumento.
|
|
431
|
-
| `findWhere` | `findMany` | Recebe um `VSRepoWhere<T>` como primeiro argumento.
|
|
432
|
-
| `findOneWhere` | `findOne` | Recebe um `VSRepoWhere<T>` como primeiro argumento.
|
|
433
|
-
| `findOne` | `findOne` | Sem filtros de campo; aplica só soft-delete/`see`.
|
|
434
|
-
| `countBy` | `count` | Filtros de campo seguem o prefixo.
|
|
435
|
-
| `countWhere` | `count` | Recebe um `VSRepoWhere<T>` como primeiro argumento.
|
|
436
|
-
| `count` | `count` | Sem filtros de campo.
|
|
437
|
-
| `existsBy` | `exists` | Retorna `boolean`.
|
|
438
|
-
| `existsWhere` | `exists` | Recebe um `VSRepoWhere<T>` como primeiro argumento.
|
|
439
|
-
| `create` | `create` | Recebe `
|
|
440
|
-
| `createMany` | `createMany` | Recebe `
|
|
441
|
-
| `createManyReturning` | `createManyReturning` | Recebe `
|
|
442
|
-
| `updateBy` | `update` | Filtros de campo + `
|
|
443
|
-
| `updateWhere` | `update` | Recebe um `VSRepoWhere<T>` como primeiro argumento, depois `
|
|
444
|
-
| `updateManyBy` | `updateMany` | Filtros de campo + `
|
|
445
|
-
| `updateManyWhere` | `updateMany` | Recebe um `VSRepoWhere<T>` como primeiro argumento, depois `
|
|
446
|
-
| `updateManyReturningBy` | `updateManyReturning` | Filtros de campo + `
|
|
447
|
-
| `updateManyReturningWhere` | `updateManyReturning` | Recebe um `VSRepoWhere<T>` como primeiro argumento, depois `
|
|
448
|
-
| `upsertBy` | `upsert` | Filtros de campo + payloads `create`/`update`.
|
|
449
|
-
| `upsertWhere` | `upsert` | Recebe um `VSRepoWhere<T>` como primeiro argumento, depois os payloads `create`/`update`.
|
|
450
|
-
| `deleteBy` | `delete` | Filtros de campo seguem o prefixo.
|
|
451
|
-
| `deleteWhere` | `delete` | Recebe um `VSRepoWhere<T>` como primeiro argumento.
|
|
452
|
-
| `deleteManyBy` | `deleteMany` | Filtros de campo seguem o prefixo.
|
|
453
|
-
| `deleteManyWhere` | `deleteMany` | Recebe um `VSRepoWhere<T>` como primeiro argumento.
|
|
454
|
-
| `deleteManyReturningBy` | `deleteManyReturning` | Filtros de campo seguem o prefixo; retorna os registros removidos.
|
|
455
|
-
| `deleteManyReturningWhere` | `deleteManyReturning` | Recebe um `VSRepoWhere<T>` como primeiro argumento; retorna os registros removidos.
|
|
456
|
-
|
|
457
|
-
> `aggregate`
|
|
456
|
+
| Prefixo | Método do adapter | Observações |
|
|
457
|
+
| -------------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
458
|
+
| `findBy` | `findMany` | Filtros de campo seguem o prefixo. |
|
|
459
|
+
| `findOneBy` | `findOne` | Filtros de campo seguem o prefixo; resultado único. |
|
|
460
|
+
| `findOneOrThrowBy` | `findOneOrThrow` | Lança erro se não encontrar. |
|
|
461
|
+
| `findOneOrThrow` | `findOneOrThrow` | Sem filtros de campo; aplica só soft-delete/`see`. |
|
|
462
|
+
| `findOneOrThrowWhere` | `findOneOrThrow` | Recebe um `VSRepoWhere<T>` como primeiro argumento. |
|
|
463
|
+
| `findWhere` | `findMany` | Recebe um `VSRepoWhere<T>` como primeiro argumento. |
|
|
464
|
+
| `findOneWhere` | `findOne` | Recebe um `VSRepoWhere<T>` como primeiro argumento. |
|
|
465
|
+
| `findOne` | `findOne` | Sem filtros de campo; aplica só soft-delete/`see`. |
|
|
466
|
+
| `countBy` | `count` | Filtros de campo seguem o prefixo. |
|
|
467
|
+
| `countWhere` | `count` | Recebe um `VSRepoWhere<T>` como primeiro argumento. |
|
|
468
|
+
| `count` | `count` | Sem filtros de campo. |
|
|
469
|
+
| `existsBy` | `exists` | Retorna `boolean`. |
|
|
470
|
+
| `existsWhere` | `exists` | Recebe um `VSRepoWhere<T>` como primeiro argumento. |
|
|
471
|
+
| `create` | `create` | Recebe `DeepPartial<Entity>` como argumento. |
|
|
472
|
+
| `createMany` | `createMany` | Recebe `DeepPartial<Entity>[]` como argumento; suporta `IgnoreConflicts`. |
|
|
473
|
+
| `createManyReturning` | `createManyReturning` | Recebe `DeepPartial<Entity>[]` como argumento; suporta `IgnoreConflicts`; retorna os registros criados (`T[]`), em vez de `CountResult`. |
|
|
474
|
+
| `updateBy` | `update` | Filtros de campo + `DeepPartial<Entity>` como argumento. |
|
|
475
|
+
| `updateWhere` | `update` | Recebe um `VSRepoWhere<T>` como primeiro argumento, depois `DeepPartial<Entity>`. |
|
|
476
|
+
| `updateManyBy` | `updateMany` | Filtros de campo + `DeepPartial<Entity>`. |
|
|
477
|
+
| `updateManyWhere` | `updateMany` | Recebe um `VSRepoWhere<T>` como primeiro argumento, depois `DeepPartial<Entity>`. |
|
|
478
|
+
| `updateManyReturningBy` | `updateManyReturning` | Filtros de campo + `DeepPartial<Entity>`; retorna os registros atualizados. |
|
|
479
|
+
| `updateManyReturningWhere` | `updateManyReturning` | Recebe um `VSRepoWhere<T>` como primeiro argumento, depois `DeepPartial<Entity>`; retorna os registros atualizados. |
|
|
480
|
+
| `upsertBy` | `upsert` | Filtros de campo + payloads `create`/`update`. |
|
|
481
|
+
| `upsertWhere` | `upsert` | Recebe um `VSRepoWhere<T>` como primeiro argumento, depois os payloads `create`/`update`. |
|
|
482
|
+
| `deleteBy` | `delete` | Filtros de campo seguem o prefixo. |
|
|
483
|
+
| `deleteWhere` | `delete` | Recebe um `VSRepoWhere<T>` como primeiro argumento. |
|
|
484
|
+
| `deleteManyBy` | `deleteMany` | Filtros de campo seguem o prefixo. |
|
|
485
|
+
| `deleteManyWhere` | `deleteMany` | Recebe um `VSRepoWhere<T>` como primeiro argumento. |
|
|
486
|
+
| `deleteManyReturningBy` | `deleteManyReturning` | Filtros de campo seguem o prefixo; retorna os registros removidos. |
|
|
487
|
+
| `deleteManyReturningWhere` | `deleteManyReturning` | Recebe um `VSRepoWhere<T>` como primeiro argumento; retorna os registros removidos. |
|
|
488
|
+
|
|
489
|
+
> `groupBy` **não está planejado** para a v2 — ele não se encaixa bem no contrato agnóstico de ORM. Um prefixo `aggregate` separado também dificilmente será implementado: as operações de agregação mais comuns (`sum`, `average`, `min`, `max`, `increment`, `decrement`, `multiply`, `divide`) já estão disponíveis como métodos base dedicados — veja [Métodos atômicos e de agregação](#métodos-atômicos-e-de-agregação). Para qualquer coisa mais complexa, use um `@QueryMethod` com SQL raw.
|
|
458
490
|
|
|
459
491
|
### Filtros de campo
|
|
460
492
|
|
|
@@ -536,7 +568,7 @@ declare findByProductsSome: () => Promise<User[]>;
|
|
|
536
568
|
| `Ordered` | Injeta um argumento `order: Ordering<T>` como **penúltimo** parâmetro (antes do `MethodOptions` opcional). |
|
|
537
569
|
| `OrderedAndPaginated` | Injeta `order` como antepenúltimo, depois `pagination` como penúltimo — ambos antes do `MethodOptions`. |
|
|
538
570
|
| `PaginatedAndOrdered` | Injeta `pagination` como antepenúltimo, depois `order` como penúltimo — ambos antes do `MethodOptions`. |
|
|
539
|
-
| `OrderBy<Campo>Asc` / `OrderBy<Campo>Desc` |
|
|
571
|
+
| `OrderBy<Campo>Asc` / `OrderBy<Campo>Desc` | Embute uma ordenação fixa diretamente no nome do método — encadeie campos com `And` (ex.: `OrderByCreatedAtAscAndNameDesc`). Não precisa de argumento `order`. *OBS: Se você não especificar `Asc` ou `Desc` ele considera como `Asc`* |
|
|
540
572
|
| `Distinct<Campo>And<Campo>...` | Embute campos `distinct` fixos diretamente no nome do método (só válido em métodos da família `findBy`/`findWhere`). |
|
|
541
573
|
| `IgnoreConflicts` | No `createMany`/`createManyReturning`, ignora registros que violariam uma constraint única, em vez de lançar erro. _(Renomeado do `SkipDuplicates` da v1.)_ |
|
|
542
574
|
|
|
@@ -585,10 +617,51 @@ declare findOne: (options?: MethodOptions<User>) => Promise<User | null>;
|
|
|
585
617
|
| `injectOrdering` | `Ordering<T>` | Ordenação fixa injetada automaticamente, sobrescrevendo o `defaultOrdering` do repository. |
|
|
586
618
|
|
|
587
619
|
```typescript
|
|
620
|
+
// proxyTo: dá um nome customizado ao método reutilizando um padrão existente
|
|
621
|
+
@DynamicMethod<User>({ proxyTo: "findByEmail" })
|
|
622
|
+
declare buscarPorEmail: (email: string, options?: MethodOptions<User>) => Promise<User[]>;
|
|
623
|
+
|
|
624
|
+
// injectOrdering: sempre ordena por createdAt desc, sobrescrevendo o defaultOrdering
|
|
588
625
|
@DynamicMethod<User>({ injectOrdering: { createdAt: "desc" } })
|
|
589
626
|
declare findByStatus: (status: string) => Promise<User[]>;
|
|
590
627
|
```
|
|
591
628
|
|
|
629
|
+
### Tipagem de retorno restrita com `InferMethodType` [BETA]
|
|
630
|
+
|
|
631
|
+
Normalmente você escreve à mão a assinatura de um método dinâmico, e o retorno é o que você declarar (em geral a entidade inteira). `InferMethodType<Args, Return, OrmTypes?>` declara o método para você e infere o retorno **a cada chamada** a partir do `select`/`relations` passados — com as mesmas regras do [`InferMethodReturn`](#tipagem-de-retorno-restrita-com-infermethodreturn-beta):
|
|
632
|
+
|
|
633
|
+
```typescript
|
|
634
|
+
class UserRepository extends VSRepository<User, string, MyOrmTypes> {
|
|
635
|
+
@DynamicMethod()
|
|
636
|
+
declare findByName: InferMethodType<[name: string], User[], MyOrmTypes>;
|
|
637
|
+
|
|
638
|
+
// a terceira generic (OrmTypes) é opcional
|
|
639
|
+
@DynamicMethod()
|
|
640
|
+
declare findOneByEmail: InferMethodType<[email: string], User | null>;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
await userRepository.findByName("John");
|
|
644
|
+
// { id: string; name: string; email: string }[] (apenas os campos escalares)
|
|
645
|
+
|
|
646
|
+
await userRepository.findByName("John", { select: { id: true, products: { id: true } } });
|
|
647
|
+
// { id: string; products: { id: string }[] }[]
|
|
648
|
+
|
|
649
|
+
await userRepository.findOneByEmail("john@example.com", { relations: { address: true } });
|
|
650
|
+
// { id: string; name: string; email: string; address: Address | null } | null
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
| Generic | Descrição |
|
|
654
|
+
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
655
|
+
| `Args` | Tupla com os argumentos posicionais do método, **sem** `options` — ex.: `[name: string]` ou `[where: VSRepoWhere<User>, pagination: Pagination]`. |
|
|
656
|
+
| `Return` | O que o método resolve: `Entity`, `Entity \| null` ou `Entity[]`. O tipo da entidade usado em `select`/`relations` é extraído daqui. |
|
|
657
|
+
| `OrmTypes` | _Opcional._ `VSRepoOrmTypes` do seu ORM, usado para tipar a option `db` (veja [Criando um repository](#criando-um-repository)). Padrão: `VSRepoOrmTypes`. |
|
|
658
|
+
|
|
659
|
+
- `options` (`MethodOptions<Entity, OrmTypes>`) é sempre o **último** parâmetro, opcional, depois de todos os argumentos de `Args`. Se algum desses argumentos for opcional, passe `undefined` explicitamente para alcançar `options`.
|
|
660
|
+
- Sem `options` o resultado tem apenas os campos escalares; com elas, segue as [mesmas regras](#tipagem-de-retorno-restrita-com-infermethodreturn-beta) do `InferMethodReturn` (inclusive `select` vencendo `relations`).
|
|
661
|
+
- Chaves inexistentes em `select`/`relations` (em qualquer profundidade) são rejeitadas em tempo de compilação, e o editor as sugere via autocomplete — igual a um parâmetro `MethodOptions<Entity>` comum.
|
|
662
|
+
- Funciona junto com as [options do decorador](#options-do-decorador) (`proxyTo`, `injectOrdering`).
|
|
663
|
+
- Foi pensado para métodos dinâmicos que retornam entidades (`findBy…`, `findOneBy…`, `findWhere…`, …). Os que não retornam — `countBy…`, `existsBy…` — mantêm a assinatura normal.
|
|
664
|
+
|
|
592
665
|
---
|
|
593
666
|
|
|
594
667
|
## Query methods (SQL raw)
|
|
@@ -612,7 +685,7 @@ class UserRepository extends VSRepository<User, string> {
|
|
|
612
685
|
|
|
613
686
|
| Option | Tipo | Padrão | Descrição |
|
|
614
687
|
| -------------- | --------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
615
|
-
| `modifying` | `boolean` | `false` | Quando `true`,
|
|
688
|
+
| `modifying` | `boolean` | `false` | Quando `true`, o método resolve para o número de linhas afetadas. Quando `false`, executa como query de leitura e resolve para o tipo de retorno declarado. |
|
|
616
689
|
| `singleResult` | `boolean` | `false` | Quando `true`, transforma um resultado em array no seu primeiro elemento (`null` se vazio), permitindo declarar o tipo de retorno como um objeto único em vez de array. Não tem efeito em resultados que não são array (ex.: o número de linhas afetadas de uma query `modifying`). |
|
|
617
690
|
|
|
618
691
|
Query methods aceitam `{ args, db? }` na chamada — `db` permite que participem de um bloco `transaction()`, assim como os métodos base e dinâmicos.
|
|
@@ -629,6 +702,10 @@ class UserRepository extends VSRepository<User, string> {
|
|
|
629
702
|
declare findByEmailAndType: (
|
|
630
703
|
...args: QueryArgs<[email: string, userType: string]>
|
|
631
704
|
) => Promise<User[]>;
|
|
705
|
+
|
|
706
|
+
// Ao invés de usar o `QueryArgs`, você também pode simplesmente definir `DbArg` como último parâmetro
|
|
707
|
+
@QueryMethod('SELECT * FROM "user" WHERE id = $1', { spreadArgs: true })
|
|
708
|
+
declare findById: (id: string, db?: DbArg) => Promise<User[]>;
|
|
632
709
|
}
|
|
633
710
|
|
|
634
711
|
const admins = await userRepository.findByEmailAndType("joao@email.com", "admin");
|
|
@@ -646,10 +723,10 @@ await userRepository.transaction(async tx => {
|
|
|
646
723
|
|
|
647
724
|
### Queries raw pontuais com `query()`
|
|
648
725
|
|
|
649
|
-
Para SQL raw pontual que não justifica declarar um `@QueryMethod` na classe do repository, chame `query()` diretamente — ele está disponível em toda instância de `VSRepository` e
|
|
726
|
+
Para SQL raw pontual que não justifica declarar um `@QueryMethod` na classe do repository, chame `query()` diretamente — ele está disponível em toda instância de `VSRepository` e usa o `query()` do adapter por baixo dos panos:
|
|
650
727
|
|
|
651
728
|
```typescript
|
|
652
|
-
query<T = any>(query: string, options?:
|
|
729
|
+
query<T = any>(query: string, options?: VSRepoQueryOptions<OrmTypes>): Promise<T>;
|
|
653
730
|
```
|
|
654
731
|
|
|
655
732
|
```typescript
|
|
@@ -674,7 +751,7 @@ const user = await userRepository.query<User | null>('SELECT * FROM "user" WHERE
|
|
|
674
751
|
| -------------- | --------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
675
752
|
| `args` | `any[]` | `undefined` | Parâmetros posicionais injetados nos placeholders do SQL — a sintaxe dos placeholders depende do banco/driver usado pelo seu adapter. Nunca interpole valores diretamente na string SQL. |
|
|
676
753
|
| `db` | `any` | Client padrão do repository | Client ou transação do banco em que essa query deve rodar. |
|
|
677
|
-
| `modifying` | `boolean` | `false` | Quando `true`,
|
|
754
|
+
| `modifying` | `boolean` | `false` | Quando `true`, retorna o número de linhas afetadas. |
|
|
678
755
|
| `singleResult` | `boolean` | `false` | Quando `true`, transforma um resultado em array no seu primeiro elemento (`null` se vazio). Não tem efeito em resultados que não são array (ex.: o número de linhas afetadas de uma query `modifying`). |
|
|
679
756
|
|
|
680
757
|
Assim como os métodos base, dinâmicos e query, `query()` aceita `db` em `options` para participar de um bloco `transaction()`.
|
|
@@ -731,6 +808,8 @@ Além dos tipos que descrevem o formato da entidade já vistos acima (`VSRepoSel
|
|
|
731
808
|
import type {
|
|
732
809
|
MethodOptions,
|
|
733
810
|
RestrictMethodOptions,
|
|
811
|
+
InferMethodReturn,
|
|
812
|
+
InferMethodType,
|
|
734
813
|
Pagination,
|
|
735
814
|
Ordering,
|
|
736
815
|
OrderByField,
|
|
@@ -754,12 +833,14 @@ import type {
|
|
|
754
833
|
|
|
755
834
|
| Tipo | Descrição | Usado por |
|
|
756
835
|
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
757
|
-
| `MethodOptions<T, K>` | Options aceitas como último argumento pela maioria dos métodos base
|
|
836
|
+
| `MethodOptions<T, K>` | Options aceitas como último argumento por todos os métodos dinâmicos e pela maioria dos métodos base: `select`, `relations`, `see`, `db`. | [Métodos base](#métodos-base), [Métodos Dinâmicos](#métodos-dinâmicos). |
|
|
758
837
|
| `RestrictMethodOptions<T, K>` | `MethodOptions<T, K>` restrito, expondo só `see`/`db` — usado pelos métodos que não retornam/moldam uma `Entity` (`total`, `has`, `sum`, `average`, `min`, `max`, `removeList`, `softRemoveList`, `restoreList`). | [Métodos base](#métodos-base), [Métodos atômicos e de agregação](#métodos-atômicos-e-de-agregação). |
|
|
838
|
+
| `InferMethodReturn<T, Options>` | Tipagem de retorno restrita (opt-in): estreita `T` (`Entity`, `Entity \| null` ou `Entity[]`) para os campos e relações realmente pedidos via `select`/`relations`. `select` vence `relations`. | [Tipagem de retorno restrita com `InferMethodReturn`](#tipagem-de-retorno-restrita-com-infermethodreturn-beta). |
|
|
839
|
+
| `InferMethodType<Args, Return, OrmTypes?>` | Declara um método dinâmico cujo retorno é inferido a cada chamada a partir do `select`/`relations` passados como `options`. `OrmTypes` é opcional e tipa a option `db`. | [Tipagem de retorno restrita com `InferMethodType`](#tipagem-de-retorno-restrita-com-infermethodtype-beta). |
|
|
759
840
|
| `Pagination` | `{ limit?, offset? }` aceito por `getAll` e pelos métodos dinâmicos com `Paginated`. | [Métodos base](#métodos-base), [Ordenação, paginação e distinct](#ordenação-paginação-e-distinct). |
|
|
760
|
-
| `Ordering<T>` / `OrderByField<T>` / `SortDirection` | Formato de ordenação aceito por `getAll`, `defaultOrdering
|
|
841
|
+
| `Ordering<T>` / `OrderByField<T>` / `SortDirection` | Formato de ordenação aceito por `getAll`, `defaultOrdering`, `injectOrdering` e pelos métodos dinâmicos com `Ordered`. Pode ser um único objeto ou um array encadeado. | [Options do construtor](#options-do-construtor), [Options do decorador](#options-do-decorador), [Ordenação, paginação e distinct](#ordenação-paginação-e-distinct). |
|
|
761
842
|
| `SeeMode` | `"active" \| "removed" \| "all"` — controla a visibilidade de registros com soft-delete. | [Soft-delete](#soft-delete). |
|
|
762
|
-
| `DeepPartial<T>` | Torna todas as propriedades de `T` opcionais recursivamente, incluindo objetos aninhados e elementos de array. | `save`, `saveList`, `patch`, `merge`, e todo
|
|
843
|
+
| `DeepPartial<T>` | Torna todas as propriedades de `T` opcionais recursivamente, incluindo objetos aninhados e elementos de array. | `save`, `saveList`, `patch`, `merge`, e todo os métodos dinâmicos de escrita. |
|
|
763
844
|
| `CountResult` | `{ count: number }` — o formato retornado por operações em lote. | `removeList`, `softRemoveList`, `restoreList`, `createManyIgnoreConflicts`. |
|
|
764
845
|
| `QueryMethodArg<T>` | `{ args?: T, db? }` — parâmetros posicionais do SQL (a sintaxe dos placeholders depende do banco/driver usado pelo seu adapter: `$1`, `$2`, ... para PostgreSQL, `?` para MySQL) e cliente de transação para o `@QueryMethod`. | [Query methods (SQL raw)](#query-methods-sql-raw). |
|
|
765
846
|
| `QueryArgs<T, O>` | Tipa a lista de parâmetros via spread de um `@QueryMethod` declarado com `{ spreadArgs: true }`: os valores de `T`, em ordem, seguidos de um `DbArg<O>` opcional construído via `withDb()`. | [Argumentos via spread com `spreadArgs`](#argumentos-via-spread-com-spreadargs). |
|
|
@@ -767,7 +848,7 @@ import type {
|
|
|
767
848
|
| `NumericKeys<T>` | Extrai as chaves de `T` cujo tipo de valor (ignorando `null`/`undefined`) é atribuível a `NumericLike`. Campos numéricos nullable (`number \| null`) são incluídos. | Restringe `field` em [Métodos atômicos e de agregação](#métodos-atômicos-e-de-agregação) (`increment`, `sum`, etc). |
|
|
768
849
|
| `NumericLike` | `number \| bigint \| DecimalLike`. | [Métodos atômicos e de agregação](#métodos-atômicos-e-de-agregação). |
|
|
769
850
|
| `DecimalLike` | Formato estrutural de um valor decimal de precisão arbitrária (`{ toNumber(): number; decimalPlaces(): number }`), compatível com o `Prisma.Decimal` do Prisma sem precisar importá-lo diretamente. | [Quais campos são elegíveis](#quais-campos-são-elegíveis). |
|
|
770
|
-
| `Primitive` | União de tipos escalares (`string \| number \| boolean \| bigint \| symbol \| undefined \| null \| Date`) tratados como valores-folha — e não relações — ao percorrer o formato de uma entidade.
|
|
851
|
+
| `Primitive` | União de tipos escalares (`string \| number \| boolean \| bigint \| symbol \| undefined \| null \| Date \| DecimalLike`) tratados como valores-folha — e não relações — ao percorrer o formato de uma entidade. | Usado por `Ordering<T>` para distinguir campos escalares de campos de relação. |
|
|
771
852
|
| `VSRepoWhere<T>` | Tipo de filtro agnóstico de ORM aceito pelos métodos dinâmicos `*Where` (ex.: `findWhere`, `findOneWhere`, `updateWhere`). Suporta filtros de campo, operadores lógicos (`AND`/`OR`/`NOT`) e filtros de relação. | [Prefixos `findWhere`, `findOneWhere` e demais `*Where`](#prefixos-disponíveis). |
|
|
772
853
|
| `VSRepoOrmTypes` | `{ dbClient; dbTransaction }` — descreve os tipos de client/transaction do seu ORM. Passado como terceiro generic de `VSRepository<Entity, PKType, OrmTypes>` para tipar `getDbClient()`, `transaction()` e a option `db` em vez de `any`. | [Criando um repository](#criando-um-repository). |
|
|
773
854
|
| `VSRepoTransactionOptions` | `{ isolationLevel?, timeoutMs? }` — options aceitas como segundo argumento de `transaction()`. | [Transações](#transações). |
|
|
@@ -875,7 +956,6 @@ export abstract class VSRepoAdapter<T> {
|
|
|
875
956
|
update: DeepPartial<T>,
|
|
876
957
|
options?: AdapterMethodOptions<T>,
|
|
877
958
|
): Promise<T>;
|
|
878
|
-
|
|
879
959
|
abstract incrementOne<K extends NumericKeys<T>>(
|
|
880
960
|
field: K,
|
|
881
961
|
value: NonNullable<T[K]>,
|
|
@@ -920,9 +1000,12 @@ export abstract class VSRepoAdapter<T> {
|
|
|
920
1000
|
where?: VSRepoWhere<T>,
|
|
921
1001
|
options?: AdapterMethodOptions<T>,
|
|
922
1002
|
): Promise<number | null>;
|
|
1003
|
+
getPkName?(): string;
|
|
923
1004
|
}
|
|
924
1005
|
```
|
|
925
1006
|
|
|
1007
|
+
O `getPkName()` opcional permite que o adapter declare ao repository qual campo é a primary key da entidade. Ao instanciar um `VSRepository`, você pode omitir o `pkName` das options do construtor e ele será lido do `adapter.getPkName()`. Se você omitir e o adapter não implementar o `getPkName()`, o construtor lança um `VSRepoError`.
|
|
1008
|
+
|
|
926
1009
|
O `VSRepository` nunca fala diretamente com o ORM — ele só chama esses métodos com um `VSRepoWhere<T>` e um `AdapterMethodOptions<T>` já resolvidos. Uma vez que um adapter implemente esse contrato, todo método base, método dinâmico e query method passa a funcionar com ele automaticamente. Pra uma implementação completa e funcional, veja o repositório externo [`VSRepoPrisma7Adapter`](https://github.com/jaobrabo123/VSRepoPrisma7Adapter).
|
|
927
1010
|
|
|
928
1011
|
### Logging a partir do seu adapter
|
|
@@ -950,13 +1033,13 @@ export class MyOrmAdapter<T> extends VSRepoAdapter<T> {
|
|
|
950
1033
|
}
|
|
951
1034
|
```
|
|
952
1035
|
|
|
953
|
-
| Método | Descrição
|
|
954
|
-
| ---------------------------------------------------- |
|
|
955
|
-
| `new VSLogger(logLevel, name, slowThresholdMs?)` | Cria um logger; `name` prefixa cada linha
|
|
956
|
-
| `logDebug/logInfo/logWarn(text, obj?)` | Loga no nível dado se `logLevel` permitir; `obj` é anexado como JSON formatado.
|
|
957
|
-
| `logError(text, err?)` | Loga em `ERROR`; se `err` for uma `Error`, só `name`/`message`/`stack`/`cause` são logados.
|
|
958
|
-
| `startPerformLog(operation)` / `endPerformLog(data)` | Envolve um trecho de código para logar sua duração, escalando pra `WARN` se ultrapassar `slowThresholdMs`.
|
|
959
|
-
| `getLogLevel()` | Retorna o `VSLogLevel` configurado do logger.
|
|
1036
|
+
| Método | Descrição |
|
|
1037
|
+
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1038
|
+
| `new VSLogger(logLevel, name, slowThresholdMs?)` | Cria um logger; `name` prefixa cada linha. `slowThresholdMs` controla o threshold de operação lenta: um `number` define o valor em ms (padrão 300), `false` desabilita os avisos de operação lenta completamente, `true` ou omitido usa o padrão de 300ms. |
|
|
1039
|
+
| `logDebug/logInfo/logWarn(text, obj?)` | Loga no nível dado se `logLevel` permitir; `obj` é anexado como JSON formatado. |
|
|
1040
|
+
| `logError(text, err?)` | Loga em `ERROR`; se `err` for uma `Error`, só `name`/`message`/`stack`/`cause` são logados. |
|
|
1041
|
+
| `startPerformLog(operation)` / `endPerformLog(data)` | Envolve um trecho de código para logar sua duração, escalando pra `WARN` se ultrapassar `slowThresholdMs`. |
|
|
1042
|
+
| `getLogLevel()` | Retorna o `VSLogLevel` configurado do logger. |
|
|
960
1043
|
|
|
961
1044
|
Isso é puramente uma conveniência para autores de adapters — nada no core exige que seu adapter o utilize.
|
|
962
1045
|
|
|
@@ -978,14 +1061,14 @@ try {
|
|
|
978
1061
|
}
|
|
979
1062
|
```
|
|
980
1063
|
|
|
981
|
-
| `VSRepoErrorType` | Quando é lançado
|
|
982
|
-
| ----------------- |
|
|
983
|
-
| `DECORATOR` | Argumentos inválidos foram passados para `@DynamicMethod` ou `@QueryMethod`.
|
|
984
|
-
| `RESOLVER` | A biblioteca falhou ao resolver a configuração de um método dinâmico/de query em um método chamável (ex.: um nome de método desconhecido).
|
|
985
|
-
| `DYNAMIC` | Um dynamic/query method já resolvido falhou em tempo de execução (ex.: argumentos faltando).
|
|
986
|
-
| `VALIDATOR` | Options ou argumentos de método inválidos foram detectados durante a validação.
|
|
987
|
-
| `BASE` | Uso inválido de um método base (`get`, `save`, `remove`, etc).
|
|
988
|
-
| `ADAPTER` | Um `VSRepoAdapter` falhou ao falar com o ORM/banco subjacente — sempre é lançado como `VSRepoAdapterError`.
|
|
1064
|
+
| `VSRepoErrorType` | Quando é lançado |
|
|
1065
|
+
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1066
|
+
| `DECORATOR` | Argumentos inválidos foram passados para `@DynamicMethod` ou `@QueryMethod`. |
|
|
1067
|
+
| `RESOLVER` | A biblioteca falhou ao resolver a configuração de um método dinâmico/de query em um método chamável (ex.: um nome de método desconhecido). |
|
|
1068
|
+
| `DYNAMIC` | Um dynamic/query method já resolvido falhou em tempo de execução (ex.: argumentos faltando). |
|
|
1069
|
+
| `VALIDATOR` | Options ou argumentos de método inválidos foram detectados durante a validação (ex.: `pkName` ausente quando o adapter não tem `getPkName()`). |
|
|
1070
|
+
| `BASE` | Uso inválido de um método base (`get`, `save`, `remove`, etc). |
|
|
1071
|
+
| `ADAPTER` | Um `VSRepoAdapter` falhou ao falar com o ORM/banco subjacente — sempre é lançado como `VSRepoAdapterError`. |
|
|
989
1072
|
|
|
990
1073
|
### `VSRepoAdapterError` e `AdapterErrorCode`
|
|
991
1074
|
|
|
@@ -1091,7 +1174,8 @@ super({
|
|
|
1091
1174
|
pkName: "id",
|
|
1092
1175
|
adapter,
|
|
1093
1176
|
logLevel: VSLogLevel.DEBUG,
|
|
1094
|
-
logSlowThresholdMs: 200,
|
|
1177
|
+
logSlowThresholdMs: 200, // avisa se qualquer operação levar mais de 200ms
|
|
1178
|
+
// logSlowThresholdMs: false, // desabilita os avisos de operação lenta completamente
|
|
1095
1179
|
});
|
|
1096
1180
|
```
|
|
1097
1181
|
|
|
@@ -1122,14 +1206,13 @@ npm pack --dry-run
|
|
|
1122
1206
|
npm pack
|
|
1123
1207
|
|
|
1124
1208
|
# 5. Consumir localmente em outro projeto
|
|
1125
|
-
npm install ../caminho/vsrepo
|
|
1209
|
+
npm install ../caminho/vsrepo-*.tgz
|
|
1126
1210
|
```
|
|
1127
1211
|
|
|
1128
1212
|
Observações:
|
|
1129
1213
|
|
|
1130
1214
|
- `pnpm build` executa `tsc -p tsconfig.build.json`, que gera o JS compilado e as declarações de tipo em `dist/` com `rootDir: src`.
|
|
1131
1215
|
- O pacote publicado contém **apenas** a pasta `dist/` além dos READMEs e da `LICENSE` (veja `files` no `package.json`). Os adapters viverão em seus próprios pacotes `@vsrepo/*-adapter`.
|
|
1132
|
-
- O core é ORM-agnóstico e não tem dependência peer de `@prisma/client`.
|
|
1133
1216
|
|
|
1134
1217
|
---
|
|
1135
1218
|
|
|
@@ -1153,11 +1236,11 @@ Observações:
|
|
|
1153
1236
|
|
|
1154
1237
|
## Contribuindo
|
|
1155
1238
|
|
|
1156
|
-
Contribuições são bem-vindas, especialmente para
|
|
1239
|
+
Contribuições são bem-vindas, especialmente para melhorar o adapter do Prisma e finalizar o do Drizzle! (**[Repositório do GitHub](https://github.com/jaobrabo123/VSRepository)**):
|
|
1157
1240
|
|
|
1158
1241
|
1. Faça um **Fork** do projeto.
|
|
1159
|
-
2. Crie uma branch
|
|
1160
|
-
3. Faça o push da sua branch: `git push origin
|
|
1161
|
-
4. Abra um **Pull Request
|
|
1242
|
+
2. Crie uma branch para sua alteração: `git checkout -b minha-alteracao`.
|
|
1243
|
+
3. Faça o push da sua branch: `git push origin minha-alteracao`.
|
|
1244
|
+
4. Abra um **Pull Request**.
|
|
1162
1245
|
|
|
1163
1246
|
Para reportar problemas ou sugerir funcionalidades, abra uma **Issue**.
|