@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.
- package/LICENSE +21 -0
- package/README.md +233 -0
- package/README.pt-BR.md +233 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/parsers/data.parser.d.ts +34 -0
- package/dist/parsers/data.parser.js +134 -0
- package/dist/parsers/include.parser.d.ts +3 -0
- package/dist/parsers/include.parser.js +16 -0
- package/dist/parsers/order-by.parser.d.ts +15 -0
- package/dist/parsers/order-by.parser.js +38 -0
- package/dist/parsers/select.parser.d.ts +3 -0
- package/dist/parsers/select.parser.js +16 -0
- package/dist/parsers/where.parser.d.ts +31 -0
- package/dist/parsers/where.parser.js +187 -0
- package/dist/prisma7.adapter.d.ts +125 -0
- package/dist/prisma7.adapter.js +505 -0
- package/dist/resolvers/map-prisma-error.resolver.d.ts +13 -0
- package/dist/resolvers/map-prisma-error.resolver.js +100 -0
- package/dist/resolvers/merge-entities.resolver.d.ts +19 -0
- package/dist/resolvers/merge-entities.resolver.js +73 -0
- package/dist/types/adapter-config.type.d.ts +22 -0
- package/dist/types/adapter-config.type.js +2 -0
- package/dist/types/adapter-relations.type.d.ts +16 -0
- package/dist/types/adapter-relations.type.js +2 -0
- package/dist/types/plain-object.type.d.ts +1 -0
- package/dist/types/plain-object.type.js +2 -0
- package/dist/types/prisma-arg-like.type.d.ts +14 -0
- package/dist/types/prisma-arg-like.type.js +2 -0
- package/dist/types/prisma-repository-like.type.d.ts +20 -0
- package/dist/types/prisma-repository-like.type.js +2 -0
- package/dist/types/prisma7-client-like.type.d.ts +19 -0
- package/dist/types/prisma7-client-like.type.js +2 -0
- package/dist/types/prisma7-orm-types.type.d.ts +19 -0
- package/dist/types/prisma7-orm-types.type.js +2 -0
- package/dist/types/relation.type.d.ts +26 -0
- package/dist/types/relation.type.js +2 -0
- package/dist/validators/adapter-config.schema.d.ts +13 -0
- package/dist/validators/adapter-config.schema.js +45 -0
- package/dist/validators/is-decimal.validator.d.ts +5 -0
- package/dist/validators/is-decimal.validator.js +10 -0
- package/dist/validators/is-plain-object.validator.d.ts +2 -0
- package/dist/validators/is-plain-object.validator.js +11 -0
- package/dist/validators/relation.schema.d.ts +8 -0
- package/dist/validators/relation.schema.js +44 -0
- package/dist/validators/validate-adapter-config.validator.d.ts +8 -0
- package/dist/validators/validate-adapter-config.validator.js +54 -0
- 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;
|