vsrepo 1.0.3 → 1.0.4

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
@@ -1,22 +1,95 @@
1
1
  # VSRepo
2
2
 
3
- Biblioteca de repository para projetos que usam Prisma. Ela permite criar repositories tipados com métodos base (`get`, `save` e `remove`) e métodos dinâmicos mapeados pelo nome, como `findByEmail`, `findManyPaginated`, `updateById` e `deleteManyByIdIn`.
3
+ Biblioteca de repository para projetos que usam Prisma.
4
+ O VSRepo permite criar repositories tipados com métodos base (`get`, `save` e `remove`) e métodos dinâmicos inferidos pelo nome, como:
4
5
 
5
- ## Instalação
6
+ - `findByEmail`
7
+ - `findManyPaginated`
8
+ - `updateById`
9
+ - `deleteManyByIdIn`
10
+
11
+ Tudo com inferência automática baseada no Prisma Client do próprio projeto.
12
+
13
+ ---
14
+
15
+ # Instalação
16
+
17
+ Instale o VSRepo e o Prisma Client:
6
18
 
7
19
  ```bash
8
20
  pnpm add vsrepo @prisma/client
9
21
  ```
10
22
 
11
- O Prisma Client deve estar configurado no projeto:
23
+ Gere o Prisma Client:
12
24
 
13
25
  ```bash
14
26
  pnpm prisma generate
15
27
  ```
16
28
 
17
- ## Uso básico
29
+ ---
30
+
31
+ # Gerando os tipos do VSRepo
32
+
33
+ O VSRepo precisa conhecer o caminho real do seu Prisma Client para gerar as tipagens corretamente.
34
+
35
+ Execute:
36
+
37
+ ```bash
38
+ pnpm vsrepo generate --output src/generated/vsrepo --prisma src/generated/prisma/client
39
+ ```
40
+
41
+ ou:
42
+
43
+ ```bash
44
+ pnpm vsrepo generate -o src/generated/vsrepo -p src/generated/prisma/client
45
+ ```
46
+
47
+ ## O que esse comando faz
48
+
49
+ O script:
50
+
51
+ 1. Lê os arquivos de tipagem internos do VSRepo
52
+ 2. Substitui o import do Prisma pelo caminho informado em `--prisma`
53
+ 3. Gera uma façade tipada dentro da pasta definida em `--output`
54
+
55
+ Arquivos gerados:
56
+
57
+ ```txt
58
+ src/generated/vsrepo/
59
+ ├── VSRepoError.ts
60
+ ├── VSRepoError.types.d.ts
61
+ ├── VSRepository.ts
62
+ ├── VSRepository.types.d.ts
63
+ └── index.ts
64
+ ```
65
+
66
+ ---
67
+
68
+ # Como importar depois
69
+
70
+ Após gerar os arquivos:
71
+
72
+ ```ts
73
+ import { setupVSRepo } from "../generated/vsrepo";
74
+ ```
75
+
76
+ ou:
77
+
78
+ ```ts
79
+ import {
80
+ setupVSRepo,
81
+ type SelectModels,
82
+ type WhereModel,
83
+ } from "../generated/vsrepo";
84
+ ```
85
+
86
+ Você NÃO deve mais importar diretamente de `vsrepo`.
18
87
 
19
- Crie uma instância do Prisma Client:
88
+ ---
89
+
90
+ # Uso básico
91
+
92
+ ## Criando o Prisma Client
20
93
 
21
94
  ```ts
22
95
  // src/db.ts
@@ -27,12 +100,21 @@ const prisma = new PrismaClient();
27
100
  export default prisma;
28
101
  ```
29
102
 
30
- Depois crie o repository do modelo:
103
+ ---
104
+
105
+ # Criando um repository
31
106
 
