@vsrepo/prisma7-adapter 1.0.0 → 1.0.3

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
@@ -34,6 +34,7 @@
34
34
  - [`relations` in method options (read)](#relations-in-method-options-read)
35
35
  - [`merge`](#merge)
36
36
  - [`createMany`/`createManyReturning`/`updateMany`/`updateManyReturning` don't support nested writes](#createmanycreatemanyreturningupdatemanyupdatemanyreturning-dont-support-nested-writes)
37
+ - [Provider-specific limitations](#provider-specific-limitations)
37
38
  - [Transactions](#transactions)
38
39
  - [Logging](#logging)
39
40
  - [Requirements](#requirements)
@@ -202,6 +203,18 @@ For to-many relations (`otm`/`mtm`), items in the stored record and items in `ob
202
203
 
203
204
  > Note on return order: `createManyReturning` don't guarantee the returned records follow the order of the input payload (`objs`). Their result comes from a second `findMany` (re-querying the inserted/updated rows by primary key), so the order is only guaranteed when you pass `order` in the options.
204
205
 
206
+ ## Provider-specific limitations
207
+
208
+ Some Prisma features this adapter relies on aren't available on every database provider. If you use one of these features on an unsupported provider, Prisma itself will throw a validation error (or, in future versions, the adapter may validate this upfront — see below).
209
+
210
+ | Feature | Supported providers | Not supported |
211
+ | --- | --- | --- |
212
+ | `mode: "insensitive"` (used by `findByXIgnoreCase` dynamic methods) | PostgreSQL, MongoDB | MySQL, SQLite, SQL Server, CockroachDB — MySQL and SQL Server are case-insensitive by default, so no `mode` is needed there; SQLite is only case-insensitive for ASCII characters |
213
+ | `createManyReturning` / `updateManyReturning` (built on Prisma's `createManyAndReturn`/`updateManyAndReturn`) | PostgreSQL, CockroachDB, SQLite | MySQL, SQL Server, MongoDB |
214
+ | `skipDuplicates` on `createManyIgnoreConflicts` / `createManyReturningIgnoreConflicts` | PostgreSQL, MySQL, CockroachDB | MongoDB, SQL Server, SQLite |
215
+
216
+ > These limitations come from Prisma itself, not from this adapter — see [Prisma's case sensitivity docs](https://www.prisma.io/docs/orm/v7/reference/prisma-client-reference#mode) and [CRUD reference](https://www.prisma.io/docs/orm/v7/prisma-client/queries/crud) for details.
217
+
205
218
  ## Transactions
206
219
 
207
220
  **Every** method accepts `options.db` and runs its operation on the client/transaction you pass — the difference is in **how** each one treats it:
@@ -223,6 +236,36 @@ await userRepository.transaction(async tx => {
223
236
  });
224
237
  ```
225
238
 
239
+ ### Concurrency in `deleteManyReturning`
240
+
241
+ `deleteManyReturning` runs a `findMany` on the given `where` (to capture the records it will return) and then **re-applies the same `where`** to a `deleteMany`. Prisma has no native `deleteManyAndReturn`, which is why the delete is driven by the `where` instead of by the records fetched in the `findMany`.
242
+
243
+ Because of this two-step shape, a concurrent change between the `findMany` and the `deleteMany` can make them diverge:
244
+
245
+ - a row **inserted after** the `findMany` that matches the `where` will still be deleted, even though it wasn't returned;
246
+ - a row that **stops matching** the `where` before the `deleteMany` runs won't be deleted, even though it was returned.
247
+
248
+ In other words, the returned records and the rows actually deleted are not guaranteed to be identical under concurrency.
249
+
250
+ If you need to guarantee there are no concurrency issues, run this method inside a `repository.transaction()` at a higher isolation level — e.g. `SERIALIZABLE`:
251
+
252
+ ```typescript
253
+ import { VSRepository, TransactionIsolationLevel } from "vsrepo";
254
+
255
+ await userRepository.transaction(async tx => {
256
+ // Atomic: no concurrent insert/update can slip between the findMany
257
+ // and the deleteMany, since SERIALIZABLE isolates this transaction.
258
+ // `deleteManyReturning` is reached through the dynamic method you mapped
259
+ // to the adapter (e.g. a `@DynamicMethod()` exposing `deleteManyReturning`),
260
+ // always passing `{ db: tx }` so it runs inside this transaction.
261
+ const deleted = await userRepository.deleteManyReturningByIdIn([...], { db: tx });
262
+ }, {
263
+ isolationLevel: TransactionIsolationLevel.SERIALIZABLE,
264
+ });
265
+ ```
266
+
267
+ > The isolation level applies only when the repository call actually runs **inside** that transaction (i.e. with `{ db: tx }`). When `deleteManyReturning` starts its own internal transaction (no `options.db` passed), it falls back to the default isolation level.
268
+
226
269
  ## Logging
227
270
 
228
271
  The adapter uses `VSLogger` (from `vsrepo`) internally: every method logs a `DEBUG` line with the resolved Prisma arg, and start/end performance logs (surfacing a `WARN` for slow operations). Set `logLevel` in the constructor config to control verbosity; it defaults to `VSLogLevel.WARN`.
package/README.pt-BR.md CHANGED
@@ -34,6 +34,7 @@ Implementação de `VSRepoAdapter` para o [VSRepository v2](https://github.com/j
34
34
  - [`relations` nas options (leitura)](#relations-nas-options-leitura)
35
35
  - [`merge`](#merge)
36
36
  - [`createMany`/`createManyReturning`/`updateMany`/`updateManyReturning` não suportam nested writes](#createmanycreatemanyreturningupdatemanyupdatemanyreturning-não-suportam-nested-writes)
37
+ - [Limitações por provider](#limitações-por-provider)
37
38
  - [Transactions](#transactions)
38
39
  - [Logging](#logging)
39
40
  - [Requisitos](#requisitos)
@@ -202,6 +203,18 @@ Pra relations to-many (`otm`/`mtm`), os itens do registro salvo e os itens de `o
202
203
 
203
204
  > Nota sobre a ordem de retorno: `createManyReturning` não garante que os registros devolvidos seguem a ordem do payload de entrada (`objs`). O resultado vem de um segundo `findMany` (re-buscando as linhas inseridas/atualizadas pela primary key), então a ordem só é garantida quando você passa `order` nas options.
204
205
 
206
+ ## Limitações por provider
207
+
208
+ Alguns recursos do Prisma que esse adapter usa não estão disponíveis em todo banco. Se você usar um desses recursos num provider sem suporte, o próprio Prisma lança um erro de validação (ou, em versões futuras, o adapter pode validar isso antecipadamente — ver abaixo).
209
+
210
+ | Recurso | Providers com suporte | Sem suporte |
211
+ | --- | --- | --- |
212
+ | `mode: "insensitive"` (usado pelos métodos dinâmicos `findByXIgnoreCase`) | PostgreSQL, MongoDB | MySQL, SQLite, SQL Server, CockroachDB — MySQL e SQL Server já são case-insensitive por padrão, então não precisam de `mode`; SQLite só é case-insensitive pra caracteres ASCII |
213
+ | `createManyReturning` / `updateManyReturning` (baseados no `createManyAndReturn`/`updateManyAndReturn` do Prisma) | PostgreSQL, CockroachDB, SQLite | MySQL, SQL Server, MongoDB |
214
+ | `skipDuplicates` no `createManyIgnoreConflicts` / `createManyReturningIgnoreConflicts` | PostgreSQL, MySQL, CockroachDB | MongoDB, SQL Server, SQLite |
215
+
216
+ > Essas limitações vêm do próprio Prisma, não do adapter — veja a [doc de case sensitivity](https://www.prisma.io/docs/orm/v7/reference/prisma-client-reference#mode) e a [referência de CRUD](https://www.prisma.io/docs/orm/v7/prisma-client/queries/crud) do Prisma pra mais detalhes.
217
+
205
218
  ## Transactions
206
219
 
207
220
  **Todos** os métodos aceitam `options.db` e executam a operação no client/transaction passado — a diferença está em **como** cada um o trata:
@@ -223,6 +236,36 @@ await userRepository.transaction(async tx => {
223
236
  });
224
237
  ```
225
238
 
239
+ ### Concorrência em `deleteManyReturning`
240
+
241
+ O `deleteManyReturning` roda um `findMany` no `where` informado (pra capturar os registros que vai devolver) e depois **re-aplica o mesmo `where`** num `deleteMany`. Como o Prisma não tem um `deleteManyAndReturn` nativo, o delete é guiado pelo `where` — e não pelos registros retornados no `findMany`.
242
+
243
+ Por causa desse formato em duas etapas, uma alteração concorrente entre o `findMany` e o `deleteMany` pode fazer os dois divergirem:
244
+
245
+ - uma linha **inserida depois** do `findMany` que bate com o `where` ainda será deletada, mesmo não tendo sido retornada;
246
+ - uma linha que **deixa de bater** com o `where` antes do `deleteMany` rodar não será deletada, mesmo tendo sido retornada.
247
+
248
+ Ou seja: os registros retornados e as linhas realmente deletadas não têm garantia de serem idênticos sob concorrência.
249
+
250
+ Se você quiser garantir que não haverá problemas de concorrência, rode esse método **dentro** de um `repository.transaction()` num nível de isolamento mais alto — ex.: `SERIALIZABLE`:
251
+
252
+ ```typescript
253
+ import { VSRepository, TransactionIsolationLevel } from "vsrepo";
254
+
255
+ await userRepository.transaction(async tx => {
256
+ // Atômico: nenhum insert/update concorrente consegue entrar entre o
257
+ // findMany e o deleteMany, pois o SERIALIZABLE isola esta transaction.
258
+ // O `deleteManyReturning` é acessado através do método dinâmico que você
259
+ // mapeou pro adapter (ex.: um `@DynamicMethod()` expondo `deleteManyReturning`),
260
+ // sempre passando `{ db: tx }` pra rodar dentro desta transaction.
261
+ const deleted = await userRepository.deleteManyReturningByIdIn([...], { db: tx });
262
+ }, {
263
+ isolationLevel: TransactionIsolationLevel.SERIALIZABLE,
264
+ });
265
+ ```
266
+
267
+ > O nível de isolamento só se aplica quando a chamada roda **dentro** daquela transaction (ou seja, com `{ db: tx }`). Quando o `deleteManyReturning` inicia a própria transaction interna (sem `options.db`), ele usa o nível de isolamento padrão.
268
+
226
269
  ## Logging
227
270
 
228
271
  O adapter usa o `VSLogger` (de `vsrepo`) internamente: todo método loga uma linha `DEBUG` com o arg resolvido do Prisma, além de logs de performance de início/fim (com `WARN` pra operações lentas). Configure `logLevel` na config do construtor pra controlar a verbosidade; o default é `VSLogLevel.WARN`.
@@ -79,10 +79,18 @@ export declare class VSRepoPrisma7Adapter<T> extends VSRepoAdapter<T> {
79
79
  deleteMany(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<CountResult>;
80
80
  /**
81
81
  * Deletes every record matching `where`, returning the deleted records.
82
- * Prisma has no `deleteManyAndReturn`, so this fetches then deletes inside
83
- * a transaction — reusing `options.db` when it's already a transaction
84
- * client (see `runTransactional`), so it stays atomic even when it's not
85
- * the one starting the transaction.
82
+ *
83
+ * Prisma has no `deleteManyAndReturn`, so this does a `findMany` on
84
+ * `where` first and then re-applies the same `where` to a `deleteMany`,
85
+ * both inside a transaction (`runTransactional`).
86
+ *
87
+ * Because the delete is driven by the same `where` (not by the fetched
88
+ * records), a concurrent change between the two operations can make them
89
+ * diverge — e.g. a row inserted after the `findMany` matching the `where`
90
+ * would still be deleted, while a row changed to stop matching wouldn't.
91
+ * If you need to guarantee no concurrency issues, run this method inside a
92
+ * `repository.transaction()` with a higher isolation level, e.g.
93
+ * `TransactionIsolationLevel.SERIALIZABLE`.
86
94
  */
87
95
  deleteManyReturning(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<T[]>;
88
96
  /** Updates a single record matching `where`. */
@@ -40,7 +40,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
40
40
  this.tableName = validated.tableName;
41
41
  this.pkName = validated.pkName;
42
42
  this.relations = validated.relations;
43
- this.logger = new vsrepo_1.VSLogger(validated.logLevel ?? vsrepo_1.VSLogLevel.WARN, this.constructor.name + "Logger");
43
+ this.logger = new vsrepo_1.VSLogger(validated.logLevel ?? vsrepo_1.VSLogLevel.WARN, this.constructor.name + "Logger", validated.logSlowThresholdMs ?? 300);
44
44
  this.logger.logInfo(`${this.constructor.name} initialized for table '${this.tableName}' (pkName: '${this.pkName}'${this.relations ? `, relations: [${Object.keys(this.relations).join(", ")}]` : ""})`);
45
45
  }
46
46
  getPrismaRepository(db) {
@@ -121,7 +121,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
121
121
  }
122
122
  /** Fetches a single record matching `where`. */
123
123
  async findOne(where, options) {
124
- const start = this.logger.startPerformLog("findOne");
124
+ const start = this.logger.startPerformLog("run findOne");
125
125
  try {
126
126
  const arg = { ...this.resolveReadArg(options), where: (0, where_parser_1.parsePrismaWhere)(where) };
127
127
  this.logger.logDebug("Resolved Prisma arg for 'findOne'", arg);
@@ -136,7 +136,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
136
136
  }
137
137
  /** Fetches a single record matching `where`, throwing if none is found. */
138
138
  async findOneOrThrow(where, options) {
139
- const start = this.logger.startPerformLog("findOneOrThrow");
139
+ const start = this.logger.startPerformLog("run findOneOrThrow");
140
140
  try {
141
141
  const arg = { ...this.resolveReadArg(options), where: (0, where_parser_1.parsePrismaWhere)(where) };
142
142
  this.logger.logDebug("Resolved Prisma arg for 'findOneOrThrow'", arg);
@@ -151,7 +151,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
151
151
  }
152
152
  /** Fetches all records matching `where`. */
153
153
  async findMany(where, options) {
154
- const start = this.logger.startPerformLog("findMany");
154
+ const start = this.logger.startPerformLog("run findMany");
155
155
  try {
156
156
  const arg = { ...this.resolveReadArg(options), where: (0, where_parser_1.parsePrismaWhere)(where) };
157
157
  this.logger.logDebug("Resolved Prisma arg for 'findMany'", arg);
@@ -166,7 +166,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
166
166
  }
167
167
  /** Creates or updates (upsert) a single record. */
168
168
  async save(obj, options) {
169
- const start = this.logger.startPerformLog("save");
169
+ const start = this.logger.startPerformLog("run save");
170
170
  try {
171
171
  const objAny = obj;
172
172
  const pkValue = objAny[this.pkName];
@@ -197,7 +197,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
197
197
  * `runTransactional`).
198
198
  */
199
199
  async saveMany(objs, options) {
200
- const start = this.logger.startPerformLog("saveMany");
200
+ const start = this.logger.startPerformLog("run saveMany");
201
201
  try {
202
202
  this.logger.logDebug(`Saving ${objs.length} record(s) individually inside a transaction`);
203
203
  return await this.runTransactional(options?.db, tx => Promise.all(objs.map(obj => this.save(obj, { ...options, db: tx }))));
@@ -211,7 +211,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
211
211
  }
212
212
  /** Deletes a single record matching `where`. */
213
213
  async delete(where, options) {
214
- const start = this.logger.startPerformLog("delete");
214
+ const start = this.logger.startPerformLog("run delete");
215
215
  try {
216
216
  const arg = { ...this.resolveReadArg(options), where: (0, where_parser_1.parsePrismaWhere)(where) };
217
217
  this.logger.logDebug("Resolved Prisma arg for 'delete'", arg);
@@ -226,7 +226,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
226
226
  }
227
227
  /** Deletes every record matching `where`, returning the count of affected rows. */
228
228
  async deleteMany(where, options) {
229
- const start = this.logger.startPerformLog("deleteMany");
229
+ const start = this.logger.startPerformLog("run deleteMany");
230
230
  try {
231
231
  const arg = { where: (0, where_parser_1.parsePrismaWhere)(where) };
232
232
  this.logger.logDebug("Resolved Prisma arg for 'deleteMany'", arg);
@@ -241,13 +241,21 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
241
241
  }
242
242
  /**
243
243
  * Deletes every record matching `where`, returning the deleted records.
244
- * Prisma has no `deleteManyAndReturn`, so this fetches then deletes inside
245
- * a transaction — reusing `options.db` when it's already a transaction
246
- * client (see `runTransactional`), so it stays atomic even when it's not
247
- * the one starting the transaction.
244
+ *
245
+ * Prisma has no `deleteManyAndReturn`, so this does a `findMany` on
246
+ * `where` first and then re-applies the same `where` to a `deleteMany`,
247
+ * both inside a transaction (`runTransactional`).
248
+ *
249
+ * Because the delete is driven by the same `where` (not by the fetched
250
+ * records), a concurrent change between the two operations can make them
251
+ * diverge — e.g. a row inserted after the `findMany` matching the `where`
252
+ * would still be deleted, while a row changed to stop matching wouldn't.
253
+ * If you need to guarantee no concurrency issues, run this method inside a
254
+ * `repository.transaction()` with a higher isolation level, e.g.
255
+ * `TransactionIsolationLevel.SERIALIZABLE`.
248
256
  */
249
257
  async deleteManyReturning(where, options) {
250
- const start = this.logger.startPerformLog("deleteManyReturning");
258
+ const start = this.logger.startPerformLog("run deleteManyReturning");
251
259
  try {
252
260
  const prismaWhere = (0, where_parser_1.parsePrismaWhere)(where);
253
261
  const findArg = { ...this.resolveReadArg(options), where: prismaWhere };
@@ -272,7 +280,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
272
280
  }
273
281
  /** Updates a single record matching `where`. */
274
282
  async update(where, obj, options) {
275
- const start = this.logger.startPerformLog("update");
283
+ const start = this.logger.startPerformLog("run update");
276
284
  try {
277
285
  const { update } = (0, data_parser_1.parsePrismaWriteData)(obj, this.pkName, this.relations);
278
286
  const arg = {
@@ -292,7 +300,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
292
300
  }
293
301
  /** Updates every record matching `where`, returning the count of affected rows. */
294
302
  async updateMany(where, obj, options) {
295
- const start = this.logger.startPerformLog("updateMany");
303
+ const start = this.logger.startPerformLog("run updateMany");
296
304
  try {
297
305
  const data = this.stripRelationFields(obj);
298
306
  const arg = { where: (0, where_parser_1.parsePrismaWhere)(where), data };
@@ -308,7 +316,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
308
316
  }
309
317
  /** Updates every record matching `where`, returning the updated records. */
310
318
  async updateManyReturning(where, obj, options) {
311
- const start = this.logger.startPerformLog("updateManyReturning");
319
+ const start = this.logger.startPerformLog("run updateManyReturning");
312
320
  try {
313
321
  const data = this.stripRelationFields(obj);
314
322
  const readArg = this.resolveReadArg(options);
@@ -341,7 +349,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
341
349
  }
342
350
  /** Returns the number of records matching `where`. */
343
351
  async count(where, options) {
344
- const start = this.logger.startPerformLog("count");
352
+ const start = this.logger.startPerformLog("run count");
345
353
  try {
346
354
  const arg = { where: (0, where_parser_1.parsePrismaWhere)(where) };
347
355
  this.logger.logDebug("Resolved Prisma arg for 'count'", arg);
@@ -356,7 +364,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
356
364
  }
357
365
  /** Checks whether at least one record matching `where` exists. */
358
366
  async exists(where, options) {
359
- const start = this.logger.startPerformLog("exists");
367
+ const start = this.logger.startPerformLog("run exists");
360
368
  try {
361
369
  const arg = { where: (0, where_parser_1.parsePrismaWhere)(where), select: { [this.pkName]: true } };
362
370
  this.logger.logDebug("Resolved Prisma arg for 'exists'", arg);
@@ -391,7 +399,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
391
399
  }
392
400
  /** Creates a single record. */
393
401
  async create(obj, options) {
394
- const start = this.logger.startPerformLog("create");
402
+ const start = this.logger.startPerformLog("run create");
395
403
  try {
396
404
  const { create } = (0, data_parser_1.parsePrismaWriteData)(obj, this.pkName, this.relations);
397
405
  const arg = { ...this.resolveReadArg(options), data: create };
@@ -411,7 +419,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
411
419
  * support nested writes.
412
420
  */
413
421
  async createMany(objs, options) {
414
- const start = this.logger.startPerformLog("createMany");
422
+ const start = this.logger.startPerformLog("run createMany");
415
423
  try {
416
424
  const data = objs.map(obj => this.stripRelationFields(obj));
417
425
  const arg = { data, skipDuplicates: options?.ignoreConflicts };
@@ -429,7 +437,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
429
437
  * Creates multiple records in a single operation, returning the created records.
430
438
  */
431
439
  async createManyReturning(objs, options) {
432
- const start = this.logger.startPerformLog("createManyReturning");
440
+ const start = this.logger.startPerformLog("run createManyReturning");
433
441
  try {
434
442
  const data = objs.map(obj => this.stripRelationFields(obj));
435
443
  const readArg = this.resolveReadArg(options);
@@ -463,7 +471,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
463
471
  * what to do with the result (e.g. call `save` next).
464
472
  */
465
473
  async merge(where, obj, options) {
466
- const start = this.logger.startPerformLog("merge");
474
+ const start = this.logger.startPerformLog("run merge");
467
475
  try {
468
476
  const arg = { ...this.resolveReadArg(options), where: (0, where_parser_1.parsePrismaWhere)(where) };
469
477
  this.logger.logDebug("Resolved Prisma arg for 'merge'", arg);
@@ -481,7 +489,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
481
489
  }
482
490
  /** Creates a record if none matches `where`, otherwise updates it. */
483
491
  async upsert(where, create, update, options) {
484
- const start = this.logger.startPerformLog("upsert");
492
+ const start = this.logger.startPerformLog("run upsert");
485
493
  try {
486
494
  const parsedCreate = (0, data_parser_1.parsePrismaWriteData)(create, this.pkName, this.relations).create;
487
495
  const parsedUpdate = (0, data_parser_1.parsePrismaWriteData)(update, this.pkName, this.relations).update;
@@ -46,7 +46,12 @@ function mergeToManyRelation(target, source, relationPk) {
46
46
  }
47
47
  function mergeEntities(result, obj, relations) {
48
48
  if (!relations) {
49
- return (0, deepmerge_1.default)(result, obj, {
49
+ const definedObj = {};
50
+ for (const key of Object.keys(obj)) {
51
+ if (obj[key] !== undefined)
52
+ definedObj[key] = obj[key];
53
+ }
54
+ return (0, deepmerge_1.default)(result, definedObj, {
50
55
  arrayMerge: (target, source) => target.concat(source),
51
56
  });
52
57
  }
@@ -19,4 +19,8 @@ export interface VSRepoPrisma7AdapterConfig<T = any> {
19
19
  relations?: AdapterRelations<T>;
20
20
  /** Minimum log level for the adapter's internal `VSLogger`. @default VSLogLevel.WARN */
21
21
  logLevel?: VSLogLevel;
22
+ /**
23
+ * Duration (in ms) above which a finished operation is logged, flagging potentially slow queries. Defaults to 300ms.
24
+ */
25
+ logSlowThresholdMs?: number;
22
26
  }
@@ -10,4 +10,5 @@ export declare const adapterConfigSchema: v.ObjectSchema<{
10
10
  readonly nullable: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
11
11
  }, undefined>, undefined>, undefined>;
12
12
  readonly logLevel: v.OptionalSchema<v.EnumSchema<typeof VSLogLevel, undefined>, undefined>;
13
+ readonly logSlowThresholdMs: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.GtValueAction<number, 0, undefined>]>, undefined>;
13
14
  }, undefined>;
@@ -42,4 +42,5 @@ exports.adapterConfigSchema = v.object({
42
42
  pkName: v.pipe(v.string(), v.minLength(1)),
43
43
  relations: v.optional(v.record(v.string(), relation_schema_1.relationSchema)),
44
44
  logLevel: v.optional(v.enum(vsrepo_1.VSLogLevel)),
45
+ logSlowThresholdMs: v.optional(v.pipe(v.number(), v.gtValue(0))),
45
46
  });
package/package.json CHANGED
@@ -1,84 +1,84 @@
1
- {
2
- "name": "@vsrepo/prisma7-adapter",
3
- "version": "1.0.0",
4
- "description": "VSRepository adapter implementation backed by Prisma 7, translating every VSRepository operation into Prisma Client calls.",
5
- "homepage": "https://github.com/jaobrabo123/VSRepoPrisma7Adapter#readme",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/jaobrabo123/VSRepoPrisma7Adapter.git"
9
- },
10
- "main": "./dist/index.js",
11
- "types": "./dist/index.d.ts",
12
- "exports": {
13
- ".": {
14
- "types": "./dist/index.d.ts",
15
- "import": "./dist/index.js",
16
- "require": "./dist/index.js"
17
- }
18
- },
19
- "type": "commonjs",
20
- "files": [
21
- "dist",
22
- "README.md",
23
- "README.pt-BR.md"
24
- ],
25
- "engines": {
26
- "node": ">=18"
27
- },
28
- "scripts": {
29
- "clean": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\"",
30
- "build": "npm run clean && tsc -p tsconfig.build.json",
31
- "prepack": "npm run build",
32
- "typecheck": "tsc --noEmit -p tsconfig.build.json",
33
- "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
34
- "test:watch": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --watch",
35
- "test:types": "tsc --noEmit -p tests/tsconfig.json"
36
- },
37
- "keywords": [
38
- "prisma",
39
- "prisma7",
40
- "repository",
41
- "orm",
42
- "typescript",
43
- "repository-pattern",
44
- "vsrepo",
45
- "vsrepository",
46
- "adapter"
47
- ],
48
- "author": "João Pedro Azevedo Freire Mecenas",
49
- "license": "MIT",
50
- "peerDependencies": {
51
- "@prisma/client": "^7.10.0",
52
- "vsrepo": "^2.0.0"
53
- },
54
- "dependencies": {
55
- "deepmerge": "^4.3.1",
56
- "valibot": "^1.4.2"
57
- },
58
- "devDependencies": {
59
- "@babel/core": "^7.29.7",
60
- "@babel/plugin-proposal-decorators": "^7.29.7",
61
- "@babel/preset-env": "^7.29.7",
62
- "@babel/preset-typescript": "^7.29.7",
63
- "@jest/globals": "^30.5.0",
64
- "@prisma/adapter-pg": "^7.10.0",
65
- "@types/jest": "^30.0.0",
66
- "@types/node": "^26.4.0",
67
- "@types/pg": "^8.23.1",
68
- "babel-jest": "^30.5.0",
69
- "cross-env": "^10.1.0",
70
- "dotenv": "^17.4.2",
71
- "jest": "^30.5.0",
72
- "pg": "^8.23.0",
73
- "prisma": "^7.10.0",
74
- "tsx": "^4.23.12",
75
- "typescript": "^7.0.2"
76
- },
77
- "allowScripts": {
78
- "prisma@7.10.0": true,
79
- "@parcel/watcher@2.6.0": true,
80
- "unrs-resolver@1.12.2": true,
81
- "@prisma/engines@7.10.0": true,
82
- "esbuild@0.28.2": true
83
- }
84
- }
1
+ {
2
+ "name": "@vsrepo/prisma7-adapter",
3
+ "version": "1.0.3",
4
+ "description": "VSRepository adapter implementation backed by Prisma 7, translating every VSRepository operation into Prisma Client calls.",
5
+ "homepage": "https://github.com/jaobrabo123/VSRepoPrisma7Adapter#readme",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/jaobrabo123/VSRepoPrisma7Adapter.git"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.js"
17
+ }
18
+ },
19
+ "type": "commonjs",
20
+ "files": [
21
+ "dist",
22
+ "README.md",
23
+ "README.pt-BR.md"
24
+ ],
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "scripts": {
29
+ "clean": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\"",
30
+ "build": "npm run clean && tsc -p tsconfig.build.json",
31
+ "prepack": "npm run build",
32
+ "typecheck": "tsc --noEmit -p tsconfig.build.json",
33
+ "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
34
+ "test:watch": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --watch",
35
+ "test:types": "tsc --noEmit -p tests/tsconfig.json"
36
+ },
37
+ "keywords": [
38
+ "prisma",
39
+ "prisma7",
40
+ "repository",
41
+ "orm",
42
+ "typescript",
43
+ "repository-pattern",
44
+ "vsrepo",
45
+ "vsrepository",
46
+ "adapter"
47
+ ],
48
+ "author": "João Pedro Azevedo Freire Mecenas",
49
+ "license": "MIT",
50
+ "peerDependencies": {
51
+ "@prisma/client": "^7.10.0",
52
+ "vsrepo": "^2.0.0"
53
+ },
54
+ "dependencies": {
55
+ "deepmerge": "^4.3.1",
56
+ "valibot": "^1.4.2"
57
+ },
58
+ "devDependencies": {
59
+ "@babel/core": "^7.29.7",
60
+ "@babel/plugin-proposal-decorators": "^7.29.7",
61
+ "@babel/preset-env": "^7.29.7",
62
+ "@babel/preset-typescript": "^7.29.7",
63
+ "@jest/globals": "^30.5.0",
64
+ "@prisma/adapter-pg": "^7.10.0",
65
+ "@types/jest": "^30.0.0",
66
+ "@types/node": "^26.4.0",
67
+ "@types/pg": "^8.23.1",
68
+ "babel-jest": "^30.5.0",
69
+ "cross-env": "^10.1.0",
70
+ "dotenv": "^17.4.2",
71
+ "jest": "^30.5.0",
72
+ "pg": "^8.23.0",
73
+ "prisma": "^7.10.0",
74
+ "tsx": "^4.23.12",
75
+ "typescript": "^7.0.2"
76
+ },
77
+ "allowScripts": {
78
+ "prisma@7.10.0": true,
79
+ "@parcel/watcher@2.6.0": true,
80
+ "unrs-resolver@1.12.2": true,
81
+ "@prisma/engines@7.10.0": true,
82
+ "esbuild@0.28.2": true
83
+ }
84
+ }