@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,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser que converte um `DeepPartial<T>` (formato "cru" vindo do vsrepo) nos
|
|
4
|
+
* payloads `create`/`update` esperados pelo Prisma para escritas com relations,
|
|
5
|
+
* replicando o comportamento da v1 (`resolveCreateUpdatePayloadsWithRelations`).
|
|
6
|
+
*
|
|
7
|
+
* Sem `relations` configurado no adapter, cada campo é repassado como está —
|
|
8
|
+
* exceto `pkName`, que é omitido do `update` (não faz sentido reatribuir a PK
|
|
9
|
+
* num update parcial).
|
|
10
|
+
*
|
|
11
|
+
* Com `relations`, cada campo de relação é resolvido de acordo com seu `mode`/
|
|
12
|
+
* `restriction` (ver `Relation`):
|
|
13
|
+
* - to-one (`oto`/`mto`) sem PK no valor enviado -> `create` (e `upsert` no
|
|
14
|
+
* update, a não ser que `restriction: "add"`, que usa só `create`)
|
|
15
|
+
* - to-one com PK no valor enviado -> `connectOrCreate` (e
|
|
16
|
+
* `upsert` no update, a não ser que `restriction: "add"`)
|
|
17
|
+
* - to-one enviado como `null` -> `delete` (`oto` +
|
|
18
|
+
* `restriction: "set"`) ou `disconnect` (`mto` + `nullable`)
|
|
19
|
+
* - to-many (`otm`/`mtm`) — itens são separados entre "com PK" e "sem PK":
|
|
20
|
+
* os sem PK viram `create`, os com PK viram `connectOrCreate` no create e
|
|
21
|
+
* `upsert` no update; com `restriction: "set"`, itens de `otm` que não
|
|
22
|
+
* vieram no array são removidos (`deleteMany`) e itens de `mtm` têm o
|
|
23
|
+
* vínculo resetado (`set: []`) antes de reconectar
|
|
24
|
+
*
|
|
25
|
+
* Essa mesma saída `{ create, update }` é reaproveitada em todos os métodos de
|
|
26
|
+
* escrita do adapter: `create()` usa só `.create`, `update()`/`updateMany()`
|
|
27
|
+
* usam só `.update`, e `save()`/`upsert()` usam os dois.
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.parsePrismaWriteData = parsePrismaWriteData;
|
|
31
|
+
function omitKey(item, key) {
|
|
32
|
+
const clone = { ...item };
|
|
33
|
+
delete clone[key];
|
|
34
|
+
return clone;
|
|
35
|
+
}
|
|
36
|
+
function splitByPk(items, pk) {
|
|
37
|
+
const withPk = [];
|
|
38
|
+
const withoutPk = [];
|
|
39
|
+
for (const item of items) {
|
|
40
|
+
(item[pk] !== undefined ? withPk : withoutPk).push(item);
|
|
41
|
+
}
|
|
42
|
+
return { withPk, withoutPk };
|
|
43
|
+
}
|
|
44
|
+
function parseToOneRelation(field, relation) {
|
|
45
|
+
if (field === null) {
|
|
46
|
+
if (relation.mode === "oto" && relation.restriction === "set") {
|
|
47
|
+
return { update: { delete: true } };
|
|
48
|
+
}
|
|
49
|
+
if (relation.mode === "mto" && relation.nullable) {
|
|
50
|
+
return { update: { disconnect: true } };
|
|
51
|
+
}
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
const pkValue = field[relation.pk];
|
|
55
|
+
if (pkValue == null) {
|
|
56
|
+
return {
|
|
57
|
+
create: { create: field },
|
|
58
|
+
update: relation.restriction === "add"
|
|
59
|
+
? { create: field }
|
|
60
|
+
: { upsert: { create: field, update: field } },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const connectOrCreate = { where: { [relation.pk]: pkValue }, create: field };
|
|
64
|
+
return {
|
|
65
|
+
create: { connectOrCreate },
|
|
66
|
+
update: relation.restriction === "add"
|
|
67
|
+
? { connectOrCreate }
|
|
68
|
+
: {
|
|
69
|
+
upsert: {
|
|
70
|
+
where: { [relation.pk]: pkValue },
|
|
71
|
+
create: field,
|
|
72
|
+
update: omitKey(field, relation.pk),
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function parseToManyRelation(field, relation) {
|
|
78
|
+
const { withPk, withoutPk } = splitByPk(field, relation.pk);
|
|
79
|
+
const connectOrCreate = withPk.map(item => ({
|
|
80
|
+
where: { [relation.pk]: item[relation.pk] },
|
|
81
|
+
create: item,
|
|
82
|
+
}));
|
|
83
|
+
const upsert = withPk.map(item => ({
|
|
84
|
+
where: { [relation.pk]: item[relation.pk] },
|
|
85
|
+
create: item,
|
|
86
|
+
update: omitKey(item, relation.pk),
|
|
87
|
+
}));
|
|
88
|
+
const create = { create: withoutPk, connectOrCreate };
|
|
89
|
+
if (relation.restriction === "add") {
|
|
90
|
+
return {
|
|
91
|
+
create,
|
|
92
|
+
update: relation.mode === "mtm"
|
|
93
|
+
? { create: withoutPk, connectOrCreate }
|
|
94
|
+
: { create: withoutPk, upsert },
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
create,
|
|
99
|
+
update: relation.mode === "mtm"
|
|
100
|
+
? { set: [], create: withoutPk, connectOrCreate }
|
|
101
|
+
: {
|
|
102
|
+
deleteMany: { [relation.pk]: { notIn: withPk.map(item => item[relation.pk]) } },
|
|
103
|
+
create: withoutPk,
|
|
104
|
+
upsert,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function parsePrismaWriteData(obj, pkName, relations) {
|
|
109
|
+
const create = {};
|
|
110
|
+
const update = {};
|
|
111
|
+
for (const [key, field] of Object.entries(obj)) {
|
|
112
|
+
if (field === undefined)
|
|
113
|
+
continue;
|
|
114
|
+
const relation = relations?.[key];
|
|
115
|
+
if (!relation) {
|
|
116
|
+
create[key] = field;
|
|
117
|
+
if (key !== pkName)
|
|
118
|
+
update[key] = field;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (relation.mode === "oto" || relation.mode === "mto") {
|
|
122
|
+
const parsed = parseToOneRelation(field, relation);
|
|
123
|
+
if (parsed.create !== undefined)
|
|
124
|
+
create[key] = parsed.create;
|
|
125
|
+
if (parsed.update !== undefined)
|
|
126
|
+
update[key] = parsed.update;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const parsed = parseToManyRelation(field, relation);
|
|
130
|
+
create[key] = parsed.create;
|
|
131
|
+
update[key] = parsed.update;
|
|
132
|
+
}
|
|
133
|
+
return { create, update };
|
|
134
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parsePrismaInclude = parsePrismaInclude;
|
|
4
|
+
const is_plain_object_validator_1 = require("../validators/is-plain-object.validator");
|
|
5
|
+
function parsePrismaInclude(relations) {
|
|
6
|
+
const include = {};
|
|
7
|
+
for (const [key, value] of Object.entries(relations)) {
|
|
8
|
+
if ((0, is_plain_object_validator_1.isPlainObject)(value)) {
|
|
9
|
+
include[key] = { include: parsePrismaInclude(value) };
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
include[key] = value;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return include;
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser que converte um `Ordering<T>` (formato "amigável" do vsrepo) num
|
|
3
|
+
* `orderBy` válido de qualquer modelo do Prisma.
|
|
4
|
+
*
|
|
5
|
+
* Suporta:
|
|
6
|
+
* - Direção em qualquer caixa: { name: "ASC" } / { name: "asc" } -> { name: "asc" }
|
|
7
|
+
* - Ordenação encadeada: [{ name: "asc" }, { createdAt: "desc" }]
|
|
8
|
+
* - Ordenação aninhada em relations to-one: { address: { city: "asc" } }
|
|
9
|
+
*/
|
|
10
|
+
import { Ordering } from "vsrepo";
|
|
11
|
+
/**
|
|
12
|
+
* Use o segundo generic para tipar o retorno com o `OrderByInput`
|
|
13
|
+
* do Prisma correspondente ao modelo.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parsePrismaOrderBy<T, O = any>(order: Ordering<T> | undefined | null): O | undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser que converte um `Ordering<T>` (formato "amigável" do vsrepo) num
|
|
4
|
+
* `orderBy` válido de qualquer modelo do Prisma.
|
|
5
|
+
*
|
|
6
|
+
* Suporta:
|
|
7
|
+
* - Direção em qualquer caixa: { name: "ASC" } / { name: "asc" } -> { name: "asc" }
|
|
8
|
+
* - Ordenação encadeada: [{ name: "asc" }, { createdAt: "desc" }]
|
|
9
|
+
* - Ordenação aninhada em relations to-one: { address: { city: "asc" } }
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.parsePrismaOrderBy = parsePrismaOrderBy;
|
|
13
|
+
const is_plain_object_validator_1 = require("../validators/is-plain-object.validator");
|
|
14
|
+
function isSortDirection(value) {
|
|
15
|
+
return value === "asc" || value === "desc" || value === "ASC" || value === "DESC";
|
|
16
|
+
}
|
|
17
|
+
function parseOrderByField(order) {
|
|
18
|
+
const result = {};
|
|
19
|
+
for (const [key, value] of Object.entries(order)) {
|
|
20
|
+
if (value === undefined)
|
|
21
|
+
continue;
|
|
22
|
+
result[key] = isSortDirection(value)
|
|
23
|
+
? value.toLowerCase()
|
|
24
|
+
: (0, is_plain_object_validator_1.isPlainObject)(value)
|
|
25
|
+
? parseOrderByField(value)
|
|
26
|
+
: value;
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Use o segundo generic para tipar o retorno com o `OrderByInput`
|
|
32
|
+
* do Prisma correspondente ao modelo.
|
|
33
|
+
*/
|
|
34
|
+
function parsePrismaOrderBy(order) {
|
|
35
|
+
if (order === undefined || order === null)
|
|
36
|
+
return undefined;
|
|
37
|
+
return (Array.isArray(order) ? order.map(o => parseOrderByField(o)) : parseOrderByField(order));
|
|
38
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parsePrismaSelect = parsePrismaSelect;
|
|
4
|
+
const is_plain_object_validator_1 = require("../validators/is-plain-object.validator");
|
|
5
|
+
function parsePrismaSelect(relations) {
|
|
6
|
+
const select = {};
|
|
7
|
+
for (const [key, value] of Object.entries(relations)) {
|
|
8
|
+
if ((0, is_plain_object_validator_1.isPlainObject)(value)) {
|
|
9
|
+
select[key] = { select: parsePrismaSelect(value) };
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
select[key] = value;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return select;
|
|
16
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser que converte um `VSRepoWhere<T>` (formato "amigável" da aplicação)
|
|
3
|
+
* em um `where` válido de qualquer modelo do Prisma.
|
|
4
|
+
*
|
|
5
|
+
* Suporta:
|
|
6
|
+
* - Valor direto: { name: "Ana" }
|
|
7
|
+
* - Operadores de campo: { age: { gte: 18, lte: 65 } }
|
|
8
|
+
* - `between`: { age: { between: [18, 65] } } -> { gte: 18, lte: 65 }
|
|
9
|
+
* - `not` (valor ou operador): { name: { not: "Ana" } }
|
|
10
|
+
* { name: { not: { contains: "an" } } }
|
|
11
|
+
* - Strings case-insensitive: { name: { contains: "ana", ignoreCase: true } }
|
|
12
|
+
* -> { contains: "ana", mode: "insensitive" }
|
|
13
|
+
* - Relação lista (array): { posts: { _some: { title: "x" } } } -> { posts: { some: {...} } }
|
|
14
|
+
* - Relação objeto (to-one): { author: { _with: { id: 1 } } } -> { author: { is: {...} } }
|
|
15
|
+
* { author: { _without: { id: 1 } } } -> { author: { isNot: {...} } }
|
|
16
|
+
* - Lógicos no nível raiz: { AND: [...] , OR: [...], NOT: {...} }
|
|
17
|
+
*
|
|
18
|
+
* Reflete a tipagem atual: `AND`/`OR`/`NOT` só existem em `VSRepoWhere<T>`
|
|
19
|
+
* (nível raiz). Dentro de relações (`_some`/`_every`/`_none`/`_with`/
|
|
20
|
+
* `_without`), o tipo usado é `VSRepoWherePlain<U>`, que NÃO tem esses
|
|
21
|
+
* operadores lógicos — por isso a recursão para relações usa
|
|
22
|
+
* `parsePlainWhere`, e não `parseWhere`.
|
|
23
|
+
*/
|
|
24
|
+
import { VSRepoWhere } from "vsrepo";
|
|
25
|
+
/**
|
|
26
|
+
* API pública. Use o segundo generic para tipar o retorno com o `WhereInput`
|
|
27
|
+
* do Prisma correspondente ao modelo, ex:
|
|
28
|
+
*
|
|
29
|
+
* parseVSRepoWhere<User, Prisma.UserWhereInput>(where)
|
|
30
|
+
*/
|
|
31
|
+
export declare function parsePrismaWhere<T, W = any>(where: VSRepoWhere<T> | undefined | null): W | undefined;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser que converte um `VSRepoWhere<T>` (formato "amigável" da aplicação)
|
|
4
|
+
* em um `where` válido de qualquer modelo do Prisma.
|
|
5
|
+
*
|
|
6
|
+
* Suporta:
|
|
7
|
+
* - Valor direto: { name: "Ana" }
|
|
8
|
+
* - Operadores de campo: { age: { gte: 18, lte: 65 } }
|
|
9
|
+
* - `between`: { age: { between: [18, 65] } } -> { gte: 18, lte: 65 }
|
|
10
|
+
* - `not` (valor ou operador): { name: { not: "Ana" } }
|
|
11
|
+
* { name: { not: { contains: "an" } } }
|
|
12
|
+
* - Strings case-insensitive: { name: { contains: "ana", ignoreCase: true } }
|
|
13
|
+
* -> { contains: "ana", mode: "insensitive" }
|
|
14
|
+
* - Relação lista (array): { posts: { _some: { title: "x" } } } -> { posts: { some: {...} } }
|
|
15
|
+
* - Relação objeto (to-one): { author: { _with: { id: 1 } } } -> { author: { is: {...} } }
|
|
16
|
+
* { author: { _without: { id: 1 } } } -> { author: { isNot: {...} } }
|
|
17
|
+
* - Lógicos no nível raiz: { AND: [...] , OR: [...], NOT: {...} }
|
|
18
|
+
*
|
|
19
|
+
* Reflete a tipagem atual: `AND`/`OR`/`NOT` só existem em `VSRepoWhere<T>`
|
|
20
|
+
* (nível raiz). Dentro de relações (`_some`/`_every`/`_none`/`_with`/
|
|
21
|
+
* `_without`), o tipo usado é `VSRepoWherePlain<U>`, que NÃO tem esses
|
|
22
|
+
* operadores lógicos — por isso a recursão para relações usa
|
|
23
|
+
* `parsePlainWhere`, e não `parseWhere`.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.parsePrismaWhere = parsePrismaWhere;
|
|
27
|
+
const is_decimal_validator_1 = require("../validators/is-decimal.validator");
|
|
28
|
+
const is_plain_object_validator_1 = require("../validators/is-plain-object.validator");
|
|
29
|
+
/** Chaves reconhecidas como operadores de campo do VSRepoFieldOperators */
|
|
30
|
+
const FIELD_OPERATOR_KEYS = new Set([
|
|
31
|
+
"equals",
|
|
32
|
+
"not",
|
|
33
|
+
"in",
|
|
34
|
+
"notIn",
|
|
35
|
+
"gt",
|
|
36
|
+
"gte",
|
|
37
|
+
"lt",
|
|
38
|
+
"lte",
|
|
39
|
+
"between",
|
|
40
|
+
"contains",
|
|
41
|
+
"startsWith",
|
|
42
|
+
"endsWith",
|
|
43
|
+
"ignoreCase",
|
|
44
|
+
]);
|
|
45
|
+
/** Chaves de operadores de string que aceitam `mode: 'insensitive'` no Prisma */
|
|
46
|
+
const CASE_SENSITIVE_STRING_KEYS = ["equals", "contains", "startsWith", "endsWith"];
|
|
47
|
+
const LOGICAL_KEYS = new Set(["AND", "OR", "NOT"]);
|
|
48
|
+
/** Verifica se um objeto "parece" um VSRepoFieldOperators (e não um filtro de relação/nested where) */
|
|
49
|
+
function isFieldOperatorObject(value) {
|
|
50
|
+
return Object.keys(value).some(key => FIELD_OPERATOR_KEYS.has(key));
|
|
51
|
+
}
|
|
52
|
+
function isArrayRelationFilter(value) {
|
|
53
|
+
return "_some" in value || "_every" in value || "_none" in value;
|
|
54
|
+
}
|
|
55
|
+
function isObjectRelationFilter(value) {
|
|
56
|
+
return "_with" in value || "_without" in value;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Converte um VSRepoFieldOperators<V> em um filtro de campo do Prisma.
|
|
60
|
+
*/
|
|
61
|
+
function parseFieldOperators(value) {
|
|
62
|
+
const result = {};
|
|
63
|
+
const ignoreCase = value.ignoreCase === true;
|
|
64
|
+
for (const [key, val] of Object.entries(value)) {
|
|
65
|
+
if (val === undefined)
|
|
66
|
+
continue;
|
|
67
|
+
switch (key) {
|
|
68
|
+
case "ignoreCase":
|
|
69
|
+
// tratado à parte, no final (vira `mode`)
|
|
70
|
+
break;
|
|
71
|
+
case "between": {
|
|
72
|
+
const [min, max] = val;
|
|
73
|
+
if (min !== undefined)
|
|
74
|
+
result.gte = min;
|
|
75
|
+
if (max !== undefined)
|
|
76
|
+
result.lte = max;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case "not": {
|
|
80
|
+
result.not =
|
|
81
|
+
(0, is_plain_object_validator_1.isPlainObject)(val) && isFieldOperatorObject(val)
|
|
82
|
+
? parseFieldOperators(val)
|
|
83
|
+
: val;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
default:
|
|
87
|
+
result[key] = val;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (ignoreCase) {
|
|
91
|
+
const hasCaseSensitiveKey = CASE_SENSITIVE_STRING_KEYS.some(k => k in result);
|
|
92
|
+
if (hasCaseSensitiveKey) {
|
|
93
|
+
result.mode = "insensitive";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
/** Converte { _some, _every, _none } em { some, every, none } (VSRepoWherePlain — sem AND/OR/NOT) */
|
|
99
|
+
function parseArrayRelationFilter(value) {
|
|
100
|
+
const result = {};
|
|
101
|
+
if (value._some !== undefined) {
|
|
102
|
+
result.some = parsePlainWhere(value._some);
|
|
103
|
+
}
|
|
104
|
+
if (value._every !== undefined) {
|
|
105
|
+
result.every = parsePlainWhere(value._every);
|
|
106
|
+
}
|
|
107
|
+
if (value._none !== undefined) {
|
|
108
|
+
result.none = parsePlainWhere(value._none);
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
/** Converte { _with, _without } em { is, isNot } (VSRepoWherePlain — sem AND/OR/NOT) */
|
|
113
|
+
function parseObjectRelationFilter(value) {
|
|
114
|
+
if (value._with !== undefined) {
|
|
115
|
+
return { is: parsePlainWhere(value._with) };
|
|
116
|
+
}
|
|
117
|
+
if (value._without !== undefined) {
|
|
118
|
+
return { isNot: parsePlainWhere(value._without) };
|
|
119
|
+
}
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
/** Decide como interpretar o valor de um campo do where */
|
|
123
|
+
function parseFieldValue(value) {
|
|
124
|
+
// valores primitivos (string, number, boolean, Date, null, bigint, Decimal) passam direto
|
|
125
|
+
if (value === null || typeof value !== "object" || value instanceof Date || (0, is_decimal_validator_1.isDecimal)(value)) {
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
// arrays (usados em `in`/`notIn` no nível mais alto, ex: { status: ["A","B"] })
|
|
129
|
+
if (Array.isArray(value)) {
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
const obj = value;
|
|
133
|
+
if (isArrayRelationFilter(obj)) {
|
|
134
|
+
return parseArrayRelationFilter(obj);
|
|
135
|
+
}
|
|
136
|
+
if (isObjectRelationFilter(obj)) {
|
|
137
|
+
return parseObjectRelationFilter(obj);
|
|
138
|
+
}
|
|
139
|
+
if (isFieldOperatorObject(obj)) {
|
|
140
|
+
return parseFieldOperators(obj);
|
|
141
|
+
}
|
|
142
|
+
// fallback: nested where (não deveria ocorrer para campos primitivos bem tipados)
|
|
143
|
+
return parsePlainWhere(obj);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Converte um `VSRepoWherePlain<T>` (sem AND/OR/NOT) — usado para o corpo de
|
|
147
|
+
* relações (`_some`/`_every`/`_none`/`_with`/`_without`).
|
|
148
|
+
*/
|
|
149
|
+
function parsePlainWhere(where) {
|
|
150
|
+
if (where === undefined || where === null)
|
|
151
|
+
return undefined;
|
|
152
|
+
const result = {};
|
|
153
|
+
for (const [key, value] of Object.entries(where)) {
|
|
154
|
+
if (value === undefined)
|
|
155
|
+
continue;
|
|
156
|
+
result[key] = parseFieldValue(value);
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Converte um `VSRepoWhere<T>` completo (nível raiz, com AND/OR/NOT) em um
|
|
162
|
+
* where do Prisma.
|
|
163
|
+
*/
|
|
164
|
+
function parseWhere(where) {
|
|
165
|
+
if (where === undefined || where === null)
|
|
166
|
+
return undefined;
|
|
167
|
+
const result = {};
|
|
168
|
+
for (const [key, value] of Object.entries(where)) {
|
|
169
|
+
if (value === undefined)
|
|
170
|
+
continue;
|
|
171
|
+
if (LOGICAL_KEYS.has(key)) {
|
|
172
|
+
result[key] = Array.isArray(value) ? value.map(v => parseWhere(v)) : parseWhere(value);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
result[key] = parseFieldValue(value);
|
|
176
|
+
}
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* API pública. Use o segundo generic para tipar o retorno com o `WhereInput`
|
|
181
|
+
* do Prisma correspondente ao modelo, ex:
|
|
182
|
+
*
|
|
183
|
+
* parseVSRepoWhere<User, Prisma.UserWhereInput>(where)
|
|
184
|
+
*/
|
|
185
|
+
function parsePrismaWhere(where) {
|
|
186
|
+
return parseWhere(where);
|
|
187
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { AdapterMethodOptions, AdapterQueryOptions, CountResult, DeepPartial, VSRepoAdapter, VSRepoWhere } from "vsrepo";
|
|
2
|
+
import { VSRepoPrisma7AdapterConfig } from "./types/adapter-config.type";
|
|
3
|
+
/**
|
|
4
|
+
* `VSRepoAdapter` implementation for Prisma 7. Translates every method of the
|
|
5
|
+
* adapter contract into Prisma Client calls, resolving `VSRepoWhere`/`Ordering`/
|
|
6
|
+
* `select`/`include` through the parsers, and — when a
|
|
7
|
+
* `relations` config is provided — resolving relation fields on `create`/
|
|
8
|
+
* `update`/`upsert`/`merge` the same way the v1 of VSRepository did (see
|
|
9
|
+
* `Relation`).
|
|
10
|
+
*
|
|
11
|
+
* Every method always rejects with a `VSRepoAdapterError` — any error raised
|
|
12
|
+
* by Prisma (or by the adapter itself) is translated through `mapPrismaError`
|
|
13
|
+
* before leaving the adapter, so callers never have to deal with a raw
|
|
14
|
+
* Prisma/driver error shape.
|
|
15
|
+
*
|
|
16
|
+
* @template T Entity type this adapter operates on.
|
|
17
|
+
*
|
|
18
|
+
* @publicApi
|
|
19
|
+
*/
|
|
20
|
+
export declare class VSRepoPrisma7Adapter<T> extends VSRepoAdapter<T> {
|
|
21
|
+
private readonly prisma;
|
|
22
|
+
private readonly tableName;
|
|
23
|
+
private readonly pkName;
|
|
24
|
+
private readonly relations?;
|
|
25
|
+
private readonly logger;
|
|
26
|
+
constructor(prisma: any, config: VSRepoPrisma7AdapterConfig<T>);
|
|
27
|
+
private getPrismaRepository;
|
|
28
|
+
/** Resolves the "read" part of a Prisma arg: select/include/orderBy/pagination. */
|
|
29
|
+
private resolveReadArg;
|
|
30
|
+
/**
|
|
31
|
+
* Strips relation fields from a payload — used by `createMany`/`updateMany`/
|
|
32
|
+
* `updateManyReturning`, since those Prisma operations don't support nested
|
|
33
|
+
* writes (scalar fields only). Throws a `VSRepoAdapterError` (code
|
|
34
|
+
* `NOT_SUPPORTED`) instead of silently ignoring the field, when a configured
|
|
35
|
+
* relation field is present in the payload.
|
|
36
|
+
*/
|
|
37
|
+
private stripRelationFields;
|
|
38
|
+
/**
|
|
39
|
+
* Tells apart a root Prisma Client from an active transaction client: only
|
|
40
|
+
* the root client exposes `$on` (used for event listeners), Prisma's
|
|
41
|
+
* interactive transaction client doesn't (see `Prisma7ClientLike`).
|
|
42
|
+
*/
|
|
43
|
+
private isRootClient;
|
|
44
|
+
/**
|
|
45
|
+
* Runs `fn` against a transaction, reusing `db` when it's already a
|
|
46
|
+
* transaction client (see `isRootClient`) instead of nesting a new one —
|
|
47
|
+
* this is what lets `saveMany`/`deleteManyReturning` participate in a
|
|
48
|
+
* transaction already started via `VSRepository.transaction()`.
|
|
49
|
+
* When `db` isn't provided, or is a root client, a new transaction is
|
|
50
|
+
* started on it (or on `this.prisma` when `db` isn't provided at all).
|
|
51
|
+
*/
|
|
52
|
+
private runTransactional;
|
|
53
|
+
/** Returns the underlying ORM client instance used outside of transactions. */
|
|
54
|
+
getDbClient(): any;
|
|
55
|
+
/** Runs `fn` inside a native transaction of the underlying ORM/database. */
|
|
56
|
+
runInTransaction<R>(fn: (tx: any) => Promise<R>, options?: {
|
|
57
|
+
isolationLevel?: any;
|
|
58
|
+
}): Promise<R>;
|
|
59
|
+
/** Fetches a single record matching `where`. */
|
|
60
|
+
findOne(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<T | null>;
|
|
61
|
+
/** Fetches a single record matching `where`, throwing if none is found. */
|
|
62
|
+
findOneOrThrow(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<T>;
|
|
63
|
+
/** Fetches all records matching `where`. */
|
|
64
|
+
findMany(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T> & {
|
|
65
|
+
distinct?: (keyof T)[];
|
|
66
|
+
}): Promise<T[]>;
|
|
67
|
+
/** Creates or updates (upsert) a single record. */
|
|
68
|
+
save(obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<T>;
|
|
69
|
+
/**
|
|
70
|
+
* Creates or updates (upsert) multiple records. Prisma has no native bulk
|
|
71
|
+
* upsert, so each record is saved individually inside a transaction —
|
|
72
|
+
* reusing `options.db` when it's already a transaction client (see
|
|
73
|
+
* `runTransactional`).
|
|
74
|
+
*/
|
|
75
|
+
saveMany(objs: DeepPartial<T>[], options?: AdapterMethodOptions<T>): Promise<T[]>;
|
|
76
|
+
/** Deletes a single record matching `where`. */
|
|
77
|
+
delete(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<T>;
|
|
78
|
+
/** Deletes every record matching `where`, returning the count of affected rows. */
|
|
79
|
+
deleteMany(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<CountResult>;
|
|
80
|
+
/**
|
|
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.
|
|
86
|
+
*/
|
|
87
|
+
deleteManyReturning(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<T[]>;
|
|
88
|
+
/** Updates a single record matching `where`. */
|
|
89
|
+
update(where: VSRepoWhere<T>, obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<T>;
|
|
90
|
+
/** Updates every record matching `where`, returning the count of affected rows. */
|
|
91
|
+
updateMany(where: VSRepoWhere<T>, obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<CountResult>;
|
|
92
|
+
/** Updates every record matching `where`, returning the updated records. */
|
|
93
|
+
updateManyReturning(where: VSRepoWhere<T>, obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<T[]>;
|
|
94
|
+
/** Returns the number of records matching `where`. */
|
|
95
|
+
count(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<number>;
|
|
96
|
+
/** Checks whether at least one record matching `where` exists. */
|
|
97
|
+
exists(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<boolean>;
|
|
98
|
+
/** Executes a raw query/statement against the underlying database, used by `@QueryMethod`. */
|
|
99
|
+
query<R = any>(query: string, options?: AdapterQueryOptions): Promise<R>;
|
|
100
|
+
/** Creates a single record. */
|
|
101
|
+
create(obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<T>;
|
|
102
|
+
/**
|
|
103
|
+
* Creates multiple records in a single operation. Relation fields are
|
|
104
|
+
* stripped (see `stripRelationFields`), since Prisma's `createMany` doesn't
|
|
105
|
+
* support nested writes.
|
|
106
|
+
*/
|
|
107
|
+
createMany(objs: DeepPartial<T>[], options?: AdapterMethodOptions<T> & {
|
|
108
|
+
ignoreConflicts?: boolean;
|
|
109
|
+
}): Promise<CountResult>;
|
|
110
|
+
/**
|
|
111
|
+
* Creates multiple records in a single operation, returning the created records.
|
|
112
|
+
*/
|
|
113
|
+
createManyReturning(objs: DeepPartial<T>[], options?: AdapterMethodOptions<T> & {
|
|
114
|
+
ignoreConflicts?: boolean;
|
|
115
|
+
}): Promise<T[]>;
|
|
116
|
+
/**
|
|
117
|
+
* Fetches the record matching `where` and returns it deep-merged with
|
|
118
|
+
* `obj`, WITHOUT persisting anything — same behavior the v1 of
|
|
119
|
+
* VSRepository had (see `mergeEntities`). It's on the caller to decide
|
|
120
|
+
* what to do with the result (e.g. call `save` next).
|
|
121
|
+
*/
|
|
122
|
+
merge<K>(where: VSRepoWhere<T>, obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<K & T>;
|
|
123
|
+
/** Creates a record if none matches `where`, otherwise updates it. */
|
|
124
|
+
upsert(where: VSRepoWhere<T>, create: DeepPartial<T>, update: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<T>;
|
|
125
|
+
}
|