32
107
  ```ts
33
108
  // src/repositories/usuarioRepository.ts
109
+
34
110
  import prisma from "../db";
35
- import { setupVSRepo, type SelectModels, type WhereModel } from "vsrepo";
111
+
112
+ import {
113
+ setupVSRepo,
114
+ type SelectModels,
115
+ type WhereModel,
116
+ } from "../generated/vsrepo";
117
+
36
118
  import type { Prisma } from "@prisma/client";
37
119
 
38
120
  type Usuario = Prisma.usuarioGetPayload<{
@@ -48,6 +130,7 @@ const usuarioSelectModels = {
48
130
  nome: true,
49
131
  email: true,
50
132
  },
133
+
51
134
  minimal: {
52
135
  id: true,
53
136
  },
@@ -60,15 +143,34 @@ const usuarioRequiredWhere = {
60
143
  const usuarioVSRepo = setupVSRepo<Usuario, "usuario">()({
61
144
  tableName: "usuario",
62
145
  pkName: "id",
146
+
63
147
  selectModels: usuarioSelectModels,
64
148
  defaultSelectModel: "public",
149
+
65
150
  requiredWhere: usuarioRequiredWhere,
151
+
66
152
  methods: {
67
- findByEmail: { map: true, fbMode: "one" },
68
- findManyPaginated: { map: true },
69
- updateById: { map: true },
70
- deleteManyByIdIn: { map: true, whereType: "overwrite" },
71
- count: { map: true },
153
+ findByEmail: {
154
+ map: true,
155
+ fbMode: "one",
156
+ },
157
+
158
+ findManyPaginated: {
159
+ map: true,
160
+ },
161
+
162
+ updateById: {
163
+ map: true,
164
+ },
165
+
166
+ deleteManyByIdIn: {
167
+ map: true,
168
+ whereType: "overwrite",
169
+ },
170
+
171
+ count: {
172
+ map: true,
173
+ },
72
174
  },
73
175
  });
74
176
 
@@ -77,7 +179,9 @@ const usuarioRepository = usuarioVSRepo.build(prisma);
77
179
  export default usuarioRepository;
78
180
  ```
79
181
 
80
- Use o repository na aplicação:
182
+ ---
183
+
184
+ # Usando o repository
81
185
 
82
186
  ```ts
83
187
  import usuarioRepository from "./repositories/usuarioRepository";
@@ -99,30 +203,44 @@ await usuarioRepository.updateById(usuario.id, {
99
203
  await usuarioRepository.remove(usuario.id);
100
204
  ```
101
205
 
102
- ## Métodos base
206
+ ---
103
207
 
104
- Ao chamar `build(prisma)`, o VSRepo cria três métodos por padrão:
208
+ # Métodos base
209
+
210
+ Ao executar:
211
+
212
+ ```ts
213
+ .build(prisma)
214
+ ```
105
215
 
106
- | Método | Descrição |
107
- | --- | --- |
108
- | `get(pk, options?)` | Busca um registro pela primary key definida em `pkName`. |
109
- | `save(obj, options?)` | Cria ou atualiza um registro. Se `obj` tiver a primary key, usa `upsert`; se não tiver, usa `create`. |
110
- | `remove(pk, options?)` | Remove um registro pela primary key. |
216
+ o VSRepo cria automaticamente:
111
217
 
112
- Você pode configurar esses métodos no `build`:
218
+ | Método | Descrição |
219
+ | ------------ | ----------------------- |
220
+ | `get(pk)` | Busca pela primary key |
221
+ | `save(obj)` | Cria ou atualiza |
222
+ | `remove(pk)` | Remove pela primary key |
223
+
224
+ ---
225
+
226
+ # Configurando métodos base
113
227
 
114
228
  ```ts
115
229
  const usuarioRepository = usuarioVSRepo.build(prisma, {
116
230
  freeze: true,
231
+
117
232
  showWorking: false,
233
+
118
234
  baseMethods: {
119
235
  get: {
120
236
  active: true,
121
237
  defaultSelect: "public",
122
238
  },
239
+
123
240
  remove: {
124
241
  defaultSelect: "minimal",
125
242
  },
243
+
126
244
  save: {
127
245
  active: true,
128
246
  },
@@ -130,43 +248,82 @@ const usuarioRepository = usuarioVSRepo.build(prisma, {
130
248
  });
131
249
  ```
132
250
 
133
- ## Select models
251
+ ---
252
+
253
+ # Select models
134
254
 
135
- `selectModels` define selects reutilizáveis do Prisma. O `defaultSelectModel` é aplicado quando o método não recebe outro select.
255
+ `selectModels` cria selects reutilizáveis.
136
256
 
137
257
  ```ts
138
258
  const usuario = await usuarioRepository.get(id, {
139
259
  selectModel: "minimal",
140
260
  });
261
+ ```
262
+
263
+ Para retornar o payload padrão do Prisma:
141
264
 
265
+ ```ts
142
266
  const usuarioCompleto = await usuarioRepository.get(id, {
143
267
  selectModel: false,
144
268
  });
145
269
  ```
