@vsrepo/prisma7-adapter 1.0.0

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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/README.pt-BR.md +233 -0
  4. package/dist/index.d.ts +6 -0
  5. package/dist/index.js +5 -0
  6. package/dist/parsers/data.parser.d.ts +34 -0
  7. package/dist/parsers/data.parser.js +134 -0
  8. package/dist/parsers/include.parser.d.ts +3 -0
  9. package/dist/parsers/include.parser.js +16 -0
  10. package/dist/parsers/order-by.parser.d.ts +15 -0
  11. package/dist/parsers/order-by.parser.js +38 -0
  12. package/dist/parsers/select.parser.d.ts +3 -0
  13. package/dist/parsers/select.parser.js +16 -0
  14. package/dist/parsers/where.parser.d.ts +31 -0
  15. package/dist/parsers/where.parser.js +187 -0
  16. package/dist/prisma7.adapter.d.ts +125 -0
  17. package/dist/prisma7.adapter.js +505 -0
  18. package/dist/resolvers/map-prisma-error.resolver.d.ts +13 -0
  19. package/dist/resolvers/map-prisma-error.resolver.js +100 -0
  20. package/dist/resolvers/merge-entities.resolver.d.ts +19 -0
  21. package/dist/resolvers/merge-entities.resolver.js +73 -0
  22. package/dist/types/adapter-config.type.d.ts +22 -0
  23. package/dist/types/adapter-config.type.js +2 -0
  24. package/dist/types/adapter-relations.type.d.ts +16 -0
  25. package/dist/types/adapter-relations.type.js +2 -0
  26. package/dist/types/plain-object.type.d.ts +1 -0
  27. package/dist/types/plain-object.type.js +2 -0
  28. package/dist/types/prisma-arg-like.type.d.ts +14 -0
  29. package/dist/types/prisma-arg-like.type.js +2 -0
  30. package/dist/types/prisma-repository-like.type.d.ts +20 -0
  31. package/dist/types/prisma-repository-like.type.js +2 -0
  32. package/dist/types/prisma7-client-like.type.d.ts +19 -0
  33. package/dist/types/prisma7-client-like.type.js +2 -0
  34. package/dist/types/prisma7-orm-types.type.d.ts +19 -0
  35. package/dist/types/prisma7-orm-types.type.js +2 -0
  36. package/dist/types/relation.type.d.ts +26 -0
  37. package/dist/types/relation.type.js +2 -0
  38. package/dist/validators/adapter-config.schema.d.ts +13 -0
  39. package/dist/validators/adapter-config.schema.js +45 -0
  40. package/dist/validators/is-decimal.validator.d.ts +5 -0
  41. package/dist/validators/is-decimal.validator.js +10 -0
  42. package/dist/validators/is-plain-object.validator.d.ts +2 -0
  43. package/dist/validators/is-plain-object.validator.js +11 -0
  44. package/dist/validators/relation.schema.d.ts +8 -0
  45. package/dist/validators/relation.schema.js +44 -0
  46. package/dist/validators/validate-adapter-config.validator.d.ts +8 -0
  47. package/dist/validators/validate-adapter-config.validator.js +54 -0
  48. package/package.json +84 -0
