arkormx 2.0.0-next.16 → 2.0.0-next.18
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/dist/cli.mjs +696 -137
- package/dist/index.cjs +3526 -2920
- package/dist/index.d.cts +243 -2
- package/dist/index.d.mts +243 -2
- package/dist/index.mjs +3523 -2921
- package/package.json +1 -1
- package/stubs/model.js.stub +1 -1
- package/stubs/model.stub +2 -2
- package/stubs/pivot-model.stub +4 -0
package/dist/cli.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
3
|
+
import { dirname, extname, join, resolve } from "node:path";
|
|
2
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
-
import { dirname, extname, join, resolve } from "node:path";
|
|
5
6
|
import { spawnSync } from "node:child_process";
|
|
6
7
|
import { str } from "@h3ravel/support";
|
|
7
|
-
import path, { dirname as dirname$1, extname as extname$1, join as join$1, relative } from "path";
|
|
8
|
-
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";
|
|
9
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
10
8
|
import { createJiti } from "@rexxars/jiti";
|
|
11
9
|
import { pathToFileURL } from "node:url";
|
|
12
10
|
import { createRequire } from "module";
|
|
11
|
+
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";
|
|
13
12
|
import { fileURLToPath } from "url";
|
|
13
|
+
import path, { dirname as dirname$1, extname as extname$1, join as join$1, relative } from "path";
|
|
14
14
|
import { Logger } from "@h3ravel/shared";
|
|
15
15
|
import { Command, Kernel } from "@h3ravel/musket";
|
|
16
16
|
|
|
@@ -55,6 +55,171 @@ var ArkormException = class extends Error {
|
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/Exceptions/MissingDelegateException.ts
|
|
60
|
+
var MissingDelegateException = class extends ArkormException {
|
|
61
|
+
constructor(message, context = {}) {
|
|
62
|
+
super(message, {
|
|
63
|
+
code: "MISSING_DELEGATE",
|
|
64
|
+
...context
|
|
65
|
+
});
|
|
66
|
+
this.name = "MissingDelegateException";
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/Exceptions/QueryExecutionException.ts
|
|
72
|
+
var QueryExecutionException = class extends ArkormException {
|
|
73
|
+
inspection;
|
|
74
|
+
constructor(message = "Database query execution failed.", context = {}) {
|
|
75
|
+
super(message, {
|
|
76
|
+
code: "QUERY_EXECUTION_FAILED",
|
|
77
|
+
...context,
|
|
78
|
+
meta: {
|
|
79
|
+
...context.meta ?? {},
|
|
80
|
+
...context.inspection ? { inspection: context.inspection } : {}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
this.name = "QueryExecutionException";
|
|
84
|
+
this.inspection = context.inspection;
|
|
85
|
+
}
|
|
86
|
+
getInspection() {
|
|
87
|
+
return this.inspection;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/Exceptions/UnsupportedAdapterFeatureException.ts
|
|
93
|
+
var UnsupportedAdapterFeatureException = class extends ArkormException {
|
|
94
|
+
constructor(message, context = {}) {
|
|
95
|
+
super(message, {
|
|
96
|
+
code: "UNSUPPORTED_ADAPTER_FEATURE",
|
|
97
|
+
...context
|
|
98
|
+
});
|
|
99
|
+
this.name = "UnsupportedAdapterFeatureException";
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/helpers/migration-history.ts
|
|
105
|
+
const createEmptyAppliedMigrationsState = () => ({
|
|
106
|
+
version: 1,
|
|
107
|
+
migrations: [],
|
|
108
|
+
runs: []
|
|
109
|
+
});
|
|
110
|
+
const supportsDatabaseMigrationState = (adapter) => {
|
|
111
|
+
return typeof adapter?.readAppliedMigrationsState === "function" && typeof adapter?.writeAppliedMigrationsState === "function";
|
|
112
|
+
};
|
|
113
|
+
const resolveMigrationStateFilePath = (cwd, configuredPath) => {
|
|
114
|
+
if (configuredPath && configuredPath.trim().length > 0) return resolve(configuredPath);
|
|
115
|
+
return join(cwd, ".arkormx", "migrations.applied.json");
|
|
116
|
+
};
|
|
117
|
+
const buildMigrationIdentity = (filePath, className) => {
|
|
118
|
+
const fileName = filePath.split("/").pop()?.split("\\").pop() ?? filePath;
|
|
119
|
+
return `${fileName.slice(0, fileName.length - extname(fileName).length)}:${className}`;
|
|
120
|
+
};
|
|
121
|
+
const computeMigrationChecksum = (filePath) => {
|
|
122
|
+
const source = readFileSync(filePath, "utf-8");
|
|
123
|
+
return createHash("sha256").update(source).digest("hex");
|
|
124
|
+
};
|
|
125
|
+
const readAppliedMigrationsState = (stateFilePath) => {
|
|
126
|
+
if (!existsSync(stateFilePath)) return createEmptyAppliedMigrationsState();
|
|
127
|
+
try {
|
|
128
|
+
const parsed = JSON.parse(readFileSync(stateFilePath, "utf-8"));
|
|
129
|
+
if (!Array.isArray(parsed.migrations)) return createEmptyAppliedMigrationsState();
|
|
130
|
+
return {
|
|
131
|
+
version: 1,
|
|
132
|
+
migrations: parsed.migrations.filter((migration) => {
|
|
133
|
+
return typeof migration?.id === "string" && typeof migration?.file === "string" && typeof migration?.className === "string" && typeof migration?.appliedAt === "string" && (migration?.checksum === void 0 || typeof migration?.checksum === "string");
|
|
134
|
+
}),
|
|
135
|
+
runs: Array.isArray(parsed.runs) ? parsed.runs.filter((run) => {
|
|
136
|
+
return typeof run?.id === "string" && typeof run?.appliedAt === "string" && Array.isArray(run?.migrationIds) && run.migrationIds.every((item) => typeof item === "string");
|
|
137
|
+
}) : []
|
|
138
|
+
};
|
|
139
|
+
} catch {
|
|
140
|
+
return createEmptyAppliedMigrationsState();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const readAppliedMigrationsStateFromStore = async (adapter, stateFilePath) => {
|
|
144
|
+
if (supportsDatabaseMigrationState(adapter)) return await adapter.readAppliedMigrationsState();
|
|
145
|
+
return readAppliedMigrationsState(stateFilePath);
|
|
146
|
+
};
|
|
147
|
+
const writeAppliedMigrationsState = (stateFilePath, state) => {
|
|
148
|
+
const directory = dirname(stateFilePath);
|
|
149
|
+
if (!existsSync(directory)) mkdirSync(directory, { recursive: true });
|
|
150
|
+
writeFileSync(stateFilePath, JSON.stringify(state, null, 2));
|
|
151
|
+
};
|
|
152
|
+
const writeAppliedMigrationsStateToStore = async (adapter, stateFilePath, state) => {
|
|
153
|
+
if (supportsDatabaseMigrationState(adapter)) {
|
|
154
|
+
await adapter.writeAppliedMigrationsState(state);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
writeAppliedMigrationsState(stateFilePath, state);
|
|
158
|
+
};
|
|
159
|
+
const isMigrationApplied = (state, identity, checksum) => {
|
|
160
|
+
const matched = state.migrations.find((migration) => migration.id === identity);
|
|
161
|
+
if (!matched) return false;
|
|
162
|
+
if (checksum && matched.checksum) return matched.checksum === checksum;
|
|
163
|
+
if (checksum && !matched.checksum) return false;
|
|
164
|
+
return true;
|
|
165
|
+
};
|
|
166
|
+
const findAppliedMigration = (state, identity) => {
|
|
167
|
+
return state.migrations.find((migration) => migration.id === identity);
|
|
168
|
+
};
|
|
169
|
+
const markMigrationApplied = (state, entry) => {
|
|
170
|
+
const next = state.migrations.filter((migration) => migration.id !== entry.id);
|
|
171
|
+
next.push(entry);
|
|
172
|
+
return {
|
|
173
|
+
version: 1,
|
|
174
|
+
migrations: next,
|
|
175
|
+
runs: state.runs ?? []
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
const removeAppliedMigration = (state, identity) => {
|
|
179
|
+
return {
|
|
180
|
+
version: 1,
|
|
181
|
+
migrations: state.migrations.filter((migration) => migration.id !== identity),
|
|
182
|
+
runs: (state.runs ?? []).map((run) => ({
|
|
183
|
+
...run,
|
|
184
|
+
migrationIds: run.migrationIds.filter((id) => id !== identity)
|
|
185
|
+
})).filter((run) => run.migrationIds.length > 0)
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
const buildMigrationRunId = () => {
|
|
189
|
+
return `run_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
190
|
+
};
|
|
191
|
+
const markMigrationRun = (state, run) => {
|
|
192
|
+
const nextRuns = (state.runs ?? []).filter((existing) => existing.id !== run.id);
|
|
193
|
+
nextRuns.push(run);
|
|
194
|
+
return {
|
|
195
|
+
version: 1,
|
|
196
|
+
migrations: state.migrations,
|
|
197
|
+
runs: nextRuns
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
const getLastMigrationRun = (state) => {
|
|
201
|
+
const runs = state.runs ?? [];
|
|
202
|
+
if (runs.length === 0) return void 0;
|
|
203
|
+
return runs.map((run, index) => ({
|
|
204
|
+
run,
|
|
205
|
+
index
|
|
206
|
+
})).sort((left, right) => {
|
|
207
|
+
const appliedAtOrder = right.run.appliedAt.localeCompare(left.run.appliedAt);
|
|
208
|
+
if (appliedAtOrder !== 0) return appliedAtOrder;
|
|
209
|
+
return right.index - left.index;
|
|
210
|
+
})[0]?.run;
|
|
211
|
+
};
|
|
212
|
+
const getLatestAppliedMigrations = (state, steps) => {
|
|
213
|
+
return state.migrations.map((migration, index) => ({
|
|
214
|
+
migration,
|
|
215
|
+
index
|
|
216
|
+
})).sort((left, right) => {
|
|
217
|
+
const appliedAtOrder = right.migration.appliedAt.localeCompare(left.migration.appliedAt);
|
|
218
|
+
if (appliedAtOrder !== 0) return appliedAtOrder;
|
|
219
|
+
return right.index - left.index;
|
|
220
|
+
}).slice(0, Math.max(0, steps)).map((entry) => entry.migration);
|
|
221
|
+
};
|
|
222
|
+
|
|
58
223
|
//#endregion
|
|
59
224
|
//#region src/helpers/PrimaryKeyGenerationPlanner.ts
|
|
60
225
|
var PrimaryKeyGenerationPlanner = class {
|
|
@@ -1457,126 +1622,6 @@ const applyMigrationRollbackToPrismaSchema = async (migration, options = {}) =>
|
|
|
1457
1622
|
};
|
|
1458
1623
|
};
|
|
1459
1624
|
|
|
1460
|
-
//#endregion
|
|
1461
|
-
//#region src/helpers/migration-history.ts
|
|
1462
|
-
const createEmptyAppliedMigrationsState = () => ({
|
|
1463
|
-
version: 1,
|
|
1464
|
-
migrations: [],
|
|
1465
|
-
runs: []
|
|
1466
|
-
});
|
|
1467
|
-
const supportsDatabaseMigrationState = (adapter) => {
|
|
1468
|
-
return typeof adapter?.readAppliedMigrationsState === "function" && typeof adapter?.writeAppliedMigrationsState === "function";
|
|
1469
|
-
};
|
|
1470
|
-
const resolveMigrationStateFilePath = (cwd, configuredPath) => {
|
|
1471
|
-
if (configuredPath && configuredPath.trim().length > 0) return resolve(configuredPath);
|
|
1472
|
-
return join(cwd, ".arkormx", "migrations.applied.json");
|
|
1473
|
-
};
|
|
1474
|
-
const buildMigrationIdentity = (filePath, className) => {
|
|
1475
|
-
const fileName = filePath.split("/").pop()?.split("\\").pop() ?? filePath;
|
|
1476
|
-
return `${fileName.slice(0, fileName.length - extname(fileName).length)}:${className}`;
|
|
1477
|
-
};
|
|
1478
|
-
const computeMigrationChecksum = (filePath) => {
|
|
1479
|
-
const source = readFileSync(filePath, "utf-8");
|
|
1480
|
-
return createHash("sha256").update(source).digest("hex");
|
|
1481
|
-
};
|
|
1482
|
-
const readAppliedMigrationsState = (stateFilePath) => {
|
|
1483
|
-
if (!existsSync(stateFilePath)) return createEmptyAppliedMigrationsState();
|
|
1484
|
-
try {
|
|
1485
|
-
const parsed = JSON.parse(readFileSync(stateFilePath, "utf-8"));
|
|
1486
|
-
if (!Array.isArray(parsed.migrations)) return createEmptyAppliedMigrationsState();
|
|
1487
|
-
return {
|
|
1488
|
-
version: 1,
|
|
1489
|
-
migrations: parsed.migrations.filter((migration) => {
|
|
1490
|
-
return typeof migration?.id === "string" && typeof migration?.file === "string" && typeof migration?.className === "string" && typeof migration?.appliedAt === "string" && (migration?.checksum === void 0 || typeof migration?.checksum === "string");
|
|
1491
|
-
}),
|
|
1492
|
-
runs: Array.isArray(parsed.runs) ? parsed.runs.filter((run) => {
|
|
1493
|
-
return typeof run?.id === "string" && typeof run?.appliedAt === "string" && Array.isArray(run?.migrationIds) && run.migrationIds.every((item) => typeof item === "string");
|
|
1494
|
-
}) : []
|
|
1495
|
-
};
|
|
1496
|
-
} catch {
|
|
1497
|
-
return createEmptyAppliedMigrationsState();
|
|
1498
|
-
}
|
|
1499
|
-
};
|
|
1500
|
-
const readAppliedMigrationsStateFromStore = async (adapter, stateFilePath) => {
|
|
1501
|
-
if (supportsDatabaseMigrationState(adapter)) return await adapter.readAppliedMigrationsState();
|
|
1502
|
-
return readAppliedMigrationsState(stateFilePath);
|
|
1503
|
-
};
|
|
1504
|
-
const writeAppliedMigrationsState = (stateFilePath, state) => {
|
|
1505
|
-
const directory = dirname(stateFilePath);
|
|
1506
|
-
if (!existsSync(directory)) mkdirSync(directory, { recursive: true });
|
|
1507
|
-
writeFileSync(stateFilePath, JSON.stringify(state, null, 2));
|
|
1508
|
-
};
|
|
1509
|
-
const writeAppliedMigrationsStateToStore = async (adapter, stateFilePath, state) => {
|
|
1510
|
-
if (supportsDatabaseMigrationState(adapter)) {
|
|
1511
|
-
await adapter.writeAppliedMigrationsState(state);
|
|
1512
|
-
return;
|
|
1513
|
-
}
|
|
1514
|
-
writeAppliedMigrationsState(stateFilePath, state);
|
|
1515
|
-
};
|
|
1516
|
-
const isMigrationApplied = (state, identity, checksum) => {
|
|
1517
|
-
const matched = state.migrations.find((migration) => migration.id === identity);
|
|
1518
|
-
if (!matched) return false;
|
|
1519
|
-
if (checksum && matched.checksum) return matched.checksum === checksum;
|
|
1520
|
-
if (checksum && !matched.checksum) return false;
|
|
1521
|
-
return true;
|
|
1522
|
-
};
|
|
1523
|
-
const findAppliedMigration = (state, identity) => {
|
|
1524
|
-
return state.migrations.find((migration) => migration.id === identity);
|
|
1525
|
-
};
|
|
1526
|
-
const markMigrationApplied = (state, entry) => {
|
|
1527
|
-
const next = state.migrations.filter((migration) => migration.id !== entry.id);
|
|
1528
|
-
next.push(entry);
|
|
1529
|
-
return {
|
|
1530
|
-
version: 1,
|
|
1531
|
-
migrations: next,
|
|
1532
|
-
runs: state.runs ?? []
|
|
1533
|
-
};
|
|
1534
|
-
};
|
|
1535
|
-
const removeAppliedMigration = (state, identity) => {
|
|
1536
|
-
return {
|
|
1537
|
-
version: 1,
|
|
1538
|
-
migrations: state.migrations.filter((migration) => migration.id !== identity),
|
|
1539
|
-
runs: (state.runs ?? []).map((run) => ({
|
|
1540
|
-
...run,
|
|
1541
|
-
migrationIds: run.migrationIds.filter((id) => id !== identity)
|
|
1542
|
-
})).filter((run) => run.migrationIds.length > 0)
|
|
1543
|
-
};
|
|
1544
|
-
};
|
|
1545
|
-
const buildMigrationRunId = () => {
|
|
1546
|
-
return `run_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1547
|
-
};
|
|
1548
|
-
const markMigrationRun = (state, run) => {
|
|
1549
|
-
const nextRuns = (state.runs ?? []).filter((existing) => existing.id !== run.id);
|
|
1550
|
-
nextRuns.push(run);
|
|
1551
|
-
return {
|
|
1552
|
-
version: 1,
|
|
1553
|
-
migrations: state.migrations,
|
|
1554
|
-
runs: nextRuns
|
|
1555
|
-
};
|
|
1556
|
-
};
|
|
1557
|
-
const getLastMigrationRun = (state) => {
|
|
1558
|
-
const runs = state.runs ?? [];
|
|
1559
|
-
if (runs.length === 0) return void 0;
|
|
1560
|
-
return runs.map((run, index) => ({
|
|
1561
|
-
run,
|
|
1562
|
-
index
|
|
1563
|
-
})).sort((left, right) => {
|
|
1564
|
-
const appliedAtOrder = right.run.appliedAt.localeCompare(left.run.appliedAt);
|
|
1565
|
-
if (appliedAtOrder !== 0) return appliedAtOrder;
|
|
1566
|
-
return right.index - left.index;
|
|
1567
|
-
})[0]?.run;
|
|
1568
|
-
};
|
|
1569
|
-
const getLatestAppliedMigrations = (state, steps) => {
|
|
1570
|
-
return state.migrations.map((migration, index) => ({
|
|
1571
|
-
migration,
|
|
1572
|
-
index
|
|
1573
|
-
})).sort((left, right) => {
|
|
1574
|
-
const appliedAtOrder = right.migration.appliedAt.localeCompare(left.migration.appliedAt);
|
|
1575
|
-
if (appliedAtOrder !== 0) return appliedAtOrder;
|
|
1576
|
-
return right.index - left.index;
|
|
1577
|
-
}).slice(0, Math.max(0, steps)).map((entry) => entry.migration);
|
|
1578
|
-
};
|
|
1579
|
-
|
|
1580
1625
|
//#endregion
|
|
1581
1626
|
//#region src/helpers/column-mappings.ts
|
|
1582
1627
|
let cachedColumnMappingsPath;
|
|
@@ -1944,7 +1989,27 @@ let runtimeClientResolver;
|
|
|
1944
1989
|
let runtimeAdapter;
|
|
1945
1990
|
let runtimePaginationURLDriverFactory;
|
|
1946
1991
|
let runtimePaginationCurrentPageResolver;
|
|
1992
|
+
let runtimeDebugHandler;
|
|
1947
1993
|
const transactionClientStorage = new AsyncLocalStorage();
|
|
1994
|
+
const defaultDebugHandler = (event) => {
|
|
1995
|
+
const prefix = `[arkorm:${event.adapter}] ${event.operation}${event.target ? ` [${event.target}]` : ""}`;
|
|
1996
|
+
const payload = {
|
|
1997
|
+
phase: event.phase,
|
|
1998
|
+
durationMs: event.durationMs,
|
|
1999
|
+
inspection: event.inspection ?? void 0,
|
|
2000
|
+
meta: event.meta,
|
|
2001
|
+
error: event.error
|
|
2002
|
+
};
|
|
2003
|
+
if (event.phase === "error") {
|
|
2004
|
+
console.error(prefix, payload);
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
console.debug(prefix, payload);
|
|
2008
|
+
};
|
|
2009
|
+
const resolveDebugHandler = (debug) => {
|
|
2010
|
+
if (debug === true) return defaultDebugHandler;
|
|
2011
|
+
return typeof debug === "function" ? debug : void 0;
|
|
2012
|
+
};
|
|
1948
2013
|
const mergePathConfig = (paths) => {
|
|
1949
2014
|
const defaults = baseConfig.paths ?? {};
|
|
1950
2015
|
const current = userConfig.paths ?? {};
|
|
@@ -1999,12 +2064,14 @@ const configureArkormRuntime = (prisma, options = {}) => {
|
|
|
1999
2064
|
if (options.pagination !== void 0) nextConfig.pagination = options.pagination;
|
|
2000
2065
|
if (options.adapter !== void 0) nextConfig.adapter = options.adapter;
|
|
2001
2066
|
if (options.boot !== void 0) nextConfig.boot = options.boot;
|
|
2067
|
+
if (options.debug !== void 0) nextConfig.debug = options.debug;
|
|
2002
2068
|
if (options.outputExt !== void 0) nextConfig.outputExt = options.outputExt;
|
|
2003
2069
|
Object.assign(userConfig, { ...nextConfig });
|
|
2004
2070
|
runtimeClientResolver = prisma;
|
|
2005
2071
|
runtimeAdapter = options.adapter;
|
|
2006
2072
|
runtimePaginationURLDriverFactory = nextConfig.pagination?.urlDriver;
|
|
2007
2073
|
runtimePaginationCurrentPageResolver = nextConfig.pagination?.resolveCurrentPage;
|
|
2074
|
+
runtimeDebugHandler = resolveDebugHandler(nextConfig.debug);
|
|
2008
2075
|
options.boot?.({
|
|
2009
2076
|
prisma: resolveClient(prisma),
|
|
2010
2077
|
bindAdapter: bindAdapterToModels
|
|
@@ -2037,6 +2104,7 @@ const resolveAndApplyConfig = (imported) => {
|
|
|
2037
2104
|
configureArkormRuntime(config.prisma, {
|
|
2038
2105
|
adapter: config.adapter,
|
|
2039
2106
|
boot: config.boot,
|
|
2107
|
+
debug: config.debug,
|
|
2040
2108
|
features: config.features,
|
|
2041
2109
|
pagination: config.pagination,
|
|
2042
2110
|
paths: config.paths,
|
|
@@ -2095,8 +2163,497 @@ const loadArkormConfig = async () => {
|
|
|
2095
2163
|
const getDefaultStubsPath = () => {
|
|
2096
2164
|
return resolveDefaultStubsPath();
|
|
2097
2165
|
};
|
|
2166
|
+
const getRuntimeDebugHandler = () => {
|
|
2167
|
+
if (!runtimeConfigLoaded) loadRuntimeConfigSync();
|
|
2168
|
+
return runtimeDebugHandler;
|
|
2169
|
+
};
|
|
2170
|
+
const emitRuntimeDebugEvent = (event) => {
|
|
2171
|
+
getRuntimeDebugHandler()?.(event);
|
|
2172
|
+
};
|
|
2173
|
+
/**
|
|
2174
|
+
* Check if a given value is a Prisma delegate-like object
|
|
2175
|
+
* by verifying the presence of common delegate methods.
|
|
2176
|
+
*
|
|
2177
|
+
* @param value The value to check.
|
|
2178
|
+
* @returns True if the value is a Prisma delegate-like object, false otherwise.
|
|
2179
|
+
*/
|
|
2180
|
+
const isDelegateLike = (value) => {
|
|
2181
|
+
if (!value || typeof value !== "object") return false;
|
|
2182
|
+
const candidate = value;
|
|
2183
|
+
return [
|
|
2184
|
+
"findMany",
|
|
2185
|
+
"findFirst",
|
|
2186
|
+
"create",
|
|
2187
|
+
"update",
|
|
2188
|
+
"delete",
|
|
2189
|
+
"count"
|
|
2190
|
+
].every((method) => typeof candidate[method] === "function");
|
|
2191
|
+
};
|
|
2098
2192
|
loadArkormConfig();
|
|
2099
2193
|
|
|
2194
|
+
//#endregion
|
|
2195
|
+
//#region src/helpers/prisma.ts
|
|
2196
|
+
/**
|
|
2197
|
+
* Infer the Prisma delegate name for a given model name using a simple convention.
|
|
2198
|
+
*
|
|
2199
|
+
* @param modelName The name of the model to infer the delegate name for.
|
|
2200
|
+
* @returns The inferred Prisma delegate name.
|
|
2201
|
+
*/
|
|
2202
|
+
function inferDelegateName(modelName) {
|
|
2203
|
+
return `${modelName.charAt(0).toLowerCase()}${modelName.slice(1)}s`;
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
//#endregion
|
|
2207
|
+
//#region src/adapters/PrismaDatabaseAdapter.ts
|
|
2208
|
+
/**
|
|
2209
|
+
* Database adapter implementation for Prisma, allowing Arkorm to execute queries using Prisma
|
|
2210
|
+
* as the underlying query builder and executor.
|
|
2211
|
+
*
|
|
2212
|
+
* @author Legacy (3m1n3nc3)
|
|
2213
|
+
* @since 2.0.0-next.0
|
|
2214
|
+
*/
|
|
2215
|
+
var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
2216
|
+
capabilities;
|
|
2217
|
+
delegates;
|
|
2218
|
+
constructor(prisma, mapping = {}) {
|
|
2219
|
+
this.prisma = prisma;
|
|
2220
|
+
this.mapping = mapping;
|
|
2221
|
+
this.delegates = Object.entries(prisma).reduce((accumulator, [key, value]) => {
|
|
2222
|
+
if (!isDelegateLike(value)) return accumulator;
|
|
2223
|
+
accumulator[key] = value;
|
|
2224
|
+
return accumulator;
|
|
2225
|
+
}, {});
|
|
2226
|
+
this.capabilities = {
|
|
2227
|
+
transactions: this.hasTransactionSupport(prisma),
|
|
2228
|
+
insertMany: Object.values(this.delegates).some((delegate) => typeof delegate.createMany === "function"),
|
|
2229
|
+
upsert: false,
|
|
2230
|
+
updateMany: Object.values(this.delegates).some((delegate) => typeof delegate.updateMany === "function"),
|
|
2231
|
+
deleteMany: false,
|
|
2232
|
+
exists: true,
|
|
2233
|
+
relationLoads: false,
|
|
2234
|
+
relationAggregates: false,
|
|
2235
|
+
relationFilters: false,
|
|
2236
|
+
rawWhere: false,
|
|
2237
|
+
returning: false
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
hasTransactionSupport(client) {
|
|
2241
|
+
return Boolean(client) && typeof client.$transaction === "function";
|
|
2242
|
+
}
|
|
2243
|
+
normalizeCandidate(value) {
|
|
2244
|
+
return value.trim();
|
|
2245
|
+
}
|
|
2246
|
+
unique(values) {
|
|
2247
|
+
return [...new Set(values.filter(Boolean))];
|
|
2248
|
+
}
|
|
2249
|
+
runtimeModelTypeToTs(typeName, kind, enumValues) {
|
|
2250
|
+
if (kind === "enum" && enumValues && enumValues.length > 0) return enumValues.map((value) => `'${value.replace(/'/g, "\\'")}'`).join(" | ");
|
|
2251
|
+
switch (typeName) {
|
|
2252
|
+
case "Int":
|
|
2253
|
+
case "Float":
|
|
2254
|
+
case "Decimal":
|
|
2255
|
+
case "BigInt": return "number";
|
|
2256
|
+
case "Boolean": return "boolean";
|
|
2257
|
+
case "DateTime": return "Date";
|
|
2258
|
+
case "Json": return "Record<string, unknown> | unknown[]";
|
|
2259
|
+
case "Bytes": return "Uint8Array";
|
|
2260
|
+
case "String":
|
|
2261
|
+
case "UUID": return "string";
|
|
2262
|
+
default: return "string";
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
getRuntimeDataModel() {
|
|
2266
|
+
const runtimeDataModel = this.prisma._runtimeDataModel;
|
|
2267
|
+
if (runtimeDataModel && typeof runtimeDataModel === "object") return runtimeDataModel;
|
|
2268
|
+
return null;
|
|
2269
|
+
}
|
|
2270
|
+
toQuerySelect(columns) {
|
|
2271
|
+
if (!columns || columns.length === 0) return void 0;
|
|
2272
|
+
return columns.reduce((select, column) => {
|
|
2273
|
+
select[column.column] = true;
|
|
2274
|
+
return select;
|
|
2275
|
+
}, {});
|
|
2276
|
+
}
|
|
2277
|
+
toQueryOrderBy(orderBy) {
|
|
2278
|
+
if (!orderBy || orderBy.length === 0) return void 0;
|
|
2279
|
+
return orderBy.map((entry) => ({ [entry.column]: entry.direction }));
|
|
2280
|
+
}
|
|
2281
|
+
toComparisonWhere(condition) {
|
|
2282
|
+
if (condition.operator === "is-null") return { [condition.column]: null };
|
|
2283
|
+
if (condition.operator === "is-not-null") return { [condition.column]: { not: null } };
|
|
2284
|
+
if (condition.operator === "=") return { [condition.column]: condition.value };
|
|
2285
|
+
if (condition.operator === "!=") return { [condition.column]: { not: condition.value } };
|
|
2286
|
+
if (condition.operator === ">") return { [condition.column]: { gt: condition.value } };
|
|
2287
|
+
if (condition.operator === ">=") return { [condition.column]: { gte: condition.value } };
|
|
2288
|
+
if (condition.operator === "<") return { [condition.column]: { lt: condition.value } };
|
|
2289
|
+
if (condition.operator === "<=") return { [condition.column]: { lte: condition.value } };
|
|
2290
|
+
if (condition.operator === "in") return { [condition.column]: { in: Array.isArray(condition.value) ? condition.value : [condition.value] } };
|
|
2291
|
+
if (condition.operator === "not-in") return { [condition.column]: { notIn: Array.isArray(condition.value) ? condition.value : [condition.value] } };
|
|
2292
|
+
if (condition.operator === "contains") return { [condition.column]: { contains: condition.value } };
|
|
2293
|
+
if (condition.operator === "starts-with") return { [condition.column]: { startsWith: condition.value } };
|
|
2294
|
+
return { [condition.column]: { endsWith: condition.value } };
|
|
2295
|
+
}
|
|
2296
|
+
toQueryWhere(condition) {
|
|
2297
|
+
if (!condition) return void 0;
|
|
2298
|
+
if (condition.type === "comparison") return this.toComparisonWhere(condition);
|
|
2299
|
+
if (condition.type === "group") {
|
|
2300
|
+
const group = condition;
|
|
2301
|
+
const grouped = group.conditions.map((entry) => this.toQueryWhere(entry)).filter((entry) => Boolean(entry));
|
|
2302
|
+
if (grouped.length === 0) return void 0;
|
|
2303
|
+
return group.operator === "and" ? { AND: grouped } : { OR: grouped };
|
|
2304
|
+
}
|
|
2305
|
+
if (condition.type === "not") {
|
|
2306
|
+
const notCondition = condition;
|
|
2307
|
+
const nested = this.toQueryWhere(notCondition.condition);
|
|
2308
|
+
if (!nested) return void 0;
|
|
2309
|
+
return { NOT: nested };
|
|
2310
|
+
}
|
|
2311
|
+
throw new UnsupportedAdapterFeatureException("Raw where clauses are not supported by the Prisma compatibility adapter.", {
|
|
2312
|
+
operation: "adapter.where",
|
|
2313
|
+
meta: {
|
|
2314
|
+
feature: "rawWhere",
|
|
2315
|
+
sql: condition.sql
|
|
2316
|
+
}
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
2319
|
+
buildFindArgs(spec) {
|
|
2320
|
+
return {
|
|
2321
|
+
include: this.toQueryInclude(spec.relationLoads),
|
|
2322
|
+
where: this.toQueryWhere(spec.where),
|
|
2323
|
+
orderBy: this.toQueryOrderBy(spec.orderBy),
|
|
2324
|
+
select: this.toQuerySelect(spec.columns),
|
|
2325
|
+
skip: spec.offset,
|
|
2326
|
+
take: spec.limit
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
emitDebugQuery(phase, operation, target, meta, durationMs, error, inspection = null) {
|
|
2330
|
+
emitRuntimeDebugEvent({
|
|
2331
|
+
type: "query",
|
|
2332
|
+
phase,
|
|
2333
|
+
adapter: "prisma",
|
|
2334
|
+
operation,
|
|
2335
|
+
target: target.table,
|
|
2336
|
+
inspection,
|
|
2337
|
+
meta,
|
|
2338
|
+
durationMs,
|
|
2339
|
+
error
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
wrapExecutionError(error, operation, target, meta) {
|
|
2343
|
+
if (error instanceof ArkormException) return error;
|
|
2344
|
+
return new QueryExecutionException(`Failed to execute ${operation} query.`, {
|
|
2345
|
+
operation: `adapter.${operation}`,
|
|
2346
|
+
model: target.modelName,
|
|
2347
|
+
delegate: target.table,
|
|
2348
|
+
meta,
|
|
2349
|
+
cause: error
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
2352
|
+
async runWithDebug(operation, target, executor, meta) {
|
|
2353
|
+
const startedAt = Date.now();
|
|
2354
|
+
this.emitDebugQuery("before", operation, target, meta);
|
|
2355
|
+
try {
|
|
2356
|
+
const result = await executor();
|
|
2357
|
+
this.emitDebugQuery("after", operation, target, meta, Date.now() - startedAt);
|
|
2358
|
+
return result;
|
|
2359
|
+
} catch (error) {
|
|
2360
|
+
const wrapped = this.wrapExecutionError(error, operation, target, meta);
|
|
2361
|
+
this.emitDebugQuery("error", operation, target, meta, Date.now() - startedAt, wrapped);
|
|
2362
|
+
throw wrapped;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
inspectQuery(_request) {
|
|
2366
|
+
return null;
|
|
2367
|
+
}
|
|
2368
|
+
toQueryInclude(relationLoads) {
|
|
2369
|
+
if (!relationLoads || relationLoads.length === 0) return void 0;
|
|
2370
|
+
return relationLoads.reduce((include, plan) => {
|
|
2371
|
+
const nestedInclude = this.toQueryInclude(plan.relationLoads);
|
|
2372
|
+
const nestedSelect = this.toQuerySelect(plan.columns);
|
|
2373
|
+
const nestedWhere = this.toQueryWhere(plan.constraint);
|
|
2374
|
+
const nestedOrderBy = this.toQueryOrderBy(plan.orderBy);
|
|
2375
|
+
if (!nestedInclude && !nestedSelect && !nestedWhere && !nestedOrderBy && plan.offset === void 0 && plan.limit === void 0) {
|
|
2376
|
+
include[plan.relation] = true;
|
|
2377
|
+
return include;
|
|
2378
|
+
}
|
|
2379
|
+
include[plan.relation] = {
|
|
2380
|
+
where: nestedWhere,
|
|
2381
|
+
orderBy: nestedOrderBy,
|
|
2382
|
+
select: nestedSelect,
|
|
2383
|
+
include: nestedInclude,
|
|
2384
|
+
skip: plan.offset,
|
|
2385
|
+
take: plan.limit
|
|
2386
|
+
};
|
|
2387
|
+
return include;
|
|
2388
|
+
}, {});
|
|
2389
|
+
}
|
|
2390
|
+
async introspectModels(options = {}) {
|
|
2391
|
+
const runtimeDataModel = this.getRuntimeDataModel();
|
|
2392
|
+
if (!runtimeDataModel?.models) return [];
|
|
2393
|
+
const requestedTables = new Set(options.tables?.filter(Boolean) ?? []);
|
|
2394
|
+
const enums = runtimeDataModel.enums ?? {};
|
|
2395
|
+
return Object.entries(runtimeDataModel.models).flatMap(([name, model]) => {
|
|
2396
|
+
const table = model.dbName ?? `${str(name).camel().plural()}`;
|
|
2397
|
+
if (requestedTables.size > 0 && !requestedTables.has(table)) return [];
|
|
2398
|
+
return [{
|
|
2399
|
+
name,
|
|
2400
|
+
table,
|
|
2401
|
+
fields: (model.fields ?? []).filter((field) => field.kind !== "object").map((field) => {
|
|
2402
|
+
const enumValues = field.kind === "enum" ? (enums[field.type]?.values ?? []).map((value) => typeof value === "string" ? value : value.name ?? "").filter(Boolean) : null;
|
|
2403
|
+
const baseType = this.runtimeModelTypeToTs(field.type, field.kind, enumValues);
|
|
2404
|
+
return {
|
|
2405
|
+
name: field.name,
|
|
2406
|
+
type: field.isList ? `Array<${baseType}>` : baseType,
|
|
2407
|
+
nullable: field.isRequired === false
|
|
2408
|
+
};
|
|
2409
|
+
})
|
|
2410
|
+
}];
|
|
2411
|
+
});
|
|
2412
|
+
}
|
|
2413
|
+
resolveDelegate(target) {
|
|
2414
|
+
const tableName = target.table ? this.normalizeCandidate(target.table) : "";
|
|
2415
|
+
const singularTableName = tableName ? `${str(tableName).singular()}` : "";
|
|
2416
|
+
const camelTableName = tableName ? `${str(tableName).camel()}` : "";
|
|
2417
|
+
const camelSingularTableName = tableName ? `${str(tableName).camel().singular()}` : "";
|
|
2418
|
+
const candidates = this.unique([
|
|
2419
|
+
target.table ? this.mapping[target.table] : "",
|
|
2420
|
+
tableName,
|
|
2421
|
+
singularTableName ? this.mapping[singularTableName] : "",
|
|
2422
|
+
singularTableName,
|
|
2423
|
+
camelTableName ? this.mapping[camelTableName] : "",
|
|
2424
|
+
camelTableName,
|
|
2425
|
+
camelSingularTableName ? this.mapping[camelSingularTableName] : "",
|
|
2426
|
+
camelSingularTableName,
|
|
2427
|
+
target.modelName ? this.mapping[target.modelName] : "",
|
|
2428
|
+
target.modelName ? this.normalizeCandidate(target.modelName) : "",
|
|
2429
|
+
target.modelName ? inferDelegateName(target.modelName) : "",
|
|
2430
|
+
target.modelName ? this.mapping[inferDelegateName(target.modelName)] : ""
|
|
2431
|
+
]);
|
|
2432
|
+
const resolved = candidates.map((candidate) => this.delegates[candidate]).find(Boolean);
|
|
2433
|
+
if (resolved) return resolved;
|
|
2434
|
+
throw new MissingDelegateException("Prisma delegate could not be resolved for adapter target.", {
|
|
2435
|
+
operation: "getDelegate",
|
|
2436
|
+
model: target.modelName,
|
|
2437
|
+
delegate: target.table,
|
|
2438
|
+
meta: {
|
|
2439
|
+
target,
|
|
2440
|
+
candidates
|
|
2441
|
+
}
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2444
|
+
/**
|
|
2445
|
+
* @todo Implement relationLoads by performing separate queries and merging results
|
|
2446
|
+
* in-memory, since Prisma does not support nested reads with constraints, ordering, or
|
|
2447
|
+
* pagination on related models as of now.
|
|
2448
|
+
*
|
|
2449
|
+
* @param spec
|
|
2450
|
+
* @returns
|
|
2451
|
+
*/
|
|
2452
|
+
async select(spec) {
|
|
2453
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
2454
|
+
const args = this.buildFindArgs(spec);
|
|
2455
|
+
return await this.runWithDebug("select", spec.target, async () => {
|
|
2456
|
+
return await delegate.findMany(args);
|
|
2457
|
+
}, { args });
|
|
2458
|
+
}
|
|
2459
|
+
/**
|
|
2460
|
+
* Selects a single record matching the specified criteria.
|
|
2461
|
+
*
|
|
2462
|
+
* @param spec
|
|
2463
|
+
* @returns
|
|
2464
|
+
*/
|
|
2465
|
+
async selectOne(spec) {
|
|
2466
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
2467
|
+
const args = this.buildFindArgs(spec);
|
|
2468
|
+
return await this.runWithDebug("selectOne", spec.target, async () => {
|
|
2469
|
+
return await delegate.findFirst(args);
|
|
2470
|
+
}, { args });
|
|
2471
|
+
}
|
|
2472
|
+
/**
|
|
2473
|
+
* Inserts a single record into the database and returns the created record.
|
|
2474
|
+
*
|
|
2475
|
+
* @param spec
|
|
2476
|
+
* @returns
|
|
2477
|
+
*/
|
|
2478
|
+
async insert(spec) {
|
|
2479
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
2480
|
+
return await this.runWithDebug("insert", spec.target, async () => {
|
|
2481
|
+
return await delegate.create({ data: spec.values });
|
|
2482
|
+
}, { values: spec.values });
|
|
2483
|
+
}
|
|
2484
|
+
/**
|
|
2485
|
+
* Inserts multiple records into the database.
|
|
2486
|
+
*
|
|
2487
|
+
* @param spec
|
|
2488
|
+
* @returns
|
|
2489
|
+
*/
|
|
2490
|
+
async insertMany(spec) {
|
|
2491
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
2492
|
+
const meta = {
|
|
2493
|
+
values: spec.values,
|
|
2494
|
+
ignoreDuplicates: spec.ignoreDuplicates
|
|
2495
|
+
};
|
|
2496
|
+
if (typeof delegate.createMany === "function") {
|
|
2497
|
+
const result = await this.runWithDebug("insertMany", spec.target, async () => {
|
|
2498
|
+
return await delegate.createMany?.({
|
|
2499
|
+
data: spec.values,
|
|
2500
|
+
skipDuplicates: spec.ignoreDuplicates
|
|
2501
|
+
});
|
|
2502
|
+
}, meta);
|
|
2503
|
+
if (typeof result === "number") return result;
|
|
2504
|
+
return typeof result?.count === "number" ? result.count : spec.values.length;
|
|
2505
|
+
}
|
|
2506
|
+
let inserted = 0;
|
|
2507
|
+
for (const values of spec.values) try {
|
|
2508
|
+
await delegate.create({ data: values });
|
|
2509
|
+
inserted += 1;
|
|
2510
|
+
} catch (error) {
|
|
2511
|
+
if (!spec.ignoreDuplicates) throw error;
|
|
2512
|
+
}
|
|
2513
|
+
return spec.ignoreDuplicates ? inserted : spec.values.length;
|
|
2514
|
+
}
|
|
2515
|
+
/**
|
|
2516
|
+
* Updates a single record matching the specified criteria and returns the updated record.
|
|
2517
|
+
*
|
|
2518
|
+
* @param spec
|
|
2519
|
+
* @returns
|
|
2520
|
+
*/
|
|
2521
|
+
async update(spec) {
|
|
2522
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
2523
|
+
const where = this.toQueryWhere(spec.where);
|
|
2524
|
+
if (!where) return null;
|
|
2525
|
+
return await this.runWithDebug("update", spec.target, async () => {
|
|
2526
|
+
return await delegate.update({
|
|
2527
|
+
where,
|
|
2528
|
+
data: spec.values
|
|
2529
|
+
});
|
|
2530
|
+
}, {
|
|
2531
|
+
where,
|
|
2532
|
+
values: spec.values
|
|
2533
|
+
});
|
|
2534
|
+
}
|
|
2535
|
+
/**
|
|
2536
|
+
* Updates multiple records matching the specified criteria.
|
|
2537
|
+
*
|
|
2538
|
+
* @param spec
|
|
2539
|
+
* @returns
|
|
2540
|
+
*/
|
|
2541
|
+
async updateMany(spec) {
|
|
2542
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
2543
|
+
const where = this.toQueryWhere(spec.where);
|
|
2544
|
+
const meta = {
|
|
2545
|
+
where,
|
|
2546
|
+
values: spec.values
|
|
2547
|
+
};
|
|
2548
|
+
if (typeof delegate.updateMany === "function") {
|
|
2549
|
+
const result = await this.runWithDebug("updateMany", spec.target, async () => {
|
|
2550
|
+
return await delegate.updateMany?.({
|
|
2551
|
+
where,
|
|
2552
|
+
data: spec.values
|
|
2553
|
+
});
|
|
2554
|
+
}, meta);
|
|
2555
|
+
if (typeof result === "number") return result;
|
|
2556
|
+
return typeof result?.count === "number" ? result.count : 0;
|
|
2557
|
+
}
|
|
2558
|
+
const rows = await delegate.findMany({ where });
|
|
2559
|
+
await Promise.all(rows.map(async (row) => {
|
|
2560
|
+
await delegate.update({
|
|
2561
|
+
where: row,
|
|
2562
|
+
data: spec.values
|
|
2563
|
+
});
|
|
2564
|
+
}));
|
|
2565
|
+
return rows.length;
|
|
2566
|
+
}
|
|
2567
|
+
/**
|
|
2568
|
+
* Deletes a single record matching the specified criteria and returns the deleted record.
|
|
2569
|
+
*
|
|
2570
|
+
* @param spec
|
|
2571
|
+
* @returns
|
|
2572
|
+
*/
|
|
2573
|
+
async delete(spec) {
|
|
2574
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
2575
|
+
const where = this.toQueryWhere(spec.where);
|
|
2576
|
+
if (!where) return null;
|
|
2577
|
+
return await this.runWithDebug("delete", spec.target, async () => {
|
|
2578
|
+
return await delegate.delete({ where });
|
|
2579
|
+
}, { where });
|
|
2580
|
+
}
|
|
2581
|
+
/**
|
|
2582
|
+
* Deletes multiple records matching the specified criteria.
|
|
2583
|
+
*
|
|
2584
|
+
* @param spec
|
|
2585
|
+
* @returns
|
|
2586
|
+
*/
|
|
2587
|
+
async deleteMany(spec) {
|
|
2588
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
2589
|
+
const where = this.toQueryWhere(spec.where);
|
|
2590
|
+
const rows = await this.runWithDebug("deleteMany", spec.target, async () => {
|
|
2591
|
+
return await delegate.findMany({ where });
|
|
2592
|
+
}, { where });
|
|
2593
|
+
await Promise.all(rows.map(async (row) => {
|
|
2594
|
+
await delegate.delete({ where: row });
|
|
2595
|
+
}));
|
|
2596
|
+
return rows.length;
|
|
2597
|
+
}
|
|
2598
|
+
/**
|
|
2599
|
+
* Counts the number of records matching the specified criteria.
|
|
2600
|
+
*
|
|
2601
|
+
* @param spec
|
|
2602
|
+
* @returns
|
|
2603
|
+
*/
|
|
2604
|
+
async count(spec) {
|
|
2605
|
+
const delegate = this.resolveDelegate(spec.target);
|
|
2606
|
+
const where = this.toQueryWhere(spec.where);
|
|
2607
|
+
return await this.runWithDebug("count", spec.target, async () => {
|
|
2608
|
+
return await delegate.count({ where });
|
|
2609
|
+
}, { where });
|
|
2610
|
+
}
|
|
2611
|
+
/**
|
|
2612
|
+
* Checks for the existence of records matching the specified criteria.
|
|
2613
|
+
*
|
|
2614
|
+
* @param spec
|
|
2615
|
+
* @returns
|
|
2616
|
+
*/
|
|
2617
|
+
async exists(spec) {
|
|
2618
|
+
return await this.selectOne({
|
|
2619
|
+
...spec,
|
|
2620
|
+
limit: 1
|
|
2621
|
+
}) != null;
|
|
2622
|
+
}
|
|
2623
|
+
/**
|
|
2624
|
+
* Loads related models for a batch of parent records based on the specified relation load plans.
|
|
2625
|
+
*
|
|
2626
|
+
* @param _spec
|
|
2627
|
+
*/
|
|
2628
|
+
async loadRelations(_spec) {
|
|
2629
|
+
throw new UnsupportedAdapterFeatureException("Relation batch loading is not supported by the Prisma compatibility adapter yet.", {
|
|
2630
|
+
operation: "adapter.loadRelations",
|
|
2631
|
+
meta: { feature: "relationLoads" }
|
|
2632
|
+
});
|
|
2633
|
+
}
|
|
2634
|
+
/**
|
|
2635
|
+
* Executes a series of database operations within a transaction.
|
|
2636
|
+
* If the underlying Prisma client does not support transactions, an exception is thrown.
|
|
2637
|
+
*
|
|
2638
|
+
* @param callback
|
|
2639
|
+
* @param context
|
|
2640
|
+
* @returns
|
|
2641
|
+
*/
|
|
2642
|
+
async transaction(callback, context = {}) {
|
|
2643
|
+
if (!this.hasTransactionSupport(this.prisma)) throw new UnsupportedAdapterFeatureException("Transactions are not supported by the Prisma compatibility adapter.", {
|
|
2644
|
+
operation: "adapter.transaction",
|
|
2645
|
+
meta: { feature: "transactions" }
|
|
2646
|
+
});
|
|
2647
|
+
return await this.prisma.$transaction(async (transactionClient) => {
|
|
2648
|
+
return await callback(new PrismaDatabaseAdapter(transactionClient, this.mapping));
|
|
2649
|
+
}, {
|
|
2650
|
+
isolationLevel: context.isolationLevel,
|
|
2651
|
+
maxWait: context.maxWait,
|
|
2652
|
+
timeout: context.timeout
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
};
|
|
2656
|
+
|
|
2100
2657
|
//#endregion
|
|
2101
2658
|
//#region src/cli/CliApp.ts
|
|
2102
2659
|
/**
|
|
@@ -2118,6 +2675,9 @@ var CliApp = class {
|
|
|
2118
2675
|
* @returns The entire configuration object or the value of the specified key
|
|
2119
2676
|
*/
|
|
2120
2677
|
getConfig = getUserConfig;
|
|
2678
|
+
isUsingPrismaAdapter() {
|
|
2679
|
+
return this.getConfig("adapter") instanceof PrismaDatabaseAdapter;
|
|
2680
|
+
}
|
|
2121
2681
|
/**
|
|
2122
2682
|
* Utility to ensure directory exists
|
|
2123
2683
|
*
|
|
@@ -2330,14 +2890,16 @@ var CliApp = class {
|
|
|
2330
2890
|
const factoryName = `${baseName}Factory`;
|
|
2331
2891
|
const factoryPath = join$1(this.resolveConfigPath("factories", join$1(process.cwd(), "database", "factories")), `${factoryName}.${outputExt}`);
|
|
2332
2892
|
const factoryImportPath = `./${relative(dirname$1(outputPath), factoryPath).replace(/\\/g, "/").replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i, "")}${outputExt === "js" ? ".js" : ""}`;
|
|
2333
|
-
|
|
2893
|
+
let stubPath;
|
|
2894
|
+
if (options.pivot) stubPath = this.resolveStubPath("pivot-model.stub");
|
|
2895
|
+
else stubPath = this.resolveStubPath(outputExt === "js" ? "model.js.stub" : "model.stub");
|
|
2334
2896
|
const modelPath = this.generateFile(stubPath, outputPath, {
|
|
2335
2897
|
ModelName: modelName,
|
|
2336
2898
|
DelegateName: delegateName,
|
|
2337
2899
|
FactoryImport: shouldBuildFactory ? `import { ${factoryName} } from '${factoryImportPath}'\n` : "",
|
|
2338
2900
|
FactoryLink: shouldBuildFactory ? outputExt === "js" ? `\n static factoryClass = ${factoryName}` : `\n protected static override factoryClass = ${factoryName}` : ""
|
|
2339
2901
|
}, options);
|
|
2340
|
-
const prisma = this.ensurePrismaModelEntry(modelName, delegateName);
|
|
2902
|
+
const prisma = this.isUsingPrismaAdapter() ? this.ensurePrismaModelEntry(modelName, delegateName) : void 0;
|
|
2341
2903
|
const created = {
|
|
2342
2904
|
model: {
|
|
2343
2905
|
name: modelName,
|
|
@@ -2367,10 +2929,7 @@ var CliApp = class {
|
|
|
2367
2929
|
*/
|
|
2368
2930
|
ensurePrismaModelEntry(modelName, delegateName) {
|
|
2369
2931
|
const schemaPath = join$1(process.cwd(), "prisma", "schema.prisma");
|
|
2370
|
-
if (!existsSync$1(schemaPath)) return
|
|
2371
|
-
path: schemaPath,
|
|
2372
|
-
updated: false
|
|
2373
|
-
};
|
|
2932
|
+
if (!existsSync$1(schemaPath)) return void 0;
|
|
2374
2933
|
const source = readFileSync$1(schemaPath, "utf-8");
|
|
2375
2934
|
const existingByTable = findModelBlock(source, delegateName);
|
|
2376
2935
|
const existingByName = new RegExp(`model\\s+${modelName}\\s*\\{`, "m").test(source);
|
|
@@ -2983,6 +3542,7 @@ var MakeModelCommand = class extends Command {
|
|
|
2983
3542
|
{--factory : Create and link a factory}
|
|
2984
3543
|
{--seeder : Create a seeder}
|
|
2985
3544
|
{--migration : Create a migration}
|
|
3545
|
+
{--p|pivot : Indicate the required model is an intermediate pivot model}
|
|
2986
3546
|
{--all : Create and link factory, seeder, and migration}
|
|
2987
3547
|
`;
|
|
2988
3548
|
description = "Create a new model and optional linked resources";
|
|
@@ -2996,14 +3556,13 @@ var MakeModelCommand = class extends Command {
|
|
|
2996
3556
|
const name = this.argument("name");
|
|
2997
3557
|
if (!name) return void this.error("Error: Name argument is required.");
|
|
2998
3558
|
const created = this.app.makeModel(name, this.options());
|
|
3559
|
+
const createdFiles = [["Model", created.model.path]];
|
|
3560
|
+
if (created.prisma) createdFiles.push([`Prisma schema ${created.prisma.updated ? "(updated)" : "(already up to date)"}`, created.prisma.path]);
|
|
3561
|
+
if (created.factory) createdFiles.push(["Factory", created.factory.path]);
|
|
3562
|
+
if (created.seeder) createdFiles.push(["Seeder", created.seeder.path]);
|
|
3563
|
+
if (created.migration) createdFiles.push(["Migration", created.migration.path]);
|
|
2999
3564
|
this.success("Created files:");
|
|
3000
|
-
[
|
|
3001
|
-
["Model", created.model.path],
|
|
3002
|
-
[`Prisma schema ${created.prisma.updated ? "(updated)" : "(already up to date)"}`, created.prisma.path],
|
|
3003
|
-
created.factory ? ["Factory", created.factory.path] : "",
|
|
3004
|
-
created.seeder ? ["Seeder", created.seeder.path] : "",
|
|
3005
|
-
created.migration ? ["Migration", created.migration.path] : ""
|
|
3006
|
-
].filter(Boolean).map(([name, path]) => this.success(this.app.splitLogger(name, path)));
|
|
3565
|
+
createdFiles.map(([fileType, path]) => this.success(this.app.splitLogger(fileType, path)));
|
|
3007
3566
|
}
|
|
3008
3567
|
};
|
|
3009
3568
|
|