146
270
 
147
- Use `selectModel: false` para não aplicar nenhum select model e deixar o Prisma retornar o payload padrão.
271
+ ---
272
+
273
+ # Métodos dinâmicos
148
274
 
149
- ## Métodos dinâmicos
275
+ Métodos dinâmicos são definidos em:
150
276
 
151
- Métodos dinâmicos são declarados em `methods` com `map: true`. O nome do método informa qual operação do Prisma será usada e quais campos entram no `where`.
277
+ ```ts
278
+ methods: {
279
+ }
280
+ ```
152
281
 
153
- Exemplos:
282
+ Exemplo:
154
283
 
155
284
  ```ts
156
285
  methods: {
157
286
  findUniqueByIdAndEmail: { map: true },
158
- findByNomeContainsInsensitive: { map: true },
159
- findManyPaginated: { map: true },
160
- findListWhereOrderedAndPaginated: { map: true, whereType: "overwrite" },
161
- createManyAndReturn: { map: true },
162
- updateById: { map: true },
163
- deleteManyByIdIn: { map: true, whereType: "overwrite" },
164
- existsByEmail: { map: true },
165
- countByPerfil: { map: true },
287
+
288
+ findByNomeContainsInsensitive: {
289
+ map: true,
290
+ },
291
+
292
+ findManyPaginated: {
293
+ map: true,
294
+ },
295
+
296
+ findListWhereOrderedAndPaginated: {
297
+ map: true,
298
+ whereType: 'overwrite',
299
+ },
300
+
301
+ createManyAndReturn: {
302
+ map: true,
303
+ },
304
+
305
+ updateById: {
306
+ map: true,
307
+ },
308
+
309
+ deleteManyByIdIn: {
310
+ map: true,
311
+ whereType: 'overwrite',
312
+ },
313
+
314
+ existsByEmail: {
315
+ map: true,
316
+ },
317
+
318
+ countByPerfil: {
319
+ map: true,
320
+ },
166
321
  }
167
322
  ```
168
323
 
169
- Uso:
324
+ ---
325
+
326
+ # Exemplos de uso
170
327
 
171
328
  ```ts
172
329
  const usuario = await usuarioRepository.findUniqueByIdAndEmail(id, email);
@@ -179,43 +336,58 @@ const pagina = await usuarioRepository.findManyPaginated({
179
336
  });
180
337
 
181
338
  const ordenado = await usuarioRepository.findListWhereOrderedAndPaginated(
182
- { nome: { startsWith: "A" } },
183
- { dataCriacao: "desc" },
184
- { take: 10 }
339
+ {
340
+ nome: {
341
+ startsWith: "A",
342
+ },
343
+ },
344
+
345
+ {
346
+ dataCriacao: "desc",
347
+ },
348
+
349
+ {
350
+ take: 10,
351
+ },
185
352
  );
186
353
  ```
187
354
 
188
- ## Filtros pelo nome do método
355
+ ---
189
356
 
190
- O VSRepo interpreta sufixos no nome dos métodos para montar o `where`:
357
+ # Filtros suportados
191
358
 
192
- | Padrão | Exemplo |
193
- | --- | --- |
194
- | `And` / `Or` | `findByIdAndEmail`, `findByNomeOrEmail` |
195
- | `In` / `NotIn` | `deleteManyByIdIn` |
196
- | `Contains` / `StartsWith` / `EndsWith` | `findByNomeContains` |
197
- | `Insensitive` | `findByNomeContainsInsensitive` |
198
- | `GreaterThan` / `GreaterThanEqual` | `findByIdadeGreaterThan` |
199
- | `LessThan` / `LessThanEqual` | `findByIdadeLessThanEqual` |
200
- | `IsNull` / `IsNotNull` | `findByPerfilIsNotNull` |
201
- | `IsTrue` / `IsFalse` | `findByAtivoIsTrue` |
202
- | `Some` / `Every` / `None` | `findByPostagensSomeTituloContains` |
203
- | `With` / `Without` | `findByPerfilWith`, `findByPerfilWithout` |
359
+ | Padrão | Exemplo |
360
+ | ------------------ | ----------------------------------- |
361
+ | `And` / `Or` | `findByIdAndEmail` |
362
+ | `In` / `NotIn` | `deleteManyByIdIn` |
363
+ | `Contains` | `findByNomeContains` |
364
+ | `StartsWith` | `findByNomeStartsWith` |
365
+ | `EndsWith` | `findByEmailEndsWith` |
366
+ | `Insensitive` | `findByNomeContainsInsensitive` |
367
+ | `GreaterThan` | `findByIdadeGreaterThan` |
368
+ | `LessThanEqual` | `findByIdadeLessThanEqual` |
369
+ | `IsNull` | `findByPerfilIsNull` |
370
+ | `IsTrue` | `findByAtivoIsTrue` |
371
+ | `Some` | `findByPostagensSomeTituloContains` |
372
+ | `With` / `Without` | `findByPerfilWith` |
204
373
 