@@ -0,0 +1,505 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VSRepoPrisma7Adapter = void 0;
4
+ const vsrepo_1 = require("vsrepo");
5
+ const where_parser_1 = require("./parsers/where.parser");
6
+ const include_parser_1 = require("./parsers/include.parser");
7
+ const select_parser_1 = require("./parsers/select.parser");
8
+ const order_by_parser_1 = require("./parsers/order-by.parser");
9
+ const data_parser_1 = require("./parsers/data.parser");
10
+ const merge_entities_resolver_1 = require("./resolvers/merge-entities.resolver");
11
+ const map_prisma_error_resolver_1 = require("./resolvers/map-prisma-error.resolver");
12
+ const validate_adapter_config_validator_1 = require("./validators/validate-adapter-config.validator");
13
+ /**
14
+ * `VSRepoAdapter` implementation for Prisma 7. Translates every method of the
15
+ * adapter contract into Prisma Client calls, resolving `VSRepoWhere`/`Ordering`/
16
+ * `select`/`include` through the parsers, and — when a
17
+ * `relations` config is provided — resolving relation fields on `create`/
18
+ * `update`/`upsert`/`merge` the same way the v1 of VSRepository did (see
19
+ * `Relation`).
20
+ *
21
+ * Every method always rejects with a `VSRepoAdapterError` — any error raised
22
+ * by Prisma (or by the adapter itself) is translated through `mapPrismaError`
23
+ * before leaving the adapter, so callers never have to deal with a raw
24
+ * Prisma/driver error shape.
25
+ *
26
+ * @template T Entity type this adapter operates on.
27
+ *
28
+ * @publicApi
29
+ */
30
+ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
31
+ prisma;
32
+ tableName;
33
+ pkName;
34
+ relations;
35
+ logger;
36
+ constructor(prisma, config) {
37
+ super();
38
+ this.prisma = prisma;
39
+ const validated = (0, validate_adapter_config_validator_1.validateAdapterConfig)(config);
40
+ this.tableName = validated.tableName;
41
+ this.pkName = validated.pkName;
42
+ this.relations = validated.relations;
43
+ this.logger = new vsrepo_1.VSLogger(validated.logLevel ?? vsrepo_1.VSLogLevel.WARN, this.constructor.name + "Logger");
44
+ this.logger.logInfo(`${this.constructor.name} initialized for table '${this.tableName}' (pkName: '${this.pkName}'${this.relations ? `, relations: [${Object.keys(this.relations).join(", ")}]` : ""})`);
45
+ }
46
+ getPrismaRepository(db) {
47
+ return db ? db[this.tableName] : this.prisma[this.tableName];
48
+ }
49
+ /** Resolves the "read" part of a Prisma arg: select/include/orderBy/pagination. */
50
+ resolveReadArg(options) {
51
+ options ??= {};
52
+ const prismaSelect = options.select && (0, select_parser_1.parsePrismaSelect)(options.select);
53
+ const prismaInclude = prismaSelect
54
+ ? undefined
55
+ : options.relations && (0, include_parser_1.parsePrismaInclude)(options.relations);
56
+ return {
57
+ select: prismaSelect,
58
+ include: prismaInclude,
59
+ orderBy: (0, order_by_parser_1.parsePrismaOrderBy)(options.order),
60
+ skip: options.pagination?.offset,
61
+ take: options.pagination?.limit,
62
+ distinct: options.distinct,
63
+ };
64
+ }
65
+ /**
66
+ * Strips relation fields from a payload — used by `createMany`/`updateMany`/
67
+ * `updateManyReturning`, since those Prisma operations don't support nested
68
+ * writes (scalar fields only). Throws a `VSRepoAdapterError` (code
69
+ * `NOT_SUPPORTED`) instead of silently ignoring the field, when a configured
70
+ * relation field is present in the payload.
71
+ */
72
+ stripRelationFields(obj) {
73
+ if (!this.relations)
74
+ return obj;
75
+ const data = {};
76
+ for (const [key, value] of Object.entries(obj)) {
77
+ if (value === undefined)
78
+ continue;
79
+ if (key in this.relations) {
80
+ throw new vsrepo_1.VSRepoAdapterError(`Field '${key}' is a configured relation, but Prisma's *Many operations don't support nested relation writes.`, vsrepo_1.AdapterErrorCode.NOT_SUPPORTED, null);
81
+ }
82
+ data[key] = value;
83
+ }
84
+ return data;
85
+ }
86
+ /**
87
+ * Tells apart a root Prisma Client from an active transaction client: only
88
+ * the root client exposes `$on` (used for event listeners), Prisma's
89
+ * interactive transaction client doesn't (see `Prisma7ClientLike`).
90
+ */
91
+ isRootClient(db) {
92
+ return typeof db?.$on === "function";
93
+ }
94
+ /**
95
+ * Runs `fn` against a transaction, reusing `db` when it's already a
96
+ * transaction client (see `isRootClient`) instead of nesting a new one —
97
+ * this is what lets `saveMany`/`deleteManyReturning` participate in a
98
+ * transaction already started via `VSRepository.transaction()`.
99
+ * When `db` isn't provided, or is a root client, a new transaction is
100
+ * started on it (or on `this.prisma` when `db` isn't provided at all).
101
+ */
102
+ async runTransactional(db, fn) {
103
+ if (db && !this.isRootClient(db)) {
104
+ this.logger.logDebug("Reusing an already-active transaction client");
105
+ return fn(db);
106
+ }
107
+ return (db ?? this.prisma).$transaction(fn);
108
+ }
109
+ /** Returns the underlying ORM client instance used outside of transactions. */
110
+ getDbClient() {
111
+ return this.prisma;
112
+ }
113
+ /** Runs `fn` inside a native transaction of the underlying ORM/database. */
114
+ async runInTransaction(fn, options) {
115
+ try {
116
+ return await this.prisma.$transaction(fn, options);
117
+ }
118
+ catch (error) {
119
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "runInTransaction");
120
+ }
121
+ }
122
+ /** Fetches a single record matching `where`. */
123
+ async findOne(where, options) {
124
+ const start = this.logger.startPerformLog("findOne");
125
+ try {
126
+ const arg = { ...this.resolveReadArg(options), where: (0, where_parser_1.parsePrismaWhere)(where) };
127
+ this.logger.logDebug("Resolved Prisma arg for 'findOne'", arg);
128
+ return await this.getPrismaRepository(options?.db).findFirst(arg);
129
+ }
130
+ catch (error) {
131
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "findOne");
132
+ }
133
+ finally {
134
+ this.logger.endPerformLog(start);
135
+ }
136
+ }
137
+ /** Fetches a single record matching `where`, throwing if none is found. */
138
+ async findOneOrThrow(where, options) {
139
+ const start = this.logger.startPerformLog("findOneOrThrow");
140
+ try {
141
+ const arg = { ...this.resolveReadArg(options), where: (0, where_parser_1.parsePrismaWhere)(where) };
142
+ this.logger.logDebug("Resolved Prisma arg for 'findOneOrThrow'", arg);
143
+ return await this.getPrismaRepository(options?.db).findFirstOrThrow(arg);
144
+ }
145
+ catch (error) {
146
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "findOneOrThrow");
147
+ }
148
+ finally {
149
+ this.logger.endPerformLog(start);
150
+ }
151
+ }
152
+ /** Fetches all records matching `where`. */
153
+ async findMany(where, options) {
154
+ const start = this.logger.startPerformLog("findMany");
155
+ try {
156
+ const arg = { ...this.resolveReadArg(options), where: (0, where_parser_1.parsePrismaWhere)(where) };
157
+ this.logger.logDebug("Resolved Prisma arg for 'findMany'", arg);
158
+ return await this.getPrismaRepository(options?.db).findMany(arg);
159
+ }
160
+ catch (error) {
161
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "findMany");
162
+ }
163
+ finally {
164
+ this.logger.endPerformLog(start);
165
+ }
166
+ }
167
+ /** Creates or updates (upsert) a single record. */
168
+ async save(obj, options) {
169
+ const start = this.logger.startPerformLog("save");
170
+ try {
171
+ const objAny = obj;
172
+ const pkValue = objAny[this.pkName];
173
+ const readArg = this.resolveReadArg(options);
174
+ const repo = this.getPrismaRepository(options?.db);
175
+ if (pkValue === undefined) {
176
+ const { create } = (0, data_parser_1.parsePrismaWriteData)(objAny, this.pkName, this.relations);
177
+ const arg = { ...readArg, data: create };
178
+ this.logger.logDebug("Resolved Prisma arg for 'save' (create)", arg);
179
+ return await repo.create(arg);
180
+ }
181
+ const { create, update } = (0, data_parser_1.parsePrismaWriteData)(objAny, this.pkName, this.relations);
182
+ const arg = { ...readArg, where: { [this.pkName]: pkValue }, create, update };
183
+ this.logger.logDebug("Resolved Prisma arg for 'save' (upsert)", arg);
184
+ return await repo.upsert(arg);
185
+ }
186
+ catch (error) {
187
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "save");
188
+ }
189
+ finally {
190
+ this.logger.endPerformLog(start);
191
+ }
192
+ }
193
+ /**
194
+ * Creates or updates (upsert) multiple records. Prisma has no native bulk
195
+ * upsert, so each record is saved individually inside a transaction —
196
+ * reusing `options.db` when it's already a transaction client (see
197
+ * `runTransactional`).
198
+ */
199
+ async saveMany(objs, options) {
200
+ const start = this.logger.startPerformLog("saveMany");
201
+ try {
202
+ this.logger.logDebug(`Saving ${objs.length} record(s) individually inside a transaction`);
203
+ return await this.runTransactional(options?.db, tx => Promise.all(objs.map(obj => this.save(obj, { ...options, db: tx }))));
204
+ }
205
+ catch (error) {
206
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "saveMany");
207
+ }
208
+ finally {
209
+ this.logger.endPerformLog(start);
210
+ }
211
+ }
212
+ /** Deletes a single record matching `where`. */
213
+ async delete(where, options) {
214
+ const start = this.logger.startPerformLog("delete");
215
+ try {
216
+ const arg = { ...this.resolveReadArg(options), where: (0, where_parser_1.parsePrismaWhere)(where) };
217
+ this.logger.logDebug("Resolved Prisma arg for 'delete'", arg);
218
+ return await this.getPrismaRepository(options?.db).delete(arg);
219
+ }
220
+ catch (error) {
221
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "delete");
222
+ }
223
+ finally {
224
+ this.logger.endPerformLog(start);
225
+ }
226
+ }
227
+ /** Deletes every record matching `where`, returning the count of affected rows. */
228
+ async deleteMany(where, options) {
229
+ const start = this.logger.startPerformLog("deleteMany");
230
+ try {
231
+ const arg = { where: (0, where_parser_1.parsePrismaWhere)(where) };
232
+ this.logger.logDebug("Resolved Prisma arg for 'deleteMany'", arg);
233
+ return await this.getPrismaRepository(options?.db).deleteMany(arg);
234
+ }
235
+ catch (error) {
236
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "deleteMany");
237
+ }
238
+ finally {
239
+ this.logger.endPerformLog(start);
240
+ }
241
+ }
242
+ /**
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.
248
+ */
249
+ async deleteManyReturning(where, options) {
250
+ const start = this.logger.startPerformLog("deleteManyReturning");
251
+ try {
252
+ const prismaWhere = (0, where_parser_1.parsePrismaWhere)(where);
253
+ const findArg = { ...this.resolveReadArg(options), where: prismaWhere };
254
+ const deleteArg = { where: prismaWhere };
255
+ this.logger.logDebug("Resolved Prisma args for 'deleteManyReturning'", {
256
+ findArg,
257
+ deleteArg,
258
+ });
259
+ return await this.runTransactional(options?.db, async (tx) => {
260
+ const repo = this.getPrismaRepository(tx);
261
+ const records = await repo.findMany(findArg);
262
+ await repo.deleteMany(deleteArg);
263
+ return records;
264
+ });
265
+ }
266
+ catch (error) {
267
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "deleteManyReturning");
268
+ }
269
+ finally {
270
+ this.logger.endPerformLog(start);
271
+ }
272
+ }
273
+ /** Updates a single record matching `where`. */
274
+ async update(where, obj, options) {
275
+ const start = this.logger.startPerformLog("update");
276
+ try {
277
+ const { update } = (0, data_parser_1.parsePrismaWriteData)(obj, this.pkName, this.relations);
278
+ const arg = {
279
+ ...this.resolveReadArg(options),
280
+ where: (0, where_parser_1.parsePrismaWhere)(where),
281
+ data: update,
282
+ };
283
+ this.logger.logDebug("Resolved Prisma arg for 'update'", arg);
284
+ return await this.getPrismaRepository(options?.db).update(arg);
285
+ }
286
+ catch (error) {
287
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "update");
288
+ }
289
+ finally {
290
+ this.logger.endPerformLog(start);
291
+ }
292
+ }
293
+ /** Updates every record matching `where`, returning the count of affected rows. */
294
+ async updateMany(where, obj, options) {
295
+ const start = this.logger.startPerformLog("updateMany");
296
+ try {
297
+ const data = this.stripRelationFields(obj);
298
+ const arg = { where: (0, where_parser_1.parsePrismaWhere)(where), data };
299
+ this.logger.logDebug("Resolved Prisma arg for 'updateMany'", arg);
300
+ return await this.getPrismaRepository(options?.db).updateMany(arg);
301
+ }
302
+ catch (error) {
303
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "updateMany");
304
+ }
305
+ finally {
306
+ this.logger.endPerformLog(start);
307
+ }
308
+ }
309
+ /** Updates every record matching `where`, returning the updated records. */
310
+ async updateManyReturning(where, obj, options) {
311
+ const start = this.logger.startPerformLog("updateManyReturning");
312
+ try {
313
+ const data = this.stripRelationFields(obj);
314
+ const readArg = this.resolveReadArg(options);
315
+ const arg = {
316
+ ...readArg,
317
+ where: (0, where_parser_1.parsePrismaWhere)(where),
318
+ data,
319
+ };
320
+ this.logger.logDebug("Resolved Prisma arg for 'updateManyReturning'", arg);
321
+ return await this.runTransactional(options?.db, async (tx) => {
322
+ const repo = this.getPrismaRepository(tx);
323
+ const updated = await repo.updateManyAndReturn({
324
+ ...arg,
325
+ include: undefined,
326
+ select: { [this.pkName]: true },
327
+ });
328
+ const idsUpdated = updated.map((_) => _[this.pkName]);
329
+ return repo.findMany({
330
+ ...readArg,
331
+ where: { [this.pkName]: { in: idsUpdated } },
332
+ });
333
+ });
334
+ }
335
+ catch (error) {
336
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "updateManyReturning");
337
+ }
338
+ finally {
339
+ this.logger.endPerformLog(start);
340
+ }
341
+ }
342
+ /** Returns the number of records matching `where`. */
343
+ async count(where, options) {
344
+ const start = this.logger.startPerformLog("count");
345
+ try {
346
+ const arg = { where: (0, where_parser_1.parsePrismaWhere)(where) };
347
+ this.logger.logDebug("Resolved Prisma arg for 'count'", arg);
348
+ return await this.getPrismaRepository(options?.db).count(arg);
349
+ }
350
+ catch (error) {
351
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "count");
352
+ }
353
+ finally {
354
+ this.logger.endPerformLog(start);
355
+ }
356
+ }
357
+ /** Checks whether at least one record matching `where` exists. */
358
+ async exists(where, options) {
359
+ const start = this.logger.startPerformLog("exists");
360
+ try {
361
+ const arg = { where: (0, where_parser_1.parsePrismaWhere)(where), select: { [this.pkName]: true } };
362
+ this.logger.logDebug("Resolved Prisma arg for 'exists'", arg);
363
+ const result = await this.getPrismaRepository(options?.db).findFirst(arg);
364
+ return result !== null;
365
+ }
366
+ catch (error) {
367
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "exists");
368
+ }
369
+ finally {
370
+ this.logger.endPerformLog(start);
371
+ }
372
+ }
373
+ /** Executes a raw query/statement against the underlying database, used by `@QueryMethod`. */
374
+ async query(query, options) {
375
+ const modifying = options?.modifying ?? false;
376
+ const db = options?.db ?? this.prisma;
377
+ const args = options?.args ?? [];
378
+ const start = this.logger.startPerformLog(`run raw query (modifying: ${modifying})`);
379
+ try {
380
+ this.logger.logDebug("Running raw query", { query, args, modifying });
381
+ return modifying
382
+ ? await db.$executeRawUnsafe(query, ...args)
383
+ : await db.$queryRawUnsafe(query, ...args);
384
+ }
385
+ catch (error) {
386
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "query");
387
+ }
388
+ finally {
389
+ this.logger.endPerformLog(start);
390
+ }
391
+ }
392
+ /** Creates a single record. */
393
+ async create(obj, options) {
394
+ const start = this.logger.startPerformLog("create");
395
+ try {
396
+ const { create } = (0, data_parser_1.parsePrismaWriteData)(obj, this.pkName, this.relations);
397
+ const arg = { ...this.resolveReadArg(options), data: create };
398
+ this.logger.logDebug("Resolved Prisma arg for 'create'", arg);
399
+ return await this.getPrismaRepository(options?.db).create(arg);
400
+ }
401
+ catch (error) {
402
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "create");
403
+ }
404
+ finally {
405
+ this.logger.endPerformLog(start);
406
+ }
407
+ }
408
+ /**
409
+ * Creates multiple records in a single operation. Relation fields are
410
+ * stripped (see `stripRelationFields`), since Prisma's `createMany` doesn't
411
+ * support nested writes.
412
+ */
413
+ async createMany(objs, options) {
414
+ const start = this.logger.startPerformLog("createMany");
415
+ try {
416
+ const data = objs.map(obj => this.stripRelationFields(obj));
417
+ const arg = { data, skipDuplicates: options?.ignoreConflicts };
418
+ this.logger.logDebug("Resolved Prisma arg for 'createMany'", arg);
419
+ return await this.getPrismaRepository(options?.db).createMany(arg);
420
+ }
421
+ catch (error) {
422
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "createMany");
423
+ }
424
+ finally {
425
+ this.logger.endPerformLog(start);
426
+ }
427
+ }
428
+ /**
429
+ * Creates multiple records in a single operation, returning the created records.
430
+ */
431
+ async createManyReturning(objs, options) {
432
+ const start = this.logger.startPerformLog("createManyReturning");
433
+ try {
434
+ const data = objs.map(obj => this.stripRelationFields(obj));
435
+ const readArg = this.resolveReadArg(options);
436
+ const arg = { data, skipDuplicates: options?.ignoreConflicts };
437
+ this.logger.logDebug("Resolved Prisma arg for 'createManyReturning'", arg);
438
+ return await this.runTransactional(options?.db, async (tx) => {
439
+ const repo = this.getPrismaRepository(tx);
440
+ const created = await repo.createManyAndReturn({
441
+ ...arg,
442
+ include: undefined,
443
+ select: { [this.pkName]: true },
444
+ });
445
+ const idsCreated = created.map((_) => _[this.pkName]);
446
+ return repo.findMany({
447
+ ...readArg,
448
+ where: { [this.pkName]: { in: idsCreated } },
449
+ });
450
+ });
451
+ }
452
+ catch (error) {
453
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "createManyReturning");
454
+ }
455
+ finally {
456
+ this.logger.endPerformLog(start);
457
+ }
458
+ }
459
+ /**
460
+ * Fetches the record matching `where` and returns it deep-merged with
461
+ * `obj`, WITHOUT persisting anything — same behavior the v1 of
462
+ * VSRepository had (see `mergeEntities`). It's on the caller to decide
463
+ * what to do with the result (e.g. call `save` next).
464
+ */
465
+ async merge(where, obj, options) {
466
+ const start = this.logger.startPerformLog("merge");
467
+ try {
468
+ const arg = { ...this.resolveReadArg(options), where: (0, where_parser_1.parsePrismaWhere)(where) };
469
+ this.logger.logDebug("Resolved Prisma arg for 'merge'", arg);
470
+ const result = await this.getPrismaRepository(options?.db).findFirst(arg);
471
+ if (!result)
472
+ return null;
473
+ return (0, merge_entities_resolver_1.mergeEntities)(result, obj, this.relations);
474
+ }
475
+ catch (error) {
476
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "merge");
477
+ }
478
+ finally {
479
+ this.logger.endPerformLog(start);
480
+ }
481
+ }
482
+ /** Creates a record if none matches `where`, otherwise updates it. */
483
+ async upsert(where, create, update, options) {
484
+ const start = this.logger.startPerformLog("upsert");
485
+ try {
486
+ const parsedCreate = (0, data_parser_1.parsePrismaWriteData)(create, this.pkName, this.relations).create;
487
+ const parsedUpdate = (0, data_parser_1.parsePrismaWriteData)(update, this.pkName, this.relations).update;
488
+ const arg = {
489
+ ...this.resolveReadArg(options),
490
+ where: (0, where_parser_1.parsePrismaWhere)(where),
491
+ create: parsedCreate,
492
+ update: parsedUpdate,
493
+ };
494
+ this.logger.logDebug("Resolved Prisma arg for 'upsert'", arg);
495
+ return await this.getPrismaRepository(options?.db).upsert(arg);
496
+ }
497
+ catch (error) {
498
+ throw (0, map_prisma_error_resolver_1.mapPrismaError)(error, "upsert");
499
+ }
500
+ finally {
501
+ this.logger.endPerformLog(start);
502
+ }
503
+ }
504
+ }
505
+ exports.VSRepoPrisma7Adapter = VSRepoPrisma7Adapter;
@@ -0,0 +1,13 @@
1
+ import { VSRepoAdapterError } from "vsrepo";
2
+ /**
3
+ * Converts any error caught around a Prisma Client call into a
4
+ * `VSRepoAdapterError`, so `VSRepoPrisma7Adapter` never lets a raw Prisma (or
5
+ * other) error escape. Already-wrapped errors — e.g. bubbling up from a
6
+ * nested adapter call, such as `save` inside `saveMany`, or a config/usage
7
+ * error thrown by the adapter itself (see `stripRelationFields`) — are
8
+ * returned as-is instead of being wrapped a second time.
9
+ *
10
+ * @param error - The raw error caught around a Prisma Client call.
11
+ * @param operation - Name of the adapter method that failed, used in the message.
12
+ */
13
+ export declare function mapPrismaError(error: unknown, operation: string): VSRepoAdapterError;
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapPrismaError = mapPrismaError;
4
+ const vsrepo_1 = require("vsrepo");
5
+ /**
6
+ * Maps a Prisma `PrismaClientKnownRequestError.code` (e.g. `"P2002"`) to a
7
+ * stable, adapter-agnostic `AdapterErrorCode`. Only codes an application is
8
+ * realistically expected to react to are listed here — anything else falls
9
+ * back to `AdapterErrorCode.UNKNOWN` in `mapPrismaError`.
10
+ *
11
+ * @see https://www.prisma.io/docs/orm/reference/error-reference
12
+ */
13
+ const KNOWN_REQUEST_ERROR_CODE_MAP = {
14
+ // Connection / engine errors (P1xxx)
15
+ P1000: vsrepo_1.AdapterErrorCode.INVALID_CREDENTIALS,
16
+ P1001: vsrepo_1.AdapterErrorCode.CONNECTION_FAILED,
17
+ P1002: vsrepo_1.AdapterErrorCode.TIMEOUT,
18
+ P1008: vsrepo_1.AdapterErrorCode.TIMEOUT,
19
+ P1010: vsrepo_1.AdapterErrorCode.ACCESS_DENIED,
20
+ P1011: vsrepo_1.AdapterErrorCode.CONNECTION_FAILED,
21
+ P1017: vsrepo_1.AdapterErrorCode.CONNECTION_CLOSED,
22
+ // Query engine errors (P2xxx)
23
+ P2000: vsrepo_1.AdapterErrorCode.VALUE_TOO_LONG,
24
+ P2001: vsrepo_1.AdapterErrorCode.NOT_FOUND,
25
+ P2002: vsrepo_1.AdapterErrorCode.UNIQUE_CONSTRAINT_VIOLATION,
26
+ P2003: vsrepo_1.AdapterErrorCode.FOREIGN_KEY_VIOLATION,
27
+ P2004: vsrepo_1.AdapterErrorCode.CONSTRAINT_VIOLATION,
28
+ P2005: vsrepo_1.AdapterErrorCode.CONVERSION_ERROR,
29
+ P2006: vsrepo_1.AdapterErrorCode.INVALID_DATA,
30
+ P2007: vsrepo_1.AdapterErrorCode.INVALID_DATA,
31
+ P2010: vsrepo_1.AdapterErrorCode.INVALID_QUERY,
32
+ P2011: vsrepo_1.AdapterErrorCode.NOT_NULL_VIOLATION,
33
+ P2012: vsrepo_1.AdapterErrorCode.INVALID_DATA,
34
+ P2014: vsrepo_1.AdapterErrorCode.CONSTRAINT_VIOLATION,
35
+ P2015: vsrepo_1.AdapterErrorCode.NOT_FOUND,
36
+ P2016: vsrepo_1.AdapterErrorCode.INVALID_QUERY,
37
+ P2017: vsrepo_1.AdapterErrorCode.NOT_FOUND,
38
+ P2018: vsrepo_1.AdapterErrorCode.NOT_FOUND,
39
+ P2019: vsrepo_1.AdapterErrorCode.INVALID_DATA,
40
+ P2020: vsrepo_1.AdapterErrorCode.CONVERSION_ERROR,
41
+ P2021: vsrepo_1.AdapterErrorCode.TABLE_OR_COLUMN_NOT_FOUND,
42
+ P2022: vsrepo_1.AdapterErrorCode.TABLE_OR_COLUMN_NOT_FOUND,
43
+ P2023: vsrepo_1.AdapterErrorCode.CONVERSION_ERROR,
44
+ P2024: vsrepo_1.AdapterErrorCode.CONNECTION_POOL_EXHAUSTED,
45
+ P2025: vsrepo_1.AdapterErrorCode.NOT_FOUND,
46
+ P2026: vsrepo_1.AdapterErrorCode.NOT_SUPPORTED,
47
+ P2028: vsrepo_1.AdapterErrorCode.TRANSACTION_CLOSED,
48
+ P2030: vsrepo_1.AdapterErrorCode.NOT_SUPPORTED,
49
+ P2031: vsrepo_1.AdapterErrorCode.NOT_SUPPORTED,
50
+ P2033: vsrepo_1.AdapterErrorCode.CONVERSION_ERROR,
51
+ P2034: vsrepo_1.AdapterErrorCode.TRANSACTION_CONFLICT,
52
+ P2037: vsrepo_1.AdapterErrorCode.CONNECTION_POOL_EXHAUSTED,
53
+ };
54
+ /**
55
+ * Reads the Prisma error class name (`PrismaClientKnownRequestError`,
56
+ * `PrismaClientInitializationError`, ...) off a caught error without
57
+ * importing `@prisma/client` — the adapter never imports concrete Prisma
58
+ * types (see `Prisma7ClientLike`), so this duck-types via `error.name`
59
+ * instead of using `instanceof`.
60
+ */
61
+ function prismaErrorName(error) {
62
+ if (!(error instanceof Error))
63
+ return undefined;
64
+ return error.name || error.constructor?.name;
65
+ }
66
+ /**
67
+ * Converts any error caught around a Prisma Client call into a
68
+ * `VSRepoAdapterError`, so `VSRepoPrisma7Adapter` never lets a raw Prisma (or
69
+ * other) error escape. Already-wrapped errors — e.g. bubbling up from a
70
+ * nested adapter call, such as `save` inside `saveMany`, or a config/usage
71
+ * error thrown by the adapter itself (see `stripRelationFields`) — are
72
+ * returned as-is instead of being wrapped a second time.
73
+ *
74
+ * @param error - The raw error caught around a Prisma Client call.
75
+ * @param operation - Name of the adapter method that failed, used in the message.
76
+ */
77
+ function mapPrismaError(error, operation) {
78
+ if (error instanceof vsrepo_1.VSRepoAdapterError)
79
+ return error;
80
+ const name = prismaErrorName(error);
81
+ const prismaError = error;
82
+ switch (name) {
83
+ case "PrismaClientKnownRequestError": {
84
+ const code = prismaError.code
85
+ ? KNOWN_REQUEST_ERROR_CODE_MAP[prismaError.code]
86
+ : undefined;
87
+ return new vsrepo_1.VSRepoAdapterError(`'${operation}' failed (Prisma code: ${prismaError.code ?? "unknown"})`, code ?? vsrepo_1.AdapterErrorCode.UNKNOWN, error);
88
+ }
89
+ case "PrismaClientInitializationError":
90
+ return new vsrepo_1.VSRepoAdapterError(`'${operation}' failed to initialize/connect to the database`, vsrepo_1.AdapterErrorCode.CONNECTION_FAILED, error);
91
+ case "PrismaClientRustPanicError":
92
+ return new vsrepo_1.VSRepoAdapterError(`'${operation}' failed due to an internal Prisma engine panic`, vsrepo_1.AdapterErrorCode.INTERNAL, error);
93
+ case "PrismaClientValidationError":
94
+ return new vsrepo_1.VSRepoAdapterError(`'${operation}' received invalid data/arguments`, vsrepo_1.AdapterErrorCode.INVALID_DATA, error);
95
+ case "PrismaClientUnknownRequestError":
96
+ return new vsrepo_1.VSRepoAdapterError(`'${operation}' failed with an unrecognized Prisma error`, vsrepo_1.AdapterErrorCode.UNKNOWN, error);
97
+ default:
98
+ return new vsrepo_1.VSRepoAdapterError(`'${operation}' failed: ${prismaError?.message ?? "unknown error"}`, vsrepo_1.AdapterErrorCode.UNKNOWN, error);
99
+ }
100
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Faz o merge profundo, EM MEMÓRIA (sem persistir nada no banco), de `result`
3
+ * (registro já encontrado) com `obj` (payload enviado) — réplica exata do
4
+ * comportamento do `merge` da v1: ele só busca o registro e devolve o merge,
5
+ * quem decide o que fazer com o resultado (ex: chamar `save` depois) é quem
6
+ * chamou.
7
+ *
8
+ * Sem `relations`, é um deep merge padrão (arrays são concatenados). Com
9
+ * `relations`:
10
+ * - to-one (`oto`/`mto`) recebe merge direto (ou vira `null` se `obj` mandar
11
+ * `null` explicitamente)
12
+ * - to-many (`otm`/`mtm`) casa os itens de `result[key]` com os de `obj[key]`
13
+ * pela `pk` configurada: item com PK que bate -> merge entre os dois; item
14
+ * sem correspondente -> apenas adicionado. Itens existentes que não
15
+ * aparecem em `obj[key]` são mantidos (merge nunca remove nada).
16
+ */
17
+ import { AdapterRelations } from "../types/adapter-relations.type";
18
+ import { PlainObject } from "../types/plain-object.type";
19
+ export declare function mergeEntities<T extends PlainObject, U extends PlainObject>(result: T, obj: U, relations?: AdapterRelations<T>): U & T;