arkormx 1.3.3 → 2.0.0-next.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/README.md +9 -0
- package/dist/cli.mjs +16 -2
- package/dist/index.cjs +2767 -1504
- package/dist/index.d.cts +579 -174
- package/dist/index.d.mts +579 -174
- package/dist/index.mjs +2454 -1196
- package/package.json +7 -4
package/dist/index.mjs
CHANGED
|
@@ -1,99 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { dirname, extname, join, resolve } from "node:path";
|
|
3
|
-
import { spawnSync } from "node:child_process";
|
|
4
|
-
import { str } from "@h3ravel/support";
|
|
5
|
-
import path, { dirname as dirname$1, extname as extname$1, join as join$1, relative } from "path";
|
|
6
|
-
import { copyFileSync, existsSync as existsSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, rmSync as rmSync$1, writeFileSync as writeFileSync$1 } from "fs";
|
|
1
|
+
import { sql } from "kysely";
|
|
7
2
|
import { AsyncLocalStorage } from "async_hooks";
|
|
8
3
|
import { createJiti } from "@rexxars/jiti";
|
|
9
4
|
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { dirname, extname, join, resolve } from "node:path";
|
|
10
6
|
import { createRequire } from "module";
|
|
7
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "fs";
|
|
11
8
|
import { fileURLToPath } from "url";
|
|
9
|
+
import path, { dirname as dirname$1, extname as extname$1, join as join$1, relative } from "path";
|
|
10
|
+
import { str } from "@h3ravel/support";
|
|
11
|
+
import { existsSync as existsSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, rmSync as rmSync$1, writeFileSync as writeFileSync$1 } from "node:fs";
|
|
12
|
+
import { spawnSync } from "node:child_process";
|
|
12
13
|
import { Logger } from "@h3ravel/shared";
|
|
13
14
|
import { Command } from "@h3ravel/musket";
|
|
14
15
|
import { createHash } from "node:crypto";
|
|
15
16
|
import { Collection } from "@h3ravel/collect.js";
|
|
16
17
|
|
|
17
|
-
//#region src/Attribute.ts
|
|
18
|
-
var Attribute = class Attribute {
|
|
19
|
-
get;
|
|
20
|
-
set;
|
|
21
|
-
constructor(options = {}) {
|
|
22
|
-
this.get = options.get;
|
|
23
|
-
this.set = options.set;
|
|
24
|
-
}
|
|
25
|
-
static make(options) {
|
|
26
|
-
return new Attribute(options);
|
|
27
|
-
}
|
|
28
|
-
static isAttribute(value) {
|
|
29
|
-
if (!value || typeof value !== "object") return false;
|
|
30
|
-
return value instanceof Attribute;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
//#endregion
|
|
35
|
-
//#region src/casts.ts
|
|
36
|
-
const builtinCasts = {
|
|
37
|
-
string: {
|
|
38
|
-
get: (value) => value == null ? value : String(value),
|
|
39
|
-
set: (value) => value == null ? value : String(value)
|
|
40
|
-
},
|
|
41
|
-
number: {
|
|
42
|
-
get: (value) => value == null ? value : Number(value),
|
|
43
|
-
set: (value) => value == null ? value : Number(value)
|
|
44
|
-
},
|
|
45
|
-
boolean: {
|
|
46
|
-
get: (value) => value == null ? value : Boolean(value),
|
|
47
|
-
set: (value) => value == null ? value : Boolean(value)
|
|
48
|
-
},
|
|
49
|
-
date: {
|
|
50
|
-
get: (value) => {
|
|
51
|
-
if (value == null || value instanceof Date) return value;
|
|
52
|
-
return new Date(String(value));
|
|
53
|
-
},
|
|
54
|
-
set: (value) => {
|
|
55
|
-
if (value == null || value instanceof Date) return value;
|
|
56
|
-
return new Date(String(value));
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
json: {
|
|
60
|
-
get: (value) => {
|
|
61
|
-
if (value == null || typeof value !== "string") return value;
|
|
62
|
-
try {
|
|
63
|
-
return JSON.parse(value);
|
|
64
|
-
} catch {
|
|
65
|
-
return value;
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
set: (value) => {
|
|
69
|
-
if (value == null || typeof value === "string") return value;
|
|
70
|
-
return JSON.stringify(value);
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
array: {
|
|
74
|
-
get: (value) => {
|
|
75
|
-
if (Array.isArray(value)) return value;
|
|
76
|
-
if (typeof value === "string") try {
|
|
77
|
-
const parsed = JSON.parse(value);
|
|
78
|
-
return Array.isArray(parsed) ? parsed : [parsed];
|
|
79
|
-
} catch {
|
|
80
|
-
return [value];
|
|
81
|
-
}
|
|
82
|
-
if (value == null) return value;
|
|
83
|
-
return [value];
|
|
84
|
-
},
|
|
85
|
-
set: (value) => {
|
|
86
|
-
if (value == null) return value;
|
|
87
|
-
return Array.isArray(value) ? value : [value];
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
function resolveCast(definition) {
|
|
92
|
-
if (typeof definition === "string") return builtinCasts[definition];
|
|
93
|
-
return definition;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
//#endregion
|
|
97
18
|
//#region src/Exceptions/ArkormException.ts
|
|
98
19
|
var ArkormException = class extends Error {
|
|
99
20
|
code;
|
|
@@ -136,119 +57,1066 @@ var ArkormException = class extends Error {
|
|
|
136
57
|
};
|
|
137
58
|
|
|
138
59
|
//#endregion
|
|
139
|
-
//#region src/
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
* @since 0.2.2
|
|
148
|
-
*/
|
|
149
|
-
var ForeignKeyBuilder = class {
|
|
150
|
-
foreignKey;
|
|
151
|
-
constructor(foreignKey) {
|
|
152
|
-
this.foreignKey = foreignKey;
|
|
60
|
+
//#region src/Exceptions/UnsupportedAdapterFeatureException.ts
|
|
61
|
+
var UnsupportedAdapterFeatureException = class extends ArkormException {
|
|
62
|
+
constructor(message, context = {}) {
|
|
63
|
+
super(message, {
|
|
64
|
+
code: "UNSUPPORTED_ADAPTER_FEATURE",
|
|
65
|
+
...context
|
|
66
|
+
});
|
|
67
|
+
this.name = "UnsupportedAdapterFeatureException";
|
|
153
68
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/adapters/KyselyDatabaseAdapter.ts
|
|
73
|
+
var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
74
|
+
capabilities = {
|
|
75
|
+
transactions: true,
|
|
76
|
+
returning: true,
|
|
77
|
+
insertMany: true,
|
|
78
|
+
updateMany: true,
|
|
79
|
+
deleteMany: true,
|
|
80
|
+
exists: true,
|
|
81
|
+
relationLoads: false,
|
|
82
|
+
relationAggregates: false,
|
|
83
|
+
relationFilters: false,
|
|
84
|
+
rawWhere: false
|
|
85
|
+
};
|
|
86
|
+
constructor(db) {
|
|
87
|
+
this.db = db;
|
|
88
|
+
}
|
|
89
|
+
resolveTable(target) {
|
|
90
|
+
if (target.table && target.table.trim().length > 0) return target.table;
|
|
91
|
+
throw new ArkormException("Kysely adapter requires a concrete target table.", {
|
|
92
|
+
operation: "adapter.table",
|
|
93
|
+
model: target.modelName,
|
|
94
|
+
meta: { target }
|
|
95
|
+
});
|
|
165
96
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
* as "CASCADE", "SET NULL", or "RESTRICT".
|
|
169
|
-
*
|
|
170
|
-
* @param action
|
|
171
|
-
* @returns
|
|
172
|
-
*/
|
|
173
|
-
onDelete(action) {
|
|
174
|
-
this.foreignKey.onDelete = action;
|
|
175
|
-
return this;
|
|
97
|
+
resolvePrimaryKey(target) {
|
|
98
|
+
return this.mapColumn(target, target.primaryKey || "id");
|
|
176
99
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
* can be used in the ORM for more intuitive access to related models.
|
|
180
|
-
*
|
|
181
|
-
* @param name
|
|
182
|
-
* @returns
|
|
183
|
-
*/
|
|
184
|
-
alias(name) {
|
|
185
|
-
this.foreignKey.relationAlias = name;
|
|
186
|
-
return this;
|
|
100
|
+
mapColumn(target, column) {
|
|
101
|
+
return target.columns?.[column] ?? column;
|
|
187
102
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
*/
|
|
194
|
-
inverseAlias(name) {
|
|
195
|
-
this.foreignKey.inverseRelationAlias = name;
|
|
196
|
-
return this;
|
|
103
|
+
reverseColumnMap(target) {
|
|
104
|
+
return Object.entries(target.columns ?? {}).reduce((all, [attribute, column]) => {
|
|
105
|
+
all[column] = attribute;
|
|
106
|
+
return all;
|
|
107
|
+
}, {});
|
|
197
108
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return this;
|
|
109
|
+
mapRow(target, row) {
|
|
110
|
+
if (!row) return null;
|
|
111
|
+
const reverseMap = this.reverseColumnMap(target);
|
|
112
|
+
return Object.entries(row).reduce((mapped, [key, value]) => {
|
|
113
|
+
mapped[reverseMap[key] ?? key] = value;
|
|
114
|
+
return mapped;
|
|
115
|
+
}, {});
|
|
116
|
+
}
|
|
117
|
+
mapRows(target, rows) {
|
|
118
|
+
return rows.map((row) => this.mapRow(target, row));
|
|
119
|
+
}
|
|
120
|
+
mapValues(target, values) {
|
|
121
|
+
return Object.entries(values).reduce((mapped, [key, value]) => {
|
|
122
|
+
mapped[this.mapColumn(target, key)] = value;
|
|
123
|
+
return mapped;
|
|
124
|
+
}, {});
|
|
125
|
+
}
|
|
126
|
+
buildSelectList(target, columns) {
|
|
127
|
+
if (!columns || columns.length === 0) return sql.raw("*");
|
|
128
|
+
return sql.join(columns.map(({ column, alias }) => {
|
|
129
|
+
const mappedColumn = this.mapColumn(target, column);
|
|
130
|
+
const resultAlias = alias ?? column;
|
|
131
|
+
if (mappedColumn === resultAlias) return sql.ref(mappedColumn);
|
|
132
|
+
return sql`${sql.ref(mappedColumn)} as ${sql.id(resultAlias)}`;
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
buildOrderBy(target, orderBy) {
|
|
136
|
+
if (!orderBy || orderBy.length === 0) return sql``;
|
|
137
|
+
return sql` order by ${sql.join(orderBy.map(({ column, direction }) => {
|
|
138
|
+
return sql`${sql.ref(this.mapColumn(target, column))} ${sql.raw(direction === "desc" ? "desc" : "asc")}`;
|
|
139
|
+
}), sql`, `)}`;
|
|
140
|
+
}
|
|
141
|
+
buildConditionValueList(value) {
|
|
142
|
+
if (Array.isArray(value)) return value;
|
|
143
|
+
return typeof value === "undefined" ? [] : [value];
|
|
144
|
+
}
|
|
145
|
+
buildComparisonCondition(target, condition) {
|
|
146
|
+
const column = sql.ref(this.mapColumn(target, condition.column));
|
|
147
|
+
if (condition.operator === "is-null") return sql`${column} is null`;
|
|
148
|
+
if (condition.operator === "is-not-null") return sql`${column} is not null`;
|
|
149
|
+
if (condition.operator === "in") {
|
|
150
|
+
const values = this.buildConditionValueList(condition.value);
|
|
151
|
+
if (values.length === 0) return sql`1 = 0`;
|
|
152
|
+
return sql`${column} in (${sql.join(values)})`;
|
|
153
|
+
}
|
|
154
|
+
if (condition.operator === "not-in") {
|
|
155
|
+
const values = this.buildConditionValueList(condition.value);
|
|
156
|
+
if (values.length === 0) return sql`1 = 1`;
|
|
157
|
+
return sql`${column} not in (${sql.join(values)})`;
|
|
158
|
+
}
|
|
159
|
+
if (condition.operator === "contains") return sql`${column} like ${`%${String(condition.value ?? "")}%`}`;
|
|
160
|
+
if (condition.operator === "starts-with") return sql`${column} like ${`${String(condition.value ?? "")}%`}`;
|
|
161
|
+
if (condition.operator === "ends-with") return sql`${column} like ${`%${String(condition.value ?? "")}`}`;
|
|
162
|
+
return sql`${column} ${condition.operator === "!=" ? sql.raw("!=") : sql.raw(condition.operator)} ${condition.value}`;
|
|
163
|
+
}
|
|
164
|
+
buildWhereCondition(target, condition) {
|
|
165
|
+
if (!condition) return sql`1 = 1`;
|
|
166
|
+
if (condition.type === "comparison") return this.buildComparisonCondition(target, condition);
|
|
167
|
+
if (condition.type === "group") {
|
|
168
|
+
const group = condition;
|
|
169
|
+
const conditions = group.conditions.map((entry) => {
|
|
170
|
+
return this.buildWhereCondition(target, entry);
|
|
171
|
+
});
|
|
172
|
+
if (conditions.length === 0) return sql`1 = 1`;
|
|
173
|
+
const separator = group.operator === "or" ? sql` or ` : sql` and `;
|
|
174
|
+
return sql`(${sql.join(conditions, separator)})`;
|
|
175
|
+
}
|
|
176
|
+
if (condition.type === "not") {
|
|
177
|
+
const notCondition = condition;
|
|
178
|
+
return sql`not (${this.buildWhereCondition(target, notCondition.condition)})`;
|
|
179
|
+
}
|
|
180
|
+
throw new UnsupportedAdapterFeatureException("Raw where clauses are not supported by the Kysely adapter.", {
|
|
181
|
+
operation: "adapter.where",
|
|
182
|
+
meta: {
|
|
183
|
+
feature: "rawWhere",
|
|
184
|
+
sql: condition.sql
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
buildWhereClause(target, condition) {
|
|
189
|
+
if (!condition) return sql``;
|
|
190
|
+
return sql` where ${this.buildWhereCondition(target, condition)}`;
|
|
191
|
+
}
|
|
192
|
+
buildPaginationClause(spec) {
|
|
193
|
+
const clauses = [];
|
|
194
|
+
if (typeof spec.limit === "number") clauses.push(sql` limit ${spec.limit}`);
|
|
195
|
+
if (typeof spec.offset === "number") clauses.push(sql` offset ${spec.offset}`);
|
|
196
|
+
if (clauses.length === 0) return sql``;
|
|
197
|
+
return sql.join(clauses, sql``);
|
|
198
|
+
}
|
|
199
|
+
assertNoRelationLoads(spec) {
|
|
200
|
+
if ("relationLoads" in spec && spec.relationLoads && spec.relationLoads.length > 0) throw new UnsupportedAdapterFeatureException("Kysely adapter relation-load execution is planned for a later phase.", {
|
|
201
|
+
operation: "adapter.relationLoads",
|
|
202
|
+
meta: { feature: "relationLoads" }
|
|
203
|
+
});
|
|
208
204
|
}
|
|
205
|
+
async select(spec) {
|
|
206
|
+
this.assertNoRelationLoads(spec);
|
|
207
|
+
const result = await sql`
|
|
208
|
+
select ${this.buildSelectList(spec.target, spec.columns)}
|
|
209
|
+
from ${sql.table(this.resolveTable(spec.target))}
|
|
210
|
+
${this.buildWhereClause(spec.target, spec.where)}
|
|
211
|
+
${this.buildOrderBy(spec.target, spec.orderBy)}
|
|
212
|
+
${this.buildPaginationClause(spec)}
|
|
213
|
+
`.execute(this.db);
|
|
214
|
+
return this.mapRows(spec.target, result.rows);
|
|
215
|
+
}
|
|
216
|
+
async selectOne(spec) {
|
|
217
|
+
return (await this.select({
|
|
218
|
+
...spec,
|
|
219
|
+
limit: spec.limit ?? 1
|
|
220
|
+
}))[0] ?? null;
|
|
221
|
+
}
|
|
222
|
+
async insert(spec) {
|
|
223
|
+
const values = this.mapValues(spec.target, spec.values);
|
|
224
|
+
const columns = Object.keys(values);
|
|
225
|
+
const result = columns.length === 0 ? await sql`
|
|
226
|
+
insert into ${sql.table(this.resolveTable(spec.target))}
|
|
227
|
+
default values
|
|
228
|
+
returning *
|
|
229
|
+
`.execute(this.db) : await sql`
|
|
230
|
+
insert into ${sql.table(this.resolveTable(spec.target))} (${sql.join(columns.map((column) => sql.id(column)), sql`, `)})
|
|
231
|
+
values (${sql.join(columns.map((column) => values[column]), sql`, `)})
|
|
232
|
+
returning *
|
|
233
|
+
`.execute(this.db);
|
|
234
|
+
return this.mapRow(spec.target, result.rows[0]);
|
|
235
|
+
}
|
|
236
|
+
async insertMany(spec) {
|
|
237
|
+
if (spec.values.length === 0) return 0;
|
|
238
|
+
const rows = spec.values.map((row) => this.mapValues(spec.target, row));
|
|
239
|
+
const columns = Array.from(new Set(rows.flatMap((row) => Object.keys(row))));
|
|
240
|
+
if (columns.length === 0) return (await sql`
|
|
241
|
+
insert into ${sql.table(this.resolveTable(spec.target))}
|
|
242
|
+
default values
|
|
243
|
+
${spec.ignoreDuplicates ? sql` on conflict do nothing` : sql``}
|
|
244
|
+
returning ${sql.id(this.resolvePrimaryKey(spec.target))}
|
|
245
|
+
`.execute(this.db)).rows.length;
|
|
246
|
+
const values = sql.join(rows.map((row) => {
|
|
247
|
+
return sql`(${sql.join(columns.map((column) => row[column] ?? null), sql`, `)})`;
|
|
248
|
+
}), sql`, `);
|
|
249
|
+
return (await sql`
|
|
250
|
+
insert into ${sql.table(this.resolveTable(spec.target))} (${sql.join(columns.map((column) => sql.id(column)), sql`, `)})
|
|
251
|
+
values ${values}
|
|
252
|
+
${spec.ignoreDuplicates ? sql` on conflict do nothing` : sql``}
|
|
253
|
+
returning ${sql.id(this.resolvePrimaryKey(spec.target))}
|
|
254
|
+
`.execute(this.db)).rows.length;
|
|
255
|
+
}
|
|
256
|
+
async update(spec) {
|
|
257
|
+
const values = this.mapValues(spec.target, spec.values);
|
|
258
|
+
const assignments = Object.entries(values).map(([column, value]) => {
|
|
259
|
+
return sql`${sql.id(column)} = ${value}`;
|
|
260
|
+
});
|
|
261
|
+
if (assignments.length === 0) return await this.selectOne({
|
|
262
|
+
target: spec.target,
|
|
263
|
+
where: spec.where,
|
|
264
|
+
limit: 1
|
|
265
|
+
});
|
|
266
|
+
const result = await sql`
|
|
267
|
+
update ${sql.table(this.resolveTable(spec.target))}
|
|
268
|
+
set ${sql.join(assignments, sql`, `)}
|
|
269
|
+
${this.buildWhereClause(spec.target, spec.where)}
|
|
270
|
+
returning *
|
|
271
|
+
`.execute(this.db);
|
|
272
|
+
return this.mapRow(spec.target, result.rows[0]);
|
|
273
|
+
}
|
|
274
|
+
async updateMany(spec) {
|
|
275
|
+
const values = this.mapValues(spec.target, spec.values);
|
|
276
|
+
const assignments = Object.entries(values).map(([column, value]) => {
|
|
277
|
+
return sql`${sql.id(column)} = ${value}`;
|
|
278
|
+
});
|
|
279
|
+
if (assignments.length === 0) return 0;
|
|
280
|
+
return (await sql`
|
|
281
|
+
update ${sql.table(this.resolveTable(spec.target))}
|
|
282
|
+
set ${sql.join(assignments, sql`, `)}
|
|
283
|
+
${this.buildWhereClause(spec.target, spec.where)}
|
|
284
|
+
returning ${sql.id(this.resolvePrimaryKey(spec.target))}
|
|
285
|
+
`.execute(this.db)).rows.length;
|
|
286
|
+
}
|
|
287
|
+
async delete(spec) {
|
|
288
|
+
const result = await sql`
|
|
289
|
+
delete from ${sql.table(this.resolveTable(spec.target))}
|
|
290
|
+
${this.buildWhereClause(spec.target, spec.where)}
|
|
291
|
+
returning *
|
|
292
|
+
`.execute(this.db);
|
|
293
|
+
return this.mapRow(spec.target, result.rows[0]);
|
|
294
|
+
}
|
|
295
|
+
async deleteMany(spec) {
|
|
296
|
+
return (await sql`
|
|
297
|
+
delete from ${sql.table(this.resolveTable(spec.target))}
|
|
298
|
+
${this.buildWhereClause(spec.target, spec.where)}
|
|
299
|
+
returning ${sql.id(this.resolvePrimaryKey(spec.target))}
|
|
300
|
+
`.execute(this.db)).rows.length;
|
|
301
|
+
}
|
|
302
|
+
async count(spec) {
|
|
303
|
+
const result = await sql`
|
|
304
|
+
select count(*)::int as count
|
|
305
|
+
from ${sql.table(this.resolveTable(spec.target))}
|
|
306
|
+
${this.buildWhereClause(spec.target, spec.where)}
|
|
307
|
+
`.execute(this.db);
|
|
308
|
+
return Number(result.rows[0]?.count ?? 0);
|
|
309
|
+
}
|
|
310
|
+
async exists(spec) {
|
|
311
|
+
const result = await sql`
|
|
312
|
+
select exists(
|
|
313
|
+
select 1
|
|
314
|
+
from ${sql.table(this.resolveTable(spec.target))}
|
|
315
|
+
${this.buildWhereClause(spec.target, spec.where)}
|
|
316
|
+
limit 1
|
|
317
|
+
) as exists
|
|
318
|
+
`.execute(this.db);
|
|
319
|
+
return Boolean(result.rows[0]?.exists);
|
|
320
|
+
}
|
|
321
|
+
async transaction(callback, context = {}) {
|
|
322
|
+
let transactionBuilder = this.db.transaction();
|
|
323
|
+
if (context.readOnly !== void 0) transactionBuilder = transactionBuilder.setAccessMode(context.readOnly ? "read only" : "read write");
|
|
324
|
+
if (context.isolationLevel) transactionBuilder = transactionBuilder.setIsolationLevel(context.isolationLevel);
|
|
325
|
+
return await transactionBuilder.execute(async (transaction) => {
|
|
326
|
+
return await callback(new KyselyDatabaseAdapter(transaction));
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
const createKyselyAdapter = (db) => {
|
|
331
|
+
return new KyselyDatabaseAdapter(db);
|
|
209
332
|
};
|
|
210
333
|
|
|
211
334
|
//#endregion
|
|
212
|
-
//#region src/
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
335
|
+
//#region src/Exceptions/MissingDelegateException.ts
|
|
336
|
+
var MissingDelegateException = class extends ArkormException {
|
|
337
|
+
constructor(message, context = {}) {
|
|
338
|
+
super(message, {
|
|
339
|
+
code: "MISSING_DELEGATE",
|
|
340
|
+
...context
|
|
341
|
+
});
|
|
342
|
+
this.name = "MissingDelegateException";
|
|
343
|
+
}
|
|
219
344
|
};
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
345
|
+
|
|
346
|
+
//#endregion
|
|
347
|
+
//#region src/helpers/runtime-module-loader.ts
|
|
348
|
+
var RuntimeModuleLoader = class {
|
|
349
|
+
static async load(filePath) {
|
|
350
|
+
const resolvedPath = resolve(filePath);
|
|
351
|
+
return await createJiti(pathToFileURL(resolvedPath).href, {
|
|
352
|
+
interopDefault: false,
|
|
353
|
+
tsconfigPaths: true
|
|
354
|
+
}).import(resolvedPath);
|
|
226
355
|
}
|
|
227
|
-
return normalizedValues;
|
|
228
356
|
};
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
this.columnName = columnName;
|
|
357
|
+
|
|
358
|
+
//#endregion
|
|
359
|
+
//#region src/helpers/runtime-config.ts
|
|
360
|
+
const resolveDefaultStubsPath = () => {
|
|
361
|
+
let current = path.dirname(fileURLToPath(import.meta.url));
|
|
362
|
+
while (true) {
|
|
363
|
+
const packageJsonPath = path.join(current, "package.json");
|
|
364
|
+
const stubsPath = path.join(current, "stubs");
|
|
365
|
+
if (existsSync(packageJsonPath) && existsSync(stubsPath)) return stubsPath;
|
|
366
|
+
const parent = path.dirname(current);
|
|
367
|
+
if (parent === current) break;
|
|
368
|
+
current = parent;
|
|
242
369
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
370
|
+
return path.join(process.cwd(), "stubs");
|
|
371
|
+
};
|
|
372
|
+
const baseConfig = {
|
|
373
|
+
paths: {
|
|
374
|
+
stubs: resolveDefaultStubsPath(),
|
|
375
|
+
seeders: path.join(process.cwd(), "database", "seeders"),
|
|
376
|
+
models: path.join(process.cwd(), "src", "models"),
|
|
377
|
+
migrations: path.join(process.cwd(), "database", "migrations"),
|
|
378
|
+
factories: path.join(process.cwd(), "database", "factories"),
|
|
379
|
+
buildOutput: path.join(process.cwd(), "dist")
|
|
380
|
+
},
|
|
381
|
+
outputExt: "ts"
|
|
382
|
+
};
|
|
383
|
+
const userConfig = {
|
|
384
|
+
...baseConfig,
|
|
385
|
+
paths: { ...baseConfig.paths ?? {} }
|
|
386
|
+
};
|
|
387
|
+
let runtimeConfigLoaded = false;
|
|
388
|
+
let runtimeConfigLoadingPromise;
|
|
389
|
+
let runtimeClientResolver;
|
|
390
|
+
let runtimePaginationURLDriverFactory;
|
|
391
|
+
let runtimePaginationCurrentPageResolver;
|
|
392
|
+
const transactionClientStorage = new AsyncLocalStorage();
|
|
393
|
+
const mergePathConfig = (paths) => {
|
|
394
|
+
const defaults = baseConfig.paths ?? {};
|
|
395
|
+
const current = userConfig.paths ?? {};
|
|
396
|
+
const incoming = Object.entries(paths ?? {}).reduce((all, [key, value]) => {
|
|
397
|
+
if (typeof value === "string" && value.trim().length > 0) all[key] = path.isAbsolute(value) ? value : path.resolve(process.cwd(), value);
|
|
398
|
+
return all;
|
|
399
|
+
}, {});
|
|
400
|
+
return {
|
|
401
|
+
...defaults,
|
|
402
|
+
...current,
|
|
403
|
+
...incoming
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
/**
|
|
407
|
+
* Define the ArkORM runtime configuration. This function can be used to provide.
|
|
408
|
+
*
|
|
409
|
+
* @param config The ArkORM configuration object.
|
|
410
|
+
* @returns The same configuration object.
|
|
411
|
+
*/
|
|
412
|
+
const defineConfig = (config) => {
|
|
413
|
+
return config;
|
|
414
|
+
};
|
|
415
|
+
/**
|
|
416
|
+
* Get the user-provided ArkORM configuration.
|
|
417
|
+
*
|
|
418
|
+
* @returns The user-provided ArkORM configuration object.
|
|
419
|
+
*/
|
|
420
|
+
const getUserConfig = (key) => {
|
|
421
|
+
if (key) return userConfig[key];
|
|
422
|
+
return userConfig;
|
|
423
|
+
};
|
|
424
|
+
/**
|
|
425
|
+
* Configure the ArkORM runtime with the provided Prisma client resolver and
|
|
426
|
+
* delegate mapping resolver.
|
|
427
|
+
*
|
|
428
|
+
* @param prisma
|
|
429
|
+
* @param mapping
|
|
430
|
+
*/
|
|
431
|
+
const configureArkormRuntime = (prisma, options = {}) => {
|
|
432
|
+
const nextConfig = {
|
|
433
|
+
...userConfig,
|
|
434
|
+
prisma,
|
|
435
|
+
paths: mergePathConfig(options.paths)
|
|
436
|
+
};
|
|
437
|
+
if (options.pagination !== void 0) nextConfig.pagination = options.pagination;
|
|
438
|
+
if (options.outputExt !== void 0) nextConfig.outputExt = options.outputExt;
|
|
439
|
+
Object.assign(userConfig, { ...nextConfig });
|
|
440
|
+
runtimeClientResolver = prisma;
|
|
441
|
+
runtimePaginationURLDriverFactory = nextConfig.pagination?.urlDriver;
|
|
442
|
+
runtimePaginationCurrentPageResolver = nextConfig.pagination?.resolveCurrentPage;
|
|
443
|
+
};
|
|
444
|
+
/**
|
|
445
|
+
* Reset the ArkORM runtime configuration.
|
|
446
|
+
* This is primarily intended for testing purposes.
|
|
447
|
+
*/
|
|
448
|
+
const resetArkormRuntimeForTests = () => {
|
|
449
|
+
Object.assign(userConfig, {
|
|
450
|
+
...baseConfig,
|
|
451
|
+
paths: { ...baseConfig.paths ?? {} }
|
|
452
|
+
});
|
|
453
|
+
runtimeConfigLoaded = false;
|
|
454
|
+
runtimeConfigLoadingPromise = void 0;
|
|
455
|
+
runtimeClientResolver = void 0;
|
|
456
|
+
runtimePaginationURLDriverFactory = void 0;
|
|
457
|
+
runtimePaginationCurrentPageResolver = void 0;
|
|
458
|
+
};
|
|
459
|
+
/**
|
|
460
|
+
* Resolve a Prisma client instance from the provided resolver, which can be either
|
|
461
|
+
* a direct client instance or a function that returns a client instance.
|
|
462
|
+
*
|
|
463
|
+
* @param resolver
|
|
464
|
+
* @returns
|
|
465
|
+
*/
|
|
466
|
+
const resolveClient = (resolver) => {
|
|
467
|
+
if (!resolver) return void 0;
|
|
468
|
+
const client = typeof resolver === "function" ? resolver() : resolver;
|
|
469
|
+
if (!client || typeof client !== "object") return void 0;
|
|
470
|
+
return client;
|
|
471
|
+
};
|
|
472
|
+
/**
|
|
473
|
+
* Resolve and apply the ArkORM configuration from an imported module.
|
|
474
|
+
* This function checks for a default export and falls back to the module itself, then validates
|
|
475
|
+
* the configuration object and applies it to the runtime if valid.
|
|
476
|
+
*
|
|
477
|
+
* @param imported
|
|
478
|
+
* @returns
|
|
479
|
+
*/
|
|
480
|
+
const resolveAndApplyConfig = (imported) => {
|
|
481
|
+
const config = imported?.default ?? imported;
|
|
482
|
+
if (!config || typeof config !== "object" || !config.prisma) return;
|
|
483
|
+
configureArkormRuntime(config.prisma, {
|
|
484
|
+
pagination: config.pagination,
|
|
485
|
+
paths: config.paths,
|
|
486
|
+
outputExt: config.outputExt
|
|
487
|
+
});
|
|
488
|
+
runtimeConfigLoaded = true;
|
|
489
|
+
};
|
|
490
|
+
/**
|
|
491
|
+
* Dynamically import a configuration file.
|
|
492
|
+
* A cache-busting query parameter is appended to ensure the latest version is loaded.
|
|
493
|
+
*
|
|
494
|
+
* @param configPath
|
|
495
|
+
* @returns A promise that resolves to the imported configuration module.
|
|
496
|
+
*/
|
|
497
|
+
const importConfigFile = (configPath) => {
|
|
498
|
+
return RuntimeModuleLoader.load(configPath);
|
|
499
|
+
};
|
|
500
|
+
const loadRuntimeConfigSync = () => {
|
|
501
|
+
const require = createRequire(import.meta.url);
|
|
502
|
+
const syncConfigPaths = [path.join(process.cwd(), "arkormx.config.cjs")];
|
|
503
|
+
for (const configPath of syncConfigPaths) {
|
|
504
|
+
if (!existsSync(configPath)) continue;
|
|
505
|
+
try {
|
|
506
|
+
resolveAndApplyConfig(require(configPath));
|
|
507
|
+
return true;
|
|
508
|
+
} catch {
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return false;
|
|
513
|
+
};
|
|
514
|
+
/**
|
|
515
|
+
* Load the ArkORM configuration by searching for configuration files in the
|
|
516
|
+
* current working directory.
|
|
517
|
+
* @returns
|
|
518
|
+
*/
|
|
519
|
+
const loadArkormConfig = async () => {
|
|
520
|
+
if (runtimeConfigLoaded) return;
|
|
521
|
+
if (runtimeConfigLoadingPromise) return await runtimeConfigLoadingPromise;
|
|
522
|
+
if (loadRuntimeConfigSync()) return;
|
|
523
|
+
runtimeConfigLoadingPromise = (async () => {
|
|
524
|
+
const configPaths = [path.join(process.cwd(), "arkormx.config.js"), path.join(process.cwd(), "arkormx.config.ts")];
|
|
525
|
+
for (const configPath of configPaths) {
|
|
526
|
+
if (!existsSync(configPath)) continue;
|
|
527
|
+
try {
|
|
528
|
+
resolveAndApplyConfig(await importConfigFile(configPath));
|
|
529
|
+
return;
|
|
530
|
+
} catch {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
runtimeConfigLoaded = true;
|
|
535
|
+
})();
|
|
536
|
+
await runtimeConfigLoadingPromise;
|
|
537
|
+
};
|
|
538
|
+
/**
|
|
539
|
+
* Ensure that the ArkORM configuration is loaded.
|
|
540
|
+
* This function can be called to trigger the loading process if it hasn't already been initiated.
|
|
541
|
+
* If the configuration is already loaded, it will return immediately.
|
|
542
|
+
*
|
|
543
|
+
* @returns
|
|
544
|
+
*/
|
|
545
|
+
const ensureArkormConfigLoading = () => {
|
|
546
|
+
if (runtimeConfigLoaded) return;
|
|
547
|
+
if (!runtimeConfigLoadingPromise) loadArkormConfig();
|
|
548
|
+
};
|
|
549
|
+
const getDefaultStubsPath = () => {
|
|
550
|
+
return resolveDefaultStubsPath();
|
|
551
|
+
};
|
|
552
|
+
/**
|
|
553
|
+
* Get the runtime Prisma client.
|
|
554
|
+
* This function will trigger the loading of the ArkORM configuration if
|
|
555
|
+
* it hasn't already been loaded.
|
|
556
|
+
*
|
|
557
|
+
* @returns
|
|
558
|
+
*/
|
|
559
|
+
const getRuntimePrismaClient = () => {
|
|
560
|
+
const activeTransactionClient = transactionClientStorage.getStore();
|
|
561
|
+
if (activeTransactionClient) return activeTransactionClient;
|
|
562
|
+
if (!runtimeConfigLoaded) loadRuntimeConfigSync();
|
|
563
|
+
return resolveClient(runtimeClientResolver);
|
|
564
|
+
};
|
|
565
|
+
const getActiveTransactionClient = () => {
|
|
566
|
+
return transactionClientStorage.getStore();
|
|
567
|
+
};
|
|
568
|
+
const isTransactionCapableClient = (value) => {
|
|
569
|
+
if (!value || typeof value !== "object") return false;
|
|
570
|
+
return typeof value.$transaction === "function";
|
|
571
|
+
};
|
|
572
|
+
const runArkormTransaction = async (callback, options = {}) => {
|
|
573
|
+
const activeTransactionClient = transactionClientStorage.getStore();
|
|
574
|
+
if (activeTransactionClient) return await callback(activeTransactionClient);
|
|
575
|
+
const client = getRuntimePrismaClient();
|
|
576
|
+
if (!client) throw new ArkormException("Cannot start a transaction without a configured Prisma client.", {
|
|
577
|
+
code: "CLIENT_NOT_CONFIGURED",
|
|
578
|
+
operation: "transaction"
|
|
579
|
+
});
|
|
580
|
+
if (!isTransactionCapableClient(client)) throw new UnsupportedAdapterFeatureException("Transactions are not supported by the current adapter.", {
|
|
581
|
+
code: "TRANSACTION_NOT_SUPPORTED",
|
|
582
|
+
operation: "transaction"
|
|
583
|
+
});
|
|
584
|
+
return await client.$transaction(async (transactionClient) => {
|
|
585
|
+
return await transactionClientStorage.run(transactionClient, async () => {
|
|
586
|
+
return await callback(transactionClient);
|
|
587
|
+
});
|
|
588
|
+
}, options);
|
|
589
|
+
};
|
|
590
|
+
/**
|
|
591
|
+
* Get the configured pagination URL driver factory from runtime config.
|
|
592
|
+
*
|
|
593
|
+
* @returns
|
|
594
|
+
*/
|
|
595
|
+
const getRuntimePaginationURLDriverFactory = () => {
|
|
596
|
+
if (!runtimeConfigLoaded) loadRuntimeConfigSync();
|
|
597
|
+
return runtimePaginationURLDriverFactory;
|
|
598
|
+
};
|
|
599
|
+
/**
|
|
600
|
+
* Get the configured current-page resolver from runtime config.
|
|
601
|
+
*
|
|
602
|
+
* @returns
|
|
603
|
+
*/
|
|
604
|
+
const getRuntimePaginationCurrentPageResolver = () => {
|
|
605
|
+
if (!runtimeConfigLoaded) loadRuntimeConfigSync();
|
|
606
|
+
return runtimePaginationCurrentPageResolver;
|
|
607
|
+
};
|
|
608
|
+
/**
|
|
609
|
+
* Check if a given value is a Prisma delegate-like object
|
|
610
|
+
* by verifying the presence of common delegate methods.
|
|
611
|
+
*
|
|
612
|
+
* @param value The value to check.
|
|
613
|
+
* @returns True if the value is a Prisma delegate-like object, false otherwise.
|
|
614
|
+
*/
|
|
615
|
+
const isDelegateLike = (value) => {
|
|
616
|
+
if (!value || typeof value !== "object") return false;
|
|
617
|
+
const candidate = value;
|
|
618
|
+
return [
|
|
619
|
+
"findMany",
|
|
620
|
+
"findFirst",
|
|
621
|
+
"create",
|
|
622
|
+
"update",
|
|
623
|
+
"delete",
|
|
624
|
+
"count"
|
|
625
|
+
].every((method) => typeof candidate[method] === "function");
|
|
626
|
+
};
|
|
627
|
+
loadArkormConfig();
|
|
628
|
+
|
|
629
|
+
//#endregion
|
|
630
|
+
//#region src/helpers/prisma.ts
|
|
631
|
+
/**
|
|
632
|
+
* Create an adapter to convert a Prisma client instance into a format
|
|
633
|
+
* compatible with ArkORM's expectations.
|
|
634
|
+
*
|
|
635
|
+
* @param prisma The Prisma client instance to adapt.
|
|
636
|
+
* @param mapping An optional mapping of Prisma delegate names to ArkORM delegate names.
|
|
637
|
+
* @returns A record of adapted Prisma delegates compatible with ArkORM.
|
|
638
|
+
*/
|
|
639
|
+
function createPrismaAdapter(prisma) {
|
|
640
|
+
return Object.entries(prisma).reduce((accumulator, [key, value]) => {
|
|
641
|
+
if (!isDelegateLike(value)) return accumulator;
|
|
642
|
+
accumulator[key] = value;
|
|
643
|
+
return accumulator;
|
|
644
|
+
}, {});
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Create a delegate mapping record for Model.setClient() from a Prisma client.
|
|
648
|
+
*
|
|
649
|
+
* @param prisma The Prisma client instance.
|
|
650
|
+
* @param mapping Optional mapping of Arkormˣ delegate names to Prisma delegate names.
|
|
651
|
+
* @returns A delegate map keyed by Arkormˣ delegate names.
|
|
652
|
+
*/
|
|
653
|
+
function createPrismaDelegateMap(prisma, mapping = {}) {
|
|
654
|
+
const delegates = createPrismaAdapter(prisma);
|
|
655
|
+
if (Object.keys(mapping).length === 0) return delegates;
|
|
656
|
+
return Object.entries(mapping).reduce((accumulator, [arkormDelegate, prismaDelegate]) => {
|
|
657
|
+
const resolved = delegates[prismaDelegate];
|
|
658
|
+
if (!resolved) return accumulator;
|
|
659
|
+
accumulator[arkormDelegate] = resolved;
|
|
660
|
+
return accumulator;
|
|
661
|
+
}, {});
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Infer the Prisma delegate name for a given model name using a simple convention.
|
|
665
|
+
*
|
|
666
|
+
* @param modelName The name of the model to infer the delegate name for.
|
|
667
|
+
* @returns The inferred Prisma delegate name.
|
|
668
|
+
*/
|
|
669
|
+
function inferDelegateName(modelName) {
|
|
670
|
+
return `${modelName.charAt(0).toLowerCase()}${modelName.slice(1)}s`;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
//#endregion
|
|
674
|
+
//#region src/adapters/PrismaDatabaseAdapter.ts
|
|
675
|
+
var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
676
|
+
capabilities;
|
|
677
|
+
delegates;
|
|
678
|
+
constructor(prisma, mapping = {}) {
|
|
679
|
+
this.prisma = prisma;
|
|
680
|
+
this.mapping = mapping;
|
|
681
|
+
this.delegates = Object.entries(prisma).reduce((accumulator, [key, value]) => {
|
|
682
|
+
if (!isDelegateLike(value)) return accumulator;
|
|
683
|
+
accumulator[key] = value;
|
|
684
|
+
return accumulator;
|
|
685
|
+
}, {});
|
|
686
|
+
this.capabilities = {
|
|
687
|
+
transactions: this.hasTransactionSupport(prisma),
|
|
688
|
+
insertMany: Object.values(this.delegates).some((delegate) => typeof delegate.createMany === "function"),
|
|
689
|
+
updateMany: Object.values(this.delegates).some((delegate) => typeof delegate.updateMany === "function"),
|
|
690
|
+
deleteMany: false,
|
|
691
|
+
exists: true,
|
|
692
|
+
relationLoads: false,
|
|
693
|
+
relationAggregates: false,
|
|
694
|
+
relationFilters: false,
|
|
695
|
+
rawWhere: false,
|
|
696
|
+
returning: false
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
hasTransactionSupport(client) {
|
|
700
|
+
return Boolean(client) && typeof client.$transaction === "function";
|
|
701
|
+
}
|
|
702
|
+
normalizeCandidate(value) {
|
|
703
|
+
return value.trim();
|
|
704
|
+
}
|
|
705
|
+
unique(values) {
|
|
706
|
+
return [...new Set(values.filter(Boolean))];
|
|
707
|
+
}
|
|
708
|
+
toQuerySelect(columns) {
|
|
709
|
+
if (!columns || columns.length === 0) return void 0;
|
|
710
|
+
return columns.reduce((select, column) => {
|
|
711
|
+
select[column.column] = true;
|
|
712
|
+
return select;
|
|
713
|
+
}, {});
|
|
714
|
+
}
|
|
715
|
+
toQueryOrderBy(orderBy) {
|
|
716
|
+
if (!orderBy || orderBy.length === 0) return void 0;
|
|
717
|
+
return orderBy.map((entry) => ({ [entry.column]: entry.direction }));
|
|
718
|
+
}
|
|
719
|
+
toComparisonWhere(condition) {
|
|
720
|
+
if (condition.operator === "is-null") return { [condition.column]: null };
|
|
721
|
+
if (condition.operator === "is-not-null") return { [condition.column]: { not: null } };
|
|
722
|
+
if (condition.operator === "=") return { [condition.column]: condition.value };
|
|
723
|
+
if (condition.operator === "!=") return { [condition.column]: { not: condition.value } };
|
|
724
|
+
if (condition.operator === ">") return { [condition.column]: { gt: condition.value } };
|
|
725
|
+
if (condition.operator === ">=") return { [condition.column]: { gte: condition.value } };
|
|
726
|
+
if (condition.operator === "<") return { [condition.column]: { lt: condition.value } };
|
|
727
|
+
if (condition.operator === "<=") return { [condition.column]: { lte: condition.value } };
|
|
728
|
+
if (condition.operator === "in") return { [condition.column]: { in: Array.isArray(condition.value) ? condition.value : [condition.value] } };
|
|
729
|
+
if (condition.operator === "not-in") return { [condition.column]: { notIn: Array.isArray(condition.value) ? condition.value : [condition.value] } };
|
|
730
|
+
if (condition.operator === "contains") return { [condition.column]: { contains: condition.value } };
|
|
731
|
+
if (condition.operator === "starts-with") return { [condition.column]: { startsWith: condition.value } };
|
|
732
|
+
return { [condition.column]: { endsWith: condition.value } };
|
|
733
|
+
}
|
|
734
|
+
toQueryWhere(condition) {
|
|
735
|
+
if (!condition) return void 0;
|
|
736
|
+
if (condition.type === "comparison") return this.toComparisonWhere(condition);
|
|
737
|
+
if (condition.type === "group") {
|
|
738
|
+
const group = condition;
|
|
739
|
+
const grouped = group.conditions.map((entry) => this.toQueryWhere(entry)).filter((entry) => Boolean(entry));
|
|
740
|
+
if (grouped.length === 0) return void 0;
|
|
741
|
+
return group.operator === "and" ? { AND: grouped } : { OR: grouped };
|
|
742
|
+
}
|
|
743
|
+
if (condition.type === "not") {
|
|
744
|
+
const notCondition = condition;
|
|
745
|
+
const nested = this.toQueryWhere(notCondition.condition);
|
|
746
|
+
if (!nested) return void 0;
|
|
747
|
+
return { NOT: nested };
|
|
748
|
+
}
|
|
749
|
+
throw new UnsupportedAdapterFeatureException("Raw where clauses are not supported by the Prisma compatibility adapter.", {
|
|
750
|
+
operation: "adapter.where",
|
|
751
|
+
meta: {
|
|
752
|
+
feature: "rawWhere",
|
|
753
|
+
sql: condition.sql
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
buildFindArgs(spec) {
|
|
758
|
+
return {
|
|
759
|
+
include: this.toQueryInclude(spec.relationLoads),
|
|
760
|
+
where: this.toQueryWhere(spec.where),
|
|
761
|
+
orderBy: this.toQueryOrderBy(spec.orderBy),
|
|
762
|
+
select: this.toQuerySelect(spec.columns),
|
|
763
|
+
skip: spec.offset,
|
|
764
|
+
take: spec.limit
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
toQueryInclude(relationLoads) {
|
|
768
|
+
if (!relationLoads || relationLoads.length === 0) return void 0;
|
|
769
|
+
return relationLoads.reduce((include, plan) => {
|
|
770
|
+
const nestedInclude = this.toQueryInclude(plan.relationLoads);
|
|
771
|
+
const nestedSelect = this.toQuerySelect(plan.columns);
|
|
772
|
+
const nestedWhere = this.toQueryWhere(plan.constraint);
|
|
773
|
+
const nestedOrderBy = this.toQueryOrderBy(plan.orderBy);
|
|
774
|
+
if (!nestedInclude && !nestedSelect && !nestedWhere && !nestedOrderBy && plan.offset === void 0 && plan.limit === void 0) {
|
|
775
|
+
include[plan.relation] = true;
|
|
776
|
+
return include;
|
|
777
|
+
}
|
|
778
|
+
include[plan.relation] = {
|
|
779
|
+
where: nestedWhere,
|
|
780
|
+
orderBy: nestedOrderBy,
|
|
781
|
+
select: nestedSelect,
|
|
782
|
+
include: nestedInclude,
|
|
783
|
+
skip: plan.offset,
|
|
784
|
+
take: plan.limit
|
|
785
|
+
};
|
|
786
|
+
return include;
|
|
787
|
+
}, {});
|
|
788
|
+
}
|
|
789
|
+
resolveDelegate(target) {
|
|
790
|
+
const tableName = target.table ? this.normalizeCandidate(target.table) : "";
|
|
791
|
+
const singularTableName = tableName ? `${str(tableName).singular()}` : "";
|
|
792
|
+
const camelTableName = tableName ? `${str(tableName).camel()}` : "";
|
|
793
|
+
const camelSingularTableName = tableName ? `${str(tableName).camel().singular()}` : "";
|
|
794
|
+
const candidates = this.unique([
|
|
795
|
+
target.table ? this.mapping[target.table] : "",
|
|
796
|
+
tableName,
|
|
797
|
+
singularTableName ? this.mapping[singularTableName] : "",
|
|
798
|
+
singularTableName,
|
|
799
|
+
camelTableName ? this.mapping[camelTableName] : "",
|
|
800
|
+
camelTableName,
|
|
801
|
+
camelSingularTableName ? this.mapping[camelSingularTableName] : "",
|
|
802
|
+
camelSingularTableName,
|
|
803
|
+
target.modelName ? this.mapping[target.modelName] : "",
|
|
804
|
+
target.modelName ? this.normalizeCandidate(target.modelName) : "",
|
|
805
|
+
target.modelName ? inferDelegateName(target.modelName) : "",
|
|
806
|
+
target.modelName ? this.mapping[inferDelegateName(target.modelName)] : ""
|
|
807
|
+
]);
|
|
808
|
+
const resolved = candidates.map((candidate) => this.delegates[candidate]).find(Boolean);
|
|
809
|
+
if (resolved) return resolved;
|
|
810
|
+
throw new MissingDelegateException("Prisma delegate could not be resolved for adapter target.", {
|
|
811
|
+
operation: "getDelegate",
|
|
812
|
+
model: target.modelName,
|
|
813
|
+
delegate: target.table,
|
|
814
|
+
meta: {
|
|
815
|
+
target,
|
|
816
|
+
candidates
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
async select(spec) {
|
|
821
|
+
return await this.resolveDelegate(spec.target).findMany(this.buildFindArgs(spec));
|
|
822
|
+
}
|
|
823
|
+
async selectOne(spec) {
|
|
824
|
+
return await this.resolveDelegate(spec.target).findFirst(this.buildFindArgs(spec));
|
|
825
|
+
}
|
|
826
|
+
async insert(spec) {
|
|
827
|
+
return await this.resolveDelegate(spec.target).create({ data: spec.values });
|
|
828
|
+
}
|
|
829
|
+
async insertMany(spec) {
|
|
830
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
831
|
+
if (typeof delegate.createMany === "function") {
|
|
832
|
+
const result = await delegate.createMany({
|
|
833
|
+
data: spec.values,
|
|
834
|
+
skipDuplicates: spec.ignoreDuplicates
|
|
835
|
+
});
|
|
836
|
+
if (typeof result === "number") return result;
|
|
837
|
+
return typeof result?.count === "number" ? result.count : spec.values.length;
|
|
838
|
+
}
|
|
839
|
+
let inserted = 0;
|
|
840
|
+
for (const values of spec.values) try {
|
|
841
|
+
await delegate.create({ data: values });
|
|
842
|
+
inserted += 1;
|
|
843
|
+
} catch (error) {
|
|
844
|
+
if (!spec.ignoreDuplicates) throw error;
|
|
845
|
+
}
|
|
846
|
+
return spec.ignoreDuplicates ? inserted : spec.values.length;
|
|
847
|
+
}
|
|
848
|
+
async update(spec) {
|
|
849
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
850
|
+
const where = this.toQueryWhere(spec.where);
|
|
851
|
+
if (!where) return null;
|
|
852
|
+
return await delegate.update({
|
|
853
|
+
where,
|
|
854
|
+
data: spec.values
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
async updateMany(spec) {
|
|
858
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
859
|
+
const where = this.toQueryWhere(spec.where);
|
|
860
|
+
if (typeof delegate.updateMany === "function") {
|
|
861
|
+
const result = await delegate.updateMany({
|
|
862
|
+
where,
|
|
863
|
+
data: spec.values
|
|
864
|
+
});
|
|
865
|
+
if (typeof result === "number") return result;
|
|
866
|
+
return typeof result?.count === "number" ? result.count : 0;
|
|
867
|
+
}
|
|
868
|
+
const rows = await delegate.findMany({ where });
|
|
869
|
+
await Promise.all(rows.map(async (row) => {
|
|
870
|
+
await delegate.update({
|
|
871
|
+
where: row,
|
|
872
|
+
data: spec.values
|
|
873
|
+
});
|
|
874
|
+
}));
|
|
875
|
+
return rows.length;
|
|
876
|
+
}
|
|
877
|
+
async delete(spec) {
|
|
878
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
879
|
+
const where = this.toQueryWhere(spec.where);
|
|
880
|
+
if (!where) return null;
|
|
881
|
+
return await delegate.delete({ where });
|
|
882
|
+
}
|
|
883
|
+
async deleteMany(spec) {
|
|
884
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
885
|
+
const where = this.toQueryWhere(spec.where);
|
|
886
|
+
const rows = await delegate.findMany({ where });
|
|
887
|
+
await Promise.all(rows.map(async (row) => {
|
|
888
|
+
await delegate.delete({ where: row });
|
|
889
|
+
}));
|
|
890
|
+
return rows.length;
|
|
891
|
+
}
|
|
892
|
+
async count(spec) {
|
|
893
|
+
return await this.resolveDelegate(spec.target).count({ where: this.toQueryWhere(spec.where) });
|
|
894
|
+
}
|
|
895
|
+
async exists(spec) {
|
|
896
|
+
return await this.selectOne({
|
|
897
|
+
...spec,
|
|
898
|
+
limit: 1
|
|
899
|
+
}) != null;
|
|
900
|
+
}
|
|
901
|
+
async loadRelations(_spec) {
|
|
902
|
+
throw new UnsupportedAdapterFeatureException("Relation batch loading is not supported by the Prisma compatibility adapter yet.", {
|
|
903
|
+
operation: "adapter.loadRelations",
|
|
904
|
+
meta: { feature: "relationLoads" }
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
async transaction(callback, context = {}) {
|
|
908
|
+
if (!this.hasTransactionSupport(this.prisma)) throw new UnsupportedAdapterFeatureException("Transactions are not supported by the Prisma compatibility adapter.", {
|
|
909
|
+
operation: "adapter.transaction",
|
|
910
|
+
meta: { feature: "transactions" }
|
|
911
|
+
});
|
|
912
|
+
return await this.prisma.$transaction(async (transactionClient) => {
|
|
913
|
+
return await callback(new PrismaDatabaseAdapter(transactionClient, this.mapping));
|
|
914
|
+
}, {
|
|
915
|
+
isolationLevel: context.isolationLevel,
|
|
916
|
+
maxWait: context.maxWait,
|
|
917
|
+
timeout: context.timeout
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
const createPrismaDatabaseAdapter = (prisma, mapping = {}) => {
|
|
922
|
+
return new PrismaDatabaseAdapter(prisma, mapping);
|
|
923
|
+
};
|
|
924
|
+
const createPrismaCompatibilityAdapter = createPrismaDatabaseAdapter;
|
|
925
|
+
|
|
926
|
+
//#endregion
|
|
927
|
+
//#region src/Attribute.ts
|
|
928
|
+
var Attribute = class Attribute {
|
|
929
|
+
get;
|
|
930
|
+
set;
|
|
931
|
+
constructor(options = {}) {
|
|
932
|
+
this.get = options.get;
|
|
933
|
+
this.set = options.set;
|
|
934
|
+
}
|
|
935
|
+
static make(options) {
|
|
936
|
+
return new Attribute(options);
|
|
937
|
+
}
|
|
938
|
+
static isAttribute(value) {
|
|
939
|
+
if (!value || typeof value !== "object") return false;
|
|
940
|
+
return value instanceof Attribute;
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
//#endregion
|
|
945
|
+
//#region src/casts.ts
|
|
946
|
+
const builtinCasts = {
|
|
947
|
+
string: {
|
|
948
|
+
get: (value) => value == null ? value : String(value),
|
|
949
|
+
set: (value) => value == null ? value : String(value)
|
|
950
|
+
},
|
|
951
|
+
number: {
|
|
952
|
+
get: (value) => value == null ? value : Number(value),
|
|
953
|
+
set: (value) => value == null ? value : Number(value)
|
|
954
|
+
},
|
|
955
|
+
boolean: {
|
|
956
|
+
get: (value) => value == null ? value : Boolean(value),
|
|
957
|
+
set: (value) => value == null ? value : Boolean(value)
|
|
958
|
+
},
|
|
959
|
+
date: {
|
|
960
|
+
get: (value) => {
|
|
961
|
+
if (value == null || value instanceof Date) return value;
|
|
962
|
+
return new Date(String(value));
|
|
963
|
+
},
|
|
964
|
+
set: (value) => {
|
|
965
|
+
if (value == null || value instanceof Date) return value;
|
|
966
|
+
return new Date(String(value));
|
|
967
|
+
}
|
|
968
|
+
},
|
|
969
|
+
json: {
|
|
970
|
+
get: (value) => {
|
|
971
|
+
if (value == null || typeof value !== "string") return value;
|
|
972
|
+
try {
|
|
973
|
+
return JSON.parse(value);
|
|
974
|
+
} catch {
|
|
975
|
+
return value;
|
|
976
|
+
}
|
|
977
|
+
},
|
|
978
|
+
set: (value) => {
|
|
979
|
+
if (value == null || typeof value === "string") return value;
|
|
980
|
+
return JSON.stringify(value);
|
|
981
|
+
}
|
|
982
|
+
},
|
|
983
|
+
array: {
|
|
984
|
+
get: (value) => {
|
|
985
|
+
if (Array.isArray(value)) return value;
|
|
986
|
+
if (typeof value === "string") try {
|
|
987
|
+
const parsed = JSON.parse(value);
|
|
988
|
+
return Array.isArray(parsed) ? parsed : [parsed];
|
|
989
|
+
} catch {
|
|
990
|
+
return [value];
|
|
991
|
+
}
|
|
992
|
+
if (value == null) return value;
|
|
993
|
+
return [value];
|
|
994
|
+
},
|
|
995
|
+
set: (value) => {
|
|
996
|
+
if (value == null) return value;
|
|
997
|
+
return Array.isArray(value) ? value : [value];
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
function resolveCast(definition) {
|
|
1002
|
+
if (typeof definition === "string") return builtinCasts[definition];
|
|
1003
|
+
return definition;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
//#endregion
|
|
1007
|
+
//#region src/database/ForeignKeyBuilder.ts
|
|
1008
|
+
/**
|
|
1009
|
+
* The ForeignKeyBuilder class provides a fluent interface for defining
|
|
1010
|
+
* foreign key constraints in a migration. It allows you to specify
|
|
1011
|
+
* the referenced table and column, as well as actions to take on
|
|
1012
|
+
* delete and aliases for the relation.
|
|
1013
|
+
*
|
|
1014
|
+
* @author Legacy (3m1n3nc3)
|
|
1015
|
+
* @since 0.2.2
|
|
1016
|
+
*/
|
|
1017
|
+
var ForeignKeyBuilder = class {
|
|
1018
|
+
foreignKey;
|
|
1019
|
+
constructor(foreignKey) {
|
|
1020
|
+
this.foreignKey = foreignKey;
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Defines the referenced table and column for this foreign key constraint.
|
|
1024
|
+
*
|
|
1025
|
+
* @param table
|
|
1026
|
+
* @param column
|
|
1027
|
+
* @returns
|
|
1028
|
+
*/
|
|
1029
|
+
references(table, column) {
|
|
1030
|
+
this.foreignKey.referencesTable = table;
|
|
1031
|
+
this.foreignKey.referencesColumn = column;
|
|
1032
|
+
return this;
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Defines the action to take when a referenced record is deleted, such
|
|
1036
|
+
* as "CASCADE", "SET NULL", or "RESTRICT".
|
|
1037
|
+
*
|
|
1038
|
+
* @param action
|
|
1039
|
+
* @returns
|
|
1040
|
+
*/
|
|
1041
|
+
onDelete(action) {
|
|
1042
|
+
this.foreignKey.onDelete = action;
|
|
1043
|
+
return this;
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Defines an alias for the relation represented by this foreign key, which
|
|
1047
|
+
* can be used in the ORM for more intuitive access to related models.
|
|
1048
|
+
*
|
|
1049
|
+
* @param name
|
|
1050
|
+
* @returns
|
|
1051
|
+
*/
|
|
1052
|
+
alias(name) {
|
|
1053
|
+
this.foreignKey.relationAlias = name;
|
|
1054
|
+
return this;
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Defines an alias for the inverse relation represented by this foreign key.
|
|
1058
|
+
*
|
|
1059
|
+
* @param name
|
|
1060
|
+
* @returns
|
|
1061
|
+
*/
|
|
1062
|
+
inverseAlias(name) {
|
|
1063
|
+
this.foreignKey.inverseRelationAlias = name;
|
|
1064
|
+
return this;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Defines an alias for the foreign key field itself, which can be
|
|
1068
|
+
* used in the ORM for more intuitive access to the foreign key value.
|
|
1069
|
+
*
|
|
1070
|
+
* @param fieldName
|
|
1071
|
+
* @returns
|
|
1072
|
+
*/
|
|
1073
|
+
as(fieldName) {
|
|
1074
|
+
this.foreignKey.fieldAlias = fieldName;
|
|
1075
|
+
return this;
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
|
|
1079
|
+
//#endregion
|
|
1080
|
+
//#region src/database/TableBuilder.ts
|
|
1081
|
+
const PRISMA_ENUM_MEMBER_REGEX$1 = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
1082
|
+
const normalizeEnumMember = (columnName, value) => {
|
|
1083
|
+
const normalized = value.trim();
|
|
1084
|
+
if (!normalized) throw new Error(`Enum column [${columnName}] must define only non-empty values.`);
|
|
1085
|
+
if (!PRISMA_ENUM_MEMBER_REGEX$1.test(normalized)) throw new Error(`Enum column [${columnName}] contains invalid Prisma enum value [${normalized}].`);
|
|
1086
|
+
return normalized;
|
|
1087
|
+
};
|
|
1088
|
+
const normalizeEnumMembers = (columnName, values) => {
|
|
1089
|
+
const normalizedValues = values.map((value) => normalizeEnumMember(columnName, value));
|
|
1090
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1091
|
+
for (const value of normalizedValues) {
|
|
1092
|
+
if (seen.has(value)) throw new Error(`Enum column [${columnName}] contains duplicate enum value [${value}].`);
|
|
1093
|
+
seen.add(value);
|
|
1094
|
+
}
|
|
1095
|
+
return normalizedValues;
|
|
1096
|
+
};
|
|
1097
|
+
/**
|
|
1098
|
+
* The EnumBuilder class provides a fluent interface for configuring enum columns
|
|
1099
|
+
* after they are defined on a table.
|
|
1100
|
+
*
|
|
1101
|
+
* @author Legacy (3m1n3nc3)
|
|
1102
|
+
* @since 0.2.3
|
|
1103
|
+
*/
|
|
1104
|
+
var EnumBuilder = class {
|
|
1105
|
+
tableBuilder;
|
|
1106
|
+
columnName;
|
|
1107
|
+
constructor(tableBuilder, columnName) {
|
|
1108
|
+
this.tableBuilder = tableBuilder;
|
|
1109
|
+
this.columnName = columnName;
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Defines the Prisma enum name for this column.
|
|
1113
|
+
*
|
|
1114
|
+
* @param name
|
|
1115
|
+
* @returns
|
|
1116
|
+
*/
|
|
1117
|
+
enumName(name) {
|
|
1118
|
+
this.tableBuilder.enumName(name, this.columnName);
|
|
1119
|
+
return this;
|
|
252
1120
|
}
|
|
253
1121
|
/**
|
|
254
1122
|
* Marks the enum column as nullable.
|
|
@@ -952,272 +1820,34 @@ const buildFieldLine = (column) => {
|
|
|
952
1820
|
return ` ${column.name} ${scalar}${nullable}${primary}${unique}${defaultSuffix}${updatedAt}${mapped}`;
|
|
953
1821
|
};
|
|
954
1822
|
/**
|
|
955
|
-
* Build a Prisma enum block string based on an enum name and its values, validating that
|
|
956
|
-
* at least one value is provided.
|
|
957
|
-
*
|
|
958
|
-
* @param enumName The name of the enum to create.
|
|
959
|
-
* @param values The array of values for the enum.
|
|
960
|
-
* @returns The Prisma enum block string.
|
|
961
|
-
*/
|
|
962
|
-
const buildEnumBlock = (enumName, values) => {
|
|
963
|
-
if (values.length === 0) throw new ArkormException(`Enum [${enumName}] must define at least one value.`);
|
|
964
|
-
return `enum ${enumName} {\n${values.map((value) => ` ${value}`).join("\n")}\n}`;
|
|
965
|
-
};
|
|
966
|
-
/**
|
|
967
|
-
* Find the Prisma enum block in a schema string that corresponds to a given enum
|
|
968
|
-
* name, returning its details if found.
|
|
969
|
-
*
|
|
970
|
-
* @param schema The Prisma schema string to search for the enum block.
|
|
971
|
-
* @param enumName The name of the enum to find in the schema.
|
|
972
|
-
* @returns
|
|
973
|
-
*/
|
|
974
|
-
const findEnumBlock = (schema, enumName) => {
|
|
975
|
-
const candidates = [...schema.matchAll(PRISMA_ENUM_REGEX)];
|
|
976
|
-
for (const match of candidates) {
|
|
977
|
-
const block = match[0];
|
|
978
|
-
const matchedEnumName = match[1];
|
|
979
|
-
const start = match.index ?? 0;
|
|
980
|
-
const end = start + block.length;
|
|
981
|
-
if (matchedEnumName === enumName) return {
|
|
982
|
-
enumName: matchedEnumName,
|
|
983
|
-
block,
|
|
984
|
-
start,
|
|
985
|
-
end
|
|
986
|
-
};
|
|
987
|
-
}
|
|
988
|
-
return null;
|
|
989
|
-
};
|
|
990
|
-
/**
|
|
991
|
-
* Ensure that Prisma enum blocks exist in the schema for any enum columns defined in a
|
|
992
|
-
* create or alter table operation, adding them if necessary and validating against
|
|
993
|
-
* existing blocks.
|
|
994
|
-
*
|
|
995
|
-
* @param schema The current Prisma schema string to check and modify.
|
|
996
|
-
* @param columns The array of schema column definitions to check for enum types and ensure corresponding blocks exist for.
|
|
997
|
-
* @returns
|
|
998
|
-
*/
|
|
999
|
-
const ensureEnumBlocks = (schema, columns) => {
|
|
1000
|
-
let nextSchema = schema;
|
|
1001
|
-
for (const column of columns) {
|
|
1002
|
-
if (column.type !== "enum") continue;
|
|
1003
|
-
const enumName = resolveEnumName(column);
|
|
1004
|
-
const enumValues = column.enumValues ?? [];
|
|
1005
|
-
const existing = findEnumBlock(nextSchema, enumName);
|
|
1006
|
-
if (existing) {
|
|
1007
|
-
const existingValues = validateEnumValues(column, enumName, extractEnumBlockValues(existing.block));
|
|
1008
|
-
if (enumValues.length === 0) {
|
|
1009
|
-
validateEnumDefaultValue(column, enumName, existingValues);
|
|
1010
|
-
continue;
|
|
1011
|
-
}
|
|
1012
|
-
const normalizedEnumValues = validateEnumValues(column, enumName, enumValues);
|
|
1013
|
-
if (existingValues.join("|") !== normalizedEnumValues.join("|")) throw new ArkormException(`Prisma enum [${enumName}] already exists with different values.`);
|
|
1014
|
-
validateEnumDefaultValue(column, enumName, existingValues);
|
|
1015
|
-
continue;
|
|
1016
|
-
}
|
|
1017
|
-
if (enumValues.length === 0) throw new ArkormException(`Prisma enum [${enumName}] was not found for column [${column.name}].`);
|
|
1018
|
-
const normalizedEnumValues = validateEnumValues(column, enumName, enumValues);
|
|
1019
|
-
validateEnumDefaultValue(column, enumName, normalizedEnumValues);
|
|
1020
|
-
const block = buildEnumBlock(enumName, normalizedEnumValues);
|
|
1021
|
-
nextSchema = `${nextSchema.trimEnd()}\n\n${block}\n`;
|
|
1022
|
-
}
|
|
1023
|
-
return nextSchema;
|
|
1024
|
-
};
|
|
1025
|
-
/**
|
|
1026
|
-
* Build a Prisma model-level @@index definition line.
|
|
1027
|
-
*
|
|
1028
|
-
* @param index The schema index definition to convert to a Prisma \@\@index line.
|
|
1029
|
-
* @returns
|
|
1030
|
-
*/
|
|
1031
|
-
const buildIndexLine = (index) => {
|
|
1032
|
-
return ` @@index([${index.columns.join(", ")}]${typeof index.name === "string" && index.name.trim().length > 0 ? `, name: "${index.name.replace(/"/g, "\\\"")}"` : ""})`;
|
|
1033
|
-
};
|
|
1034
|
-
/**
|
|
1035
|
-
* Derive a relation field name from a foreign key column name by applying
|
|
1036
|
-
* common conventions, such as removing "Id" suffixes and converting to camelCase.
|
|
1037
|
-
*
|
|
1038
|
-
* @param columnName The name of the foreign key column.
|
|
1039
|
-
* @returns The derived relation field name.
|
|
1040
|
-
*/
|
|
1041
|
-
const deriveRelationFieldName = (columnName) => {
|
|
1042
|
-
const trimmed = columnName.trim();
|
|
1043
|
-
if (!trimmed) return "relation";
|
|
1044
|
-
if (trimmed.endsWith("Id") && trimmed.length > 2) {
|
|
1045
|
-
const root = trimmed.slice(0, -2);
|
|
1046
|
-
return `${root.charAt(0).toLowerCase()}${root.slice(1)}`;
|
|
1047
|
-
}
|
|
1048
|
-
if (trimmed.endsWith("_id") && trimmed.length > 3) return trimmed.slice(0, -3).replace(/_([a-zA-Z0-9])/g, (_, letter) => letter.toUpperCase());
|
|
1049
|
-
return `${trimmed.charAt(0).toLowerCase()}${trimmed.slice(1)}`;
|
|
1050
|
-
};
|
|
1051
|
-
/**
|
|
1052
|
-
* Derive a relation name for both sides of a relation based on the
|
|
1053
|
-
* source and target model names, using an explicit alias if provided or a
|
|
1054
|
-
* convention of combining the full source model name with the target model name.
|
|
1055
|
-
*
|
|
1056
|
-
* @param sourceModelName The name of the source model in the relation.
|
|
1057
|
-
* @param targetModelName The name of the target model in the relation.
|
|
1058
|
-
* @param explicitAlias An optional explicit alias for the relation.
|
|
1059
|
-
* @returns The derived or explicit relation alias.
|
|
1060
|
-
*/
|
|
1061
|
-
const deriveRelationAlias = (sourceModelName, targetModelName, explicitAlias) => {
|
|
1062
|
-
if (explicitAlias && explicitAlias.trim().length > 0) return explicitAlias.trim();
|
|
1063
|
-
return [sourceModelName, targetModelName].sort((left, right) => left.localeCompare(right)).join("");
|
|
1064
|
-
};
|
|
1065
|
-
const deriveInverseRelationAlias = deriveRelationAlias;
|
|
1066
|
-
const deriveSingularFieldName = (modelName) => {
|
|
1067
|
-
if (!modelName) return "item";
|
|
1068
|
-
return `${modelName.charAt(0).toLowerCase()}${modelName.slice(1)}`;
|
|
1069
|
-
};
|
|
1070
|
-
const deriveCollectionFieldName = (modelName) => {
|
|
1071
|
-
if (!modelName) return "items";
|
|
1072
|
-
const camel = `${modelName.charAt(0).toLowerCase()}${modelName.slice(1)}`;
|
|
1073
|
-
if (camel.endsWith("s")) return `${camel}es`;
|
|
1074
|
-
return `${camel}s`;
|
|
1075
|
-
};
|
|
1076
|
-
const resolveForeignKeyColumn = (columns, foreignKey) => {
|
|
1077
|
-
return columns.find((column) => column.name === foreignKey.column);
|
|
1078
|
-
};
|
|
1079
|
-
const isOneToOneForeignKey = (column) => {
|
|
1080
|
-
return Boolean(column?.unique || column?.primary);
|
|
1081
|
-
};
|
|
1082
|
-
/**
|
|
1083
|
-
* Format a SchemaForeignKeyAction value as a Prisma onDelete action string.
|
|
1084
|
-
*
|
|
1085
|
-
* @param action The foreign key action to format.
|
|
1086
|
-
* @returns The corresponding Prisma onDelete action string.
|
|
1087
|
-
*/
|
|
1088
|
-
const formatRelationAction = (action) => {
|
|
1089
|
-
if (action === "cascade") return "Cascade";
|
|
1090
|
-
if (action === "restrict") return "Restrict";
|
|
1091
|
-
if (action === "setNull") return "SetNull";
|
|
1092
|
-
if (action === "setDefault") return "SetDefault";
|
|
1093
|
-
return "NoAction";
|
|
1094
|
-
};
|
|
1095
|
-
/**
|
|
1096
|
-
* Build a Prisma relation field line based on a SchemaForeignKey
|
|
1097
|
-
* definition, including relation name and onDelete action.
|
|
1098
|
-
*
|
|
1099
|
-
* @param foreignKey The foreign key definition to convert to a relation line.
|
|
1100
|
-
* @returns The corresponding Prisma schema line for the relation field.
|
|
1101
|
-
*/
|
|
1102
|
-
const buildRelationLine = (sourceModelName, foreignKey, columns = []) => {
|
|
1103
|
-
if (!foreignKey.referencesTable.trim()) throw new ArkormException(`Foreign key [${foreignKey.column}] must define a referenced table.`);
|
|
1104
|
-
if (!foreignKey.referencesColumn.trim()) throw new ArkormException(`Foreign key [${foreignKey.column}] must define a referenced column.`);
|
|
1105
|
-
const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
|
|
1106
|
-
const fieldName = foreignKey.fieldAlias?.trim() || deriveRelationFieldName(foreignKey.column);
|
|
1107
|
-
const targetModel = toModelName(foreignKey.referencesTable);
|
|
1108
|
-
const relationName = deriveRelationAlias(sourceModelName, targetModel, foreignKey.relationAlias?.trim());
|
|
1109
|
-
const optional = sourceColumn?.nullable ? "?" : "";
|
|
1110
|
-
const onDelete = foreignKey.onDelete ? `, onDelete: ${formatRelationAction(foreignKey.onDelete)}` : "";
|
|
1111
|
-
return ` ${fieldName} ${targetModel}${optional} @relation("${relationName.replace(/"/g, "\\\"")}", fields: [${foreignKey.column}], references: [${foreignKey.referencesColumn}]${onDelete})`;
|
|
1112
|
-
};
|
|
1113
|
-
/**
|
|
1114
|
-
* Build a Prisma relation field line for the inverse side of a relation, based
|
|
1115
|
-
* on the source and target model names and the foreign key definition, using
|
|
1116
|
-
* naming conventions and any explicit inverse alias provided.
|
|
1117
|
-
*
|
|
1118
|
-
* @param sourceModelName The name of the source model in the relation.
|
|
1119
|
-
* @param targetModelName The name of the target model in the relation.
|
|
1120
|
-
* @param foreignKey The foreign key definition for the relation.
|
|
1121
|
-
* @returns The Prisma schema line for the inverse relation field.
|
|
1122
|
-
*/
|
|
1123
|
-
const buildInverseRelationLine = (sourceModelName, targetModelName, foreignKey, columns = []) => {
|
|
1124
|
-
const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
|
|
1125
|
-
const fieldName = isOneToOneForeignKey(sourceColumn) ? deriveSingularFieldName(sourceModelName) : deriveCollectionFieldName(sourceModelName);
|
|
1126
|
-
const relationName = deriveRelationAlias(sourceModelName, targetModelName, foreignKey.relationAlias?.trim());
|
|
1127
|
-
return ` ${fieldName} ${isOneToOneForeignKey(sourceColumn) ? `${sourceModelName}?` : `${sourceModelName}[]`} @relation("${relationName.replace(/"/g, "\\\"")}")`;
|
|
1128
|
-
};
|
|
1129
|
-
/**
|
|
1130
|
-
* Inject a line into the body of a Prisma model block if it does not already
|
|
1131
|
-
* exist, using a provided existence check function to determine if the line
|
|
1132
|
-
* is already present.
|
|
1133
|
-
*
|
|
1134
|
-
* @param bodyLines The lines of the model block body to modify.
|
|
1135
|
-
* @param line The line to inject if it does not already exist.
|
|
1136
|
-
* @param exists A function that checks if a given line already exists in the body.
|
|
1137
|
-
* @returns
|
|
1138
|
-
*/
|
|
1139
|
-
const injectLineIntoModelBody = (bodyLines, line, exists) => {
|
|
1140
|
-
if (bodyLines.some(exists)) return bodyLines;
|
|
1141
|
-
const insertIndex = Math.max(1, bodyLines.length - 1);
|
|
1142
|
-
bodyLines.splice(insertIndex, 0, line);
|
|
1143
|
-
return bodyLines;
|
|
1144
|
-
};
|
|
1145
|
-
/**
|
|
1146
|
-
* Apply inverse relation definitions to a Prisma schema string based on the
|
|
1147
|
-
* foreign keys defined in a create or alter table operation, ensuring that
|
|
1148
|
-
* related models have corresponding relation fields for bi-directional navigation.
|
|
1149
|
-
*
|
|
1150
|
-
* @param schema The Prisma schema string to modify.
|
|
1151
|
-
* @param sourceModelName The name of the source model in the relation.
|
|
1152
|
-
* @param foreignKeys An array of foreign key definitions to process.
|
|
1153
|
-
* @returns The updated Prisma schema string with inverse relations applied.
|
|
1154
|
-
*/
|
|
1155
|
-
const applyInverseRelations = (schema, sourceModelName, foreignKeys, columns = []) => {
|
|
1156
|
-
let nextSchema = schema;
|
|
1157
|
-
for (const foreignKey of foreignKeys) {
|
|
1158
|
-
const targetModel = findModelBlock(nextSchema, foreignKey.referencesTable);
|
|
1159
|
-
if (!targetModel) continue;
|
|
1160
|
-
const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
|
|
1161
|
-
const inverseLine = buildInverseRelationLine(sourceModelName, targetModel.modelName, foreignKey, columns);
|
|
1162
|
-
const targetBodyLines = targetModel.block.split("\n");
|
|
1163
|
-
const fieldName = isOneToOneForeignKey(sourceColumn) ? deriveSingularFieldName(sourceModelName) : deriveCollectionFieldName(sourceModelName);
|
|
1164
|
-
const fieldRegex = new RegExp(`^\\s*${escapeRegex(fieldName)}\\s+`);
|
|
1165
|
-
injectLineIntoModelBody(targetBodyLines, inverseLine, (line) => fieldRegex.test(line));
|
|
1166
|
-
const updatedTarget = targetBodyLines.join("\n");
|
|
1167
|
-
nextSchema = `${nextSchema.slice(0, targetModel.start)}${updatedTarget}${nextSchema.slice(targetModel.end)}`;
|
|
1168
|
-
}
|
|
1169
|
-
return nextSchema;
|
|
1170
|
-
};
|
|
1171
|
-
/**
|
|
1172
|
-
* Build a Prisma model block string based on a SchemaTableCreateOperation, including
|
|
1173
|
-
* all fields and any necessary mapping.
|
|
1823
|
+
* Build a Prisma enum block string based on an enum name and its values, validating that
|
|
1824
|
+
* at least one value is provided.
|
|
1174
1825
|
*
|
|
1175
|
-
* @param
|
|
1176
|
-
* @
|
|
1826
|
+
* @param enumName The name of the enum to create.
|
|
1827
|
+
* @param values The array of values for the enum.
|
|
1828
|
+
* @returns The Prisma enum block string.
|
|
1177
1829
|
*/
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
const fields = operation.columns.map(buildFieldLine);
|
|
1182
|
-
const relations = (operation.foreignKeys ?? []).map((foreignKey) => buildRelationLine(modelName, foreignKey, operation.columns));
|
|
1183
|
-
const metadata = [...(operation.indexes ?? []).map(buildIndexLine), ...mapped ? [` @@map("${str(operation.table).snake()}")`] : []];
|
|
1184
|
-
return `model ${modelName} {\n${(metadata.length > 0 ? [
|
|
1185
|
-
...fields,
|
|
1186
|
-
...relations,
|
|
1187
|
-
"",
|
|
1188
|
-
...metadata
|
|
1189
|
-
] : [...fields, ...relations]).join("\n")}\n}`;
|
|
1830
|
+
const buildEnumBlock = (enumName, values) => {
|
|
1831
|
+
if (values.length === 0) throw new ArkormException(`Enum [${enumName}] must define at least one value.`);
|
|
1832
|
+
return `enum ${enumName} {\n${values.map((value) => ` ${value}`).join("\n")}\n}`;
|
|
1190
1833
|
};
|
|
1191
1834
|
/**
|
|
1192
|
-
* Find the Prisma
|
|
1193
|
-
*
|
|
1835
|
+
* Find the Prisma enum block in a schema string that corresponds to a given enum
|
|
1836
|
+
* name, returning its details if found.
|
|
1194
1837
|
*
|
|
1195
|
-
* @param schema
|
|
1196
|
-
* @param
|
|
1838
|
+
* @param schema The Prisma schema string to search for the enum block.
|
|
1839
|
+
* @param enumName The name of the enum to find in the schema.
|
|
1197
1840
|
* @returns
|
|
1198
1841
|
*/
|
|
1199
|
-
const
|
|
1200
|
-
const candidates = [...schema.matchAll(
|
|
1201
|
-
const explicitMapRegex = new RegExp(`@@map\\("${escapeRegex(table)}"\\)`);
|
|
1842
|
+
const findEnumBlock = (schema, enumName) => {
|
|
1843
|
+
const candidates = [...schema.matchAll(PRISMA_ENUM_REGEX)];
|
|
1202
1844
|
for (const match of candidates) {
|
|
1203
1845
|
const block = match[0];
|
|
1204
|
-
const
|
|
1846
|
+
const matchedEnumName = match[1];
|
|
1205
1847
|
const start = match.index ?? 0;
|
|
1206
1848
|
const end = start + block.length;
|
|
1207
|
-
if (
|
|
1208
|
-
|
|
1209
|
-
block,
|
|
1210
|
-
start,
|
|
1211
|
-
end
|
|
1212
|
-
};
|
|
1213
|
-
if (modelName.toLowerCase() === table.toLowerCase()) return {
|
|
1214
|
-
modelName,
|
|
1215
|
-
block,
|
|
1216
|
-
start,
|
|
1217
|
-
end
|
|
1218
|
-
};
|
|
1219
|
-
if (modelName.toLowerCase() === toModelName(table).toLowerCase()) return {
|
|
1220
|
-
modelName,
|
|
1849
|
+
if (matchedEnumName === enumName) return {
|
|
1850
|
+
enumName: matchedEnumName,
|
|
1221
1851
|
block,
|
|
1222
1852
|
start,
|
|
1223
1853
|
end
|
|
@@ -1226,600 +1856,543 @@ const findModelBlock = (schema, table) => {
|
|
|
1226
1856
|
return null;
|
|
1227
1857
|
};
|
|
1228
1858
|
/**
|
|
1229
|
-
*
|
|
1230
|
-
*
|
|
1231
|
-
*
|
|
1232
|
-
* @param schema The current Prisma schema string.
|
|
1233
|
-
* @param operation The schema table create operation to apply.
|
|
1234
|
-
* @returns The updated Prisma schema string with the new model block.
|
|
1235
|
-
*/
|
|
1236
|
-
const applyCreateTableOperation = (schema, operation) => {
|
|
1237
|
-
if (findModelBlock(schema, operation.table)) throw new ArkormException(`Prisma model for table [${operation.table}] already exists.`);
|
|
1238
|
-
const schemaWithEnums = ensureEnumBlocks(schema, operation.columns);
|
|
1239
|
-
const block = buildModelBlock(operation);
|
|
1240
|
-
return applyInverseRelations(`${schemaWithEnums.trimEnd()}\n\n${block}\n`, toModelName(operation.table), operation.foreignKeys ?? [], operation.columns);
|
|
1241
|
-
};
|
|
1242
|
-
/**
|
|
1243
|
-
* Apply an alter table operation to a Prisma schema string, modifying the model
|
|
1244
|
-
* block for the specified table by adding and removing fields as needed.
|
|
1859
|
+
* Ensure that Prisma enum blocks exist in the schema for any enum columns defined in a
|
|
1860
|
+
* create or alter table operation, adding them if necessary and validating against
|
|
1861
|
+
* existing blocks.
|
|
1245
1862
|
*
|
|
1246
|
-
* @param schema The current Prisma schema string.
|
|
1247
|
-
* @param
|
|
1248
|
-
* @returns
|
|
1863
|
+
* @param schema The current Prisma schema string to check and modify.
|
|
1864
|
+
* @param columns The array of schema column definitions to check for enum types and ensure corresponding blocks exist for.
|
|
1865
|
+
* @returns
|
|
1249
1866
|
*/
|
|
1250
|
-
const
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1867
|
+
const ensureEnumBlocks = (schema, columns) => {
|
|
1868
|
+
let nextSchema = schema;
|
|
1869
|
+
for (const column of columns) {
|
|
1870
|
+
if (column.type !== "enum") continue;
|
|
1871
|
+
const enumName = resolveEnumName(column);
|
|
1872
|
+
const enumValues = column.enumValues ?? [];
|
|
1873
|
+
const existing = findEnumBlock(nextSchema, enumName);
|
|
1874
|
+
if (existing) {
|
|
1875
|
+
const existingValues = validateEnumValues(column, enumName, extractEnumBlockValues(existing.block));
|
|
1876
|
+
if (enumValues.length === 0) {
|
|
1877
|
+
validateEnumDefaultValue(column, enumName, existingValues);
|
|
1878
|
+
continue;
|
|
1879
|
+
}
|
|
1880
|
+
const normalizedEnumValues = validateEnumValues(column, enumName, enumValues);
|
|
1881
|
+
if (existingValues.join("|") !== normalizedEnumValues.join("|")) throw new ArkormException(`Prisma enum [${enumName}] already exists with different values.`);
|
|
1882
|
+
validateEnumDefaultValue(column, enumName, existingValues);
|
|
1883
|
+
continue;
|
|
1263
1884
|
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1269
|
-
const defaultInsertIndex = Math.max(1, bodyLines.length - 1);
|
|
1270
|
-
const afterInsertIndex = typeof column.after === "string" && column.after.length > 0 ? bodyLines.findIndex((line) => new RegExp(`^\\s*${escapeRegex(column.after)}\\s+`).test(line)) : -1;
|
|
1271
|
-
const insertIndex = afterInsertIndex > 0 ? Math.min(afterInsertIndex + 1, defaultInsertIndex) : defaultInsertIndex;
|
|
1272
|
-
bodyLines.splice(insertIndex, 0, fieldLine);
|
|
1273
|
-
});
|
|
1274
|
-
(operation.addIndexes ?? []).forEach((index) => {
|
|
1275
|
-
const indexLine = buildIndexLine(index);
|
|
1276
|
-
if (bodyLines.some((line) => line.trim() === indexLine.trim())) return;
|
|
1277
|
-
const insertIndex = Math.max(1, bodyLines.length - 1);
|
|
1278
|
-
bodyLines.splice(insertIndex, 0, indexLine);
|
|
1279
|
-
});
|
|
1280
|
-
for (const foreignKey of operation.addForeignKeys ?? []) {
|
|
1281
|
-
const relationLine = buildRelationLine(model.modelName, foreignKey, operation.addColumns);
|
|
1282
|
-
const relationRegex = new RegExp(`^\\s*${escapeRegex(foreignKey.fieldAlias?.trim() || deriveRelationFieldName(foreignKey.column))}\\s+`);
|
|
1283
|
-
injectLineIntoModelBody(bodyLines, relationLine, (line) => relationRegex.test(line));
|
|
1885
|
+
if (enumValues.length === 0) throw new ArkormException(`Prisma enum [${enumName}] was not found for column [${column.name}].`);
|
|
1886
|
+
const normalizedEnumValues = validateEnumValues(column, enumName, enumValues);
|
|
1887
|
+
validateEnumDefaultValue(column, enumName, normalizedEnumValues);
|
|
1888
|
+
const block = buildEnumBlock(enumName, normalizedEnumValues);
|
|
1889
|
+
nextSchema = `${nextSchema.trimEnd()}\n\n${block}\n`;
|
|
1284
1890
|
}
|
|
1285
|
-
|
|
1286
|
-
return applyInverseRelations(`${schemaWithEnums.slice(0, refreshedModel.start)}${block}${schemaWithEnums.slice(refreshedModel.end)}`, model.modelName, operation.addForeignKeys ?? [], operation.addColumns);
|
|
1287
|
-
};
|
|
1288
|
-
/**
|
|
1289
|
-
* Apply a drop table operation to a Prisma schema string, removing the model block
|
|
1290
|
-
* for the specified table.
|
|
1291
|
-
*/
|
|
1292
|
-
const applyDropTableOperation = (schema, operation) => {
|
|
1293
|
-
const model = findModelBlock(schema, operation.table);
|
|
1294
|
-
if (!model) return schema;
|
|
1295
|
-
const before = schema.slice(0, model.start).trimEnd();
|
|
1296
|
-
const after = schema.slice(model.end).trimStart();
|
|
1297
|
-
return `${before}${before && after ? "\n\n" : ""}${after}`;
|
|
1891
|
+
return nextSchema;
|
|
1298
1892
|
};
|
|
1299
1893
|
/**
|
|
1300
|
-
*
|
|
1301
|
-
* database schema operations in a migration, such as creating, altering, and
|
|
1302
|
-
* dropping tables.
|
|
1894
|
+
* Build a Prisma model-level @@index definition line.
|
|
1303
1895
|
*
|
|
1304
|
-
* @param
|
|
1305
|
-
* @
|
|
1306
|
-
* @returns The updated Prisma schema string after applying all operations.
|
|
1896
|
+
* @param index The schema index definition to convert to a Prisma \@\@index line.
|
|
1897
|
+
* @returns
|
|
1307
1898
|
*/
|
|
1308
|
-
const
|
|
1309
|
-
return
|
|
1310
|
-
if (operation.type === "createTable") return applyCreateTableOperation(current, operation);
|
|
1311
|
-
if (operation.type === "alterTable") return applyAlterTableOperation(current, operation);
|
|
1312
|
-
return applyDropTableOperation(current, operation);
|
|
1313
|
-
}, schema);
|
|
1899
|
+
const buildIndexLine = (index) => {
|
|
1900
|
+
return ` @@index([${index.columns.join(", ")}]${typeof index.name === "string" && index.name.trim().length > 0 ? `, name: "${index.name.replace(/"/g, "\\\"")}"` : ""})`;
|
|
1314
1901
|
};
|
|
1315
1902
|
/**
|
|
1316
|
-
*
|
|
1903
|
+
* Derive a relation field name from a foreign key column name by applying
|
|
1904
|
+
* common conventions, such as removing "Id" suffixes and converting to camelCase.
|
|
1317
1905
|
*
|
|
1318
|
-
* @param
|
|
1319
|
-
* @
|
|
1320
|
-
* @returns void
|
|
1906
|
+
* @param columnName The name of the foreign key column.
|
|
1907
|
+
* @returns The derived relation field name.
|
|
1321
1908
|
*/
|
|
1322
|
-
const
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1909
|
+
const deriveRelationFieldName = (columnName) => {
|
|
1910
|
+
const trimmed = columnName.trim();
|
|
1911
|
+
if (!trimmed) return "relation";
|
|
1912
|
+
if (trimmed.endsWith("Id") && trimmed.length > 2) {
|
|
1913
|
+
const root = trimmed.slice(0, -2);
|
|
1914
|
+
return `${root.charAt(0).toLowerCase()}${root.slice(1)}`;
|
|
1915
|
+
}
|
|
1916
|
+
if (trimmed.endsWith("_id") && trimmed.length > 3) return trimmed.slice(0, -3).replace(/_([a-zA-Z0-9])/g, (_, letter) => letter.toUpperCase());
|
|
1917
|
+
return `${trimmed.charAt(0).toLowerCase()}${trimmed.slice(1)}`;
|
|
1330
1918
|
};
|
|
1331
1919
|
/**
|
|
1332
|
-
*
|
|
1333
|
-
*
|
|
1920
|
+
* Derive a relation name for both sides of a relation based on the
|
|
1921
|
+
* source and target model names, using an explicit alias if provided or a
|
|
1922
|
+
* convention of combining the full source model name with the target model name.
|
|
1334
1923
|
*
|
|
1335
|
-
* @param name
|
|
1336
|
-
* @
|
|
1924
|
+
* @param sourceModelName The name of the source model in the relation.
|
|
1925
|
+
* @param targetModelName The name of the target model in the relation.
|
|
1926
|
+
* @param explicitAlias An optional explicit alias for the relation.
|
|
1927
|
+
* @returns The derived or explicit relation alias.
|
|
1337
1928
|
*/
|
|
1338
|
-
const
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
return `${cleaned.split(/\s+/g).map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join("")}Migration`;
|
|
1929
|
+
const deriveRelationAlias = (sourceModelName, targetModelName, explicitAlias) => {
|
|
1930
|
+
if (explicitAlias && explicitAlias.trim().length > 0) return explicitAlias.trim();
|
|
1931
|
+
return [sourceModelName, targetModelName].sort((left, right) => left.localeCompare(right)).join("");
|
|
1342
1932
|
};
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
* @param value
|
|
1348
|
-
* @returns
|
|
1349
|
-
*/
|
|
1350
|
-
const pad = (value) => String(value).padStart(2, "0");
|
|
1351
|
-
/**
|
|
1352
|
-
* Create a timestamp string in the format YYYYMMDDHHMMSS for use in migration
|
|
1353
|
-
* file names, based on the current date and time or a provided date.
|
|
1354
|
-
*
|
|
1355
|
-
* @param date
|
|
1356
|
-
* @returns
|
|
1357
|
-
*/
|
|
1358
|
-
const createMigrationTimestamp = (date = /* @__PURE__ */ new Date()) => {
|
|
1359
|
-
return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
|
|
1933
|
+
const deriveInverseRelationAlias = deriveRelationAlias;
|
|
1934
|
+
const deriveSingularFieldName = (modelName) => {
|
|
1935
|
+
if (!modelName) return "item";
|
|
1936
|
+
return `${modelName.charAt(0).toLowerCase()}${modelName.slice(1)}`;
|
|
1360
1937
|
};
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1938
|
+
const deriveCollectionFieldName = (modelName) => {
|
|
1939
|
+
if (!modelName) return "items";
|
|
1940
|
+
const camel = `${modelName.charAt(0).toLowerCase()}${modelName.slice(1)}`;
|
|
1941
|
+
if (camel.endsWith("s")) return `${camel}es`;
|
|
1942
|
+
return `${camel}s`;
|
|
1943
|
+
};
|
|
1944
|
+
const resolveForeignKeyColumn = (columns, foreignKey) => {
|
|
1945
|
+
return columns.find((column) => column.name === foreignKey.column);
|
|
1946
|
+
};
|
|
1947
|
+
const isOneToOneForeignKey = (column) => {
|
|
1948
|
+
return Boolean(column?.unique || column?.primary);
|
|
1949
|
+
};
|
|
1950
|
+
/**
|
|
1951
|
+
* Format a SchemaForeignKeyAction value as a Prisma onDelete action string.
|
|
1364
1952
|
*
|
|
1365
|
-
* @param
|
|
1366
|
-
* @returns
|
|
1953
|
+
* @param action The foreign key action to format.
|
|
1954
|
+
* @returns The corresponding Prisma onDelete action string.
|
|
1367
1955
|
*/
|
|
1368
|
-
const
|
|
1369
|
-
|
|
1956
|
+
const formatRelationAction = (action) => {
|
|
1957
|
+
if (action === "cascade") return "Cascade";
|
|
1958
|
+
if (action === "restrict") return "Restrict";
|
|
1959
|
+
if (action === "setNull") return "SetNull";
|
|
1960
|
+
if (action === "setDefault") return "SetDefault";
|
|
1961
|
+
return "NoAction";
|
|
1370
1962
|
};
|
|
1371
1963
|
/**
|
|
1372
|
-
* Build
|
|
1373
|
-
*
|
|
1964
|
+
* Build a Prisma relation field line based on a SchemaForeignKey
|
|
1965
|
+
* definition, including relation name and onDelete action.
|
|
1374
1966
|
*
|
|
1375
|
-
* @param
|
|
1376
|
-
* @returns
|
|
1967
|
+
* @param foreignKey The foreign key definition to convert to a relation line.
|
|
1968
|
+
* @returns The corresponding Prisma schema line for the relation field.
|
|
1377
1969
|
*/
|
|
1378
|
-
const
|
|
1379
|
-
if (
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
" }",
|
|
1389
|
-
"",
|
|
1390
|
-
" /**",
|
|
1391
|
-
" * @param {import('arkormx').SchemaBuilder} schema",
|
|
1392
|
-
" * @returns {Promise<void>}",
|
|
1393
|
-
" */",
|
|
1394
|
-
" async down (schema) {",
|
|
1395
|
-
" }",
|
|
1396
|
-
"}",
|
|
1397
|
-
""
|
|
1398
|
-
].join("\n");
|
|
1399
|
-
return [
|
|
1400
|
-
"import { Migration, SchemaBuilder } from 'arkormx'",
|
|
1401
|
-
"",
|
|
1402
|
-
`export default class ${className} extends Migration {`,
|
|
1403
|
-
" public async up (schema: SchemaBuilder): Promise<void> {",
|
|
1404
|
-
" }",
|
|
1405
|
-
"",
|
|
1406
|
-
" public async down (schema: SchemaBuilder): Promise<void> {",
|
|
1407
|
-
" }",
|
|
1408
|
-
"}",
|
|
1409
|
-
""
|
|
1410
|
-
].join("\n");
|
|
1970
|
+
const buildRelationLine = (sourceModelName, foreignKey, columns = []) => {
|
|
1971
|
+
if (!foreignKey.referencesTable.trim()) throw new ArkormException(`Foreign key [${foreignKey.column}] must define a referenced table.`);
|
|
1972
|
+
if (!foreignKey.referencesColumn.trim()) throw new ArkormException(`Foreign key [${foreignKey.column}] must define a referenced column.`);
|
|
1973
|
+
const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
|
|
1974
|
+
const fieldName = foreignKey.fieldAlias?.trim() || deriveRelationFieldName(foreignKey.column);
|
|
1975
|
+
const targetModel = toModelName(foreignKey.referencesTable);
|
|
1976
|
+
const relationName = deriveRelationAlias(sourceModelName, targetModel, foreignKey.relationAlias?.trim());
|
|
1977
|
+
const optional = sourceColumn?.nullable ? "?" : "";
|
|
1978
|
+
const onDelete = foreignKey.onDelete ? `, onDelete: ${formatRelationAction(foreignKey.onDelete)}` : "";
|
|
1979
|
+
return ` ${fieldName} ${targetModel}${optional} @relation("${relationName.replace(/"/g, "\\\"")}", fields: [${foreignKey.column}], references: [${foreignKey.referencesColumn}]${onDelete})`;
|
|
1411
1980
|
};
|
|
1412
1981
|
/**
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1982
|
+
* Build a Prisma relation field line for the inverse side of a relation, based
|
|
1983
|
+
* on the source and target model names and the foreign key definition, using
|
|
1984
|
+
* naming conventions and any explicit inverse alias provided.
|
|
1415
1985
|
*
|
|
1416
|
-
* @param name
|
|
1417
|
-
* @param
|
|
1418
|
-
* @
|
|
1986
|
+
* @param sourceModelName The name of the source model in the relation.
|
|
1987
|
+
* @param targetModelName The name of the target model in the relation.
|
|
1988
|
+
* @param foreignKey The foreign key definition for the relation.
|
|
1989
|
+
* @returns The Prisma schema line for the inverse relation field.
|
|
1419
1990
|
*/
|
|
1420
|
-
const
|
|
1421
|
-
const
|
|
1422
|
-
const
|
|
1423
|
-
const
|
|
1424
|
-
|
|
1425
|
-
const directory = options.directory ?? join(process.cwd(), "database", "migrations");
|
|
1426
|
-
const fileName = `${timestamp}_${fileSlug}.${extension}`;
|
|
1427
|
-
const filePath = join(directory, fileName);
|
|
1428
|
-
const content = buildMigrationSource(className, extension);
|
|
1429
|
-
if (options.write ?? true) {
|
|
1430
|
-
if (!existsSync(directory)) mkdirSync(directory, { recursive: true });
|
|
1431
|
-
if (existsSync(filePath)) throw new ArkormException(`Migration file already exists: ${filePath}`);
|
|
1432
|
-
writeFileSync(filePath, content);
|
|
1433
|
-
}
|
|
1434
|
-
return {
|
|
1435
|
-
fileName,
|
|
1436
|
-
filePath,
|
|
1437
|
-
className,
|
|
1438
|
-
content
|
|
1439
|
-
};
|
|
1991
|
+
const buildInverseRelationLine = (sourceModelName, targetModelName, foreignKey, columns = []) => {
|
|
1992
|
+
const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
|
|
1993
|
+
const fieldName = isOneToOneForeignKey(sourceColumn) ? deriveSingularFieldName(sourceModelName) : deriveCollectionFieldName(sourceModelName);
|
|
1994
|
+
const relationName = deriveRelationAlias(sourceModelName, targetModelName, foreignKey.relationAlias?.trim());
|
|
1995
|
+
return ` ${fieldName} ${isOneToOneForeignKey(sourceColumn) ? `${sourceModelName}?` : `${sourceModelName}[]`} @relation("${relationName.replace(/"/g, "\\\"")}")`;
|
|
1440
1996
|
};
|
|
1441
1997
|
/**
|
|
1442
|
-
*
|
|
1998
|
+
* Inject a line into the body of a Prisma model block if it does not already
|
|
1999
|
+
* exist, using a provided existence check function to determine if the line
|
|
2000
|
+
* is already present.
|
|
1443
2001
|
*
|
|
1444
|
-
* @param
|
|
1445
|
-
* @param
|
|
1446
|
-
* @
|
|
2002
|
+
* @param bodyLines The lines of the model block body to modify.
|
|
2003
|
+
* @param line The line to inject if it does not already exist.
|
|
2004
|
+
* @param exists A function that checks if a given line already exists in the body.
|
|
2005
|
+
* @returns
|
|
1447
2006
|
*/
|
|
1448
|
-
const
|
|
1449
|
-
|
|
1450
|
-
const
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
return schema.getOperations();
|
|
2007
|
+
const injectLineIntoModelBody = (bodyLines, line, exists) => {
|
|
2008
|
+
if (bodyLines.some(exists)) return bodyLines;
|
|
2009
|
+
const insertIndex = Math.max(1, bodyLines.length - 1);
|
|
2010
|
+
bodyLines.splice(insertIndex, 0, line);
|
|
2011
|
+
return bodyLines;
|
|
1454
2012
|
};
|
|
1455
2013
|
/**
|
|
1456
|
-
* Apply
|
|
1457
|
-
*
|
|
1458
|
-
*
|
|
2014
|
+
* Apply inverse relation definitions to a Prisma schema string based on the
|
|
2015
|
+
* foreign keys defined in a create or alter table operation, ensuring that
|
|
2016
|
+
* related models have corresponding relation fields for bi-directional navigation.
|
|
1459
2017
|
*
|
|
1460
|
-
* @param
|
|
1461
|
-
* @param
|
|
1462
|
-
* @
|
|
2018
|
+
* @param schema The Prisma schema string to modify.
|
|
2019
|
+
* @param sourceModelName The name of the source model in the relation.
|
|
2020
|
+
* @param foreignKeys An array of foreign key definitions to process.
|
|
2021
|
+
* @returns The updated Prisma schema string with inverse relations applied.
|
|
1463
2022
|
*/
|
|
1464
|
-
const
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
2023
|
+
const applyInverseRelations = (schema, sourceModelName, foreignKeys, columns = []) => {
|
|
2024
|
+
let nextSchema = schema;
|
|
2025
|
+
for (const foreignKey of foreignKeys) {
|
|
2026
|
+
const targetModel = findModelBlock(nextSchema, foreignKey.referencesTable);
|
|
2027
|
+
if (!targetModel) continue;
|
|
2028
|
+
const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
|
|
2029
|
+
const inverseLine = buildInverseRelationLine(sourceModelName, targetModel.modelName, foreignKey, columns);
|
|
2030
|
+
const targetBodyLines = targetModel.block.split("\n");
|
|
2031
|
+
const fieldName = isOneToOneForeignKey(sourceColumn) ? deriveSingularFieldName(sourceModelName) : deriveCollectionFieldName(sourceModelName);
|
|
2032
|
+
const fieldRegex = new RegExp(`^\\s*${escapeRegex(fieldName)}\\s+`);
|
|
2033
|
+
injectLineIntoModelBody(targetBodyLines, inverseLine, (line) => fieldRegex.test(line));
|
|
2034
|
+
const updatedTarget = targetBodyLines.join("\n");
|
|
2035
|
+
nextSchema = `${nextSchema.slice(0, targetModel.start)}${updatedTarget}${nextSchema.slice(targetModel.end)}`;
|
|
2036
|
+
}
|
|
2037
|
+
return nextSchema;
|
|
1476
2038
|
};
|
|
1477
2039
|
/**
|
|
1478
|
-
*
|
|
1479
|
-
*
|
|
1480
|
-
*
|
|
1481
|
-
* @param
|
|
1482
|
-
* @returns
|
|
2040
|
+
* Build a Prisma model block string based on a SchemaTableCreateOperation, including
|
|
2041
|
+
* all fields and any necessary mapping.
|
|
2042
|
+
*
|
|
2043
|
+
* @param operation The schema table create operation to convert.
|
|
2044
|
+
* @returns The corresponding Prisma model block string.
|
|
1483
2045
|
*/
|
|
1484
|
-
const
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
const
|
|
1488
|
-
const
|
|
1489
|
-
const
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
}
|
|
2046
|
+
const buildModelBlock = (operation) => {
|
|
2047
|
+
const modelName = toModelName(operation.table);
|
|
2048
|
+
const mapped = operation.table !== modelName.toLowerCase();
|
|
2049
|
+
const fields = operation.columns.map(buildFieldLine);
|
|
2050
|
+
const relations = (operation.foreignKeys ?? []).map((foreignKey) => buildRelationLine(modelName, foreignKey, operation.columns));
|
|
2051
|
+
const metadata = [...(operation.indexes ?? []).map(buildIndexLine), ...mapped ? [` @@map("${str(operation.table).snake()}")`] : []];
|
|
2052
|
+
return `model ${modelName} {\n${(metadata.length > 0 ? [
|
|
2053
|
+
...fields,
|
|
2054
|
+
...relations,
|
|
2055
|
+
"",
|
|
2056
|
+
...metadata
|
|
2057
|
+
] : [...fields, ...relations]).join("\n")}\n}`;
|
|
1496
2058
|
};
|
|
1497
2059
|
/**
|
|
1498
|
-
*
|
|
1499
|
-
*
|
|
1500
|
-
* applying the schema changes.
|
|
2060
|
+
* Find the Prisma model block in a schema string that corresponds to a given
|
|
2061
|
+
* table name, using both explicit mapping and naming conventions.
|
|
1501
2062
|
*
|
|
1502
|
-
* @param
|
|
1503
|
-
* @param
|
|
1504
|
-
* @returns
|
|
2063
|
+
* @param schema
|
|
2064
|
+
* @param table
|
|
2065
|
+
* @returns
|
|
1505
2066
|
*/
|
|
1506
|
-
const
|
|
1507
|
-
const
|
|
1508
|
-
const
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
static async load(filePath) {
|
|
1533
|
-
const resolvedPath = resolve(filePath);
|
|
1534
|
-
return await createJiti(pathToFileURL(resolvedPath).href, {
|
|
1535
|
-
interopDefault: false,
|
|
1536
|
-
tsconfigPaths: true
|
|
1537
|
-
}).import(resolvedPath);
|
|
1538
|
-
}
|
|
1539
|
-
};
|
|
1540
|
-
|
|
1541
|
-
//#endregion
|
|
1542
|
-
//#region src/Exceptions/UnsupportedAdapterFeatureException.ts
|
|
1543
|
-
var UnsupportedAdapterFeatureException = class extends ArkormException {
|
|
1544
|
-
constructor(message, context = {}) {
|
|
1545
|
-
super(message, {
|
|
1546
|
-
code: "UNSUPPORTED_ADAPTER_FEATURE",
|
|
1547
|
-
...context
|
|
1548
|
-
});
|
|
1549
|
-
this.name = "UnsupportedAdapterFeatureException";
|
|
1550
|
-
}
|
|
1551
|
-
};
|
|
1552
|
-
|
|
1553
|
-
//#endregion
|
|
1554
|
-
//#region src/helpers/runtime-config.ts
|
|
1555
|
-
const resolveDefaultStubsPath = () => {
|
|
1556
|
-
let current = path.dirname(fileURLToPath(import.meta.url));
|
|
1557
|
-
while (true) {
|
|
1558
|
-
const packageJsonPath = path.join(current, "package.json");
|
|
1559
|
-
const stubsPath = path.join(current, "stubs");
|
|
1560
|
-
if (existsSync$1(packageJsonPath) && existsSync$1(stubsPath)) return stubsPath;
|
|
1561
|
-
const parent = path.dirname(current);
|
|
1562
|
-
if (parent === current) break;
|
|
1563
|
-
current = parent;
|
|
2067
|
+
const findModelBlock = (schema, table) => {
|
|
2068
|
+
const candidates = [...schema.matchAll(PRISMA_MODEL_REGEX)];
|
|
2069
|
+
const explicitMapRegex = new RegExp(`@@map\\("${escapeRegex(table)}"\\)`);
|
|
2070
|
+
for (const match of candidates) {
|
|
2071
|
+
const block = match[0];
|
|
2072
|
+
const modelName = match[1];
|
|
2073
|
+
const start = match.index ?? 0;
|
|
2074
|
+
const end = start + block.length;
|
|
2075
|
+
if (explicitMapRegex.test(block)) return {
|
|
2076
|
+
modelName,
|
|
2077
|
+
block,
|
|
2078
|
+
start,
|
|
2079
|
+
end
|
|
2080
|
+
};
|
|
2081
|
+
if (modelName.toLowerCase() === table.toLowerCase()) return {
|
|
2082
|
+
modelName,
|
|
2083
|
+
block,
|
|
2084
|
+
start,
|
|
2085
|
+
end
|
|
2086
|
+
};
|
|
2087
|
+
if (modelName.toLowerCase() === toModelName(table).toLowerCase()) return {
|
|
2088
|
+
modelName,
|
|
2089
|
+
block,
|
|
2090
|
+
start,
|
|
2091
|
+
end
|
|
2092
|
+
};
|
|
1564
2093
|
}
|
|
1565
|
-
return
|
|
1566
|
-
};
|
|
1567
|
-
const baseConfig = {
|
|
1568
|
-
paths: {
|
|
1569
|
-
stubs: resolveDefaultStubsPath(),
|
|
1570
|
-
seeders: path.join(process.cwd(), "database", "seeders"),
|
|
1571
|
-
models: path.join(process.cwd(), "src", "models"),
|
|
1572
|
-
migrations: path.join(process.cwd(), "database", "migrations"),
|
|
1573
|
-
factories: path.join(process.cwd(), "database", "factories"),
|
|
1574
|
-
buildOutput: path.join(process.cwd(), "dist")
|
|
1575
|
-
},
|
|
1576
|
-
outputExt: "ts"
|
|
1577
|
-
};
|
|
1578
|
-
const userConfig = {
|
|
1579
|
-
...baseConfig,
|
|
1580
|
-
paths: { ...baseConfig.paths ?? {} }
|
|
1581
|
-
};
|
|
1582
|
-
let runtimeConfigLoaded = false;
|
|
1583
|
-
let runtimeConfigLoadingPromise;
|
|
1584
|
-
let runtimeClientResolver;
|
|
1585
|
-
let runtimePaginationURLDriverFactory;
|
|
1586
|
-
let runtimePaginationCurrentPageResolver;
|
|
1587
|
-
const transactionClientStorage = new AsyncLocalStorage();
|
|
1588
|
-
const mergePathConfig = (paths) => {
|
|
1589
|
-
const defaults = baseConfig.paths ?? {};
|
|
1590
|
-
const current = userConfig.paths ?? {};
|
|
1591
|
-
const incoming = Object.entries(paths ?? {}).reduce((all, [key, value]) => {
|
|
1592
|
-
if (typeof value === "string" && value.trim().length > 0) all[key] = path.isAbsolute(value) ? value : path.resolve(process.cwd(), value);
|
|
1593
|
-
return all;
|
|
1594
|
-
}, {});
|
|
1595
|
-
return {
|
|
1596
|
-
...defaults,
|
|
1597
|
-
...current,
|
|
1598
|
-
...incoming
|
|
1599
|
-
};
|
|
2094
|
+
return null;
|
|
1600
2095
|
};
|
|
1601
2096
|
/**
|
|
1602
|
-
*
|
|
2097
|
+
* Apply a create table operation to a Prisma schema string, adding a new model
|
|
2098
|
+
* block for the specified table and fields.
|
|
1603
2099
|
*
|
|
1604
|
-
* @param
|
|
1605
|
-
* @
|
|
2100
|
+
* @param schema The current Prisma schema string.
|
|
2101
|
+
* @param operation The schema table create operation to apply.
|
|
2102
|
+
* @returns The updated Prisma schema string with the new model block.
|
|
1606
2103
|
*/
|
|
1607
|
-
const
|
|
1608
|
-
|
|
2104
|
+
const applyCreateTableOperation = (schema, operation) => {
|
|
2105
|
+
if (findModelBlock(schema, operation.table)) throw new ArkormException(`Prisma model for table [${operation.table}] already exists.`);
|
|
2106
|
+
const schemaWithEnums = ensureEnumBlocks(schema, operation.columns);
|
|
2107
|
+
const block = buildModelBlock(operation);
|
|
2108
|
+
return applyInverseRelations(`${schemaWithEnums.trimEnd()}\n\n${block}\n`, toModelName(operation.table), operation.foreignKeys ?? [], operation.columns);
|
|
1609
2109
|
};
|
|
1610
2110
|
/**
|
|
1611
|
-
*
|
|
2111
|
+
* Apply an alter table operation to a Prisma schema string, modifying the model
|
|
2112
|
+
* block for the specified table by adding and removing fields as needed.
|
|
1612
2113
|
*
|
|
1613
|
-
* @
|
|
2114
|
+
* @param schema The current Prisma schema string.
|
|
2115
|
+
* @param operation The schema table alter operation to apply.
|
|
2116
|
+
* @returns The updated Prisma schema string with the modified model block.
|
|
1614
2117
|
*/
|
|
1615
|
-
const
|
|
1616
|
-
|
|
1617
|
-
|
|
2118
|
+
const applyAlterTableOperation = (schema, operation) => {
|
|
2119
|
+
const model = findModelBlock(schema, operation.table);
|
|
2120
|
+
if (!model) throw new ArkormException(`Prisma model for table [${operation.table}] was not found.`);
|
|
2121
|
+
const schemaWithEnums = ensureEnumBlocks(schema, operation.addColumns);
|
|
2122
|
+
const refreshedModel = findModelBlock(schemaWithEnums, operation.table);
|
|
2123
|
+
if (!refreshedModel) throw new ArkormException(`Prisma model for table [${operation.table}] was not found.`);
|
|
2124
|
+
let block = refreshedModel.block;
|
|
2125
|
+
const bodyLines = block.split("\n");
|
|
2126
|
+
operation.dropColumns.forEach((column) => {
|
|
2127
|
+
const columnRegex = new RegExp(`^\\s*${escapeRegex(column)}\\s+`);
|
|
2128
|
+
for (let index = 0; index < bodyLines.length; index += 1) if (columnRegex.test(bodyLines[index])) {
|
|
2129
|
+
bodyLines.splice(index, 1);
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
});
|
|
2133
|
+
operation.addColumns.forEach((column) => {
|
|
2134
|
+
const fieldLine = buildFieldLine(column);
|
|
2135
|
+
const columnRegex = new RegExp(`^\\s*${escapeRegex(column.name)}\\s+`);
|
|
2136
|
+
if (bodyLines.some((line) => columnRegex.test(line))) return;
|
|
2137
|
+
const defaultInsertIndex = Math.max(1, bodyLines.length - 1);
|
|
2138
|
+
const afterInsertIndex = typeof column.after === "string" && column.after.length > 0 ? bodyLines.findIndex((line) => new RegExp(`^\\s*${escapeRegex(column.after)}\\s+`).test(line)) : -1;
|
|
2139
|
+
const insertIndex = afterInsertIndex > 0 ? Math.min(afterInsertIndex + 1, defaultInsertIndex) : defaultInsertIndex;
|
|
2140
|
+
bodyLines.splice(insertIndex, 0, fieldLine);
|
|
2141
|
+
});
|
|
2142
|
+
(operation.addIndexes ?? []).forEach((index) => {
|
|
2143
|
+
const indexLine = buildIndexLine(index);
|
|
2144
|
+
if (bodyLines.some((line) => line.trim() === indexLine.trim())) return;
|
|
2145
|
+
const insertIndex = Math.max(1, bodyLines.length - 1);
|
|
2146
|
+
bodyLines.splice(insertIndex, 0, indexLine);
|
|
2147
|
+
});
|
|
2148
|
+
for (const foreignKey of operation.addForeignKeys ?? []) {
|
|
2149
|
+
const relationLine = buildRelationLine(model.modelName, foreignKey, operation.addColumns);
|
|
2150
|
+
const relationRegex = new RegExp(`^\\s*${escapeRegex(foreignKey.fieldAlias?.trim() || deriveRelationFieldName(foreignKey.column))}\\s+`);
|
|
2151
|
+
injectLineIntoModelBody(bodyLines, relationLine, (line) => relationRegex.test(line));
|
|
2152
|
+
}
|
|
2153
|
+
block = bodyLines.join("\n");
|
|
2154
|
+
return applyInverseRelations(`${schemaWithEnums.slice(0, refreshedModel.start)}${block}${schemaWithEnums.slice(refreshedModel.end)}`, model.modelName, operation.addForeignKeys ?? [], operation.addColumns);
|
|
1618
2155
|
};
|
|
1619
2156
|
/**
|
|
1620
|
-
*
|
|
1621
|
-
*
|
|
2157
|
+
* Apply a drop table operation to a Prisma schema string, removing the model block
|
|
2158
|
+
* for the specified table.
|
|
2159
|
+
*/
|
|
2160
|
+
const applyDropTableOperation = (schema, operation) => {
|
|
2161
|
+
const model = findModelBlock(schema, operation.table);
|
|
2162
|
+
if (!model) return schema;
|
|
2163
|
+
const before = schema.slice(0, model.start).trimEnd();
|
|
2164
|
+
const after = schema.slice(model.end).trimStart();
|
|
2165
|
+
return `${before}${before && after ? "\n\n" : ""}${after}`;
|
|
2166
|
+
};
|
|
2167
|
+
/**
|
|
2168
|
+
* The SchemaBuilder class provides a fluent interface for defining
|
|
2169
|
+
* database schema operations in a migration, such as creating, altering, and
|
|
2170
|
+
* dropping tables.
|
|
1622
2171
|
*
|
|
1623
|
-
* @param
|
|
1624
|
-
* @param
|
|
2172
|
+
* @param schema The current Prisma schema string.
|
|
2173
|
+
* @param operations The list of schema operations to apply.
|
|
2174
|
+
* @returns The updated Prisma schema string after applying all operations.
|
|
1625
2175
|
*/
|
|
1626
|
-
const
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
};
|
|
1632
|
-
if (options.pagination !== void 0) nextConfig.pagination = options.pagination;
|
|
1633
|
-
if (options.outputExt !== void 0) nextConfig.outputExt = options.outputExt;
|
|
1634
|
-
Object.assign(userConfig, { ...nextConfig });
|
|
1635
|
-
runtimeClientResolver = prisma;
|
|
1636
|
-
runtimePaginationURLDriverFactory = nextConfig.pagination?.urlDriver;
|
|
1637
|
-
runtimePaginationCurrentPageResolver = nextConfig.pagination?.resolveCurrentPage;
|
|
2176
|
+
const applyOperationsToPrismaSchema = (schema, operations) => {
|
|
2177
|
+
return operations.reduce((current, operation) => {
|
|
2178
|
+
if (operation.type === "createTable") return applyCreateTableOperation(current, operation);
|
|
2179
|
+
if (operation.type === "alterTable") return applyAlterTableOperation(current, operation);
|
|
2180
|
+
return applyDropTableOperation(current, operation);
|
|
2181
|
+
}, schema);
|
|
1638
2182
|
};
|
|
1639
2183
|
/**
|
|
1640
|
-
*
|
|
1641
|
-
*
|
|
2184
|
+
* Run a Prisma CLI command using npx, capturing and throwing any errors that occur.
|
|
2185
|
+
*
|
|
2186
|
+
* @param args The arguments to pass to the Prisma CLI command.
|
|
2187
|
+
* @param cwd The current working directory to run the command in.
|
|
2188
|
+
* @returns void
|
|
1642
2189
|
*/
|
|
1643
|
-
const
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
2190
|
+
const runPrismaCommand = (args, cwd) => {
|
|
2191
|
+
const command = spawnSync("npx", ["prisma", ...args], {
|
|
2192
|
+
cwd,
|
|
2193
|
+
encoding: "utf-8"
|
|
1647
2194
|
});
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
runtimePaginationURLDriverFactory = void 0;
|
|
1652
|
-
runtimePaginationCurrentPageResolver = void 0;
|
|
2195
|
+
if (command.status === 0) return;
|
|
2196
|
+
const errorOutput = [command.stdout, command.stderr].filter(Boolean).join("\n").trim();
|
|
2197
|
+
throw new ArkormException(errorOutput ? `Prisma command failed: prisma ${args.join(" ")}\n${errorOutput}` : `Prisma command failed: prisma ${args.join(" ")}`);
|
|
1653
2198
|
};
|
|
1654
2199
|
/**
|
|
1655
|
-
*
|
|
1656
|
-
*
|
|
2200
|
+
* Generate a new migration file with a given name and options, including
|
|
2201
|
+
* writing the file to disk if specified.
|
|
1657
2202
|
*
|
|
1658
|
-
* @param
|
|
2203
|
+
* @param name
|
|
1659
2204
|
* @returns
|
|
1660
2205
|
*/
|
|
1661
|
-
const
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
return client;
|
|
2206
|
+
const resolveMigrationClassName = (name) => {
|
|
2207
|
+
const cleaned = name.replace(/[^a-zA-Z0-9]+/g, " ").trim();
|
|
2208
|
+
if (!cleaned) return "GeneratedMigration";
|
|
2209
|
+
return `${cleaned.split(/\s+/g).map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join("")}Migration`;
|
|
1666
2210
|
};
|
|
1667
2211
|
/**
|
|
1668
|
-
*
|
|
1669
|
-
*
|
|
1670
|
-
* the configuration object and applies it to the runtime if valid.
|
|
2212
|
+
* Pad a number with leading zeros to ensure it is at least two digits, for
|
|
2213
|
+
* use in migration timestamps.
|
|
1671
2214
|
*
|
|
1672
|
-
* @param
|
|
2215
|
+
* @param value
|
|
1673
2216
|
* @returns
|
|
1674
2217
|
*/
|
|
1675
|
-
const
|
|
1676
|
-
const config = imported?.default ?? imported;
|
|
1677
|
-
if (!config || typeof config !== "object" || !config.prisma) return;
|
|
1678
|
-
configureArkormRuntime(config.prisma, {
|
|
1679
|
-
pagination: config.pagination,
|
|
1680
|
-
paths: config.paths,
|
|
1681
|
-
outputExt: config.outputExt
|
|
1682
|
-
});
|
|
1683
|
-
runtimeConfigLoaded = true;
|
|
1684
|
-
};
|
|
2218
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
1685
2219
|
/**
|
|
1686
|
-
*
|
|
1687
|
-
*
|
|
2220
|
+
* Create a timestamp string in the format YYYYMMDDHHMMSS for use in migration
|
|
2221
|
+
* file names, based on the current date and time or a provided date.
|
|
1688
2222
|
*
|
|
1689
|
-
* @param
|
|
1690
|
-
* @returns
|
|
2223
|
+
* @param date
|
|
2224
|
+
* @returns
|
|
1691
2225
|
*/
|
|
1692
|
-
const
|
|
1693
|
-
return
|
|
1694
|
-
};
|
|
1695
|
-
const loadRuntimeConfigSync = () => {
|
|
1696
|
-
const require = createRequire(import.meta.url);
|
|
1697
|
-
const syncConfigPaths = [path.join(process.cwd(), "arkormx.config.cjs")];
|
|
1698
|
-
for (const configPath of syncConfigPaths) {
|
|
1699
|
-
if (!existsSync$1(configPath)) continue;
|
|
1700
|
-
try {
|
|
1701
|
-
resolveAndApplyConfig(require(configPath));
|
|
1702
|
-
return true;
|
|
1703
|
-
} catch {
|
|
1704
|
-
continue;
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
return false;
|
|
2226
|
+
const createMigrationTimestamp = (date = /* @__PURE__ */ new Date()) => {
|
|
2227
|
+
return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
|
|
1708
2228
|
};
|
|
1709
2229
|
/**
|
|
1710
|
-
*
|
|
1711
|
-
*
|
|
2230
|
+
* Convert a migration name to a slug suitable for use in a file name, by
|
|
2231
|
+
* lowercasing and replacing non-alphanumeric characters with underscores.
|
|
2232
|
+
*
|
|
2233
|
+
* @param name
|
|
1712
2234
|
* @returns
|
|
1713
|
-
*/
|
|
1714
|
-
const
|
|
1715
|
-
|
|
1716
|
-
if (runtimeConfigLoadingPromise) return await runtimeConfigLoadingPromise;
|
|
1717
|
-
if (loadRuntimeConfigSync()) return;
|
|
1718
|
-
runtimeConfigLoadingPromise = (async () => {
|
|
1719
|
-
const configPaths = [path.join(process.cwd(), "arkormx.config.js"), path.join(process.cwd(), "arkormx.config.ts")];
|
|
1720
|
-
for (const configPath of configPaths) {
|
|
1721
|
-
if (!existsSync$1(configPath)) continue;
|
|
1722
|
-
try {
|
|
1723
|
-
resolveAndApplyConfig(await importConfigFile(configPath));
|
|
1724
|
-
return;
|
|
1725
|
-
} catch {
|
|
1726
|
-
continue;
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
runtimeConfigLoaded = true;
|
|
1730
|
-
})();
|
|
1731
|
-
await runtimeConfigLoadingPromise;
|
|
2235
|
+
*/
|
|
2236
|
+
const toMigrationFileSlug = (name) => {
|
|
2237
|
+
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration";
|
|
1732
2238
|
};
|
|
1733
2239
|
/**
|
|
1734
|
-
*
|
|
1735
|
-
*
|
|
1736
|
-
* If the configuration is already loaded, it will return immediately.
|
|
2240
|
+
* Build the source code for a new migration file based on a given class
|
|
2241
|
+
* name, using a template with empty up and down methods.
|
|
1737
2242
|
*
|
|
2243
|
+
* @param className
|
|
1738
2244
|
* @returns
|
|
1739
2245
|
*/
|
|
1740
|
-
const
|
|
1741
|
-
if (
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
2246
|
+
const buildMigrationSource = (className, extension = "ts") => {
|
|
2247
|
+
if (extension === "js") return [
|
|
2248
|
+
"import { Migration } from 'arkormx'",
|
|
2249
|
+
"",
|
|
2250
|
+
`export default class ${className} extends Migration {`,
|
|
2251
|
+
" /**",
|
|
2252
|
+
" * @param {import('arkormx').SchemaBuilder} schema",
|
|
2253
|
+
" * @returns {Promise<void>}",
|
|
2254
|
+
" */",
|
|
2255
|
+
" async up (schema) {",
|
|
2256
|
+
" }",
|
|
2257
|
+
"",
|
|
2258
|
+
" /**",
|
|
2259
|
+
" * @param {import('arkormx').SchemaBuilder} schema",
|
|
2260
|
+
" * @returns {Promise<void>}",
|
|
2261
|
+
" */",
|
|
2262
|
+
" async down (schema) {",
|
|
2263
|
+
" }",
|
|
2264
|
+
"}",
|
|
2265
|
+
""
|
|
2266
|
+
].join("\n");
|
|
2267
|
+
return [
|
|
2268
|
+
"import { Migration, SchemaBuilder } from 'arkormx'",
|
|
2269
|
+
"",
|
|
2270
|
+
`export default class ${className} extends Migration {`,
|
|
2271
|
+
" public async up (schema: SchemaBuilder): Promise<void> {",
|
|
2272
|
+
" }",
|
|
2273
|
+
"",
|
|
2274
|
+
" public async down (schema: SchemaBuilder): Promise<void> {",
|
|
2275
|
+
" }",
|
|
2276
|
+
"}",
|
|
2277
|
+
""
|
|
2278
|
+
].join("\n");
|
|
1746
2279
|
};
|
|
1747
2280
|
/**
|
|
1748
|
-
*
|
|
1749
|
-
*
|
|
1750
|
-
* it hasn't already been loaded.
|
|
2281
|
+
* Generate a new migration file with a given name and options, including
|
|
2282
|
+
* writing the file to disk if specified, and return the details of the generated file.
|
|
1751
2283
|
*
|
|
2284
|
+
* @param name
|
|
2285
|
+
* @param options
|
|
1752
2286
|
* @returns
|
|
1753
2287
|
*/
|
|
1754
|
-
const
|
|
1755
|
-
const
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
const
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
2288
|
+
const generateMigrationFile = (name, options = {}) => {
|
|
2289
|
+
const timestamp = createMigrationTimestamp(/* @__PURE__ */ new Date());
|
|
2290
|
+
const fileSlug = toMigrationFileSlug(name);
|
|
2291
|
+
const className = resolveMigrationClassName(name);
|
|
2292
|
+
const extension = options.extension ?? "ts";
|
|
2293
|
+
const directory = options.directory ?? join(process.cwd(), "database", "migrations");
|
|
2294
|
+
const fileName = `${timestamp}_${fileSlug}.${extension}`;
|
|
2295
|
+
const filePath = join(directory, fileName);
|
|
2296
|
+
const content = buildMigrationSource(className, extension);
|
|
2297
|
+
if (options.write ?? true) {
|
|
2298
|
+
if (!existsSync$1(directory)) mkdirSync$1(directory, { recursive: true });
|
|
2299
|
+
if (existsSync$1(filePath)) throw new ArkormException(`Migration file already exists: ${filePath}`);
|
|
2300
|
+
writeFileSync$1(filePath, content);
|
|
2301
|
+
}
|
|
2302
|
+
return {
|
|
2303
|
+
fileName,
|
|
2304
|
+
filePath,
|
|
2305
|
+
className,
|
|
2306
|
+
content
|
|
2307
|
+
};
|
|
1766
2308
|
};
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
return
|
|
1780
|
-
return await transactionClientStorage.run(transactionClient, async () => {
|
|
1781
|
-
return await callback(transactionClient);
|
|
1782
|
-
});
|
|
1783
|
-
}, options);
|
|
2309
|
+
/**
|
|
2310
|
+
* Get the list of schema operations that would be performed by a given migration class when run in a specified direction (up or down), without actually applying them.
|
|
2311
|
+
*
|
|
2312
|
+
* @param migration The migration class or instance to analyze.
|
|
2313
|
+
* @param direction The direction of the migration to plan for ('up' or 'down').
|
|
2314
|
+
* @returns A promise that resolves to an array of schema operations that would be performed.
|
|
2315
|
+
*/
|
|
2316
|
+
const getMigrationPlan = async (migration, direction = "up") => {
|
|
2317
|
+
const instance = typeof migration === "function" ? new migration() : migration;
|
|
2318
|
+
const schema = new SchemaBuilder();
|
|
2319
|
+
if (direction === "up") await instance.up(schema);
|
|
2320
|
+
else await instance.down(schema);
|
|
2321
|
+
return schema.getOperations();
|
|
1784
2322
|
};
|
|
1785
2323
|
/**
|
|
1786
|
-
*
|
|
1787
|
-
*
|
|
1788
|
-
*
|
|
2324
|
+
* Apply the schema operations defined in a migration to a Prisma schema
|
|
2325
|
+
* file, updating the file on disk if specified, and return the updated
|
|
2326
|
+
* schema and list of operations applied.
|
|
2327
|
+
*
|
|
2328
|
+
* @param migration The migration class or instance to apply.
|
|
2329
|
+
* @param options Options for applying the migration, including schema path and write flag.
|
|
2330
|
+
* @returns A promise that resolves to an object containing the updated schema, schema path, and list of operations applied.
|
|
1789
2331
|
*/
|
|
1790
|
-
const
|
|
1791
|
-
|
|
1792
|
-
|
|
2332
|
+
const applyMigrationToPrismaSchema = async (migration, options = {}) => {
|
|
2333
|
+
const schemaPath = options.schemaPath ?? join(process.cwd(), "prisma", "schema.prisma");
|
|
2334
|
+
if (!existsSync$1(schemaPath)) throw new ArkormException(`Prisma schema file not found: ${schemaPath}`);
|
|
2335
|
+
const source = readFileSync$1(schemaPath, "utf-8");
|
|
2336
|
+
const operations = await getMigrationPlan(migration, "up");
|
|
2337
|
+
const schema = applyOperationsToPrismaSchema(source, operations);
|
|
2338
|
+
if (options.write ?? true) writeFileSync$1(schemaPath, schema);
|
|
2339
|
+
return {
|
|
2340
|
+
schema,
|
|
2341
|
+
schemaPath,
|
|
2342
|
+
operations
|
|
2343
|
+
};
|
|
1793
2344
|
};
|
|
1794
2345
|
/**
|
|
1795
|
-
*
|
|
2346
|
+
* Apply the rollback (down) operations defined in a migration to a Prisma schema file.
|
|
1796
2347
|
*
|
|
1797
|
-
* @
|
|
2348
|
+
* @param migration The migration class or instance to rollback.
|
|
2349
|
+
* @param options Options for applying the rollback, including schema path and write flag.
|
|
2350
|
+
* @returns A promise that resolves to an object containing the updated schema, schema path, and rollback operations applied.
|
|
1798
2351
|
*/
|
|
1799
|
-
const
|
|
1800
|
-
|
|
1801
|
-
|
|
2352
|
+
const applyMigrationRollbackToPrismaSchema = async (migration, options = {}) => {
|
|
2353
|
+
const schemaPath = options.schemaPath ?? join(process.cwd(), "prisma", "schema.prisma");
|
|
2354
|
+
if (!existsSync$1(schemaPath)) throw new ArkormException(`Prisma schema file not found: ${schemaPath}`);
|
|
2355
|
+
const source = readFileSync$1(schemaPath, "utf-8");
|
|
2356
|
+
const operations = await getMigrationPlan(migration, "down");
|
|
2357
|
+
const schema = applyOperationsToPrismaSchema(source, operations);
|
|
2358
|
+
if (options.write ?? true) writeFileSync$1(schemaPath, schema);
|
|
2359
|
+
return {
|
|
2360
|
+
schema,
|
|
2361
|
+
schemaPath,
|
|
2362
|
+
operations
|
|
2363
|
+
};
|
|
1802
2364
|
};
|
|
1803
2365
|
/**
|
|
1804
|
-
*
|
|
1805
|
-
*
|
|
2366
|
+
* Run a migration by applying its schema operations to a Prisma schema
|
|
2367
|
+
* file, optionally generating Prisma client code and running migrations after
|
|
2368
|
+
* applying the schema changes.
|
|
1806
2369
|
*
|
|
1807
|
-
* @param
|
|
1808
|
-
* @
|
|
2370
|
+
* @param migration The migration class or instance to run.
|
|
2371
|
+
* @param options Options for running the migration, including schema path, write flag, and Prisma commands.
|
|
2372
|
+
* @returns A promise that resolves to an object containing the schema path and list of operations applied.
|
|
1809
2373
|
*/
|
|
1810
|
-
const
|
|
1811
|
-
|
|
1812
|
-
const
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
2374
|
+
const runMigrationWithPrisma = async (migration, options = {}) => {
|
|
2375
|
+
const cwd = options.cwd ?? process.cwd();
|
|
2376
|
+
const applied = await applyMigrationToPrismaSchema(migration, {
|
|
2377
|
+
schemaPath: options.schemaPath ?? join(cwd, "prisma", "schema.prisma"),
|
|
2378
|
+
write: options.write
|
|
2379
|
+
});
|
|
2380
|
+
const shouldGenerate = options.runGenerate ?? true;
|
|
2381
|
+
const shouldMigrate = options.runMigrate ?? true;
|
|
2382
|
+
const mode = options.migrateMode ?? "dev";
|
|
2383
|
+
if (shouldGenerate) runPrismaCommand(["generate"], cwd);
|
|
2384
|
+
if (shouldMigrate) if (mode === "deploy") runPrismaCommand(["migrate", "deploy"], cwd);
|
|
2385
|
+
else runPrismaCommand([
|
|
2386
|
+
"migrate",
|
|
2387
|
+
"dev",
|
|
2388
|
+
"--name",
|
|
2389
|
+
options.migrationName ?? `arkorm_${createMigrationTimestamp()}`
|
|
2390
|
+
], cwd);
|
|
2391
|
+
return {
|
|
2392
|
+
schemaPath: applied.schemaPath,
|
|
2393
|
+
operations: applied.operations
|
|
2394
|
+
};
|
|
1821
2395
|
};
|
|
1822
|
-
loadArkormConfig();
|
|
1823
2396
|
|
|
1824
2397
|
//#endregion
|
|
1825
2398
|
//#region src/cli/CliApp.ts
|
|
@@ -1849,7 +2422,7 @@ var CliApp = class {
|
|
|
1849
2422
|
*/
|
|
1850
2423
|
ensureDirectory(filePath) {
|
|
1851
2424
|
const dir = dirname$1(filePath);
|
|
1852
|
-
if (!existsSync
|
|
2425
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
1853
2426
|
}
|
|
1854
2427
|
/**
|
|
1855
2428
|
* Convert absolute paths under current working directory into relative display paths.
|
|
@@ -1898,13 +2471,13 @@ var CliApp = class {
|
|
|
1898
2471
|
* @returns
|
|
1899
2472
|
*/
|
|
1900
2473
|
resolveRuntimeDirectoryPath(directoryPath) {
|
|
1901
|
-
if (existsSync
|
|
2474
|
+
if (existsSync(directoryPath)) return directoryPath;
|
|
1902
2475
|
const { buildOutput } = this.getConfig("paths") || {};
|
|
1903
2476
|
if (typeof buildOutput !== "string" || buildOutput.trim().length === 0) return directoryPath;
|
|
1904
2477
|
const relativeSource = relative(process.cwd(), directoryPath);
|
|
1905
2478
|
if (!relativeSource || relativeSource.startsWith("..")) return directoryPath;
|
|
1906
2479
|
const mappedDirectory = join$1(buildOutput, relativeSource);
|
|
1907
|
-
return existsSync
|
|
2480
|
+
return existsSync(mappedDirectory) ? mappedDirectory : directoryPath;
|
|
1908
2481
|
}
|
|
1909
2482
|
/**
|
|
1910
2483
|
* Resolve a script file path for runtime execution.
|
|
@@ -1934,7 +2507,7 @@ var CliApp = class {
|
|
|
1934
2507
|
} else candidates.push(mappedFile);
|
|
1935
2508
|
}
|
|
1936
2509
|
}
|
|
1937
|
-
const runtimeMatch = candidates.find((path) => existsSync
|
|
2510
|
+
const runtimeMatch = candidates.find((path) => existsSync(path));
|
|
1938
2511
|
if (runtimeMatch) return runtimeMatch;
|
|
1939
2512
|
return filePath;
|
|
1940
2513
|
}
|
|
@@ -1946,14 +2519,14 @@ var CliApp = class {
|
|
|
1946
2519
|
* @param replacements
|
|
1947
2520
|
*/
|
|
1948
2521
|
generateFile(stubPath, outputPath, replacements, options) {
|
|
1949
|
-
if (existsSync
|
|
2522
|
+
if (existsSync(outputPath) && !options?.force) {
|
|
1950
2523
|
this.command.error(`Error: ${this.formatPathForLog(outputPath)} already exists.`);
|
|
1951
2524
|
process.exit(1);
|
|
1952
|
-
} else if (existsSync
|
|
1953
|
-
let content = readFileSync
|
|
2525
|
+
} else if (existsSync(outputPath) && options?.force) rmSync(outputPath);
|
|
2526
|
+
let content = readFileSync(stubPath, "utf-8");
|
|
1954
2527
|
for (const [key, value] of Object.entries(replacements)) content = content.replace(new RegExp(`{{${key}}}`, "g"), value);
|
|
1955
2528
|
this.ensureDirectory(outputPath);
|
|
1956
|
-
writeFileSync
|
|
2529
|
+
writeFileSync(outputPath, content);
|
|
1957
2530
|
return outputPath;
|
|
1958
2531
|
}
|
|
1959
2532
|
/**
|
|
@@ -2091,18 +2664,18 @@ var CliApp = class {
|
|
|
2091
2664
|
*/
|
|
2092
2665
|
ensurePrismaModelEntry(modelName, delegateName) {
|
|
2093
2666
|
const schemaPath = join$1(process.cwd(), "prisma", "schema.prisma");
|
|
2094
|
-
if (!existsSync
|
|
2667
|
+
if (!existsSync(schemaPath)) return {
|
|
2095
2668
|
path: schemaPath,
|
|
2096
2669
|
updated: false
|
|
2097
2670
|
};
|
|
2098
|
-
const source = readFileSync
|
|
2671
|
+
const source = readFileSync(schemaPath, "utf-8");
|
|
2099
2672
|
const existingByTable = findModelBlock(source, delegateName);
|
|
2100
2673
|
const existingByName = new RegExp(`model\\s+${modelName}\\s*\\{`, "m").test(source);
|
|
2101
2674
|
if (existingByTable || existingByName) return {
|
|
2102
2675
|
path: schemaPath,
|
|
2103
2676
|
updated: false
|
|
2104
2677
|
};
|
|
2105
|
-
writeFileSync
|
|
2678
|
+
writeFileSync(schemaPath, applyCreateTableOperation(source, {
|
|
2106
2679
|
type: "createTable",
|
|
2107
2680
|
table: delegateName,
|
|
2108
2681
|
columns: [{
|
|
@@ -2479,17 +3052,17 @@ var CliApp = class {
|
|
|
2479
3052
|
syncModelsFromPrisma(options = {}) {
|
|
2480
3053
|
const schemaPath = options.schemaPath ?? join$1(process.cwd(), "prisma", "schema.prisma");
|
|
2481
3054
|
const modelsDir = options.modelsDir ?? this.resolveConfigPath("models", join$1(process.cwd(), "src", "models"));
|
|
2482
|
-
if (!existsSync
|
|
2483
|
-
if (!existsSync
|
|
2484
|
-
const schema = readFileSync
|
|
3055
|
+
if (!existsSync(schemaPath)) throw new Error(`Prisma schema file not found: ${schemaPath}`);
|
|
3056
|
+
if (!existsSync(modelsDir)) throw new Error(`Models directory not found: ${modelsDir}`);
|
|
3057
|
+
const schema = readFileSync(schemaPath, "utf-8");
|
|
2485
3058
|
const prismaEnums = this.parsePrismaEnums(schema);
|
|
2486
3059
|
const prismaModels = this.parsePrismaModels(schema);
|
|
2487
|
-
const modelFiles = readdirSync
|
|
3060
|
+
const modelFiles = readdirSync(modelsDir).filter((file) => file.endsWith(".ts"));
|
|
2488
3061
|
const updated = [];
|
|
2489
3062
|
const skipped = [];
|
|
2490
3063
|
modelFiles.forEach((file) => {
|
|
2491
3064
|
const filePath = join$1(modelsDir, file);
|
|
2492
|
-
const source = readFileSync
|
|
3065
|
+
const source = readFileSync(filePath, "utf-8");
|
|
2493
3066
|
const classMatch = source.match(/export\s+class\s+(\w+)\s+extends\s+Model<'([^']+)'>/);
|
|
2494
3067
|
if (!classMatch) {
|
|
2495
3068
|
skipped.push(filePath);
|
|
@@ -2507,7 +3080,7 @@ var CliApp = class {
|
|
|
2507
3080
|
skipped.push(filePath);
|
|
2508
3081
|
return;
|
|
2509
3082
|
}
|
|
2510
|
-
writeFileSync
|
|
3083
|
+
writeFileSync(filePath, synced.content);
|
|
2511
3084
|
updated.push(filePath);
|
|
2512
3085
|
});
|
|
2513
3086
|
return {
|
|
@@ -2544,18 +3117,18 @@ var InitCommand = class extends Command {
|
|
|
2544
3117
|
const stubsDir = typeof stubs === "string" && stubs.trim().length > 0 ? stubs : getDefaultStubsPath();
|
|
2545
3118
|
const preferredStubPath = join(stubsDir, "arkormx.config.stub");
|
|
2546
3119
|
const legacyStubPath = join(stubsDir, "arkorm.config.stub");
|
|
2547
|
-
const stubPath = existsSync
|
|
2548
|
-
if (existsSync
|
|
3120
|
+
const stubPath = existsSync(preferredStubPath) ? preferredStubPath : legacyStubPath;
|
|
3121
|
+
if (existsSync(outputDir) && !this.option("force")) {
|
|
2549
3122
|
this.error("Error: Arkormˣ has already been initialized. Use --force to reinitialize.");
|
|
2550
3123
|
process.exit(1);
|
|
2551
3124
|
}
|
|
2552
3125
|
this.app.ensureDirectory(outputDir);
|
|
2553
|
-
if (existsSync
|
|
2554
|
-
if (!existsSync
|
|
3126
|
+
if (existsSync(outputDir) && this.option("force")) copyFileSync(outputDir, outputDir.replace(/\.js$/, `.backup.${Date.now()}.js`));
|
|
3127
|
+
if (!existsSync(stubPath)) {
|
|
2555
3128
|
this.error(`Error: Missing config stub at ${preferredStubPath} (or ${legacyStubPath})`);
|
|
2556
3129
|
process.exit(1);
|
|
2557
3130
|
}
|
|
2558
|
-
writeFileSync
|
|
3131
|
+
writeFileSync(outputDir, readFileSync(stubPath, "utf-8"));
|
|
2559
3132
|
this.success("Arkormˣ initialized successfully!");
|
|
2560
3133
|
}
|
|
2561
3134
|
};
|
|
@@ -2697,13 +3270,13 @@ const buildMigrationIdentity = (filePath, className) => {
|
|
|
2697
3270
|
return `${fileName.slice(0, fileName.length - extname(fileName).length)}:${className}`;
|
|
2698
3271
|
};
|
|
2699
3272
|
const computeMigrationChecksum = (filePath) => {
|
|
2700
|
-
const source = readFileSync(filePath, "utf-8");
|
|
3273
|
+
const source = readFileSync$1(filePath, "utf-8");
|
|
2701
3274
|
return createHash("sha256").update(source).digest("hex");
|
|
2702
3275
|
};
|
|
2703
3276
|
const readAppliedMigrationsState = (stateFilePath) => {
|
|
2704
|
-
if (!existsSync(stateFilePath)) return { ...DEFAULT_STATE };
|
|
3277
|
+
if (!existsSync$1(stateFilePath)) return { ...DEFAULT_STATE };
|
|
2705
3278
|
try {
|
|
2706
|
-
const parsed = JSON.parse(readFileSync(stateFilePath, "utf-8"));
|
|
3279
|
+
const parsed = JSON.parse(readFileSync$1(stateFilePath, "utf-8"));
|
|
2707
3280
|
if (!Array.isArray(parsed.migrations)) return { ...DEFAULT_STATE };
|
|
2708
3281
|
return {
|
|
2709
3282
|
version: 1,
|
|
@@ -2720,8 +3293,8 @@ const readAppliedMigrationsState = (stateFilePath) => {
|
|
|
2720
3293
|
};
|
|
2721
3294
|
const writeAppliedMigrationsState = (stateFilePath, state) => {
|
|
2722
3295
|
const directory = dirname(stateFilePath);
|
|
2723
|
-
if (!existsSync(directory)) mkdirSync(directory, { recursive: true });
|
|
2724
|
-
writeFileSync(stateFilePath, JSON.stringify(state, null, 2));
|
|
3296
|
+
if (!existsSync$1(directory)) mkdirSync$1(directory, { recursive: true });
|
|
3297
|
+
writeFileSync$1(stateFilePath, JSON.stringify(state, null, 2));
|
|
2725
3298
|
};
|
|
2726
3299
|
const isMigrationApplied = (state, identity, checksum) => {
|
|
2727
3300
|
const matched = state.migrations.find((migration) => migration.id === identity);
|
|
@@ -2767,10 +3340,24 @@ const markMigrationRun = (state, run) => {
|
|
|
2767
3340
|
const getLastMigrationRun = (state) => {
|
|
2768
3341
|
const runs = state.runs ?? [];
|
|
2769
3342
|
if (runs.length === 0) return void 0;
|
|
2770
|
-
return
|
|
3343
|
+
return runs.map((run, index) => ({
|
|
3344
|
+
run,
|
|
3345
|
+
index
|
|
3346
|
+
})).sort((left, right) => {
|
|
3347
|
+
const appliedAtOrder = right.run.appliedAt.localeCompare(left.run.appliedAt);
|
|
3348
|
+
if (appliedAtOrder !== 0) return appliedAtOrder;
|
|
3349
|
+
return right.index - left.index;
|
|
3350
|
+
})[0]?.run;
|
|
2771
3351
|
};
|
|
2772
3352
|
const getLatestAppliedMigrations = (state, steps) => {
|
|
2773
|
-
return
|
|
3353
|
+
return state.migrations.map((migration, index) => ({
|
|
3354
|
+
migration,
|
|
3355
|
+
index
|
|
3356
|
+
})).sort((left, right) => {
|
|
3357
|
+
const appliedAtOrder = right.migration.appliedAt.localeCompare(left.migration.appliedAt);
|
|
3358
|
+
if (appliedAtOrder !== 0) return appliedAtOrder;
|
|
3359
|
+
return right.index - left.index;
|
|
3360
|
+
}).slice(0, Math.max(0, steps)).map((entry) => entry.migration);
|
|
2774
3361
|
};
|
|
2775
3362
|
|
|
2776
3363
|
//#endregion
|
|
@@ -2822,7 +3409,7 @@ var MigrateCommand = class extends Command {
|
|
|
2822
3409
|
this.app.command = this;
|
|
2823
3410
|
const configuredMigrationsDir = this.app.getConfig("paths")?.migrations ?? join(process.cwd(), "database", "migrations");
|
|
2824
3411
|
const migrationsDir = this.app.resolveRuntimeDirectoryPath(configuredMigrationsDir);
|
|
2825
|
-
if (!existsSync(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
|
|
3412
|
+
if (!existsSync$1(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
|
|
2826
3413
|
const schemaPath = this.option("schema") ? resolve(String(this.option("schema"))) : join(process.cwd(), "prisma", "schema.prisma");
|
|
2827
3414
|
const classes = this.option("all") || !this.argument("name") ? await this.loadAllMigrations(migrationsDir) : (await this.loadNamedMigration(migrationsDir, this.argument("name"))).filter(([cls]) => cls !== void 0);
|
|
2828
3415
|
if (classes.length === 0) return void this.error("Error: No migration classes found to run.");
|
|
@@ -2890,7 +3477,7 @@ var MigrateCommand = class extends Command {
|
|
|
2890
3477
|
* @param migrationsDir The directory to load migration classes from.
|
|
2891
3478
|
*/
|
|
2892
3479
|
async loadAllMigrations(migrationsDir) {
|
|
2893
|
-
const files = readdirSync(migrationsDir).filter((file) => /\.(ts|js|mjs|cjs)$/i.test(file)).sort((left, right) => left.localeCompare(right)).map((file) => this.app.resolveRuntimeScriptPath(join(migrationsDir, file)));
|
|
3480
|
+
const files = readdirSync$1(migrationsDir).filter((file) => /\.(ts|js|mjs|cjs)$/i.test(file)).sort((left, right) => left.localeCompare(right)).map((file) => this.app.resolveRuntimeScriptPath(join(migrationsDir, file)));
|
|
2894
3481
|
return (await Promise.all(files.map(async (file) => (await this.loadMigrationClassesFromFile(file)).map((cls) => [cls, file])))).flat();
|
|
2895
3482
|
}
|
|
2896
3483
|
/**
|
|
@@ -2912,7 +3499,7 @@ var MigrateCommand = class extends Command {
|
|
|
2912
3499
|
`${base}Migration.js`,
|
|
2913
3500
|
`${base}Migration.mjs`,
|
|
2914
3501
|
`${base}Migration.cjs`
|
|
2915
|
-
].map((file) => join(migrationsDir, file)).find((file) => existsSync(file));
|
|
3502
|
+
].map((file) => join(migrationsDir, file)).find((file) => existsSync$1(file));
|
|
2916
3503
|
if (!target) return [[void 0, name]];
|
|
2917
3504
|
const runtimeTarget = this.app.resolveRuntimeScriptPath(target);
|
|
2918
3505
|
return (await this.loadMigrationClassesFromFile(runtimeTarget)).map((cls) => [cls, runtimeTarget]);
|
|
@@ -2959,7 +3546,7 @@ var MigrateRollbackCommand = class extends Command {
|
|
|
2959
3546
|
this.app.command = this;
|
|
2960
3547
|
const configuredMigrationsDir = this.app.getConfig("paths")?.migrations ?? join(process.cwd(), "database", "migrations");
|
|
2961
3548
|
const migrationsDir = this.app.resolveRuntimeDirectoryPath(configuredMigrationsDir);
|
|
2962
|
-
if (!existsSync(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
|
|
3549
|
+
if (!existsSync$1(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
|
|
2963
3550
|
const schemaPath = this.option("schema") ? resolve(String(this.option("schema"))) : join(process.cwd(), "prisma", "schema.prisma");
|
|
2964
3551
|
const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
|
|
2965
3552
|
let appliedState = readAppliedMigrationsState(stateFilePath);
|
|
@@ -3005,7 +3592,7 @@ var MigrateRollbackCommand = class extends Command {
|
|
|
3005
3592
|
rollbackClasses.forEach(([_, file]) => this.success(this.app.splitLogger("RolledBack", file)));
|
|
3006
3593
|
}
|
|
3007
3594
|
async loadAllMigrations(migrationsDir) {
|
|
3008
|
-
const files = readdirSync(migrationsDir).filter((file) => /\.(ts|js|mjs|cjs)$/i.test(file)).sort((left, right) => left.localeCompare(right)).map((file) => this.app.resolveRuntimeScriptPath(join(migrationsDir, file)));
|
|
3595
|
+
const files = readdirSync$1(migrationsDir).filter((file) => /\.(ts|js|mjs|cjs)$/i.test(file)).sort((left, right) => left.localeCompare(right)).map((file) => this.app.resolveRuntimeScriptPath(join(migrationsDir, file)));
|
|
3009
3596
|
return (await Promise.all(files.map(async (file) => (await this.loadMigrationClassesFromFile(file)).map((cls) => [cls, file])))).flat();
|
|
3010
3597
|
}
|
|
3011
3598
|
async loadMigrationClassesFromFile(filePath) {
|
|
@@ -3039,11 +3626,11 @@ var MigrationHistoryCommand = class extends Command {
|
|
|
3039
3626
|
this.app.command = this;
|
|
3040
3627
|
const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
|
|
3041
3628
|
if (this.option("delete")) {
|
|
3042
|
-
if (!existsSync(stateFilePath)) {
|
|
3629
|
+
if (!existsSync$1(stateFilePath)) {
|
|
3043
3630
|
this.success(`No migration state file found at ${this.app.formatPathForLog(stateFilePath)}`);
|
|
3044
3631
|
return;
|
|
3045
3632
|
}
|
|
3046
|
-
rmSync(stateFilePath);
|
|
3633
|
+
rmSync$1(stateFilePath);
|
|
3047
3634
|
this.success(`Deleted migration state file: ${this.app.formatPathForLog(stateFilePath)}`);
|
|
3048
3635
|
return;
|
|
3049
3636
|
}
|
|
@@ -3172,7 +3759,7 @@ var SeedCommand = class extends Command {
|
|
|
3172
3759
|
this.app.command = this;
|
|
3173
3760
|
const configuredSeedersDir = this.app.getConfig("paths")?.seeders ?? join(process.cwd(), "database", "seeders");
|
|
3174
3761
|
const seedersDir = this.app.resolveRuntimeDirectoryPath(configuredSeedersDir);
|
|
3175
|
-
if (!existsSync(seedersDir)) return void this.error(`ERROR: Seeders directory not found: ${this.app.formatPathForLog(configuredSeedersDir)}`);
|
|
3762
|
+
if (!existsSync$1(seedersDir)) return void this.error(`ERROR: Seeders directory not found: ${this.app.formatPathForLog(configuredSeedersDir)}`);
|
|
3176
3763
|
const classes = this.option("all") ? await this.loadAllSeeders(seedersDir) : await this.loadNamedSeeder(seedersDir, this.argument("name") ?? "DatabaseSeeder");
|
|
3177
3764
|
if (classes.length === 0) return void this.error("ERROR: No seeder classes found to run.");
|
|
3178
3765
|
for (const SeederClassItem of classes) await new SeederClassItem().run();
|
|
@@ -3186,7 +3773,7 @@ var SeedCommand = class extends Command {
|
|
|
3186
3773
|
* @returns
|
|
3187
3774
|
*/
|
|
3188
3775
|
async loadAllSeeders(seedersDir) {
|
|
3189
|
-
const files = readdirSync(seedersDir).filter((file) => /\.(ts|js|mjs|cjs)$/i.test(file)).map((file) => this.app.resolveRuntimeScriptPath(join(seedersDir, file)));
|
|
3776
|
+
const files = readdirSync$1(seedersDir).filter((file) => /\.(ts|js|mjs|cjs)$/i.test(file)).map((file) => this.app.resolveRuntimeScriptPath(join(seedersDir, file)));
|
|
3190
3777
|
return (await Promise.all(files.map(async (file) => await this.loadSeederClassesFromFile(file)))).flat();
|
|
3191
3778
|
}
|
|
3192
3779
|
/**
|
|
@@ -3207,7 +3794,7 @@ var SeedCommand = class extends Command {
|
|
|
3207
3794
|
`${base}Seeder.js`,
|
|
3208
3795
|
`${base}Seeder.mjs`,
|
|
3209
3796
|
`${base}Seeder.cjs`
|
|
3210
|
-
].map((file) => join(seedersDir, file)).find((file) => existsSync(file));
|
|
3797
|
+
].map((file) => join(seedersDir, file)).find((file) => existsSync$1(file));
|
|
3211
3798
|
if (!target) return [];
|
|
3212
3799
|
const runtimeTarget = this.app.resolveRuntimeScriptPath(target);
|
|
3213
3800
|
return await this.loadSeederClassesFromFile(runtimeTarget);
|
|
@@ -3377,18 +3964,6 @@ const defineFactory = (model, definition) => {
|
|
|
3377
3964
|
return new InlineFactory(model, definition);
|
|
3378
3965
|
};
|
|
3379
3966
|
|
|
3380
|
-
//#endregion
|
|
3381
|
-
//#region src/Exceptions/MissingDelegateException.ts
|
|
3382
|
-
var MissingDelegateException = class extends ArkormException {
|
|
3383
|
-
constructor(message, context = {}) {
|
|
3384
|
-
super(message, {
|
|
3385
|
-
code: "MISSING_DELEGATE",
|
|
3386
|
-
...context
|
|
3387
|
-
});
|
|
3388
|
-
this.name = "MissingDelegateException";
|
|
3389
|
-
}
|
|
3390
|
-
};
|
|
3391
|
-
|
|
3392
3967
|
//#endregion
|
|
3393
3968
|
//#region src/Exceptions/ModelNotFoundException.ts
|
|
3394
3969
|
/**
|
|
@@ -3451,55 +4026,59 @@ var ScopeNotDefinedException = class extends ArkormException {
|
|
|
3451
4026
|
};
|
|
3452
4027
|
|
|
3453
4028
|
//#endregion
|
|
3454
|
-
//#region src/Exceptions/UniqueConstraintResolutionException.ts
|
|
3455
|
-
var UniqueConstraintResolutionException = class extends ArkormException {
|
|
3456
|
-
constructor(message, context = {}) {
|
|
3457
|
-
super(message, {
|
|
3458
|
-
code: "UNIQUE_CONSTRAINT_RESOLUTION_FAILED",
|
|
3459
|
-
...context
|
|
3460
|
-
});
|
|
3461
|
-
this.name = "UniqueConstraintResolutionException";
|
|
3462
|
-
}
|
|
3463
|
-
};
|
|
3464
|
-
|
|
3465
|
-
//#endregion
|
|
3466
|
-
//#region src/
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
}
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
}
|
|
3501
|
-
|
|
3502
|
-
|
|
4029
|
+
//#region src/Exceptions/UniqueConstraintResolutionException.ts
|
|
4030
|
+
var UniqueConstraintResolutionException = class extends ArkormException {
|
|
4031
|
+
constructor(message, context = {}) {
|
|
4032
|
+
super(message, {
|
|
4033
|
+
code: "UNIQUE_CONSTRAINT_RESOLUTION_FAILED",
|
|
4034
|
+
...context
|
|
4035
|
+
});
|
|
4036
|
+
this.name = "UniqueConstraintResolutionException";
|
|
4037
|
+
}
|
|
4038
|
+
};
|
|
4039
|
+
|
|
4040
|
+
//#endregion
|
|
4041
|
+
//#region src/relationship/RelationTableLoader.ts
|
|
4042
|
+
var RelationTableLoader = class {
|
|
4043
|
+
constructor(adapter) {
|
|
4044
|
+
this.adapter = adapter;
|
|
4045
|
+
}
|
|
4046
|
+
async selectRows(spec) {
|
|
4047
|
+
return await this.adapter.select({
|
|
4048
|
+
target: { table: spec.table },
|
|
4049
|
+
where: spec.where,
|
|
4050
|
+
columns: spec.columns,
|
|
4051
|
+
orderBy: spec.orderBy,
|
|
4052
|
+
limit: spec.limit,
|
|
4053
|
+
offset: spec.offset
|
|
4054
|
+
});
|
|
4055
|
+
}
|
|
4056
|
+
async selectRow(spec) {
|
|
4057
|
+
return await this.adapter.selectOne({
|
|
4058
|
+
target: { table: spec.table },
|
|
4059
|
+
where: spec.where,
|
|
4060
|
+
columns: spec.columns,
|
|
4061
|
+
orderBy: spec.orderBy,
|
|
4062
|
+
limit: spec.limit ?? 1,
|
|
4063
|
+
offset: spec.offset
|
|
4064
|
+
});
|
|
4065
|
+
}
|
|
4066
|
+
async selectColumnValues(spec) {
|
|
4067
|
+
return (await this.selectRows({
|
|
4068
|
+
...spec.lookup,
|
|
4069
|
+
columns: [{ column: spec.column }]
|
|
4070
|
+
})).map((row) => row[spec.column]);
|
|
4071
|
+
}
|
|
4072
|
+
async selectColumnValue(spec) {
|
|
4073
|
+
return (await this.selectRow({
|
|
4074
|
+
...spec.lookup,
|
|
4075
|
+
columns: [{ column: spec.column }],
|
|
4076
|
+
limit: 1
|
|
4077
|
+
}))?.[spec.column] ?? null;
|
|
4078
|
+
}
|
|
4079
|
+
};
|
|
4080
|
+
|
|
4081
|
+
//#endregion
|
|
3503
4082
|
//#region src/relationship/Relation.ts
|
|
3504
4083
|
/**
|
|
3505
4084
|
* Base class for all relationship types. Not meant to be used directly.
|
|
@@ -3509,6 +4088,17 @@ function inferDelegateName(modelName) {
|
|
|
3509
4088
|
*/
|
|
3510
4089
|
var Relation = class {
|
|
3511
4090
|
constraint = null;
|
|
4091
|
+
getRelationAdapter() {
|
|
4092
|
+
const adapter = this.getRelatedModel().getAdapter();
|
|
4093
|
+
if (!adapter) throw new UnsupportedAdapterFeatureException("Relationship resolution requires a configured adapter.", { operation: "relation.adapter" });
|
|
4094
|
+
return adapter;
|
|
4095
|
+
}
|
|
4096
|
+
getRelatedModel() {
|
|
4097
|
+
return this.related;
|
|
4098
|
+
}
|
|
4099
|
+
createRelationTableLoader() {
|
|
4100
|
+
return new RelationTableLoader(this.getRelationAdapter());
|
|
4101
|
+
}
|
|
3512
4102
|
/**
|
|
3513
4103
|
* Apply a constraint to the relationship query.
|
|
3514
4104
|
*
|
|
@@ -3725,9 +4315,31 @@ var BelongsToManyRelation = class extends Relation {
|
|
|
3725
4315
|
*/
|
|
3726
4316
|
async getQuery() {
|
|
3727
4317
|
const parentValue = this.parent.getAttribute(this.parentKey);
|
|
3728
|
-
const ids =
|
|
4318
|
+
const ids = await this.createRelationTableLoader().selectColumnValues({
|
|
4319
|
+
lookup: {
|
|
4320
|
+
table: this.throughDelegate,
|
|
4321
|
+
where: {
|
|
4322
|
+
type: "comparison",
|
|
4323
|
+
column: this.foreignPivotKey,
|
|
4324
|
+
operator: "=",
|
|
4325
|
+
value: parentValue
|
|
4326
|
+
}
|
|
4327
|
+
},
|
|
4328
|
+
column: this.relatedPivotKey
|
|
4329
|
+
});
|
|
3729
4330
|
return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } }));
|
|
3730
4331
|
}
|
|
4332
|
+
getMetadata() {
|
|
4333
|
+
return {
|
|
4334
|
+
type: "belongsToMany",
|
|
4335
|
+
relatedModel: this.related,
|
|
4336
|
+
throughTable: this.throughDelegate,
|
|
4337
|
+
foreignPivotKey: this.foreignPivotKey,
|
|
4338
|
+
relatedPivotKey: this.relatedPivotKey,
|
|
4339
|
+
parentKey: this.parentKey,
|
|
4340
|
+
relatedKey: this.relatedKey
|
|
4341
|
+
};
|
|
4342
|
+
}
|
|
3731
4343
|
/**
|
|
3732
4344
|
* Fetches the related models for this relationship.
|
|
3733
4345
|
*
|
|
@@ -3794,6 +4406,14 @@ var BelongsToRelation = class extends SingleResultRelation {
|
|
|
3794
4406
|
const foreignValue = this.parent.getAttribute(this.foreignKey);
|
|
3795
4407
|
return this.applyConstraint(this.related.query().where({ [this.ownerKey]: foreignValue }));
|
|
3796
4408
|
}
|
|
4409
|
+
getMetadata() {
|
|
4410
|
+
return {
|
|
4411
|
+
type: "belongsTo",
|
|
4412
|
+
relatedModel: this.related,
|
|
4413
|
+
foreignKey: this.foreignKey,
|
|
4414
|
+
ownerKey: this.ownerKey
|
|
4415
|
+
};
|
|
4416
|
+
}
|
|
3797
4417
|
/**
|
|
3798
4418
|
* Fetches the related models for this relationship.
|
|
3799
4419
|
*
|
|
@@ -3829,6 +4449,14 @@ var HasManyRelation = class extends Relation {
|
|
|
3829
4449
|
const localValue = this.parent.getAttribute(this.localKey);
|
|
3830
4450
|
return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue }));
|
|
3831
4451
|
}
|
|
4452
|
+
getMetadata() {
|
|
4453
|
+
return {
|
|
4454
|
+
type: "hasMany",
|
|
4455
|
+
relatedModel: this.related,
|
|
4456
|
+
foreignKey: this.foreignKey,
|
|
4457
|
+
localKey: this.localKey
|
|
4458
|
+
};
|
|
4459
|
+
}
|
|
3832
4460
|
/**
|
|
3833
4461
|
* Fetches the related models for this relationship.
|
|
3834
4462
|
*
|
|
@@ -3866,9 +4494,31 @@ var HasManyThroughRelation = class extends Relation {
|
|
|
3866
4494
|
*/
|
|
3867
4495
|
async getQuery() {
|
|
3868
4496
|
const localValue = this.parent.getAttribute(this.localKey);
|
|
3869
|
-
const keys =
|
|
4497
|
+
const keys = await this.createRelationTableLoader().selectColumnValues({
|
|
4498
|
+
lookup: {
|
|
4499
|
+
table: this.throughDelegate,
|
|
4500
|
+
where: {
|
|
4501
|
+
type: "comparison",
|
|
4502
|
+
column: this.firstKey,
|
|
4503
|
+
operator: "=",
|
|
4504
|
+
value: localValue
|
|
4505
|
+
}
|
|
4506
|
+
},
|
|
4507
|
+
column: this.secondLocalKey
|
|
4508
|
+
});
|
|
3870
4509
|
return this.applyConstraint(this.related.query().where({ [this.secondKey]: { in: keys } }));
|
|
3871
4510
|
}
|
|
4511
|
+
getMetadata() {
|
|
4512
|
+
return {
|
|
4513
|
+
type: "hasManyThrough",
|
|
4514
|
+
relatedModel: this.related,
|
|
4515
|
+
throughTable: this.throughDelegate,
|
|
4516
|
+
firstKey: this.firstKey,
|
|
4517
|
+
secondKey: this.secondKey,
|
|
4518
|
+
localKey: this.localKey,
|
|
4519
|
+
secondLocalKey: this.secondLocalKey
|
|
4520
|
+
};
|
|
4521
|
+
}
|
|
3872
4522
|
/**
|
|
3873
4523
|
* Fetches the related models for this relationship.
|
|
3874
4524
|
*
|
|
@@ -3902,6 +4552,14 @@ var HasOneRelation = class extends SingleResultRelation {
|
|
|
3902
4552
|
const localValue = this.parent.getAttribute(this.localKey);
|
|
3903
4553
|
return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue }));
|
|
3904
4554
|
}
|
|
4555
|
+
getMetadata() {
|
|
4556
|
+
return {
|
|
4557
|
+
type: "hasOne",
|
|
4558
|
+
relatedModel: this.related,
|
|
4559
|
+
foreignKey: this.foreignKey,
|
|
4560
|
+
localKey: this.localKey
|
|
4561
|
+
};
|
|
4562
|
+
}
|
|
3905
4563
|
/**
|
|
3906
4564
|
* Fetches the related models for this relationship.
|
|
3907
4565
|
*
|
|
@@ -3937,9 +4595,31 @@ var HasOneThroughRelation = class extends SingleResultRelation {
|
|
|
3937
4595
|
*/
|
|
3938
4596
|
async getQuery() {
|
|
3939
4597
|
const localValue = this.parent.getAttribute(this.localKey);
|
|
3940
|
-
const
|
|
3941
|
-
|
|
3942
|
-
|
|
4598
|
+
const intermediateKey = await this.createRelationTableLoader().selectColumnValue({
|
|
4599
|
+
lookup: {
|
|
4600
|
+
table: this.throughDelegate,
|
|
4601
|
+
where: {
|
|
4602
|
+
type: "comparison",
|
|
4603
|
+
column: this.firstKey,
|
|
4604
|
+
operator: "=",
|
|
4605
|
+
value: localValue
|
|
4606
|
+
}
|
|
4607
|
+
},
|
|
4608
|
+
column: this.secondLocalKey
|
|
4609
|
+
});
|
|
4610
|
+
if (intermediateKey == null) return this.applyConstraint(this.related.query().where({ [this.secondKey]: { in: [] } }));
|
|
4611
|
+
return this.applyConstraint(this.related.query().where({ [this.secondKey]: intermediateKey }));
|
|
4612
|
+
}
|
|
4613
|
+
getMetadata() {
|
|
4614
|
+
return {
|
|
4615
|
+
type: "hasOneThrough",
|
|
4616
|
+
relatedModel: this.related,
|
|
4617
|
+
throughTable: this.throughDelegate,
|
|
4618
|
+
firstKey: this.firstKey,
|
|
4619
|
+
secondKey: this.secondKey,
|
|
4620
|
+
localKey: this.localKey,
|
|
4621
|
+
secondLocalKey: this.secondLocalKey
|
|
4622
|
+
};
|
|
3943
4623
|
}
|
|
3944
4624
|
/**
|
|
3945
4625
|
* Fetches the related models for this relationship.
|
|
@@ -3980,6 +4660,16 @@ var MorphManyRelation = class extends Relation {
|
|
|
3980
4660
|
[`${this.morphName}Type`]: type
|
|
3981
4661
|
}));
|
|
3982
4662
|
}
|
|
4663
|
+
getMetadata() {
|
|
4664
|
+
return {
|
|
4665
|
+
type: "morphMany",
|
|
4666
|
+
relatedModel: this.related,
|
|
4667
|
+
morphName: this.morphName,
|
|
4668
|
+
morphIdColumn: `${this.morphName}Id`,
|
|
4669
|
+
morphTypeColumn: `${this.morphName}Type`,
|
|
4670
|
+
localKey: this.localKey
|
|
4671
|
+
};
|
|
4672
|
+
}
|
|
3983
4673
|
/**
|
|
3984
4674
|
* Fetches the related models for this relationship.
|
|
3985
4675
|
*
|
|
@@ -4017,6 +4707,16 @@ var MorphOneRelation = class extends SingleResultRelation {
|
|
|
4017
4707
|
[`${this.morphName}Type`]: type
|
|
4018
4708
|
}));
|
|
4019
4709
|
}
|
|
4710
|
+
getMetadata() {
|
|
4711
|
+
return {
|
|
4712
|
+
type: "morphOne",
|
|
4713
|
+
relatedModel: this.related,
|
|
4714
|
+
morphName: this.morphName,
|
|
4715
|
+
morphIdColumn: `${this.morphName}Id`,
|
|
4716
|
+
morphTypeColumn: `${this.morphName}Type`,
|
|
4717
|
+
localKey: this.localKey
|
|
4718
|
+
};
|
|
4719
|
+
}
|
|
4020
4720
|
/**
|
|
4021
4721
|
* Fetches the related models for this relationship.
|
|
4022
4722
|
*
|
|
@@ -4054,12 +4754,42 @@ var MorphToManyRelation = class extends Relation {
|
|
|
4054
4754
|
async getQuery() {
|
|
4055
4755
|
const parentValue = this.parent.getAttribute(this.parentKey);
|
|
4056
4756
|
const morphType = this.parent.constructor.name;
|
|
4057
|
-
const ids =
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4757
|
+
const ids = await this.createRelationTableLoader().selectColumnValues({
|
|
4758
|
+
lookup: {
|
|
4759
|
+
table: this.throughDelegate,
|
|
4760
|
+
where: {
|
|
4761
|
+
type: "group",
|
|
4762
|
+
operator: "and",
|
|
4763
|
+
conditions: [{
|
|
4764
|
+
type: "comparison",
|
|
4765
|
+
column: `${this.morphName}Id`,
|
|
4766
|
+
operator: "=",
|
|
4767
|
+
value: parentValue
|
|
4768
|
+
}, {
|
|
4769
|
+
type: "comparison",
|
|
4770
|
+
column: `${this.morphName}Type`,
|
|
4771
|
+
operator: "=",
|
|
4772
|
+
value: morphType
|
|
4773
|
+
}]
|
|
4774
|
+
}
|
|
4775
|
+
},
|
|
4776
|
+
column: this.relatedPivotKey
|
|
4777
|
+
});
|
|
4061
4778
|
return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } }));
|
|
4062
4779
|
}
|
|
4780
|
+
getMetadata() {
|
|
4781
|
+
return {
|
|
4782
|
+
type: "morphToMany",
|
|
4783
|
+
relatedModel: this.related,
|
|
4784
|
+
throughTable: this.throughDelegate,
|
|
4785
|
+
morphName: this.morphName,
|
|
4786
|
+
morphIdColumn: `${this.morphName}Id`,
|
|
4787
|
+
morphTypeColumn: `${this.morphName}Type`,
|
|
4788
|
+
relatedPivotKey: this.relatedPivotKey,
|
|
4789
|
+
parentKey: this.parentKey,
|
|
4790
|
+
relatedKey: this.relatedKey
|
|
4791
|
+
};
|
|
4792
|
+
}
|
|
4063
4793
|
/**
|
|
4064
4794
|
* Fetches the related models for this relationship.
|
|
4065
4795
|
*
|
|
@@ -4261,7 +4991,13 @@ var Paginator = class {
|
|
|
4261
4991
|
* @since 0.1.0
|
|
4262
4992
|
*/
|
|
4263
4993
|
var QueryBuilder = class QueryBuilder {
|
|
4264
|
-
|
|
4994
|
+
queryWhere;
|
|
4995
|
+
legacyWhere;
|
|
4996
|
+
queryRelationLoads;
|
|
4997
|
+
queryOrderBy;
|
|
4998
|
+
querySelect;
|
|
4999
|
+
offsetValue;
|
|
5000
|
+
limitValue;
|
|
4265
5001
|
eagerLoads = {};
|
|
4266
5002
|
includeTrashed = false;
|
|
4267
5003
|
onlyTrashedRecords = false;
|
|
@@ -4271,12 +5007,11 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4271
5007
|
/**
|
|
4272
5008
|
* Creates a new QueryBuilder instance.
|
|
4273
5009
|
*
|
|
4274
|
-
* @param delegate
|
|
4275
5010
|
* @param model
|
|
4276
5011
|
*/
|
|
4277
|
-
constructor(
|
|
4278
|
-
this.delegate = delegate;
|
|
5012
|
+
constructor(model, adapter) {
|
|
4279
5013
|
this.model = model;
|
|
5014
|
+
this.adapter = adapter;
|
|
4280
5015
|
}
|
|
4281
5016
|
resolvePaginationPage(page, options) {
|
|
4282
5017
|
if (typeof page !== "undefined") return Number.isFinite(page) ? Math.max(1, page) : 1;
|
|
@@ -4450,11 +5185,26 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4450
5185
|
return this.clone().where(this.buildComparisonWhere(key, operator, value)).first();
|
|
4451
5186
|
}
|
|
4452
5187
|
addLogicalWhere(operator, where) {
|
|
4453
|
-
|
|
4454
|
-
|
|
5188
|
+
const condition = this.tryBuildQueryCondition(where);
|
|
5189
|
+
if (!this.legacyWhere && condition) {
|
|
5190
|
+
if (!this.queryWhere) {
|
|
5191
|
+
this.queryWhere = condition;
|
|
5192
|
+
return this;
|
|
5193
|
+
}
|
|
5194
|
+
this.queryWhere = {
|
|
5195
|
+
type: "group",
|
|
5196
|
+
operator: operator === "AND" ? "and" : "or",
|
|
5197
|
+
conditions: [this.queryWhere, condition]
|
|
5198
|
+
};
|
|
5199
|
+
return this;
|
|
5200
|
+
}
|
|
5201
|
+
const existingWhere = this.legacyWhere ?? this.toDelegateWhere(this.queryWhere);
|
|
5202
|
+
this.queryWhere = void 0;
|
|
5203
|
+
if (!existingWhere) {
|
|
5204
|
+
this.legacyWhere = where;
|
|
4455
5205
|
return this;
|
|
4456
5206
|
}
|
|
4457
|
-
this.
|
|
5207
|
+
this.legacyWhere = { [operator]: [existingWhere, where] };
|
|
4458
5208
|
return this;
|
|
4459
5209
|
}
|
|
4460
5210
|
buildComparisonWhere(key, operator, value) {
|
|
@@ -4498,7 +5248,12 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4498
5248
|
*/
|
|
4499
5249
|
orderBy(orderBy) {
|
|
4500
5250
|
this.randomOrderEnabled = false;
|
|
4501
|
-
|
|
5251
|
+
const normalized = this.normalizeQueryOrderBy(orderBy);
|
|
5252
|
+
if (!normalized) throw new UnsupportedAdapterFeatureException("Order clauses must use Arkorm-normalizable column directions.", {
|
|
5253
|
+
operation: "orderBy",
|
|
5254
|
+
model: this.model.name
|
|
5255
|
+
});
|
|
5256
|
+
this.queryOrderBy = normalized;
|
|
4502
5257
|
return this;
|
|
4503
5258
|
}
|
|
4504
5259
|
/**
|
|
@@ -4518,7 +5273,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4518
5273
|
* @returns
|
|
4519
5274
|
*/
|
|
4520
5275
|
reorder(column, direction = "asc") {
|
|
4521
|
-
this.
|
|
5276
|
+
this.queryOrderBy = void 0;
|
|
4522
5277
|
this.randomOrderEnabled = false;
|
|
4523
5278
|
if (!column) return this;
|
|
4524
5279
|
return this.orderBy({ [column]: direction });
|
|
@@ -4548,7 +5303,13 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4548
5303
|
* @returns
|
|
4549
5304
|
*/
|
|
4550
5305
|
include(include) {
|
|
4551
|
-
|
|
5306
|
+
const normalized = this.normalizeRelationLoads(include);
|
|
5307
|
+
if (normalized === null) throw new UnsupportedAdapterFeatureException("Include clauses could not be normalized into Arkorm relation load plans.", {
|
|
5308
|
+
operation: "include",
|
|
5309
|
+
model: this.model.name,
|
|
5310
|
+
meta: { feature: "relationLoads" }
|
|
5311
|
+
});
|
|
5312
|
+
this.queryRelationLoads = normalized;
|
|
4552
5313
|
return this;
|
|
4553
5314
|
}
|
|
4554
5315
|
/**
|
|
@@ -4560,14 +5321,6 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4560
5321
|
*/
|
|
4561
5322
|
with(relations) {
|
|
4562
5323
|
const relationMap = this.normalizeWith(relations);
|
|
4563
|
-
const names = Object.keys(relationMap);
|
|
4564
|
-
this.args.include = {
|
|
4565
|
-
...this.args.include || {},
|
|
4566
|
-
...names.reduce((accumulator, name) => {
|
|
4567
|
-
accumulator[name] = true;
|
|
4568
|
-
return accumulator;
|
|
4569
|
-
}, {})
|
|
4570
|
-
};
|
|
4571
5324
|
Object.entries(relationMap).forEach(([name, constraint]) => {
|
|
4572
5325
|
this.eagerLoads[name] = constraint;
|
|
4573
5326
|
});
|
|
@@ -4880,7 +5633,12 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4880
5633
|
* @returns
|
|
4881
5634
|
*/
|
|
4882
5635
|
select(select) {
|
|
4883
|
-
|
|
5636
|
+
const normalized = this.normalizeQuerySelect(select);
|
|
5637
|
+
if (normalized === null) throw new UnsupportedAdapterFeatureException("Select clauses must use Arkorm-normalizable column projections.", {
|
|
5638
|
+
operation: "select",
|
|
5639
|
+
model: this.model.name
|
|
5640
|
+
});
|
|
5641
|
+
this.querySelect = normalized;
|
|
4884
5642
|
return this;
|
|
4885
5643
|
}
|
|
4886
5644
|
/**
|
|
@@ -4891,7 +5649,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4891
5649
|
* @returns
|
|
4892
5650
|
*/
|
|
4893
5651
|
skip(skip) {
|
|
4894
|
-
this.
|
|
5652
|
+
this.offsetValue = skip;
|
|
4895
5653
|
return this;
|
|
4896
5654
|
}
|
|
4897
5655
|
/**
|
|
@@ -4910,7 +5668,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4910
5668
|
* @returns
|
|
4911
5669
|
*/
|
|
4912
5670
|
take(take) {
|
|
4913
|
-
this.
|
|
5671
|
+
this.limitValue = take;
|
|
4914
5672
|
return this;
|
|
4915
5673
|
}
|
|
4916
5674
|
/**
|
|
@@ -4941,16 +5699,13 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4941
5699
|
*/
|
|
4942
5700
|
async get() {
|
|
4943
5701
|
const relationCache = /* @__PURE__ */ new WeakMap();
|
|
4944
|
-
const rows = await this.
|
|
5702
|
+
const rows = await this.executeReadRows();
|
|
4945
5703
|
const normalizedRows = this.randomOrderEnabled ? this.shuffleRows(rows) : rows;
|
|
4946
5704
|
const models = await this.model.hydrateManyRetrieved(normalizedRows);
|
|
4947
5705
|
let filteredModels = models;
|
|
4948
|
-
if (this.hasRelationFilters()) if (this.hasOrRelationFilters() && this.
|
|
5706
|
+
if (this.hasRelationFilters()) if (this.hasOrRelationFilters() && this.hasBaseWhereConstraints()) {
|
|
4949
5707
|
const baseIds = new Set(models.map((model) => this.getModelId(model)).filter((id) => id != null));
|
|
4950
|
-
const allRows = await this.
|
|
4951
|
-
...this.args,
|
|
4952
|
-
where: this.buildSoftDeleteOnlyWhere()
|
|
4953
|
-
});
|
|
5708
|
+
const allRows = await this.executeReadRows(this.buildSoftDeleteOnlyWhere(), true);
|
|
4954
5709
|
const allModels = this.model.hydrateMany(allRows);
|
|
4955
5710
|
filteredModels = await this.filterModelsByRelationConstraints(allModels, relationCache, baseIds);
|
|
4956
5711
|
} else filteredModels = await this.filterModelsByRelationConstraints(models, relationCache);
|
|
@@ -4969,7 +5724,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4969
5724
|
async first() {
|
|
4970
5725
|
if (this.hasRelationFilters() || this.hasRelationAggregates()) return (await this.get()).all()[0] ?? null;
|
|
4971
5726
|
if (this.randomOrderEnabled) {
|
|
4972
|
-
const rows = await this.
|
|
5727
|
+
const rows = await this.executeReadRows();
|
|
4973
5728
|
if (rows.length === 0) return null;
|
|
4974
5729
|
const row = this.shuffleRows(rows)[0];
|
|
4975
5730
|
if (!row) return null;
|
|
@@ -4977,7 +5732,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4977
5732
|
await model.load(this.eagerLoads);
|
|
4978
5733
|
return model;
|
|
4979
5734
|
}
|
|
4980
|
-
const row = await this.
|
|
5735
|
+
const row = await this.executeReadRow();
|
|
4981
5736
|
if (!row) return null;
|
|
4982
5737
|
const model = await this.model.hydrateRetrieved(row);
|
|
4983
5738
|
await model.load(this.eagerLoads);
|
|
@@ -4993,11 +5748,12 @@ var QueryBuilder = class QueryBuilder {
|
|
|
4993
5748
|
if (!model) throw new ModelNotFoundException(this.model.name, "Record not found.");
|
|
4994
5749
|
return model;
|
|
4995
5750
|
}
|
|
4996
|
-
async find(value, key
|
|
4997
|
-
|
|
5751
|
+
async find(value, key) {
|
|
5752
|
+
const resolvedKey = key ?? this.model.getPrimaryKey();
|
|
5753
|
+
return this.where({ [resolvedKey]: value }).first();
|
|
4998
5754
|
}
|
|
4999
5755
|
async findOr(value, keyOrCallback, maybeCallback) {
|
|
5000
|
-
const key = typeof keyOrCallback === "string" ? keyOrCallback :
|
|
5756
|
+
const key = typeof keyOrCallback === "string" ? keyOrCallback : this.model.getPrimaryKey();
|
|
5001
5757
|
const callback = typeof keyOrCallback === "function" ? keyOrCallback : maybeCallback;
|
|
5002
5758
|
if (!callback) throw new QueryConstraintException("findOr requires a fallback callback.", {
|
|
5003
5759
|
operation: "findOr",
|
|
@@ -5014,7 +5770,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5014
5770
|
* @returns
|
|
5015
5771
|
*/
|
|
5016
5772
|
async value(column) {
|
|
5017
|
-
const row = await this.
|
|
5773
|
+
const row = await this.executeReadRow();
|
|
5018
5774
|
if (!row) return null;
|
|
5019
5775
|
return row[column] ?? null;
|
|
5020
5776
|
}
|
|
@@ -5037,7 +5793,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5037
5793
|
* @returns
|
|
5038
5794
|
*/
|
|
5039
5795
|
async pluck(column, key) {
|
|
5040
|
-
const rows = await this.
|
|
5796
|
+
const rows = await this.executeReadRows();
|
|
5041
5797
|
if (!key) return new ArkormCollection(rows.map((row) => row[column]));
|
|
5042
5798
|
return new ArkormCollection(rows.sort((leftRow, rightRow) => String(leftRow[key]).localeCompare(String(rightRow[key]))).map((row) => row[column]));
|
|
5043
5799
|
}
|
|
@@ -5048,7 +5804,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5048
5804
|
* @returns
|
|
5049
5805
|
*/
|
|
5050
5806
|
async create(data) {
|
|
5051
|
-
const created = await this.
|
|
5807
|
+
const created = await this.executeInsertRow(data);
|
|
5052
5808
|
return this.model.hydrate(created);
|
|
5053
5809
|
}
|
|
5054
5810
|
/**
|
|
@@ -5070,14 +5826,11 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5070
5826
|
async insert(values) {
|
|
5071
5827
|
const payloads = this.normalizeInsertPayloads(values);
|
|
5072
5828
|
if (payloads.length === 0) return true;
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
await delegate.createMany({ data: payloads });
|
|
5829
|
+
if (payloads.length === 1) {
|
|
5830
|
+
await this.executeInsertRow(payloads[0]);
|
|
5076
5831
|
return true;
|
|
5077
5832
|
}
|
|
5078
|
-
await
|
|
5079
|
-
await this.delegate.create({ data: payload });
|
|
5080
|
-
}));
|
|
5833
|
+
await this.executeInsertManyRows(payloads);
|
|
5081
5834
|
return true;
|
|
5082
5835
|
}
|
|
5083
5836
|
/**
|
|
@@ -5089,22 +5842,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5089
5842
|
async insertOrIgnore(values) {
|
|
5090
5843
|
const payloads = this.normalizeInsertPayloads(values);
|
|
5091
5844
|
if (payloads.length === 0) return 0;
|
|
5092
|
-
|
|
5093
|
-
if (typeof delegate.createMany === "function") {
|
|
5094
|
-
const result = await delegate.createMany({
|
|
5095
|
-
data: payloads,
|
|
5096
|
-
skipDuplicates: true
|
|
5097
|
-
});
|
|
5098
|
-
return this.resolveAffectedCount(result, payloads.length);
|
|
5099
|
-
}
|
|
5100
|
-
let inserted = 0;
|
|
5101
|
-
for (const payload of payloads) try {
|
|
5102
|
-
await this.delegate.create({ data: payload });
|
|
5103
|
-
inserted += 1;
|
|
5104
|
-
} catch {
|
|
5105
|
-
continue;
|
|
5106
|
-
}
|
|
5107
|
-
return inserted;
|
|
5845
|
+
return await this.executeInsertManyRows(payloads, true);
|
|
5108
5846
|
}
|
|
5109
5847
|
/**
|
|
5110
5848
|
* Insert a record and return its primary key value.
|
|
@@ -5114,8 +5852,8 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5114
5852
|
* @returns
|
|
5115
5853
|
*/
|
|
5116
5854
|
async insertGetId(values, sequence) {
|
|
5117
|
-
const created = await this.
|
|
5118
|
-
const key = sequence ??
|
|
5855
|
+
const created = await this.executeInsertRow(values);
|
|
5856
|
+
const key = sequence ?? this.model.getPrimaryKey();
|
|
5119
5857
|
if (!(key in created)) throw new UniqueConstraintResolutionException(`Inserted record does not contain key [${key}].`, {
|
|
5120
5858
|
operation: "insertGetId",
|
|
5121
5859
|
model: this.model.name,
|
|
@@ -5162,10 +5900,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5162
5900
|
model: this.model.name
|
|
5163
5901
|
});
|
|
5164
5902
|
const uniqueWhere = await this.resolveUniqueWhere(where);
|
|
5165
|
-
const updated = await this.
|
|
5166
|
-
where: uniqueWhere,
|
|
5167
|
-
data
|
|
5168
|
-
});
|
|
5903
|
+
const updated = await this.executeUpdateRow(uniqueWhere, data);
|
|
5169
5904
|
return this.model.hydrate(updated);
|
|
5170
5905
|
}
|
|
5171
5906
|
/**
|
|
@@ -5180,16 +5915,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5180
5915
|
operation: "updateFrom",
|
|
5181
5916
|
model: this.model.name
|
|
5182
5917
|
});
|
|
5183
|
-
|
|
5184
|
-
if (typeof delegate.updateMany === "function") {
|
|
5185
|
-
const result = await delegate.updateMany({
|
|
5186
|
-
where,
|
|
5187
|
-
data
|
|
5188
|
-
});
|
|
5189
|
-
return this.resolveAffectedCount(result, 0);
|
|
5190
|
-
}
|
|
5191
|
-
await this.update(data);
|
|
5192
|
-
return 1;
|
|
5918
|
+
return await this.executeUpdateManyRows(where, data);
|
|
5193
5919
|
}
|
|
5194
5920
|
/**
|
|
5195
5921
|
* Insert a record when no match exists, otherwise update the matching record.
|
|
@@ -5199,13 +5925,13 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5199
5925
|
* @returns
|
|
5200
5926
|
*/
|
|
5201
5927
|
async updateOrInsert(attributes, values = {}) {
|
|
5202
|
-
const exists = await this.
|
|
5928
|
+
const exists = await this.clone().where(attributes).first() != null;
|
|
5203
5929
|
const resolvedValues = typeof values === "function" ? await values(exists) : values;
|
|
5204
5930
|
if (!exists) {
|
|
5205
|
-
await this.
|
|
5931
|
+
await this.executeInsertRow({
|
|
5206
5932
|
...attributes,
|
|
5207
5933
|
...resolvedValues
|
|
5208
|
-
}
|
|
5934
|
+
});
|
|
5209
5935
|
return true;
|
|
5210
5936
|
}
|
|
5211
5937
|
return await this.clone().where(attributes).update(resolvedValues) != null;
|
|
@@ -5249,9 +5975,53 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5249
5975
|
model: this.model.name
|
|
5250
5976
|
});
|
|
5251
5977
|
const uniqueWhere = await this.resolveUniqueWhere(where);
|
|
5252
|
-
const deleted = await this.
|
|
5978
|
+
const deleted = await this.executeDeleteRow(uniqueWhere);
|
|
5253
5979
|
return this.model.hydrate(deleted);
|
|
5254
5980
|
}
|
|
5981
|
+
tryBuildInsertSpec(values) {
|
|
5982
|
+
return {
|
|
5983
|
+
target: this.buildQueryTarget(),
|
|
5984
|
+
values
|
|
5985
|
+
};
|
|
5986
|
+
}
|
|
5987
|
+
tryBuildInsertManySpec(values) {
|
|
5988
|
+
return {
|
|
5989
|
+
target: this.buildQueryTarget(),
|
|
5990
|
+
values
|
|
5991
|
+
};
|
|
5992
|
+
}
|
|
5993
|
+
tryBuildInsertOrIgnoreManySpec(values) {
|
|
5994
|
+
return {
|
|
5995
|
+
...this.tryBuildInsertManySpec(values),
|
|
5996
|
+
ignoreDuplicates: true
|
|
5997
|
+
};
|
|
5998
|
+
}
|
|
5999
|
+
tryBuildUpdateSpec(where, values) {
|
|
6000
|
+
const condition = this.tryBuildQueryCondition(where);
|
|
6001
|
+
if (!condition) return null;
|
|
6002
|
+
return {
|
|
6003
|
+
target: this.buildQueryTarget(),
|
|
6004
|
+
where: condition,
|
|
6005
|
+
values
|
|
6006
|
+
};
|
|
6007
|
+
}
|
|
6008
|
+
tryBuildUpdateManySpec(where, values) {
|
|
6009
|
+
const condition = this.tryBuildQueryCondition(where);
|
|
6010
|
+
if (condition === null) return null;
|
|
6011
|
+
return {
|
|
6012
|
+
target: this.buildQueryTarget(),
|
|
6013
|
+
where: condition,
|
|
6014
|
+
values
|
|
6015
|
+
};
|
|
6016
|
+
}
|
|
6017
|
+
tryBuildDeleteSpec(where) {
|
|
6018
|
+
const condition = this.tryBuildQueryCondition(where);
|
|
6019
|
+
if (!condition) return null;
|
|
6020
|
+
return {
|
|
6021
|
+
target: this.buildQueryTarget(),
|
|
6022
|
+
where: condition
|
|
6023
|
+
};
|
|
6024
|
+
}
|
|
5255
6025
|
/**
|
|
5256
6026
|
* Counts the number of records matching the current query constraints.
|
|
5257
6027
|
*
|
|
@@ -5259,7 +6029,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5259
6029
|
*/
|
|
5260
6030
|
async count() {
|
|
5261
6031
|
if (this.hasRelationFilters()) return (await this.get()).all().length;
|
|
5262
|
-
return this.
|
|
6032
|
+
return this.executeReadCount();
|
|
5263
6033
|
}
|
|
5264
6034
|
/**
|
|
5265
6035
|
* Determines if any records exist for the current query constraints.
|
|
@@ -5268,7 +6038,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5268
6038
|
*/
|
|
5269
6039
|
async exists() {
|
|
5270
6040
|
if (this.hasRelationFilters()) return await this.count() > 0;
|
|
5271
|
-
return await this.
|
|
6041
|
+
return await this.executeReadExists();
|
|
5272
6042
|
}
|
|
5273
6043
|
/**
|
|
5274
6044
|
* Determines if no records exist for the current query constraints.
|
|
@@ -5345,7 +6115,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5345
6115
|
* @returns
|
|
5346
6116
|
*/
|
|
5347
6117
|
async min(column) {
|
|
5348
|
-
const rows = await this.
|
|
6118
|
+
const rows = await this.executeReadRows();
|
|
5349
6119
|
if (rows.length === 0) return null;
|
|
5350
6120
|
const values = rows.map((row) => row[column]).filter((value) => value != null);
|
|
5351
6121
|
if (values.length === 0) return null;
|
|
@@ -5358,7 +6128,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5358
6128
|
* @returns
|
|
5359
6129
|
*/
|
|
5360
6130
|
async max(column) {
|
|
5361
|
-
const rows = await this.
|
|
6131
|
+
const rows = await this.executeReadRows();
|
|
5362
6132
|
if (rows.length === 0) return null;
|
|
5363
6133
|
const values = rows.map((row) => row[column]).filter((value) => value != null);
|
|
5364
6134
|
if (values.length === 0) return null;
|
|
@@ -5371,7 +6141,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5371
6141
|
* @returns
|
|
5372
6142
|
*/
|
|
5373
6143
|
async sum(column) {
|
|
5374
|
-
return (await this.
|
|
6144
|
+
return (await this.executeReadRows()).reduce((total, row) => {
|
|
5375
6145
|
const value = row[column];
|
|
5376
6146
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
5377
6147
|
return Number.isFinite(numeric) ? total + numeric : total;
|
|
@@ -5384,7 +6154,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5384
6154
|
* @returns
|
|
5385
6155
|
*/
|
|
5386
6156
|
async avg(column) {
|
|
5387
|
-
const values = (await this.
|
|
6157
|
+
const values = (await this.executeReadRows()).map((row) => {
|
|
5388
6158
|
const value = row[column];
|
|
5389
6159
|
return typeof value === "number" ? value : Number(value);
|
|
5390
6160
|
}).filter((value) => Number.isFinite(value));
|
|
@@ -5399,13 +6169,16 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5399
6169
|
* @returns
|
|
5400
6170
|
*/
|
|
5401
6171
|
whereRaw(sql, bindings = []) {
|
|
5402
|
-
|
|
5403
|
-
if (typeof delegate.applyRawWhere !== "function") throw new UnsupportedAdapterFeatureException("Raw where clauses are not supported by the current adapter.", {
|
|
6172
|
+
if (!this.adapter?.capabilities?.rawWhere) throw new UnsupportedAdapterFeatureException("Raw where clauses are not supported by the current adapter.", {
|
|
5404
6173
|
operation: "whereRaw",
|
|
5405
6174
|
model: this.model.name,
|
|
5406
6175
|
meta: { feature: "rawWhere" }
|
|
5407
6176
|
});
|
|
5408
|
-
this.
|
|
6177
|
+
this.appendQueryCondition("AND", {
|
|
6178
|
+
type: "raw",
|
|
6179
|
+
sql,
|
|
6180
|
+
bindings
|
|
6181
|
+
});
|
|
5409
6182
|
return this;
|
|
5410
6183
|
}
|
|
5411
6184
|
/**
|
|
@@ -5416,14 +6189,17 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5416
6189
|
* @returns
|
|
5417
6190
|
*/
|
|
5418
6191
|
orWhereRaw(sql, bindings = []) {
|
|
5419
|
-
|
|
5420
|
-
if (typeof delegate.applyRawWhere !== "function") throw new UnsupportedAdapterFeatureException("Raw where clauses are not supported by the current adapter.", {
|
|
6192
|
+
if (!this.adapter?.capabilities?.rawWhere) throw new UnsupportedAdapterFeatureException("Raw where clauses are not supported by the current adapter.", {
|
|
5421
6193
|
operation: "orWhereRaw",
|
|
5422
6194
|
model: this.model.name,
|
|
5423
6195
|
meta: { feature: "rawWhere" }
|
|
5424
6196
|
});
|
|
5425
|
-
|
|
5426
|
-
|
|
6197
|
+
this.appendQueryCondition("OR", {
|
|
6198
|
+
type: "raw",
|
|
6199
|
+
sql,
|
|
6200
|
+
bindings
|
|
6201
|
+
});
|
|
6202
|
+
return this;
|
|
5427
6203
|
}
|
|
5428
6204
|
/**
|
|
5429
6205
|
* Paginates the query results and returns a LengthAwarePaginator instance
|
|
@@ -5474,13 +6250,14 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5474
6250
|
* @returns
|
|
5475
6251
|
*/
|
|
5476
6252
|
clone() {
|
|
5477
|
-
const builder = new QueryBuilder(this.
|
|
5478
|
-
builder.
|
|
5479
|
-
builder.
|
|
5480
|
-
builder.
|
|
5481
|
-
builder.
|
|
5482
|
-
builder.
|
|
5483
|
-
builder.
|
|
6253
|
+
const builder = new QueryBuilder(this.model, this.adapter);
|
|
6254
|
+
builder.queryWhere = this.queryWhere;
|
|
6255
|
+
builder.legacyWhere = this.legacyWhere;
|
|
6256
|
+
builder.queryRelationLoads = this.queryRelationLoads ? this.cloneRelationLoads(this.queryRelationLoads) : void 0;
|
|
6257
|
+
builder.queryOrderBy = this.queryOrderBy ? [...this.queryOrderBy] : void 0;
|
|
6258
|
+
builder.querySelect = this.querySelect ? [...this.querySelect] : void 0;
|
|
6259
|
+
builder.offsetValue = this.offsetValue;
|
|
6260
|
+
builder.limitValue = this.limitValue;
|
|
5484
6261
|
builder.includeTrashed = this.includeTrashed;
|
|
5485
6262
|
builder.onlyTrashedRecords = this.onlyTrashedRecords;
|
|
5486
6263
|
builder.randomOrderEnabled = this.randomOrderEnabled;
|
|
@@ -5509,6 +6286,424 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5509
6286
|
}, {});
|
|
5510
6287
|
return relations;
|
|
5511
6288
|
}
|
|
6289
|
+
buildQueryTarget() {
|
|
6290
|
+
const metadata = this.model.getModelMetadata();
|
|
6291
|
+
return {
|
|
6292
|
+
model: this.model,
|
|
6293
|
+
modelName: this.model.name,
|
|
6294
|
+
table: metadata.table,
|
|
6295
|
+
primaryKey: metadata.primaryKey,
|
|
6296
|
+
columns: metadata.columns,
|
|
6297
|
+
softDelete: metadata.softDelete
|
|
6298
|
+
};
|
|
6299
|
+
}
|
|
6300
|
+
hasBaseWhereConstraints() {
|
|
6301
|
+
return this.queryWhere != null || this.legacyWhere != null;
|
|
6302
|
+
}
|
|
6303
|
+
normalizeQuerySelect(select) {
|
|
6304
|
+
if (Array.isArray(select) || typeof select !== "object" || !select) return null;
|
|
6305
|
+
const entries = Object.entries(select);
|
|
6306
|
+
if (entries.some(([, value]) => value !== true && value !== false && value !== void 0)) return null;
|
|
6307
|
+
const columns = entries.filter(([, value]) => value === true).map(([column]) => ({ column }));
|
|
6308
|
+
return columns.length > 0 ? columns : [];
|
|
6309
|
+
}
|
|
6310
|
+
normalizeQueryOrderBy(orderBy) {
|
|
6311
|
+
return (Array.isArray(orderBy) ? orderBy : [orderBy]).reduce((accumulator, clause) => {
|
|
6312
|
+
if (!accumulator) return null;
|
|
6313
|
+
if (!clause || typeof clause !== "object" || Array.isArray(clause)) return null;
|
|
6314
|
+
const entries = Object.entries(clause);
|
|
6315
|
+
for (const [column, direction] of entries) {
|
|
6316
|
+
if (direction !== "asc" && direction !== "desc") return null;
|
|
6317
|
+
accumulator.push({
|
|
6318
|
+
column,
|
|
6319
|
+
direction
|
|
6320
|
+
});
|
|
6321
|
+
}
|
|
6322
|
+
return accumulator;
|
|
6323
|
+
}, []);
|
|
6324
|
+
}
|
|
6325
|
+
cloneRelationLoads(plans) {
|
|
6326
|
+
return plans.map((plan) => {
|
|
6327
|
+
return {
|
|
6328
|
+
relation: plan.relation,
|
|
6329
|
+
constraint: plan.constraint,
|
|
6330
|
+
orderBy: plan.orderBy ? [...plan.orderBy] : void 0,
|
|
6331
|
+
limit: plan.limit,
|
|
6332
|
+
offset: plan.offset,
|
|
6333
|
+
columns: plan.columns ? [...plan.columns] : void 0,
|
|
6334
|
+
relationLoads: plan.relationLoads ? this.cloneRelationLoads(plan.relationLoads) : void 0
|
|
6335
|
+
};
|
|
6336
|
+
});
|
|
6337
|
+
}
|
|
6338
|
+
normalizeRelationLoadSelect(select) {
|
|
6339
|
+
if (Array.isArray(select) || typeof select !== "object" || !select) return null;
|
|
6340
|
+
const entries = Object.entries(select);
|
|
6341
|
+
if (entries.some(([, value]) => value !== true && value !== false && value !== void 0)) return null;
|
|
6342
|
+
return entries.filter(([, value]) => value === true).map(([column]) => ({ column }));
|
|
6343
|
+
}
|
|
6344
|
+
normalizeRelationLoadOrderBy(orderBy) {
|
|
6345
|
+
const clauses = Array.isArray(orderBy) ? orderBy : [orderBy];
|
|
6346
|
+
const normalized = [];
|
|
6347
|
+
for (const clause of clauses) {
|
|
6348
|
+
if (!clause || typeof clause !== "object" || Array.isArray(clause)) return null;
|
|
6349
|
+
for (const [column, direction] of Object.entries(clause)) {
|
|
6350
|
+
if (direction !== "asc" && direction !== "desc") return null;
|
|
6351
|
+
normalized.push({
|
|
6352
|
+
column,
|
|
6353
|
+
direction
|
|
6354
|
+
});
|
|
6355
|
+
}
|
|
6356
|
+
}
|
|
6357
|
+
return normalized;
|
|
6358
|
+
}
|
|
6359
|
+
normalizeRelationLoads(include) {
|
|
6360
|
+
if (Array.isArray(include) || typeof include !== "object" || !include) return null;
|
|
6361
|
+
const plans = [];
|
|
6362
|
+
for (const [relation, value] of Object.entries(include)) {
|
|
6363
|
+
if (value === false || value === void 0) continue;
|
|
6364
|
+
if (value === true) {
|
|
6365
|
+
plans.push({ relation });
|
|
6366
|
+
continue;
|
|
6367
|
+
}
|
|
6368
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
6369
|
+
const options = value;
|
|
6370
|
+
const constraint = options.where === void 0 ? void 0 : this.tryBuildQueryCondition(options.where);
|
|
6371
|
+
const orderBy = options.orderBy === void 0 ? void 0 : this.normalizeRelationLoadOrderBy(options.orderBy);
|
|
6372
|
+
const columns = options.select === void 0 ? void 0 : this.normalizeRelationLoadSelect(options.select);
|
|
6373
|
+
const relationLoads = options.include === void 0 ? void 0 : this.normalizeRelationLoads(options.include);
|
|
6374
|
+
if (constraint === null || orderBy === null || columns === null || relationLoads === null) return null;
|
|
6375
|
+
if (options.skip !== void 0 && typeof options.skip !== "number" || options.take !== void 0 && typeof options.take !== "number") return null;
|
|
6376
|
+
plans.push({
|
|
6377
|
+
relation,
|
|
6378
|
+
constraint,
|
|
6379
|
+
orderBy,
|
|
6380
|
+
limit: options.take,
|
|
6381
|
+
offset: options.skip,
|
|
6382
|
+
columns,
|
|
6383
|
+
relationLoads
|
|
6384
|
+
});
|
|
6385
|
+
}
|
|
6386
|
+
return plans;
|
|
6387
|
+
}
|
|
6388
|
+
appendQueryCondition(operator, condition) {
|
|
6389
|
+
if (!this.queryWhere) {
|
|
6390
|
+
this.queryWhere = condition;
|
|
6391
|
+
return;
|
|
6392
|
+
}
|
|
6393
|
+
this.queryWhere = {
|
|
6394
|
+
type: "group",
|
|
6395
|
+
operator: operator === "AND" ? "and" : "or",
|
|
6396
|
+
conditions: [this.queryWhere, condition]
|
|
6397
|
+
};
|
|
6398
|
+
}
|
|
6399
|
+
toDelegateWhere(condition) {
|
|
6400
|
+
if (!condition) return void 0;
|
|
6401
|
+
if (condition.type === "comparison") {
|
|
6402
|
+
if (condition.operator === "is-null") return { [condition.column]: null };
|
|
6403
|
+
if (condition.operator === "is-not-null") return { [condition.column]: { not: null } };
|
|
6404
|
+
if (condition.operator === "=") return { [condition.column]: condition.value };
|
|
6405
|
+
if (condition.operator === "!=") return { [condition.column]: { not: condition.value } };
|
|
6406
|
+
if (condition.operator === ">") return { [condition.column]: { gt: condition.value } };
|
|
6407
|
+
if (condition.operator === ">=") return { [condition.column]: { gte: condition.value } };
|
|
6408
|
+
if (condition.operator === "<") return { [condition.column]: { lt: condition.value } };
|
|
6409
|
+
if (condition.operator === "<=") return { [condition.column]: { lte: condition.value } };
|
|
6410
|
+
if (condition.operator === "in") return { [condition.column]: { in: Array.isArray(condition.value) ? condition.value : [condition.value] } };
|
|
6411
|
+
if (condition.operator === "not-in") return { [condition.column]: { notIn: Array.isArray(condition.value) ? condition.value : [condition.value] } };
|
|
6412
|
+
if (condition.operator === "contains") return { [condition.column]: { contains: condition.value } };
|
|
6413
|
+
if (condition.operator === "starts-with") return { [condition.column]: { startsWith: condition.value } };
|
|
6414
|
+
return { [condition.column]: { endsWith: condition.value } };
|
|
6415
|
+
}
|
|
6416
|
+
if (condition.type === "group") {
|
|
6417
|
+
const conditions = condition.conditions.map((entry) => this.toDelegateWhere(entry)).filter((entry) => Boolean(entry));
|
|
6418
|
+
if (conditions.length === 0) return void 0;
|
|
6419
|
+
return { [condition.operator === "and" ? "AND" : "OR"]: conditions };
|
|
6420
|
+
}
|
|
6421
|
+
if (condition.type === "not") {
|
|
6422
|
+
const nested = this.toDelegateWhere(condition.condition);
|
|
6423
|
+
if (!nested) return void 0;
|
|
6424
|
+
return { NOT: nested };
|
|
6425
|
+
}
|
|
6426
|
+
}
|
|
6427
|
+
buildSoftDeleteQueryCondition() {
|
|
6428
|
+
const softDeleteConfig = this.model.getSoftDeleteConfig();
|
|
6429
|
+
if (!softDeleteConfig.enabled || this.includeTrashed) return void 0;
|
|
6430
|
+
return {
|
|
6431
|
+
type: "comparison",
|
|
6432
|
+
column: softDeleteConfig.column,
|
|
6433
|
+
operator: this.onlyTrashedRecords ? "is-not-null" : "is-null"
|
|
6434
|
+
};
|
|
6435
|
+
}
|
|
6436
|
+
buildQueryWhereCondition(softDeleteOnly = false) {
|
|
6437
|
+
if (this.legacyWhere) {
|
|
6438
|
+
const fallbackWhere = softDeleteOnly ? this.buildSoftDeleteOnlyWhere() : this.buildWhere();
|
|
6439
|
+
return this.tryBuildQueryCondition(fallbackWhere);
|
|
6440
|
+
}
|
|
6441
|
+
const softDeleteCondition = this.buildSoftDeleteQueryCondition();
|
|
6442
|
+
if (softDeleteOnly) return softDeleteCondition;
|
|
6443
|
+
if (!this.queryWhere) return softDeleteCondition;
|
|
6444
|
+
if (!softDeleteCondition) return this.queryWhere;
|
|
6445
|
+
return {
|
|
6446
|
+
type: "group",
|
|
6447
|
+
operator: "and",
|
|
6448
|
+
conditions: [this.queryWhere, softDeleteCondition]
|
|
6449
|
+
};
|
|
6450
|
+
}
|
|
6451
|
+
tryBuildQuerySelectColumns() {
|
|
6452
|
+
return this.querySelect;
|
|
6453
|
+
}
|
|
6454
|
+
tryBuildQueryOrderBy() {
|
|
6455
|
+
return this.queryOrderBy;
|
|
6456
|
+
}
|
|
6457
|
+
tryBuildFieldCondition(column, value) {
|
|
6458
|
+
if (value === null) return {
|
|
6459
|
+
type: "comparison",
|
|
6460
|
+
column,
|
|
6461
|
+
operator: "is-null"
|
|
6462
|
+
};
|
|
6463
|
+
if (value instanceof Date || typeof value !== "object") return {
|
|
6464
|
+
type: "comparison",
|
|
6465
|
+
column,
|
|
6466
|
+
operator: "=",
|
|
6467
|
+
value
|
|
6468
|
+
};
|
|
6469
|
+
if (Array.isArray(value)) return null;
|
|
6470
|
+
const clause = value;
|
|
6471
|
+
const conditions = [];
|
|
6472
|
+
for (const [operator, operand] of Object.entries(clause)) {
|
|
6473
|
+
if (operator === "equals") {
|
|
6474
|
+
conditions.push({
|
|
6475
|
+
type: "comparison",
|
|
6476
|
+
column,
|
|
6477
|
+
operator: operand === null ? "is-null" : "=",
|
|
6478
|
+
value: operand
|
|
6479
|
+
});
|
|
6480
|
+
continue;
|
|
6481
|
+
}
|
|
6482
|
+
if (operator === "not") {
|
|
6483
|
+
if (operand && typeof operand === "object" && !Array.isArray(operand)) return null;
|
|
6484
|
+
conditions.push({
|
|
6485
|
+
type: "comparison",
|
|
6486
|
+
column,
|
|
6487
|
+
operator: operand === null ? "is-not-null" : "!=",
|
|
6488
|
+
value: operand
|
|
6489
|
+
});
|
|
6490
|
+
continue;
|
|
6491
|
+
}
|
|
6492
|
+
if (operator === "in" || operator === "notIn") {
|
|
6493
|
+
if (!Array.isArray(operand)) return null;
|
|
6494
|
+
conditions.push({
|
|
6495
|
+
type: "comparison",
|
|
6496
|
+
column,
|
|
6497
|
+
operator: operator === "in" ? "in" : "not-in",
|
|
6498
|
+
value: operand
|
|
6499
|
+
});
|
|
6500
|
+
continue;
|
|
6501
|
+
}
|
|
6502
|
+
if (operator === "gt" || operator === "gte" || operator === "lt" || operator === "lte") {
|
|
6503
|
+
const comparison = {
|
|
6504
|
+
type: "comparison",
|
|
6505
|
+
column,
|
|
6506
|
+
operator: operator === "gt" ? ">" : operator === "gte" ? ">=" : operator === "lt" ? "<" : "<=",
|
|
6507
|
+
value: operand
|
|
6508
|
+
};
|
|
6509
|
+
conditions.push(comparison);
|
|
6510
|
+
continue;
|
|
6511
|
+
}
|
|
6512
|
+
if (operator === "contains" || operator === "startsWith" || operator === "endsWith") {
|
|
6513
|
+
conditions.push({
|
|
6514
|
+
type: "comparison",
|
|
6515
|
+
column,
|
|
6516
|
+
operator: operator === "startsWith" ? "starts-with" : operator === "endsWith" ? "ends-with" : "contains",
|
|
6517
|
+
value: operand
|
|
6518
|
+
});
|
|
6519
|
+
continue;
|
|
6520
|
+
}
|
|
6521
|
+
return null;
|
|
6522
|
+
}
|
|
6523
|
+
if (conditions.length === 0) return null;
|
|
6524
|
+
return conditions.length === 1 ? conditions[0] ?? null : {
|
|
6525
|
+
type: "group",
|
|
6526
|
+
operator: "and",
|
|
6527
|
+
conditions
|
|
6528
|
+
};
|
|
6529
|
+
}
|
|
6530
|
+
tryBuildQueryCondition(where) {
|
|
6531
|
+
if (!where) return void 0;
|
|
6532
|
+
if (Array.isArray(where) || typeof where !== "object") return null;
|
|
6533
|
+
const conditions = [];
|
|
6534
|
+
for (const [key, value] of Object.entries(where)) {
|
|
6535
|
+
if (key === "AND" || key === "OR") {
|
|
6536
|
+
if (!Array.isArray(value)) return null;
|
|
6537
|
+
const nested = value.map((entry) => this.tryBuildQueryCondition(entry)).filter((entry) => entry !== void 0);
|
|
6538
|
+
if (nested.some((entry) => entry == null)) return null;
|
|
6539
|
+
if (nested.length > 0) conditions.push({
|
|
6540
|
+
type: "group",
|
|
6541
|
+
operator: key === "AND" ? "and" : "or",
|
|
6542
|
+
conditions: nested
|
|
6543
|
+
});
|
|
6544
|
+
continue;
|
|
6545
|
+
}
|
|
6546
|
+
if (key === "NOT") {
|
|
6547
|
+
const nested = Array.isArray(value) ? value.map((entry) => this.tryBuildQueryCondition(entry)).filter((entry) => entry !== void 0) : [this.tryBuildQueryCondition(value)].filter((entry) => entry !== void 0);
|
|
6548
|
+
if (nested.some((entry) => entry == null)) return null;
|
|
6549
|
+
if (nested.length === 0) continue;
|
|
6550
|
+
conditions.push({
|
|
6551
|
+
type: "not",
|
|
6552
|
+
condition: nested.length === 1 ? nested[0] : {
|
|
6553
|
+
type: "group",
|
|
6554
|
+
operator: "and",
|
|
6555
|
+
conditions: nested
|
|
6556
|
+
}
|
|
6557
|
+
});
|
|
6558
|
+
continue;
|
|
6559
|
+
}
|
|
6560
|
+
const condition = this.tryBuildFieldCondition(key, value);
|
|
6561
|
+
if (!condition) return null;
|
|
6562
|
+
conditions.push(condition);
|
|
6563
|
+
}
|
|
6564
|
+
if (conditions.length === 0) return void 0;
|
|
6565
|
+
return conditions.length === 1 ? conditions[0] ?? void 0 : {
|
|
6566
|
+
type: "group",
|
|
6567
|
+
operator: "and",
|
|
6568
|
+
conditions
|
|
6569
|
+
};
|
|
6570
|
+
}
|
|
6571
|
+
tryBuildSelectSpec(where, softDeleteOnly = false) {
|
|
6572
|
+
const columns = this.tryBuildQuerySelectColumns();
|
|
6573
|
+
const orderBy = this.tryBuildQueryOrderBy();
|
|
6574
|
+
const condition = this.buildQueryWhereCondition(softDeleteOnly);
|
|
6575
|
+
if (columns === null || orderBy === null || condition === null) return null;
|
|
6576
|
+
return {
|
|
6577
|
+
target: this.buildQueryTarget(),
|
|
6578
|
+
columns,
|
|
6579
|
+
where: condition,
|
|
6580
|
+
orderBy,
|
|
6581
|
+
limit: this.limitValue,
|
|
6582
|
+
offset: this.offsetValue,
|
|
6583
|
+
relationLoads: this.queryRelationLoads
|
|
6584
|
+
};
|
|
6585
|
+
}
|
|
6586
|
+
tryBuildAggregateSpec() {
|
|
6587
|
+
const condition = this.buildQueryWhereCondition(false);
|
|
6588
|
+
if (condition === null) return null;
|
|
6589
|
+
return {
|
|
6590
|
+
target: this.buildQueryTarget(),
|
|
6591
|
+
where: condition,
|
|
6592
|
+
aggregate: { type: "count" }
|
|
6593
|
+
};
|
|
6594
|
+
}
|
|
6595
|
+
requireAdapter() {
|
|
6596
|
+
if (!this.adapter) throw new UnsupportedAdapterFeatureException("Query execution requires a configured database adapter.", {
|
|
6597
|
+
operation: "query.execute",
|
|
6598
|
+
model: this.model.name,
|
|
6599
|
+
meta: { feature: "adapter" }
|
|
6600
|
+
});
|
|
6601
|
+
return this.adapter;
|
|
6602
|
+
}
|
|
6603
|
+
async executeReadRows(whereOverride, useWhereOverride = false) {
|
|
6604
|
+
const adapter = this.requireAdapter();
|
|
6605
|
+
const spec = this.tryBuildSelectSpec(useWhereOverride ? whereOverride : this.buildWhere(), useWhereOverride);
|
|
6606
|
+
if (!spec) throw new UnsupportedAdapterFeatureException("Query shape could not be compiled into an Arkorm select specification.", {
|
|
6607
|
+
operation: "query.select",
|
|
6608
|
+
model: this.model.name
|
|
6609
|
+
});
|
|
6610
|
+
return await adapter.select(spec);
|
|
6611
|
+
}
|
|
6612
|
+
async executeReadRow() {
|
|
6613
|
+
const adapter = this.requireAdapter();
|
|
6614
|
+
const spec = this.tryBuildSelectSpec(this.buildWhere());
|
|
6615
|
+
if (!spec) throw new UnsupportedAdapterFeatureException("Query shape could not be compiled into an Arkorm select specification.", {
|
|
6616
|
+
operation: "query.selectOne",
|
|
6617
|
+
model: this.model.name
|
|
6618
|
+
});
|
|
6619
|
+
return await adapter.selectOne(spec);
|
|
6620
|
+
}
|
|
6621
|
+
async executeReadCount() {
|
|
6622
|
+
const adapter = this.requireAdapter();
|
|
6623
|
+
const spec = this.tryBuildAggregateSpec();
|
|
6624
|
+
if (!spec) throw new UnsupportedAdapterFeatureException("Query shape could not be compiled into an Arkorm aggregate specification.", {
|
|
6625
|
+
operation: "query.count",
|
|
6626
|
+
model: this.model.name
|
|
6627
|
+
});
|
|
6628
|
+
return await adapter.count(spec);
|
|
6629
|
+
}
|
|
6630
|
+
async executeReadExists() {
|
|
6631
|
+
const adapter = this.requireAdapter();
|
|
6632
|
+
const spec = this.tryBuildSelectSpec(this.buildWhere());
|
|
6633
|
+
if (!spec) throw new UnsupportedAdapterFeatureException("Query shape could not be compiled into an Arkorm select specification.", {
|
|
6634
|
+
operation: "query.exists",
|
|
6635
|
+
model: this.model.name
|
|
6636
|
+
});
|
|
6637
|
+
if (typeof adapter.exists === "function") return await adapter.exists({
|
|
6638
|
+
...spec,
|
|
6639
|
+
limit: 1
|
|
6640
|
+
});
|
|
6641
|
+
return await adapter.selectOne({
|
|
6642
|
+
...spec,
|
|
6643
|
+
limit: 1
|
|
6644
|
+
}) != null;
|
|
6645
|
+
}
|
|
6646
|
+
async executeInsertRow(values) {
|
|
6647
|
+
return await this.requireAdapter().insert(this.tryBuildInsertSpec(values));
|
|
6648
|
+
}
|
|
6649
|
+
async executeInsertManyRows(values, ignoreDuplicates = false) {
|
|
6650
|
+
const adapter = this.requireAdapter();
|
|
6651
|
+
if (typeof adapter.insertMany === "function") return await adapter.insertMany(ignoreDuplicates ? this.tryBuildInsertOrIgnoreManySpec(values) : this.tryBuildInsertManySpec(values));
|
|
6652
|
+
let inserted = 0;
|
|
6653
|
+
for (const value of values) try {
|
|
6654
|
+
await adapter.insert(this.tryBuildInsertSpec(value));
|
|
6655
|
+
inserted += 1;
|
|
6656
|
+
} catch (error) {
|
|
6657
|
+
if (!ignoreDuplicates) throw error;
|
|
6658
|
+
}
|
|
6659
|
+
return inserted;
|
|
6660
|
+
}
|
|
6661
|
+
async executeUpdateRow(where, values) {
|
|
6662
|
+
const adapter = this.requireAdapter();
|
|
6663
|
+
const spec = this.tryBuildUpdateSpec(where, values);
|
|
6664
|
+
if (!spec) throw new UnsupportedAdapterFeatureException("Update could not be compiled into an Arkorm update specification.", {
|
|
6665
|
+
operation: "query.update",
|
|
6666
|
+
model: this.model.name
|
|
6667
|
+
});
|
|
6668
|
+
const updated = await adapter.update(spec);
|
|
6669
|
+
if (!updated) throw new ModelNotFoundException(this.model.name, "Record not found for update operation.", { operation: "update" });
|
|
6670
|
+
return updated;
|
|
6671
|
+
}
|
|
6672
|
+
async executeUpdateManyRows(where, values) {
|
|
6673
|
+
const adapter = this.requireAdapter();
|
|
6674
|
+
const spec = this.tryBuildUpdateManySpec(where, values);
|
|
6675
|
+
if (!spec) throw new UnsupportedAdapterFeatureException("Update-many could not be compiled into an Arkorm update specification.", {
|
|
6676
|
+
operation: "query.updateMany",
|
|
6677
|
+
model: this.model.name
|
|
6678
|
+
});
|
|
6679
|
+
if (typeof adapter.updateMany === "function") return await adapter.updateMany(spec);
|
|
6680
|
+
const rows = await adapter.select({
|
|
6681
|
+
target: spec.target,
|
|
6682
|
+
where: spec.where
|
|
6683
|
+
});
|
|
6684
|
+
let updated = 0;
|
|
6685
|
+
for (const row of rows) {
|
|
6686
|
+
const rowWhere = this.tryBuildQueryCondition(row);
|
|
6687
|
+
if (!rowWhere) continue;
|
|
6688
|
+
if (await adapter.update({
|
|
6689
|
+
target: spec.target,
|
|
6690
|
+
where: rowWhere,
|
|
6691
|
+
values: spec.values
|
|
6692
|
+
})) updated += 1;
|
|
6693
|
+
}
|
|
6694
|
+
return updated;
|
|
6695
|
+
}
|
|
6696
|
+
async executeDeleteRow(where) {
|
|
6697
|
+
const adapter = this.requireAdapter();
|
|
6698
|
+
const spec = this.tryBuildDeleteSpec(where);
|
|
6699
|
+
if (!spec) throw new UnsupportedAdapterFeatureException("Delete could not be compiled into an Arkorm delete specification.", {
|
|
6700
|
+
operation: "query.delete",
|
|
6701
|
+
model: this.model.name
|
|
6702
|
+
});
|
|
6703
|
+
const deleted = await adapter.delete(spec);
|
|
6704
|
+
if (!deleted) throw new ModelNotFoundException(this.model.name, "Record not found for delete operation.", { operation: "delete" });
|
|
6705
|
+
return deleted;
|
|
6706
|
+
}
|
|
5512
6707
|
/**
|
|
5513
6708
|
* Builds the where clause for the query, taking into account soft delete
|
|
5514
6709
|
* settings if applicable.
|
|
@@ -5516,24 +6711,19 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5516
6711
|
* @returns
|
|
5517
6712
|
*/
|
|
5518
6713
|
buildWhere() {
|
|
6714
|
+
const baseWhere = this.legacyWhere ?? this.toDelegateWhere(this.queryWhere);
|
|
5519
6715
|
const softDeleteConfig = this.model.getSoftDeleteConfig();
|
|
5520
|
-
if (!softDeleteConfig.enabled) return
|
|
5521
|
-
if (this.includeTrashed) return
|
|
6716
|
+
if (!softDeleteConfig.enabled) return baseWhere;
|
|
6717
|
+
if (this.includeTrashed) return baseWhere;
|
|
5522
6718
|
const softDeleteClause = this.onlyTrashedRecords ? { [softDeleteConfig.column]: { not: null } } : { [softDeleteConfig.column]: null };
|
|
5523
|
-
if (!
|
|
5524
|
-
return { AND: [
|
|
6719
|
+
if (!baseWhere) return softDeleteClause;
|
|
6720
|
+
return { AND: [baseWhere, softDeleteClause] };
|
|
5525
6721
|
}
|
|
5526
6722
|
/**
|
|
5527
6723
|
* Builds the arguments for the findMany delegate method, including the where clause.
|
|
5528
6724
|
*
|
|
5529
6725
|
* @returns
|
|
5530
6726
|
*/
|
|
5531
|
-
buildFindArgs() {
|
|
5532
|
-
return {
|
|
5533
|
-
...this.args,
|
|
5534
|
-
where: this.buildWhere()
|
|
5535
|
-
};
|
|
5536
|
-
}
|
|
5537
6727
|
/**
|
|
5538
6728
|
* Resolves a unique where clause for update and delete operations.
|
|
5539
6729
|
*
|
|
@@ -5542,18 +6732,29 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5542
6732
|
*/
|
|
5543
6733
|
async resolveUniqueWhere(where) {
|
|
5544
6734
|
if (this.isUniqueWhere(where)) return where;
|
|
5545
|
-
const
|
|
6735
|
+
const condition = this.tryBuildQueryCondition(where);
|
|
6736
|
+
if (!condition) throw new UniqueConstraintResolutionException("Unable to resolve a unique identifier for update/delete operation from the current query shape.", {
|
|
6737
|
+
operation: "resolveUniqueWhere",
|
|
6738
|
+
model: this.model.name,
|
|
6739
|
+
meta: { where }
|
|
6740
|
+
});
|
|
6741
|
+
const row = await this.requireAdapter().selectOne({
|
|
6742
|
+
target: this.buildQueryTarget(),
|
|
6743
|
+
columns: [{ column: this.model.getPrimaryKey() }],
|
|
6744
|
+
where: condition,
|
|
6745
|
+
limit: 1
|
|
6746
|
+
});
|
|
5546
6747
|
if (!row) throw new ModelNotFoundException(this.model.name, "Record not found for update/delete operation.", {
|
|
5547
6748
|
operation: "resolveUniqueWhere",
|
|
5548
6749
|
meta: { where }
|
|
5549
6750
|
});
|
|
5550
|
-
const
|
|
5551
|
-
if (!Object.prototype.hasOwnProperty.call(
|
|
6751
|
+
const primaryKey = this.model.getPrimaryKey();
|
|
6752
|
+
if (!Object.prototype.hasOwnProperty.call(row, primaryKey)) throw new UniqueConstraintResolutionException(`Unable to resolve a unique identifier for update/delete operation. Include [${primaryKey}] in the query constraints.`, {
|
|
5552
6753
|
operation: "resolveUniqueWhere",
|
|
5553
6754
|
model: this.model.name,
|
|
5554
6755
|
meta: { where }
|
|
5555
6756
|
});
|
|
5556
|
-
return {
|
|
6757
|
+
return { [primaryKey]: row[primaryKey] };
|
|
5557
6758
|
}
|
|
5558
6759
|
/**
|
|
5559
6760
|
* Checks if the provided where clause is already a unique
|
|
@@ -5563,7 +6764,8 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5563
6764
|
* @returns
|
|
5564
6765
|
*/
|
|
5565
6766
|
isUniqueWhere(where) {
|
|
5566
|
-
|
|
6767
|
+
const primaryKey = this.model.getPrimaryKey();
|
|
6768
|
+
return Object.keys(where).length === 1 && Object.prototype.hasOwnProperty.call(where, primaryKey);
|
|
5567
6769
|
}
|
|
5568
6770
|
shuffleRows(rows) {
|
|
5569
6771
|
const shuffled = [...rows];
|
|
@@ -5603,7 +6805,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5603
6805
|
getModelId(model) {
|
|
5604
6806
|
const readable = model;
|
|
5605
6807
|
if (typeof readable.getAttribute !== "function") return null;
|
|
5606
|
-
const id = readable.getAttribute(
|
|
6808
|
+
const id = readable.getAttribute(this.model.getPrimaryKey());
|
|
5607
6809
|
if (typeof id === "number" || typeof id === "string") return id;
|
|
5608
6810
|
return null;
|
|
5609
6811
|
}
|
|
@@ -5742,8 +6944,12 @@ var Model = class Model {
|
|
|
5742
6944
|
static lifecycleStates = /* @__PURE__ */ new WeakMap();
|
|
5743
6945
|
static eventsSuppressed = 0;
|
|
5744
6946
|
static factoryClass;
|
|
6947
|
+
static adapter;
|
|
5745
6948
|
static client;
|
|
5746
6949
|
static delegate;
|
|
6950
|
+
static table;
|
|
6951
|
+
static primaryKey = "id";
|
|
6952
|
+
static columns = {};
|
|
5747
6953
|
static softDeletes = false;
|
|
5748
6954
|
static deletedAtColumn = "deletedAt";
|
|
5749
6955
|
static globalScopes = {};
|
|
@@ -5787,6 +6993,37 @@ var Model = class Model {
|
|
|
5787
6993
|
static setClient(client) {
|
|
5788
6994
|
this.client = client;
|
|
5789
6995
|
}
|
|
6996
|
+
static setAdapter(adapter) {
|
|
6997
|
+
this.adapter = adapter;
|
|
6998
|
+
}
|
|
6999
|
+
static getTable() {
|
|
7000
|
+
return this.table || this.delegate || `${str(this.name).camel().plural()}`;
|
|
7001
|
+
}
|
|
7002
|
+
static getPrimaryKey() {
|
|
7003
|
+
return this.primaryKey || "id";
|
|
7004
|
+
}
|
|
7005
|
+
static getColumnMap() {
|
|
7006
|
+
return { ...this.columns };
|
|
7007
|
+
}
|
|
7008
|
+
static getColumnName(attribute) {
|
|
7009
|
+
return this.getColumnMap()[attribute] ?? attribute;
|
|
7010
|
+
}
|
|
7011
|
+
static getModelMetadata() {
|
|
7012
|
+
return {
|
|
7013
|
+
table: this.getTable(),
|
|
7014
|
+
primaryKey: this.getPrimaryKey(),
|
|
7015
|
+
columns: this.getColumnMap(),
|
|
7016
|
+
softDelete: this.getSoftDeleteConfig()
|
|
7017
|
+
};
|
|
7018
|
+
}
|
|
7019
|
+
static getRelationMetadata(name) {
|
|
7020
|
+
const resolver = this.prototype[name];
|
|
7021
|
+
if (typeof resolver !== "function") return null;
|
|
7022
|
+
const instance = new this({});
|
|
7023
|
+
const relation = resolver.call(instance);
|
|
7024
|
+
if (!(relation instanceof Relation)) return null;
|
|
7025
|
+
return relation.getMetadata();
|
|
7026
|
+
}
|
|
5790
7027
|
static setFactory(factoryClass) {
|
|
5791
7028
|
this.factoryClass = factoryClass;
|
|
5792
7029
|
}
|
|
@@ -5960,7 +7197,7 @@ var Model = class Model {
|
|
|
5960
7197
|
*/
|
|
5961
7198
|
static getDelegate(delegate) {
|
|
5962
7199
|
ensureArkormConfigLoading();
|
|
5963
|
-
const key = delegate || this.delegate ||
|
|
7200
|
+
const key = delegate || this.delegate || this.getTable();
|
|
5964
7201
|
const candidates = [
|
|
5965
7202
|
key,
|
|
5966
7203
|
`${str(key).camel()}`,
|
|
@@ -5983,6 +7220,13 @@ var Model = class Model {
|
|
|
5983
7220
|
});
|
|
5984
7221
|
return resolved;
|
|
5985
7222
|
}
|
|
7223
|
+
static getAdapter() {
|
|
7224
|
+
ensureArkormConfigLoading();
|
|
7225
|
+
if (this.adapter) return this.adapter;
|
|
7226
|
+
const client = getActiveTransactionClient() ?? this.client ?? getRuntimePrismaClient();
|
|
7227
|
+
if (!client || typeof client !== "object") return void 0;
|
|
7228
|
+
return createPrismaCompatibilityAdapter(client);
|
|
7229
|
+
}
|
|
5986
7230
|
/**
|
|
5987
7231
|
* Get a new query builder instance for the model.
|
|
5988
7232
|
*
|
|
@@ -5991,7 +7235,8 @@ var Model = class Model {
|
|
|
5991
7235
|
*/
|
|
5992
7236
|
static query() {
|
|
5993
7237
|
Model.ensureModelBooted(this);
|
|
5994
|
-
|
|
7238
|
+
const modelStatic = this;
|
|
7239
|
+
let builder = new QueryBuilder(modelStatic, modelStatic.getAdapter());
|
|
5995
7240
|
const modelClass = this;
|
|
5996
7241
|
if (!Model.areGlobalScopesSuppressed(modelClass)) {
|
|
5997
7242
|
modelClass.ensureOwnGlobalScopes();
|
|
@@ -6142,10 +7387,11 @@ var Model = class Model {
|
|
|
6142
7387
|
* @returns
|
|
6143
7388
|
*/
|
|
6144
7389
|
async save() {
|
|
6145
|
-
const
|
|
7390
|
+
const constructor = this.constructor;
|
|
7391
|
+
const primaryKey = constructor.getPrimaryKey();
|
|
7392
|
+
const identifier = this.getAttribute(primaryKey);
|
|
6146
7393
|
const payload = this.getRawAttributes();
|
|
6147
7394
|
const previousOriginal = this.getOriginal();
|
|
6148
|
-
const constructor = this.constructor;
|
|
6149
7395
|
if (identifier == null) {
|
|
6150
7396
|
await Model.dispatchEvent(constructor, "saving", this);
|
|
6151
7397
|
await Model.dispatchEvent(constructor, "creating", this);
|
|
@@ -6159,7 +7405,7 @@ var Model = class Model {
|
|
|
6159
7405
|
}
|
|
6160
7406
|
await Model.dispatchEvent(constructor, "saving", this);
|
|
6161
7407
|
await Model.dispatchEvent(constructor, "updating", this);
|
|
6162
|
-
const model = await constructor.query().where({
|
|
7408
|
+
const model = await constructor.query().where({ [primaryKey]: identifier }).update(payload);
|
|
6163
7409
|
this.fill(model.getRawAttributes());
|
|
6164
7410
|
this.syncChanges(previousOriginal);
|
|
6165
7411
|
this.syncOriginal();
|
|
@@ -6184,21 +7430,22 @@ var Model = class Model {
|
|
|
6184
7430
|
* @returns
|
|
6185
7431
|
*/
|
|
6186
7432
|
async delete() {
|
|
6187
|
-
const identifier = this.getAttribute("id");
|
|
6188
|
-
if (identifier == null) throw new ArkormException("Cannot delete a model without an id.");
|
|
6189
|
-
const previousOriginal = this.getOriginal();
|
|
6190
7433
|
const constructor = this.constructor;
|
|
7434
|
+
const primaryKey = constructor.getPrimaryKey();
|
|
7435
|
+
const identifier = this.getAttribute(primaryKey);
|
|
7436
|
+
if (identifier == null) throw new ArkormException(primaryKey === "id" ? "Cannot delete a model without an id." : `Cannot delete a model without a [${primaryKey}] value.`);
|
|
7437
|
+
const previousOriginal = this.getOriginal();
|
|
6191
7438
|
await Model.dispatchEvent(constructor, "deleting", this);
|
|
6192
7439
|
const softDeleteConfig = constructor.getSoftDeleteConfig();
|
|
6193
7440
|
if (softDeleteConfig.enabled) {
|
|
6194
|
-
const model = await constructor.query().where({
|
|
7441
|
+
const model = await constructor.query().where({ [primaryKey]: identifier }).update({ [softDeleteConfig.column]: /* @__PURE__ */ new Date() });
|
|
6195
7442
|
this.fill(model.getRawAttributes());
|
|
6196
7443
|
this.syncChanges(previousOriginal);
|
|
6197
7444
|
this.syncOriginal();
|
|
6198
7445
|
await Model.dispatchEvent(constructor, "deleted", this);
|
|
6199
7446
|
return this;
|
|
6200
7447
|
}
|
|
6201
|
-
const deleted = await constructor.query().where({
|
|
7448
|
+
const deleted = await constructor.query().where({ [primaryKey]: identifier }).delete();
|
|
6202
7449
|
this.fill(deleted.getRawAttributes());
|
|
6203
7450
|
this.syncChanges(previousOriginal);
|
|
6204
7451
|
this.syncOriginal();
|
|
@@ -6220,13 +7467,14 @@ var Model = class Model {
|
|
|
6220
7467
|
* @returns
|
|
6221
7468
|
*/
|
|
6222
7469
|
async forceDelete() {
|
|
6223
|
-
const identifier = this.getAttribute("id");
|
|
6224
|
-
if (identifier == null) throw new ArkormException("Cannot force delete a model without an id.");
|
|
6225
|
-
const previousOriginal = this.getOriginal();
|
|
6226
7470
|
const constructor = this.constructor;
|
|
7471
|
+
const primaryKey = constructor.getPrimaryKey();
|
|
7472
|
+
const identifier = this.getAttribute(primaryKey);
|
|
7473
|
+
if (identifier == null) throw new ArkormException(primaryKey === "id" ? "Cannot force delete a model without an id." : `Cannot force delete a model without a [${primaryKey}] value.`);
|
|
7474
|
+
const previousOriginal = this.getOriginal();
|
|
6227
7475
|
await Model.dispatchEvent(constructor, "forceDeleting", this);
|
|
6228
7476
|
await Model.dispatchEvent(constructor, "deleting", this);
|
|
6229
|
-
const deleted = await constructor.query().withTrashed().where({
|
|
7477
|
+
const deleted = await constructor.query().withTrashed().where({ [primaryKey]: identifier }).delete();
|
|
6230
7478
|
this.fill(deleted.getRawAttributes());
|
|
6231
7479
|
this.syncChanges(previousOriginal);
|
|
6232
7480
|
this.syncOriginal();
|
|
@@ -6248,14 +7496,15 @@ var Model = class Model {
|
|
|
6248
7496
|
* @returns
|
|
6249
7497
|
*/
|
|
6250
7498
|
async restore() {
|
|
6251
|
-
const identifier = this.getAttribute("id");
|
|
6252
|
-
if (identifier == null) throw new ArkormException("Cannot restore a model without an id.");
|
|
6253
7499
|
const constructor = this.constructor;
|
|
7500
|
+
const primaryKey = constructor.getPrimaryKey();
|
|
7501
|
+
const identifier = this.getAttribute(primaryKey);
|
|
7502
|
+
if (identifier == null) throw new ArkormException(primaryKey === "id" ? "Cannot restore a model without an id." : `Cannot restore a model without a [${primaryKey}] value.`);
|
|
6254
7503
|
const softDeleteConfig = constructor.getSoftDeleteConfig();
|
|
6255
7504
|
if (!softDeleteConfig.enabled) return this;
|
|
6256
7505
|
const previousOriginal = this.getOriginal();
|
|
6257
7506
|
await Model.dispatchEvent(constructor, "restoring", this);
|
|
6258
|
-
const model = await constructor.query().withTrashed().where({
|
|
7507
|
+
const model = await constructor.query().withTrashed().where({ [primaryKey]: identifier }).update({ [softDeleteConfig.column]: null });
|
|
6259
7508
|
this.fill(model.getRawAttributes());
|
|
6260
7509
|
this.syncChanges(previousOriginal);
|
|
6261
7510
|
this.syncOriginal();
|
|
@@ -6367,8 +7616,9 @@ var Model = class Model {
|
|
|
6367
7616
|
is(model) {
|
|
6368
7617
|
if (!(model instanceof Model)) return false;
|
|
6369
7618
|
if (this.constructor !== model.constructor) return false;
|
|
6370
|
-
const
|
|
6371
|
-
const
|
|
7619
|
+
const primaryKey = this.constructor.getPrimaryKey();
|
|
7620
|
+
const identifier = this.getAttribute(primaryKey);
|
|
7621
|
+
const otherIdentifier = model.getAttribute(primaryKey);
|
|
6372
7622
|
if (identifier == null || otherIdentifier == null) return false;
|
|
6373
7623
|
return identifier === otherIdentifier;
|
|
6374
7624
|
}
|
|
@@ -6407,8 +7657,9 @@ var Model = class Model {
|
|
|
6407
7657
|
* @param localKey
|
|
6408
7658
|
* @returns
|
|
6409
7659
|
*/
|
|
6410
|
-
hasOne(related, foreignKey, localKey
|
|
6411
|
-
|
|
7660
|
+
hasOne(related, foreignKey, localKey) {
|
|
7661
|
+
const constructor = this.constructor;
|
|
7662
|
+
return new HasOneRelation(this, related, foreignKey, localKey ?? constructor.getPrimaryKey());
|
|
6412
7663
|
}
|
|
6413
7664
|
/**
|
|
6414
7665
|
* Define a has many relationship.
|
|
@@ -6418,8 +7669,9 @@ var Model = class Model {
|
|
|
6418
7669
|
* @param localKey
|
|
6419
7670
|
* @returns
|
|
6420
7671
|
*/
|
|
6421
|
-
hasMany(related, foreignKey, localKey
|
|
6422
|
-
|
|
7672
|
+
hasMany(related, foreignKey, localKey) {
|
|
7673
|
+
const constructor = this.constructor;
|
|
7674
|
+
return new HasManyRelation(this, related, foreignKey, localKey ?? constructor.getPrimaryKey());
|
|
6423
7675
|
}
|
|
6424
7676
|
/**
|
|
6425
7677
|
* Define a belongs to relationship.
|
|
@@ -6429,8 +7681,8 @@ var Model = class Model {
|
|
|
6429
7681
|
* @param ownerKey
|
|
6430
7682
|
* @returns
|
|
6431
7683
|
*/
|
|
6432
|
-
belongsTo(related, foreignKey, ownerKey
|
|
6433
|
-
return new BelongsToRelation(this, related, foreignKey, ownerKey);
|
|
7684
|
+
belongsTo(related, foreignKey, ownerKey) {
|
|
7685
|
+
return new BelongsToRelation(this, related, foreignKey, ownerKey ?? related.getPrimaryKey());
|
|
6434
7686
|
}
|
|
6435
7687
|
/**
|
|
6436
7688
|
* Define a belongs to many relationship.
|
|
@@ -6443,8 +7695,9 @@ var Model = class Model {
|
|
|
6443
7695
|
* @param relatedKey
|
|
6444
7696
|
* @returns
|
|
6445
7697
|
*/
|
|
6446
|
-
belongsToMany(related, throughDelegate, foreignPivotKey, relatedPivotKey, parentKey
|
|
6447
|
-
|
|
7698
|
+
belongsToMany(related, throughDelegate, foreignPivotKey, relatedPivotKey, parentKey, relatedKey) {
|
|
7699
|
+
const constructor = this.constructor;
|
|
7700
|
+
return new BelongsToManyRelation(this, related, throughDelegate, foreignPivotKey, relatedPivotKey, parentKey ?? constructor.getPrimaryKey(), relatedKey ?? related.getPrimaryKey());
|
|
6448
7701
|
}
|
|
6449
7702
|
/**
|
|
6450
7703
|
* Define a has one through relationship.
|
|
@@ -6457,8 +7710,9 @@ var Model = class Model {
|
|
|
6457
7710
|
* @param secondLocalKey
|
|
6458
7711
|
* @returns
|
|
6459
7712
|
*/
|
|
6460
|
-
hasOneThrough(related, throughDelegate, firstKey, secondKey, localKey
|
|
6461
|
-
|
|
7713
|
+
hasOneThrough(related, throughDelegate, firstKey, secondKey, localKey, secondLocalKey = "id") {
|
|
7714
|
+
const constructor = this.constructor;
|
|
7715
|
+
return new HasOneThroughRelation(this, related, throughDelegate, firstKey, secondKey, localKey ?? constructor.getPrimaryKey(), secondLocalKey);
|
|
6462
7716
|
}
|
|
6463
7717
|
/**
|
|
6464
7718
|
* Define a has many through relationship.
|
|
@@ -6471,8 +7725,9 @@ var Model = class Model {
|
|
|
6471
7725
|
* @param secondLocalKey
|
|
6472
7726
|
* @returns
|
|
6473
7727
|
*/
|
|
6474
|
-
hasManyThrough(related, throughDelegate, firstKey, secondKey, localKey
|
|
6475
|
-
|
|
7728
|
+
hasManyThrough(related, throughDelegate, firstKey, secondKey, localKey, secondLocalKey = "id") {
|
|
7729
|
+
const constructor = this.constructor;
|
|
7730
|
+
return new HasManyThroughRelation(this, related, throughDelegate, firstKey, secondKey, localKey ?? constructor.getPrimaryKey(), secondLocalKey);
|
|
6476
7731
|
}
|
|
6477
7732
|
/**
|
|
6478
7733
|
* Define a polymorphic one to one relationship.
|
|
@@ -6482,8 +7737,9 @@ var Model = class Model {
|
|
|
6482
7737
|
* @param localKey
|
|
6483
7738
|
* @returns
|
|
6484
7739
|
*/
|
|
6485
|
-
morphOne(related, morphName, localKey
|
|
6486
|
-
|
|
7740
|
+
morphOne(related, morphName, localKey) {
|
|
7741
|
+
const constructor = this.constructor;
|
|
7742
|
+
return new MorphOneRelation(this, related, morphName, localKey ?? constructor.getPrimaryKey());
|
|
6487
7743
|
}
|
|
6488
7744
|
/**
|
|
6489
7745
|
* Define a polymorphic one to many relationship.
|
|
@@ -6493,8 +7749,9 @@ var Model = class Model {
|
|
|
6493
7749
|
* @param localKey
|
|
6494
7750
|
* @returns
|
|
6495
7751
|
*/
|
|
6496
|
-
morphMany(related, morphName, localKey
|
|
6497
|
-
|
|
7752
|
+
morphMany(related, morphName, localKey) {
|
|
7753
|
+
const constructor = this.constructor;
|
|
7754
|
+
return new MorphManyRelation(this, related, morphName, localKey ?? constructor.getPrimaryKey());
|
|
6498
7755
|
}
|
|
6499
7756
|
/**
|
|
6500
7757
|
* Define a polymorphic many to many relationship.
|
|
@@ -6507,8 +7764,9 @@ var Model = class Model {
|
|
|
6507
7764
|
* @param relatedKey
|
|
6508
7765
|
* @returns
|
|
6509
7766
|
*/
|
|
6510
|
-
morphToMany(related, throughDelegate, morphName, relatedPivotKey, parentKey
|
|
6511
|
-
|
|
7767
|
+
morphToMany(related, throughDelegate, morphName, relatedPivotKey, parentKey, relatedKey) {
|
|
7768
|
+
const constructor = this.constructor;
|
|
7769
|
+
return new MorphToManyRelation(this, related, throughDelegate, morphName, relatedPivotKey, parentKey ?? constructor.getPrimaryKey(), relatedKey ?? related.getPrimaryKey());
|
|
6512
7770
|
}
|
|
6513
7771
|
/**
|
|
6514
7772
|
* Resolve a get mutator method for a given attribute key, if it exists.
|
|
@@ -6758,4 +8016,4 @@ var Model = class Model {
|
|
|
6758
8016
|
};
|
|
6759
8017
|
|
|
6760
8018
|
//#endregion
|
|
6761
|
-
export { ArkormCollection, ArkormException, Attribute, CliApp, EnumBuilder, ForeignKeyBuilder, InitCommand, InlineFactory, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, MigrateRollbackCommand, Migration, MigrationHistoryCommand, MissingDelegateException, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_ENUM_MEMBER_REGEX, PRISMA_ENUM_REGEX, PRISMA_MODEL_REGEX, Paginator, QueryBuilder, QueryConstraintException, RelationResolutionException, RuntimeModuleLoader, SEEDER_BRAND, SchemaBuilder, ScopeNotDefinedException, SeedCommand, Seeder, TableBuilder, URLDriver, UniqueConstraintResolutionException, UnsupportedAdapterFeatureException, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationRollbackToPrismaSchema, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, buildEnumBlock, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationIdentity, buildMigrationRunId, buildMigrationSource, buildModelBlock, buildRelationLine, computeMigrationChecksum, configureArkormRuntime, createMigrationTimestamp, createPrismaAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationAlias, deriveRelationFieldName, deriveSingularFieldName, ensureArkormConfigLoading, escapeRegex, findAppliedMigration, findEnumBlock, findModelBlock, formatDefaultValue, formatEnumDefaultValue, formatRelationAction, generateMigrationFile, getActiveTransactionClient, getDefaultStubsPath, getLastMigrationRun, getLatestAppliedMigrations, getMigrationPlan, getRuntimePaginationCurrentPageResolver, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, isMigrationApplied, isTransactionCapableClient, loadArkormConfig, markMigrationApplied, markMigrationRun, pad, readAppliedMigrationsState, removeAppliedMigration, resetArkormRuntimeForTests, resolveCast, resolveEnumName, resolveMigrationClassName, resolveMigrationStateFilePath, resolvePrismaType, runArkormTransaction, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName, writeAppliedMigrationsState };
|
|
8019
|
+
export { ArkormCollection, ArkormException, Attribute, CliApp, EnumBuilder, ForeignKeyBuilder, InitCommand, InlineFactory, KyselyDatabaseAdapter, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, MigrateRollbackCommand, Migration, MigrationHistoryCommand, MissingDelegateException, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_ENUM_MEMBER_REGEX, PRISMA_ENUM_REGEX, PRISMA_MODEL_REGEX, Paginator, PrismaDatabaseAdapter, QueryBuilder, QueryConstraintException, RelationResolutionException, RuntimeModuleLoader, SEEDER_BRAND, SchemaBuilder, ScopeNotDefinedException, SeedCommand, Seeder, TableBuilder, URLDriver, UniqueConstraintResolutionException, UnsupportedAdapterFeatureException, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationRollbackToPrismaSchema, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, buildEnumBlock, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationIdentity, buildMigrationRunId, buildMigrationSource, buildModelBlock, buildRelationLine, computeMigrationChecksum, configureArkormRuntime, createKyselyAdapter, createMigrationTimestamp, createPrismaAdapter, createPrismaCompatibilityAdapter, createPrismaDatabaseAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationAlias, deriveRelationFieldName, deriveSingularFieldName, ensureArkormConfigLoading, escapeRegex, findAppliedMigration, findEnumBlock, findModelBlock, formatDefaultValue, formatEnumDefaultValue, formatRelationAction, generateMigrationFile, getActiveTransactionClient, getDefaultStubsPath, getLastMigrationRun, getLatestAppliedMigrations, getMigrationPlan, getRuntimePaginationCurrentPageResolver, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, isMigrationApplied, isTransactionCapableClient, loadArkormConfig, markMigrationApplied, markMigrationRun, pad, readAppliedMigrationsState, removeAppliedMigration, resetArkormRuntimeForTests, resolveCast, resolveEnumName, resolveMigrationClassName, resolveMigrationStateFilePath, resolvePrismaType, runArkormTransaction, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName, writeAppliedMigrationsState };
|