205
- ## Relações no `save`
374
+ ---
206
375
 
207
- Configure `relations` para que `save` consiga criar, conectar, atualizar ou remover relações junto com o registro principal.
376
+ # Relações no save
208
377
 
209
378
  ```ts
210
379
  const usuarioVSRepo = setupVSRepo<Usuario, "usuario">()({
211
380
  tableName: "usuario",
381
+
212
382
  pkName: "id",
383
+
213
384
  relations: {
214
385
  perfil: {
215
386
  pk: "id",
216
387
  mode: "oto",
217
388
  restriction: "set",
218
389
  },
390
+
219
391
  postagens: {
220
392
  pk: "id",
221
393
  mode: "otm",
@@ -225,20 +397,26 @@ const usuarioVSRepo = setupVSRepo<Usuario, "usuario">()({
225
397
  });
226
398
  ```
227
399
 
228
- Modos disponíveis:
400
+ ## Modos disponíveis
229
401
 
230
- | Modo | Relação |
231
- | --- | --- |
232
- | `oto` | one-to-one |
233
- | `otm` | one-to-many |
234
- | `mto` | many-to-one |
402
+ | Modo | Relação |
403
+ | ----- | ------------ |
404
+ | `oto` | one-to-one |
405
+ | `otm` | one-to-many |
406
+ | `mto` | many-to-one |
235
407
  | `mtm` | many-to-many |
236
408
 
237
- `restriction: "set"` substitui o conjunto atual quando aplicável. `restriction: "add"` adiciona/conecta sem limpar os registros já relacionados.
409
+ ---
410
+
411
+ # Transações
238
412
 
239
- ## Transações
413
+ Todos os métodos aceitam:
240
414
 
241
- Todos os métodos aceitam `options.db`. Use isso para executar operações dentro de uma transação do Prisma:
415
+ ```ts
416
+ options.db;
417
+ ```
418
+
419
+ Exemplo:
242
420
 
243
421
  ```ts
244
422
  await prisma.$transaction(async (tx) => {
@@ -248,24 +426,34 @@ await prisma.$transaction(async (tx) => {
248
426
  email: "maria@email.com",
249
427
  senha: "password",
250
428
  },
251
- { db: tx }
429
+
430
+ {
431
+ db: tx,
432
+ },
252
433
  );
253
434
 
254
435
  await usuarioRepository.updateById(
255
436
  usuario.id,
256
- { ativo: true },
257
- { db: tx }
437
+
438
+ {
439
+ ativo: true,
440
+ },
441
+
442
+ {
443
+ db: tx,
444
+ },
258
445
  );
259
446
  });
260
447
  ```
261
448
 
262
- ## Estendendo um repository
449
+ ---
263
450
 
264
- Use `extend` para adicionar métodos manuais mantendo os métodos gerados:
451
+ # Extendendo um repository
265
452
 
266
453
  ```ts
267
454
  const repository = usuarioVSRepo
268
455
  .build(prisma)
456
+
269
457
  .extend((repo) => ({
270
458
  async buscarAtivosPorDominio(dominio: string) {
271
459
  return repo.findByEmailEndsWith(`@${dominio}`);
@@ -273,9 +461,48 @@ const repository = usuarioVSRepo
273
461
  }));
274
462
  ```
275
463
 
276
- ## Scripts do projeto
464
+ ---
465
+
466
+ # Estrutura recomendada
467
+
468
+ ```txt
469
+ src/
470
+ ├── db.ts
471
+ ├── generated/
472
+ │ ├── prisma/
473
+ │ └── vsrepo/
474
+ ├── repositories/
475
+ └── services/
476
+ ```
477
+
478
+ ---
479
+
480
+ # Scripts úteis
277
481
 
