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 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
- const stubPath = this.resolveStubPath(outputExt === "js" ? "model.js.stub" : "model.stub");
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