278
482
  ```bash
279
- pnpm test:usuario
280
- pnpm test:base-methods:user
483
+ pnpm prisma generate
484
+
485
+ pnpm vsrepo generate \
486
+ --output src/generated/vsrepo \
487
+ --prisma src/generated/prisma/client
488
+ ```
489
+
490
+ ---
491
+
492
+ # Requisitos
493
+
494
+ - TypeScript
495
+ - Prisma
496
+ - Node.js ESM
497
+ - `"moduleResolution": "bundler"` ou `"nodenext"`
498
+
499
+ Exemplo:
500
+
501
+ ```json
502
+ {
503
+ "compilerOptions": {
504
+ "module": "NodeNext",
505
+ "moduleResolution": "NodeNext"
506
+ }
507
+ }
281
508
  ```
@@ -4,7 +4,7 @@ export type DbClient = PrismaClient;
4
4
  export type DbTransaction = Prisma.TransactionClient;
5
5
  export type ClientOrTransaction = DbClient | DbTransaction;
6
6
 
7
- export * from './VSRepoError';
7
+ export * from './VSRepoError.js';
8
8
 
9
9
  type OrderPattern = {
10
10
  [key: string]: 'asc' | 'desc' | { _count: 'asc' | 'desc' } | OrderPattern;
@@ -241,10 +241,16 @@ export type BuildConfig<TSelectKeys extends PropertyKey = string> = {
241
241
  };
242
242
  };
243
243
 
244
- type ResolveMethodDefaultSelect<Config, C, Method extends 'get' | 'remove' | 'save'> =
245
- C extends { baseMethods: Record<Method, { defaultSelect: infer D }> }
246
- ? D extends keyof ExtractSelectModels<Config> ? D : ExtractDefaultSelect<Config>
247
- : ExtractDefaultSelect<Config>;
244
+ type ResolveMethodDefaultSelect<Config, C, Method extends 'get' | 'remove' | 'save', TSelects = ExtractSelectModels<Config>> =
245
+ C extends { baseMethods?: { [_ in Method]?: { defaultSelect?: infer D } } }
246
+ ? D extends keyof TSelects
247
+ ? D
248
+ : ExtractDefaultSelect<Config> extends keyof TSelects
249
+ ? ExtractDefaultSelect<Config>
250
+ : false
251
+ : ExtractDefaultSelect<Config> extends keyof TSelects
252
+ ? ExtractDefaultSelect<Config>
253
+ : false;
248
254
 
249
255
  type ResolveCurrentReturn<M extends Prisma.ModelName, Models, S, D> =
250
256
  [S] extends [false]
@@ -261,7 +267,7 @@ type InjectedGet<
261
267
  TDefault = ExtractDefaultSelect<Config>,
262
268
  TPk = ExtractPkName<T, Config>
263
269
  > = C extends { baseMethods: { get: { active: false } } } ? {} : {
264
- get<S extends keyof TSelects | false = ResolveMethodDefaultSelect<Config, C, 'get'>>(
270
+ get<S extends keyof TSelects | false = ResolveMethodDefaultSelect<Config, C, 'get', TSelects>>(
265
271
  pk: T[TPk extends keyof T ? TPk : never], options?: MethodOptions<S>
266
272
  ): Promise<ResolveCurrentReturn<M, TSelects, S, TDefault> | null>;
267
273
  };
@@ -272,7 +278,7 @@ type InjectedRemove<
272
278
  TDefault = ExtractDefaultSelect<Config>,
273
279
  TPk = ExtractPkName<T, Config>
274
280
  > = C extends { baseMethods: { remove: { active: false } } } ? {} : {
275
- remove<S extends keyof TSelects | false = ResolveMethodDefaultSelect<Config, C, 'remove'>>(
281
+ remove<S extends keyof TSelects | false = ResolveMethodDefaultSelect<Config, C, 'remove', TSelects>>(
276
282
  pk: T[TPk extends keyof T ? TPk : never], options?: MethodOptions<S>
277
283
  ): Promise<ResolveCurrentReturn<M, TSelects, S, TDefault>>;
278
284
  };
@@ -304,14 +310,14 @@ type InjectedSave<
304
310
  > = C extends { baseMethods: { save: { active: false } } } ? {} : {
305
311
  save<
306
312
  O extends UpsertWithRelations<T, M, TRelations>,
307
- S extends keyof TSelects | false = ResolveMethodDefaultSelect<Config, C, 'save'>
313
+ S extends keyof TSelects | false = ResolveMethodDefaultSelect<Config, C, 'save', TSelects>
308
314
  >(
309
315
  obj: O & UpsertWithRelations<T, M, TRelations>,
310
316
  options?: MethodOptions<S>
311
317
  ): Promise<RefineSaveResult<ResolveCurrentReturn<M, TSelects, S, TDefault>, O>>;
312
318
  };
313
319
 
314
- type InjectedBaseMethods<T, M extends Prisma.ModelName, Config, C extends BuildConfig | undefined> =
320
+ type InjectedBaseMethods<T, M extends Prisma.ModelName, Config, C extends BuildConfig<any> | undefined> =
315
321
  InjectedGet<T, M, Config, C> &
316
322
  InjectedRemove<T, M, Config, C> &
317
323
  InjectedSave<T, M, Config, C>;
@@ -333,24 +339,24 @@ export type RepoConfig<T, M extends Prisma.ModelName, SM extends Record<string,
333
339
  tableName: Uncapitalize<M>;
334
340
  pkName: keyof T;
335
341
  selectModels?: SM;
336
- defaultSelectModel?: keyof SM;
342
+ defaultSelectModel?: Extract<keyof SM, string>;
337
343
  requiredWhere?: WhereModel<M>;
338
344
  relations?: RepositoryRelations<T>;
339
345
  methods?: Record<string, MethodConfig<M, SM>>;
340
346
  };
341
347
 
342
- export type BuiltRepository<T extends object, M extends Prisma.ModelName, Config extends RepoConfig<T, M>, C extends BuildConfig<keyof ExtractSelectModels<Config>> | undefined> = {
348
+ export type BuiltRepository<T extends object, M extends Prisma.ModelName, Config extends RepoConfig<T, M, any>, C extends BuildConfig<any> | undefined> = {
343
349
  extend<E>(extensionFunc: (repo: BuiltRepository<T, M, Config, C>) => E): BuiltRepository<T, M, Config, C> & E;
344
350
  } & DynamicMethods<T, M, Config, PrismaModelInputs<M>> & InjectedBaseMethods<T, M, Config, C>;
345
351
 
346
- export declare class VSRepository<T extends object, M extends Prisma.ModelName, const Config extends RepoConfig<T, M> = RepoConfig<T, M>> {
352
+ export declare class VSRepository<T extends object, M extends Prisma.ModelName, const Config extends RepoConfig<T, M, any> = RepoConfig<T, M, any>> {
347
353
  readonly config: Config;
348
354
  constructor(config: Config);
349
- build<C extends BuildConfig<keyof ExtractSelectModels<Config>>>(prisma: DbClient, config?: C): BuiltRepository<T, M, Config, C>;
355
+ build<C extends BuildConfig<any>>(prisma: DbClient, config?: C): BuiltRepository<T, M, Config, C>;
350
356
  vsrepocache: never;
351
357
  }
352
358
 
353
- export type RepositoryOf<TRepo, C = undefined, E = unknown> =
359
+ export type RepositoryOf<TRepo, C extends BuildConfig<any> | undefined = undefined, E = unknown> =
354
360
  TRepo extends VSRepository<infer T, infer M, infer Config>
355
361
  ? BuiltRepository<T, M, Config, C> & E
356
362
  : never;
@@ -359,7 +365,7 @@ export type ValidateRepoConfig<T extends object, M extends Prisma.ModelName, Con
359
365
  tableName: Uncapitalize<M>;
360
366
  pkName: keyof T;
361
367
  selectModels?: SelectModels<M>;
362
- defaultSelectModel?: Config extends { selectModels: infer SM } ? (keyof SM extends never ? string : keyof SM) : string;
368
+ defaultSelectModel?: Config extends { selectModels: infer SM } ? (keyof SM extends never ? string : Extract<keyof SM, string>) : string;
363
369
  requiredWhere?: WhereModel<M>;
364
370
  relations?: RepositoryRelations<T>;
365
371
  methods?: {
@@ -1,2 +1,2 @@
1
- export * from './VSRepoError';
2
- export * from './VSRepository';
1
+ export * from './VSRepoError.js';
2
+ export * from './VSRepository.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vsrepo",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Uma biblioteca de repositório virtual para Prisma",
5
5
  "main": "./VSRepository/index.js",
6
6
  "types": "./VSRepository/index.d.ts",
@@ -8,12 +8,29 @@
8
8
  ".": {
9
9
  "types": "./VSRepository/index.d.ts",
10
10
  "default": "./VSRepository/index.js"
11
+ },
12
+ "./VSRepository": {
13
+ "types": "./VSRepository/VSRepository.d.ts",
14
+ "import": "./VSRepository/VSRepository.js"
15
+ },
16
+ "./VSRepoError": {
17
+ "types": "./VSRepository/VSRepoError.d.ts",
18
+ "import": "./VSRepository/VSRepoError.js"
11
19
  }
12
20
  },
13
21
  "type": "module",
14
22
  "files": [
15
- "VSRepository"
23
+ "VSRepository",
24
+ "scripts"
16
25
  ],
26
+ "bin": {
27
+ "vsrepo": "./scripts/configure-prisma-import.mjs",
28
+ "vsrepo-configure-prisma-import": "./scripts/configure-prisma-import.mjs"
29
+ },
30
+ "scripts": {
31
+ "vsrepo": "node ./scripts/configure-prisma-import.mjs",
32
+ "configure:prisma-import": "node ./scripts/configure-prisma-import.mjs"
33
+ },
17
34
  "keywords": [
18
35
  "prisma",
19
36
  "repository",
@@ -0,0 +1,155 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const workspaceRoot = process.cwd();
6
+ const args = process.argv.slice(2);
7
+ const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
8
+
9
+ const command = args[0]?.startsWith('-') ? 'generate' : args[0] ?? 'generate';
10
+ const commandArgs = command === 'generate' && args[0] !== command ? args : args.slice(1);
11
+
12
+ if (command !== 'generate') {
13
+ console.error(`Comando desconhecido: ${command}`);
14
+ console.error('Uso: vsrepo generate --output <dir-output> --prisma <path-prisma-client>');
15
+ process.exit(1);
16
+ }
17
+
18
+ const outputFlagIndex = commandArgs.findIndex((arg) => arg === '--output' || arg === '-o');
19
+ const prismaFlagIndex = commandArgs.findIndex((arg) => arg === '--prisma' || arg === '-p');
20
+
21
+ const outputArg = outputFlagIndex >= 0 ? commandArgs[outputFlagIndex + 1] : commandArgs[0];
22
+ const prismaArg = prismaFlagIndex >= 0 ? commandArgs[prismaFlagIndex + 1] : null;
23
+
24
+ if (!outputArg || outputArg.startsWith('-')) {
25
+ console.error('Uso: vsrepo generate --output <dir-output> --prisma <path-prisma-client>');
26
+ process.exit(1);
27
+ }
28
+
29
+ if (!prismaArg || prismaArg.startsWith('-')) {
30
+ console.error('Uso: vsrepo generate --output <dir-output> --prisma <path-prisma-client>');
31
+ process.exit(1);
32
+ }
33
+
34
+ const outputDir = path.resolve(workspaceRoot, outputArg);
35
+ const prismaTargetPath = stripKnownExtension(path.resolve(workspaceRoot, prismaArg));
36
+
37
+ const installedNodeModulesPath = path.join(workspaceRoot, 'node_modules/vsrepo');
38
+ const installedPackageExists = fs.existsSync(installedNodeModulesPath);
39
+ const packageRoot = installedPackageExists
40
+ ? installedNodeModulesPath
41
+ : path.resolve(scriptDirectory, '..');
42
+
43
+ const sourceDir = path.join(packageRoot, 'VSRepository');
44
+
45
+ if (!fs.existsSync(sourceDir)) {
46
+ console.error(`Nao foi possivel localizar a pasta VSRepository em: ${sourceDir}`);
47
+ process.exit(1);
48
+ }
49
+
50
+ fs.mkdirSync(outputDir, { recursive: true });
51
+
52
+ const typeFiles = [
53
+ 'VSRepoError.d.ts',
54
+ 'VSRepository.d.ts',
55
+ ];
56
+
57
+ for (const fileName of typeFiles) {
58
+ const sourceFile = path.join(sourceDir, fileName);
59
+
60
+ if (!fs.existsSync(sourceFile)) {
61
+ console.warn(`Arquivo ignorado (nao encontrado): ${fileName}`);
62
+ continue;
63
+ }
64
+
65
+ const targetFile = path.join(outputDir, fileName.replace('.d.ts', '.types.d.ts'));
66
+ let contents = fs.readFileSync(sourceFile, 'utf8');
67
+
68
+ contents = contents.replace(
69
+ /from\s+(['"])@vsrepo\/prisma\/types\1/g,
70
+ `from '${relativeImportPath(path.dirname(targetFile), prismaTargetPath)}'`
71
+ );
72
+
73
+ fs.writeFileSync(targetFile, contents, 'utf8');
74
+ console.log(`Gerado: ${path.relative(workspaceRoot, targetFile)}`);
75
+ }
76
+
77
+ const tsFiles = [
78
+ {
79
+ fileName: 'VSRepoError.ts',
80
+ content: `import {
81
+ VSRepoError as VSRepoErrorRuntime,
82
+ VSRepoConfigError as VSRepoConfigErrorRuntime,
83
+ VSRepoBuildError as VSRepoBuildErrorRuntime,
84
+ VSRepoExtendError as VSRepoExtendErrorRuntime,
85
+ VSRepoRuntimeError as VSRepoRuntimeErrorRuntime
86
+ } from 'vsrepo/VSRepoError';
87
+
88
+ import type {
89
+ VSRepoError as VSRepoErrorType,
90
+ VSRepoConfigError as VSRepoConfigErrorType,
91
+ VSRepoBuildError as VSRepoBuildErrorType,
92
+ VSRepoExtendError as VSRepoExtendErrorType,
93
+ VSRepoRuntimeError as VSRepoRuntimeErrorType
94
+ } from './VSRepoError.types';
95
+
96
+ export const VSRepoError: typeof VSRepoErrorType = VSRepoErrorRuntime;
97
+ export const VSRepoConfigError: typeof VSRepoConfigErrorType = VSRepoConfigErrorRuntime;
98
+ export const VSRepoBuildError: typeof VSRepoBuildErrorType = VSRepoBuildErrorRuntime;
99
+ export const VSRepoExtendError: typeof VSRepoExtendErrorType = VSRepoExtendErrorRuntime;
100
+ export const VSRepoRuntimeError: typeof VSRepoRuntimeErrorType = VSRepoRuntimeErrorRuntime;
101
+
102
+ export type * from './VSRepoError.types';
103
+ `,
104
+ },
105
+ {
106
+ fileName: 'VSRepository.ts',
107
+ content: `import {
108
+ VSRepository as VSRepositoryRuntime,
109
+ setupVSRepo as setupVSRepoRuntime
110
+ } from 'vsrepo/VSRepository';
111
+
112
+ import type {
113
+ VSRepository as VSRepositoryType,
114
+ setupVSRepo as setupVSRepoType
115
+ } from './VSRepository.types';
116
+
117
+ export const VSRepository: typeof VSRepositoryType = VSRepositoryRuntime;
118
+ export const setupVSRepo: typeof setupVSRepoType = setupVSRepoRuntime;
119
+
120
+ export type * from './VSRepository.types';
121
+ `,
122
+ },
123
+ {
124
+ fileName: 'index.ts',
125
+ content: `export { VSRepoError, VSRepoConfigError, VSRepoBuildError, VSRepoExtendError, VSRepoRuntimeError } from './VSRepoError.js';
126
+ export { VSRepository, setupVSRepo } from './VSRepository.js';
127
+
128
+ export type * from './VSRepoError.types';
129
+ export type * from './VSRepository.types';
130
+ `,
131
+ },
132
+ ];
133
+
134
+ for (const file of tsFiles) {
135
+ const targetFile = path.join(outputDir, file.fileName);
136
+ fs.writeFileSync(targetFile, file.content, 'utf8');
137
+ console.log(`Gerado: ${path.relative(workspaceRoot, targetFile)}`);
138
+ }
139
+
140
+ console.log('\nVSRepository gerado com tipagem do Prisma.');
141
+ console.log(`Prisma usado em: ${prismaTargetPath}`);
142
+
143
+ function stripKnownExtension(inputPath) {
144
+ return inputPath.replace(/\.(?:d\.ts|ts|js|mjs|cjs)$/i, '');
145
+ }
146
+
147
+ function relativeImportPath(fromDirectory, toPath) {
148
+ const relativePath = path.relative(fromDirectory, toPath);
149
+ const normalized = relativePath.startsWith('.') ? relativePath : `./${relativePath}`;
150
+ return toPosix(normalized);
151
+ }
152
+
153
+ function toPosix(inputPath) {
154
+ return inputPath.split(path.sep).join('/');
155
+ }