arkormx 0.2.4 → 0.2.6
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 +380 -78
- package/dist/index.cjs +328 -12
- package/dist/index.d.cts +74 -1
- package/dist/index.d.mts +74 -1
- package/dist/index.mjs +379 -79
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import { createRequire } from "module";
|
|
4
|
-
import { existsSync as existsSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, writeFileSync as writeFileSync$1 } from "node:fs";
|
|
5
|
-
import { join as join$1, resolve } from "node:path";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, extname, join, resolve } from "node:path";
|
|
6
3
|
import { spawnSync } from "node:child_process";
|
|
7
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";
|
|
8
7
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
8
|
+
import { createRequire } from "module";
|
|
9
9
|
import { Logger } from "@h3ravel/shared";
|
|
10
10
|
import { Command } from "@h3ravel/musket";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
11
12
|
import { pathToFileURL as pathToFileURL$1 } from "node:url";
|
|
12
13
|
import { Collection } from "@h3ravel/collect.js";
|
|
13
14
|
|
|
@@ -1069,14 +1070,14 @@ const generateMigrationFile = (name, options = {}) => {
|
|
|
1069
1070
|
const fileSlug = toMigrationFileSlug(name);
|
|
1070
1071
|
const className = resolveMigrationClassName(name);
|
|
1071
1072
|
const extension = options.extension ?? "ts";
|
|
1072
|
-
const directory = options.directory ?? join
|
|
1073
|
+
const directory = options.directory ?? join(process.cwd(), "database", "migrations");
|
|
1073
1074
|
const fileName = `${timestamp}_${fileSlug}.${extension}`;
|
|
1074
|
-
const filePath = join
|
|
1075
|
+
const filePath = join(directory, fileName);
|
|
1075
1076
|
const content = buildMigrationSource(className, extension);
|
|
1076
1077
|
if (options.write ?? true) {
|
|
1077
|
-
if (!existsSync
|
|
1078
|
-
if (existsSync
|
|
1079
|
-
writeFileSync
|
|
1078
|
+
if (!existsSync(directory)) mkdirSync(directory, { recursive: true });
|
|
1079
|
+
if (existsSync(filePath)) throw new ArkormException(`Migration file already exists: ${filePath}`);
|
|
1080
|
+
writeFileSync(filePath, content);
|
|
1080
1081
|
}
|
|
1081
1082
|
return {
|
|
1082
1083
|
fileName,
|
|
@@ -1109,12 +1110,32 @@ const getMigrationPlan = async (migration, direction = "up") => {
|
|
|
1109
1110
|
* @returns A promise that resolves to an object containing the updated schema, schema path, and list of operations applied.
|
|
1110
1111
|
*/
|
|
1111
1112
|
const applyMigrationToPrismaSchema = async (migration, options = {}) => {
|
|
1112
|
-
const schemaPath = options.schemaPath ?? join
|
|
1113
|
-
if (!existsSync
|
|
1114
|
-
const source = readFileSync
|
|
1113
|
+
const schemaPath = options.schemaPath ?? join(process.cwd(), "prisma", "schema.prisma");
|
|
1114
|
+
if (!existsSync(schemaPath)) throw new ArkormException(`Prisma schema file not found: ${schemaPath}`);
|
|
1115
|
+
const source = readFileSync(schemaPath, "utf-8");
|
|
1115
1116
|
const operations = await getMigrationPlan(migration, "up");
|
|
1116
1117
|
const schema = applyOperationsToPrismaSchema(source, operations);
|
|
1117
|
-
if (options.write ?? true) writeFileSync
|
|
1118
|
+
if (options.write ?? true) writeFileSync(schemaPath, schema);
|
|
1119
|
+
return {
|
|
1120
|
+
schema,
|
|
1121
|
+
schemaPath,
|
|
1122
|
+
operations
|
|
1123
|
+
};
|
|
1124
|
+
};
|
|
1125
|
+
/**
|
|
1126
|
+
* Apply the rollback (down) operations defined in a migration to a Prisma schema file.
|
|
1127
|
+
*
|
|
1128
|
+
* @param migration The migration class or instance to rollback.
|
|
1129
|
+
* @param options Options for applying the rollback, including schema path and write flag.
|
|
1130
|
+
* @returns A promise that resolves to an object containing the updated schema, schema path, and rollback operations applied.
|
|
1131
|
+
*/
|
|
1132
|
+
const applyMigrationRollbackToPrismaSchema = async (migration, options = {}) => {
|
|
1133
|
+
const schemaPath = options.schemaPath ?? join(process.cwd(), "prisma", "schema.prisma");
|
|
1134
|
+
if (!existsSync(schemaPath)) throw new ArkormException(`Prisma schema file not found: ${schemaPath}`);
|
|
1135
|
+
const source = readFileSync(schemaPath, "utf-8");
|
|
1136
|
+
const operations = await getMigrationPlan(migration, "down");
|
|
1137
|
+
const schema = applyOperationsToPrismaSchema(source, operations);
|
|
1138
|
+
if (options.write ?? true) writeFileSync(schemaPath, schema);
|
|
1118
1139
|
return {
|
|
1119
1140
|
schema,
|
|
1120
1141
|
schemaPath,
|
|
@@ -1133,7 +1154,7 @@ const applyMigrationToPrismaSchema = async (migration, options = {}) => {
|
|
|
1133
1154
|
const runMigrationWithPrisma = async (migration, options = {}) => {
|
|
1134
1155
|
const cwd = options.cwd ?? process.cwd();
|
|
1135
1156
|
const applied = await applyMigrationToPrismaSchema(migration, {
|
|
1136
|
-
schemaPath: options.schemaPath ?? join
|
|
1157
|
+
schemaPath: options.schemaPath ?? join(cwd, "prisma", "schema.prisma"),
|
|
1137
1158
|
write: options.write
|
|
1138
1159
|
});
|
|
1139
1160
|
const shouldGenerate = options.runGenerate ?? true;
|
|
@@ -1160,7 +1181,7 @@ const resolveDefaultStubsPath = () => {
|
|
|
1160
1181
|
while (true) {
|
|
1161
1182
|
const packageJsonPath = path.join(current, "package.json");
|
|
1162
1183
|
const stubsPath = path.join(current, "stubs");
|
|
1163
|
-
if (existsSync(packageJsonPath) && existsSync(stubsPath)) return stubsPath;
|
|
1184
|
+
if (existsSync$1(packageJsonPath) && existsSync$1(stubsPath)) return stubsPath;
|
|
1164
1185
|
const parent = path.dirname(current);
|
|
1165
1186
|
if (parent === current) break;
|
|
1166
1187
|
current = parent;
|
|
@@ -1295,7 +1316,7 @@ const loadRuntimeConfigSync = () => {
|
|
|
1295
1316
|
const require = createRequire(import.meta.url);
|
|
1296
1317
|
const syncConfigPaths = [path.join(process.cwd(), "arkormx.config.cjs")];
|
|
1297
1318
|
for (const configPath of syncConfigPaths) {
|
|
1298
|
-
if (!existsSync(configPath)) continue;
|
|
1319
|
+
if (!existsSync$1(configPath)) continue;
|
|
1299
1320
|
try {
|
|
1300
1321
|
resolveAndApplyConfig(require(configPath));
|
|
1301
1322
|
return true;
|
|
@@ -1317,7 +1338,7 @@ const loadArkormConfig = async () => {
|
|
|
1317
1338
|
runtimeConfigLoadingPromise = (async () => {
|
|
1318
1339
|
const configPaths = [path.join(process.cwd(), "arkormx.config.js"), path.join(process.cwd(), "arkormx.config.ts")];
|
|
1319
1340
|
for (const configPath of configPaths) {
|
|
1320
|
-
if (!existsSync(configPath)) continue;
|
|
1341
|
+
if (!existsSync$1(configPath)) continue;
|
|
1321
1342
|
try {
|
|
1322
1343
|
resolveAndApplyConfig(await importConfigFile(configPath));
|
|
1323
1344
|
return;
|
|
@@ -1411,8 +1432,8 @@ var CliApp = class {
|
|
|
1411
1432
|
* @param filePath
|
|
1412
1433
|
*/
|
|
1413
1434
|
ensureDirectory(filePath) {
|
|
1414
|
-
const dir = dirname(filePath);
|
|
1415
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
1435
|
+
const dir = dirname$1(filePath);
|
|
1436
|
+
if (!existsSync$1(dir)) mkdirSync$1(dir, { recursive: true });
|
|
1416
1437
|
}
|
|
1417
1438
|
/**
|
|
1418
1439
|
* Convert absolute paths under current working directory into relative display paths.
|
|
@@ -1461,13 +1482,13 @@ var CliApp = class {
|
|
|
1461
1482
|
* @returns
|
|
1462
1483
|
*/
|
|
1463
1484
|
resolveRuntimeDirectoryPath(directoryPath) {
|
|
1464
|
-
if (existsSync(directoryPath)) return directoryPath;
|
|
1485
|
+
if (existsSync$1(directoryPath)) return directoryPath;
|
|
1465
1486
|
const { buildOutput } = this.getConfig("paths") || {};
|
|
1466
1487
|
if (typeof buildOutput !== "string" || buildOutput.trim().length === 0) return directoryPath;
|
|
1467
1488
|
const relativeSource = relative(process.cwd(), directoryPath);
|
|
1468
1489
|
if (!relativeSource || relativeSource.startsWith("..")) return directoryPath;
|
|
1469
|
-
const mappedDirectory = join(buildOutput, relativeSource);
|
|
1470
|
-
return existsSync(mappedDirectory) ? mappedDirectory : directoryPath;
|
|
1490
|
+
const mappedDirectory = join$1(buildOutput, relativeSource);
|
|
1491
|
+
return existsSync$1(mappedDirectory) ? mappedDirectory : directoryPath;
|
|
1471
1492
|
}
|
|
1472
1493
|
/**
|
|
1473
1494
|
* Resolve a script file path for runtime execution.
|
|
@@ -1478,7 +1499,7 @@ var CliApp = class {
|
|
|
1478
1499
|
* @returns
|
|
1479
1500
|
*/
|
|
1480
1501
|
resolveRuntimeScriptPath(filePath) {
|
|
1481
|
-
const extension = extname(filePath).toLowerCase();
|
|
1502
|
+
const extension = extname$1(filePath).toLowerCase();
|
|
1482
1503
|
const isTsFile = extension === ".ts" || extension === ".mts" || extension === ".cts";
|
|
1483
1504
|
const candidates = [];
|
|
1484
1505
|
if (isTsFile) {
|
|
@@ -1489,15 +1510,15 @@ var CliApp = class {
|
|
|
1489
1510
|
if (typeof buildOutput === "string" && buildOutput.trim().length > 0) {
|
|
1490
1511
|
const relativeSource = relative(process.cwd(), filePath);
|
|
1491
1512
|
if (relativeSource && !relativeSource.startsWith("..")) {
|
|
1492
|
-
const mappedFile = join(buildOutput, relativeSource);
|
|
1493
|
-
const mappedExtension = extname(mappedFile).toLowerCase();
|
|
1513
|
+
const mappedFile = join$1(buildOutput, relativeSource);
|
|
1514
|
+
const mappedExtension = extname$1(mappedFile).toLowerCase();
|
|
1494
1515
|
if (mappedExtension === ".ts" || mappedExtension === ".mts" || mappedExtension === ".cts") {
|
|
1495
1516
|
const mappedBase = mappedFile.slice(0, -mappedExtension.length);
|
|
1496
1517
|
candidates.push(`${mappedBase}.js`, `${mappedBase}.cjs`, `${mappedBase}.mjs`);
|
|
1497
1518
|
} else candidates.push(mappedFile);
|
|
1498
1519
|
}
|
|
1499
1520
|
}
|
|
1500
|
-
const runtimeMatch = candidates.find((path) => existsSync(path));
|
|
1521
|
+
const runtimeMatch = candidates.find((path) => existsSync$1(path));
|
|
1501
1522
|
if (runtimeMatch) return runtimeMatch;
|
|
1502
1523
|
return filePath;
|
|
1503
1524
|
}
|
|
@@ -1509,14 +1530,14 @@ var CliApp = class {
|
|
|
1509
1530
|
* @param replacements
|
|
1510
1531
|
*/
|
|
1511
1532
|
generateFile(stubPath, outputPath, replacements, options) {
|
|
1512
|
-
if (existsSync(outputPath) && !options?.force) {
|
|
1533
|
+
if (existsSync$1(outputPath) && !options?.force) {
|
|
1513
1534
|
this.command.error(`Error: ${this.formatPathForLog(outputPath)} already exists.`);
|
|
1514
1535
|
process.exit(1);
|
|
1515
|
-
} else if (existsSync(outputPath) && options?.force) rmSync(outputPath);
|
|
1516
|
-
let content = readFileSync(stubPath, "utf-8");
|
|
1536
|
+
} else if (existsSync$1(outputPath) && options?.force) rmSync$1(outputPath);
|
|
1537
|
+
let content = readFileSync$1(stubPath, "utf-8");
|
|
1517
1538
|
for (const [key, value] of Object.entries(replacements)) content = content.replace(new RegExp(`{{${key}}}`, "g"), value);
|
|
1518
1539
|
this.ensureDirectory(outputPath);
|
|
1519
|
-
writeFileSync(outputPath, content);
|
|
1540
|
+
writeFileSync$1(outputPath, content);
|
|
1520
1541
|
return outputPath;
|
|
1521
1542
|
}
|
|
1522
1543
|
/**
|
|
@@ -1538,7 +1559,7 @@ var CliApp = class {
|
|
|
1538
1559
|
* @returns
|
|
1539
1560
|
*/
|
|
1540
1561
|
resolveStubPath(stubName) {
|
|
1541
|
-
return join(this.resolveConfigPath("stubs", getDefaultStubsPath()), stubName);
|
|
1562
|
+
return join$1(this.resolveConfigPath("stubs", getDefaultStubsPath()), stubName);
|
|
1542
1563
|
}
|
|
1543
1564
|
/**
|
|
1544
1565
|
* Generate a factory file for a given model name.
|
|
@@ -1552,9 +1573,9 @@ var CliApp = class {
|
|
|
1552
1573
|
const factoryName = `${baseName}Factory`;
|
|
1553
1574
|
const modelName = options.modelName ? str(options.modelName).pascal() : baseName;
|
|
1554
1575
|
const outputExt = this.resolveOutputExt();
|
|
1555
|
-
const outputPath = join(this.resolveConfigPath("factories", join(process.cwd(), "database", "factories")), `${factoryName}.${outputExt}`);
|
|
1556
|
-
const modelPath = join(this.resolveConfigPath("models", join(process.cwd(), "src", "models")), `${modelName}.${outputExt}`);
|
|
1557
|
-
const relativeImport = options.modelImportPath ?? `./${this.stripKnownSourceExtension(relative(dirname(outputPath), modelPath).replace(/\\/g, "/"))}${outputExt === "js" ? ".js" : ""}`;
|
|
1576
|
+
const outputPath = join$1(this.resolveConfigPath("factories", join$1(process.cwd(), "database", "factories")), `${factoryName}.${outputExt}`);
|
|
1577
|
+
const modelPath = join$1(this.resolveConfigPath("models", join$1(process.cwd(), "src", "models")), `${modelName}.${outputExt}`);
|
|
1578
|
+
const relativeImport = options.modelImportPath ?? `./${this.stripKnownSourceExtension(relative(dirname$1(outputPath), modelPath).replace(/\\/g, "/"))}${outputExt === "js" ? ".js" : ""}`;
|
|
1558
1579
|
const stubPath = this.resolveStubPath(outputExt === "js" ? "factory.js.stub" : "factory.stub");
|
|
1559
1580
|
return {
|
|
1560
1581
|
name: factoryName,
|
|
@@ -1575,7 +1596,7 @@ var CliApp = class {
|
|
|
1575
1596
|
makeSeeder(name, options = {}) {
|
|
1576
1597
|
const seederName = `${str(name.replace(/Seeder$/, "")).pascal()}Seeder`;
|
|
1577
1598
|
const outputExt = this.resolveOutputExt();
|
|
1578
|
-
const outputPath = join(this.resolveConfigPath("seeders", join(process.cwd(), "database", "seeders")), `${seederName}.${outputExt}`);
|
|
1599
|
+
const outputPath = join$1(this.resolveConfigPath("seeders", join$1(process.cwd(), "database", "seeders")), `${seederName}.${outputExt}`);
|
|
1579
1600
|
const stubPath = this.resolveStubPath(outputExt === "js" ? "seeder.js.stub" : "seeder.stub");
|
|
1580
1601
|
return {
|
|
1581
1602
|
name: seederName,
|
|
@@ -1590,7 +1611,7 @@ var CliApp = class {
|
|
|
1590
1611
|
*/
|
|
1591
1612
|
makeMigration(name) {
|
|
1592
1613
|
const generated = generateMigrationFile(name, {
|
|
1593
|
-
directory: this.resolveConfigPath("migrations", join(process.cwd(), "database", "migrations")),
|
|
1614
|
+
directory: this.resolveConfigPath("migrations", join$1(process.cwd(), "database", "migrations")),
|
|
1594
1615
|
extension: this.resolveOutputExt()
|
|
1595
1616
|
});
|
|
1596
1617
|
return {
|
|
@@ -1610,13 +1631,13 @@ var CliApp = class {
|
|
|
1610
1631
|
const modelName = `${baseName}`;
|
|
1611
1632
|
const delegateName = str(baseName).camel().plural().toString();
|
|
1612
1633
|
const outputExt = this.resolveOutputExt();
|
|
1613
|
-
const outputPath = join(this.resolveConfigPath("models", join(process.cwd(), "src", "models")), `${modelName}.${outputExt}`);
|
|
1634
|
+
const outputPath = join$1(this.resolveConfigPath("models", join$1(process.cwd(), "src", "models")), `${modelName}.${outputExt}`);
|
|
1614
1635
|
const shouldBuildFactory = options.all || options.factory;
|
|
1615
1636
|
const shouldBuildSeeder = options.all || options.seeder;
|
|
1616
1637
|
const shouldBuildMigration = options.all || options.migration;
|
|
1617
1638
|
const factoryName = `${baseName}Factory`;
|
|
1618
|
-
const factoryPath = join(this.resolveConfigPath("factories", join(process.cwd(), "database", "factories")), `${factoryName}.${outputExt}`);
|
|
1619
|
-
const factoryImportPath = `./${relative(dirname(outputPath), factoryPath).replace(/\\/g, "/").replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i, "")}${outputExt === "js" ? ".js" : ""}`;
|
|
1639
|
+
const factoryPath = join$1(this.resolveConfigPath("factories", join$1(process.cwd(), "database", "factories")), `${factoryName}.${outputExt}`);
|
|
1640
|
+
const factoryImportPath = `./${relative(dirname$1(outputPath), factoryPath).replace(/\\/g, "/").replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i, "")}${outputExt === "js" ? ".js" : ""}`;
|
|
1620
1641
|
const stubPath = this.resolveStubPath(outputExt === "js" ? "model.js.stub" : "model.stub");
|
|
1621
1642
|
const modelPath = this.generateFile(stubPath, outputPath, {
|
|
1622
1643
|
ModelName: modelName,
|
|
@@ -1638,7 +1659,7 @@ var CliApp = class {
|
|
|
1638
1659
|
if (shouldBuildFactory) created.factory = this.makeFactory(baseName, {
|
|
1639
1660
|
force: options.force,
|
|
1640
1661
|
modelName,
|
|
1641
|
-
modelImportPath: `./${relative(dirname(factoryPath), outputPath).replace(/\\/g, "/").replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i, "")}${outputExt === "js" ? ".js" : ""}`
|
|
1662
|
+
modelImportPath: `./${relative(dirname$1(factoryPath), outputPath).replace(/\\/g, "/").replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i, "")}${outputExt === "js" ? ".js" : ""}`
|
|
1642
1663
|
});
|
|
1643
1664
|
if (shouldBuildSeeder) created.seeder = this.makeSeeder(baseName, { force: options.force });
|
|
1644
1665
|
if (shouldBuildMigration) created.migration = this.makeMigration(`create ${delegateName} table`);
|
|
@@ -1653,26 +1674,28 @@ var CliApp = class {
|
|
|
1653
1674
|
* @param delegateName The name of the delegate (table) to ensure in the Prisma schema.
|
|
1654
1675
|
*/
|
|
1655
1676
|
ensurePrismaModelEntry(modelName, delegateName) {
|
|
1656
|
-
const schemaPath = join(process.cwd(), "prisma", "schema.prisma");
|
|
1657
|
-
if (!existsSync(schemaPath)) return {
|
|
1677
|
+
const schemaPath = join$1(process.cwd(), "prisma", "schema.prisma");
|
|
1678
|
+
if (!existsSync$1(schemaPath)) return {
|
|
1658
1679
|
path: schemaPath,
|
|
1659
1680
|
updated: false
|
|
1660
1681
|
};
|
|
1661
|
-
const source = readFileSync(schemaPath, "utf-8");
|
|
1682
|
+
const source = readFileSync$1(schemaPath, "utf-8");
|
|
1662
1683
|
const existingByTable = findModelBlock(source, delegateName);
|
|
1663
1684
|
const existingByName = new RegExp(`model\\s+${modelName}\\s*\\{`, "m").test(source);
|
|
1664
1685
|
if (existingByTable || existingByName) return {
|
|
1665
1686
|
path: schemaPath,
|
|
1666
1687
|
updated: false
|
|
1667
1688
|
};
|
|
1668
|
-
writeFileSync(schemaPath, applyCreateTableOperation(source, {
|
|
1689
|
+
writeFileSync$1(schemaPath, applyCreateTableOperation(source, {
|
|
1669
1690
|
type: "createTable",
|
|
1670
1691
|
table: delegateName,
|
|
1671
1692
|
columns: [{
|
|
1672
1693
|
name: "id",
|
|
1673
1694
|
type: "id",
|
|
1674
1695
|
primary: true
|
|
1675
|
-
}]
|
|
1696
|
+
}],
|
|
1697
|
+
indexes: [],
|
|
1698
|
+
foreignKeys: []
|
|
1676
1699
|
}));
|
|
1677
1700
|
return {
|
|
1678
1701
|
path: schemaPath,
|
|
@@ -1724,14 +1747,14 @@ var CliApp = class {
|
|
|
1724
1747
|
body.split("\n").forEach((rawLine) => {
|
|
1725
1748
|
const line = rawLine.trim();
|
|
1726
1749
|
if (!line || line.startsWith("@@") || line.startsWith("//")) return;
|
|
1727
|
-
const fieldMatch = line.match(/^(\w+)\s+([A-Za-z]+)(\?)
|
|
1750
|
+
const fieldMatch = line.match(/^(\w+)\s+([A-Za-z]+)(\?)?(?:\s|$)/);
|
|
1728
1751
|
if (!fieldMatch) return;
|
|
1729
1752
|
const fieldType = fieldMatch[2];
|
|
1730
1753
|
if (!scalarTypes.has(fieldType)) return;
|
|
1731
1754
|
fields.push({
|
|
1732
1755
|
name: fieldMatch[1],
|
|
1733
1756
|
type: this.prismaTypeToTs(fieldType),
|
|
1734
|
-
|
|
1757
|
+
nullable: Boolean(fieldMatch[3])
|
|
1735
1758
|
});
|
|
1736
1759
|
});
|
|
1737
1760
|
models.push({
|
|
@@ -1798,18 +1821,18 @@ var CliApp = class {
|
|
|
1798
1821
|
* @returns An object with details about the synchronization process, including updated and skipped files.
|
|
1799
1822
|
*/
|
|
1800
1823
|
syncModelsFromPrisma(options = {}) {
|
|
1801
|
-
const schemaPath = options.schemaPath ?? join(process.cwd(), "prisma", "schema.prisma");
|
|
1802
|
-
const modelsDir = options.modelsDir ?? this.resolveConfigPath("models", join(process.cwd(), "src", "models"));
|
|
1803
|
-
if (!existsSync(schemaPath)) throw new Error(`Prisma schema file not found: ${schemaPath}`);
|
|
1804
|
-
if (!existsSync(modelsDir)) throw new Error(`Models directory not found: ${modelsDir}`);
|
|
1805
|
-
const schema = readFileSync(schemaPath, "utf-8");
|
|
1824
|
+
const schemaPath = options.schemaPath ?? join$1(process.cwd(), "prisma", "schema.prisma");
|
|
1825
|
+
const modelsDir = options.modelsDir ?? this.resolveConfigPath("models", join$1(process.cwd(), "src", "models"));
|
|
1826
|
+
if (!existsSync$1(schemaPath)) throw new Error(`Prisma schema file not found: ${schemaPath}`);
|
|
1827
|
+
if (!existsSync$1(modelsDir)) throw new Error(`Models directory not found: ${modelsDir}`);
|
|
1828
|
+
const schema = readFileSync$1(schemaPath, "utf-8");
|
|
1806
1829
|
const prismaModels = this.parsePrismaModels(schema);
|
|
1807
|
-
const modelFiles = readdirSync(modelsDir).filter((file) => file.endsWith(".ts"));
|
|
1830
|
+
const modelFiles = readdirSync$1(modelsDir).filter((file) => file.endsWith(".ts"));
|
|
1808
1831
|
const updated = [];
|
|
1809
1832
|
const skipped = [];
|
|
1810
1833
|
modelFiles.forEach((file) => {
|
|
1811
|
-
const filePath = join(modelsDir, file);
|
|
1812
|
-
const source = readFileSync(filePath, "utf-8");
|
|
1834
|
+
const filePath = join$1(modelsDir, file);
|
|
1835
|
+
const source = readFileSync$1(filePath, "utf-8");
|
|
1813
1836
|
const classMatch = source.match(/export\s+class\s+(\w+)\s+extends\s+Model<'([^']+)'>/);
|
|
1814
1837
|
if (!classMatch) {
|
|
1815
1838
|
skipped.push(filePath);
|
|
@@ -1822,13 +1845,13 @@ var CliApp = class {
|
|
|
1822
1845
|
skipped.push(filePath);
|
|
1823
1846
|
return;
|
|
1824
1847
|
}
|
|
1825
|
-
const declarations = prismaModel.fields.map((field) => `declare ${field.name}${field.
|
|
1848
|
+
const declarations = prismaModel.fields.map((field) => `declare ${field.name}: ${field.type}${field.nullable ? " | null" : ""}`);
|
|
1826
1849
|
const synced = this.syncModelDeclarations(source, declarations);
|
|
1827
1850
|
if (!synced.updated) {
|
|
1828
1851
|
skipped.push(filePath);
|
|
1829
1852
|
return;
|
|
1830
1853
|
}
|
|
1831
|
-
writeFileSync(filePath, synced.content);
|
|
1854
|
+
writeFileSync$1(filePath, synced.content);
|
|
1832
1855
|
updated.push(filePath);
|
|
1833
1856
|
});
|
|
1834
1857
|
return {
|
|
@@ -1860,23 +1883,23 @@ var InitCommand = class extends Command {
|
|
|
1860
1883
|
*/
|
|
1861
1884
|
async handle() {
|
|
1862
1885
|
this.app.command = this;
|
|
1863
|
-
const outputDir = join
|
|
1886
|
+
const outputDir = join(process.cwd(), "arkormx.config.js");
|
|
1864
1887
|
const { stubs } = getUserConfig("paths") ?? {};
|
|
1865
1888
|
const stubsDir = typeof stubs === "string" && stubs.trim().length > 0 ? stubs : getDefaultStubsPath();
|
|
1866
|
-
const preferredStubPath = join
|
|
1867
|
-
const legacyStubPath = join
|
|
1868
|
-
const stubPath = existsSync(preferredStubPath) ? preferredStubPath : legacyStubPath;
|
|
1869
|
-
if (existsSync(outputDir) && !this.option("force")) {
|
|
1889
|
+
const preferredStubPath = join(stubsDir, "arkormx.config.stub");
|
|
1890
|
+
const legacyStubPath = join(stubsDir, "arkorm.config.stub");
|
|
1891
|
+
const stubPath = existsSync$1(preferredStubPath) ? preferredStubPath : legacyStubPath;
|
|
1892
|
+
if (existsSync$1(outputDir) && !this.option("force")) {
|
|
1870
1893
|
this.error("Error: Arkormˣ has already been initialized. Use --force to reinitialize.");
|
|
1871
1894
|
process.exit(1);
|
|
1872
1895
|
}
|
|
1873
1896
|
this.app.ensureDirectory(outputDir);
|
|
1874
|
-
if (existsSync(outputDir) && this.option("force")) copyFileSync(outputDir, outputDir.replace(/\.js$/, `.backup.${Date.now()}.js`));
|
|
1875
|
-
if (!existsSync(stubPath)) {
|
|
1897
|
+
if (existsSync$1(outputDir) && this.option("force")) copyFileSync(outputDir, outputDir.replace(/\.js$/, `.backup.${Date.now()}.js`));
|
|
1898
|
+
if (!existsSync$1(stubPath)) {
|
|
1876
1899
|
this.error(`Error: Missing config stub at ${preferredStubPath} (or ${legacyStubPath})`);
|
|
1877
1900
|
process.exit(1);
|
|
1878
1901
|
}
|
|
1879
|
-
writeFileSync(outputDir, readFileSync(stubPath, "utf-8"));
|
|
1902
|
+
writeFileSync$1(outputDir, readFileSync$1(stubPath, "utf-8"));
|
|
1880
1903
|
this.success("Arkormˣ initialized successfully!");
|
|
1881
1904
|
}
|
|
1882
1905
|
};
|
|
@@ -2002,6 +2025,98 @@ var MakeSeederCommand = class extends Command {
|
|
|
2002
2025
|
}
|
|
2003
2026
|
};
|
|
2004
2027
|
|
|
2028
|
+
//#endregion
|
|
2029
|
+
//#region src/helpers/migration-history.ts
|
|
2030
|
+
const DEFAULT_STATE = {
|
|
2031
|
+
version: 1,
|
|
2032
|
+
migrations: [],
|
|
2033
|
+
runs: []
|
|
2034
|
+
};
|
|
2035
|
+
const resolveMigrationStateFilePath = (cwd, configuredPath) => {
|
|
2036
|
+
if (configuredPath && configuredPath.trim().length > 0) return resolve(configuredPath);
|
|
2037
|
+
return join(cwd, ".arkormx", "migrations.applied.json");
|
|
2038
|
+
};
|
|
2039
|
+
const buildMigrationIdentity = (filePath, className) => {
|
|
2040
|
+
const fileName = filePath.split("/").pop()?.split("\\").pop() ?? filePath;
|
|
2041
|
+
return `${fileName.slice(0, fileName.length - extname(fileName).length)}:${className}`;
|
|
2042
|
+
};
|
|
2043
|
+
const computeMigrationChecksum = (filePath) => {
|
|
2044
|
+
const source = readFileSync(filePath, "utf-8");
|
|
2045
|
+
return createHash("sha256").update(source).digest("hex");
|
|
2046
|
+
};
|
|
2047
|
+
const readAppliedMigrationsState = (stateFilePath) => {
|
|
2048
|
+
if (!existsSync(stateFilePath)) return { ...DEFAULT_STATE };
|
|
2049
|
+
try {
|
|
2050
|
+
const parsed = JSON.parse(readFileSync(stateFilePath, "utf-8"));
|
|
2051
|
+
if (!Array.isArray(parsed.migrations)) return { ...DEFAULT_STATE };
|
|
2052
|
+
return {
|
|
2053
|
+
version: 1,
|
|
2054
|
+
migrations: parsed.migrations.filter((migration) => {
|
|
2055
|
+
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");
|
|
2056
|
+
}),
|
|
2057
|
+
runs: Array.isArray(parsed.runs) ? parsed.runs.filter((run) => {
|
|
2058
|
+
return typeof run?.id === "string" && typeof run?.appliedAt === "string" && Array.isArray(run?.migrationIds) && run.migrationIds.every((item) => typeof item === "string");
|
|
2059
|
+
}) : []
|
|
2060
|
+
};
|
|
2061
|
+
} catch {
|
|
2062
|
+
return { ...DEFAULT_STATE };
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
const writeAppliedMigrationsState = (stateFilePath, state) => {
|
|
2066
|
+
const directory = dirname(stateFilePath);
|
|
2067
|
+
if (!existsSync(directory)) mkdirSync(directory, { recursive: true });
|
|
2068
|
+
writeFileSync(stateFilePath, JSON.stringify(state, null, 2));
|
|
2069
|
+
};
|
|
2070
|
+
const isMigrationApplied = (state, identity, checksum) => {
|
|
2071
|
+
const matched = state.migrations.find((migration) => migration.id === identity);
|
|
2072
|
+
if (!matched) return false;
|
|
2073
|
+
if (checksum && matched.checksum) return matched.checksum === checksum;
|
|
2074
|
+
if (checksum && !matched.checksum) return false;
|
|
2075
|
+
return true;
|
|
2076
|
+
};
|
|
2077
|
+
const findAppliedMigration = (state, identity) => {
|
|
2078
|
+
return state.migrations.find((migration) => migration.id === identity);
|
|
2079
|
+
};
|
|
2080
|
+
const markMigrationApplied = (state, entry) => {
|
|
2081
|
+
const next = state.migrations.filter((migration) => migration.id !== entry.id);
|
|
2082
|
+
next.push(entry);
|
|
2083
|
+
return {
|
|
2084
|
+
version: 1,
|
|
2085
|
+
migrations: next,
|
|
2086
|
+
runs: state.runs ?? []
|
|
2087
|
+
};
|
|
2088
|
+
};
|
|
2089
|
+
const removeAppliedMigration = (state, identity) => {
|
|
2090
|
+
return {
|
|
2091
|
+
version: 1,
|
|
2092
|
+
migrations: state.migrations.filter((migration) => migration.id !== identity),
|
|
2093
|
+
runs: (state.runs ?? []).map((run) => ({
|
|
2094
|
+
...run,
|
|
2095
|
+
migrationIds: run.migrationIds.filter((id) => id !== identity)
|
|
2096
|
+
})).filter((run) => run.migrationIds.length > 0)
|
|
2097
|
+
};
|
|
2098
|
+
};
|
|
2099
|
+
const buildMigrationRunId = () => {
|
|
2100
|
+
return `run_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
2101
|
+
};
|
|
2102
|
+
const markMigrationRun = (state, run) => {
|
|
2103
|
+
const nextRuns = (state.runs ?? []).filter((existing) => existing.id !== run.id);
|
|
2104
|
+
nextRuns.push(run);
|
|
2105
|
+
return {
|
|
2106
|
+
version: 1,
|
|
2107
|
+
migrations: state.migrations,
|
|
2108
|
+
runs: nextRuns
|
|
2109
|
+
};
|
|
2110
|
+
};
|
|
2111
|
+
const getLastMigrationRun = (state) => {
|
|
2112
|
+
const runs = state.runs ?? [];
|
|
2113
|
+
if (runs.length === 0) return void 0;
|
|
2114
|
+
return [...runs].sort((left, right) => right.appliedAt.localeCompare(left.appliedAt))[0];
|
|
2115
|
+
};
|
|
2116
|
+
const getLatestAppliedMigrations = (state, steps) => {
|
|
2117
|
+
return [...state.migrations].sort((left, right) => right.appliedAt.localeCompare(left.appliedAt)).slice(0, Math.max(0, steps));
|
|
2118
|
+
};
|
|
2119
|
+
|
|
2005
2120
|
//#endregion
|
|
2006
2121
|
//#region src/database/Migration.ts
|
|
2007
2122
|
const MIGRATION_BRAND = Symbol.for("arkormx.migration");
|
|
@@ -2033,6 +2148,7 @@ var MigrateCommand = class extends Command {
|
|
|
2033
2148
|
{--deploy : Use prisma migrate deploy instead of migrate dev}
|
|
2034
2149
|
{--skip-generate : Skip prisma generate}
|
|
2035
2150
|
{--skip-migrate : Skip prisma migrate command}
|
|
2151
|
+
{--state-file= : Path to applied migration state file}
|
|
2036
2152
|
{--schema= : Explicit prisma schema path}
|
|
2037
2153
|
{--migration-name= : Name for prisma migrate dev}
|
|
2038
2154
|
`;
|
|
@@ -2048,16 +2164,59 @@ var MigrateCommand = class extends Command {
|
|
|
2048
2164
|
*/
|
|
2049
2165
|
async handle() {
|
|
2050
2166
|
this.app.command = this;
|
|
2051
|
-
const configuredMigrationsDir = this.app.getConfig("paths")?.migrations ?? join
|
|
2167
|
+
const configuredMigrationsDir = this.app.getConfig("paths")?.migrations ?? join(process.cwd(), "database", "migrations");
|
|
2052
2168
|
const migrationsDir = this.app.resolveRuntimeDirectoryPath(configuredMigrationsDir);
|
|
2053
|
-
if (!existsSync
|
|
2054
|
-
const schemaPath = this.option("schema") ? resolve(String(this.option("schema"))) : join
|
|
2169
|
+
if (!existsSync(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
|
|
2170
|
+
const schemaPath = this.option("schema") ? resolve(String(this.option("schema"))) : join(process.cwd(), "prisma", "schema.prisma");
|
|
2055
2171
|
const classes = this.option("all") || !this.argument("name") ? await this.loadAllMigrations(migrationsDir) : (await this.loadNamedMigration(migrationsDir, this.argument("name"))).filter(([cls]) => cls !== void 0);
|
|
2056
2172
|
if (classes.length === 0) return void this.error("Error: No migration classes found to run.");
|
|
2057
|
-
|
|
2173
|
+
const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
|
|
2174
|
+
let appliedState = readAppliedMigrationsState(stateFilePath);
|
|
2175
|
+
const skipped = [];
|
|
2176
|
+
const changed = [];
|
|
2177
|
+
const pending = classes.filter(([migrationClass, file]) => {
|
|
2178
|
+
if (!appliedState) return true;
|
|
2179
|
+
const identity = buildMigrationIdentity(file, migrationClass.name);
|
|
2180
|
+
const checksum = computeMigrationChecksum(file);
|
|
2181
|
+
const alreadyApplied = isMigrationApplied(appliedState, identity, checksum);
|
|
2182
|
+
if (alreadyApplied) skipped.push([migrationClass, file]);
|
|
2183
|
+
else if (findAppliedMigration(appliedState, identity)) changed.push([migrationClass, file]);
|
|
2184
|
+
return !alreadyApplied;
|
|
2185
|
+
});
|
|
2186
|
+
skipped.forEach(([migrationClass, file]) => {
|
|
2187
|
+
this.success(this.app.splitLogger("Skipped", `${file} (${migrationClass.name})`));
|
|
2188
|
+
});
|
|
2189
|
+
changed.forEach(([migrationClass, file]) => {
|
|
2190
|
+
this.success(this.app.splitLogger("Changed", `${file} (${migrationClass.name})`));
|
|
2191
|
+
});
|
|
2192
|
+
if (pending.length === 0) {
|
|
2193
|
+
this.success("No pending migration classes to apply.");
|
|
2194
|
+
return;
|
|
2195
|
+
}
|
|
2196
|
+
for (const [MigrationClassItem] of pending) await applyMigrationToPrismaSchema(MigrationClassItem, {
|
|
2058
2197
|
schemaPath,
|
|
2059
2198
|
write: true
|
|
2060
2199
|
});
|
|
2200
|
+
if (appliedState) {
|
|
2201
|
+
const runAppliedIds = [];
|
|
2202
|
+
for (const [migrationClass, file] of pending) {
|
|
2203
|
+
const identity = buildMigrationIdentity(file, migrationClass.name);
|
|
2204
|
+
appliedState = markMigrationApplied(appliedState, {
|
|
2205
|
+
id: identity,
|
|
2206
|
+
file,
|
|
2207
|
+
className: migrationClass.name,
|
|
2208
|
+
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2209
|
+
checksum: computeMigrationChecksum(file)
|
|
2210
|
+
});
|
|
2211
|
+
runAppliedIds.push(identity);
|
|
2212
|
+
}
|
|
2213
|
+
appliedState = markMigrationRun(appliedState, {
|
|
2214
|
+
id: buildMigrationRunId(),
|
|
2215
|
+
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2216
|
+
migrationIds: runAppliedIds
|
|
2217
|
+
});
|
|
2218
|
+
writeAppliedMigrationsState(stateFilePath, appliedState);
|
|
2219
|
+
}
|
|
2061
2220
|
if (!this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
|
|
2062
2221
|
if (!this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
|
|
2063
2222
|
else runPrismaCommand([
|
|
@@ -2066,8 +2225,8 @@ var MigrateCommand = class extends Command {
|
|
|
2066
2225
|
"--name",
|
|
2067
2226
|
this.option("migration-name") ? String(this.option("migration-name")) : `arkorm_cli_${Date.now()}`
|
|
2068
2227
|
], process.cwd());
|
|
2069
|
-
this.success(`Applied ${
|
|
2070
|
-
|
|
2228
|
+
this.success(`Applied ${pending.length} migration(s).`);
|
|
2229
|
+
pending.forEach(([_, file]) => this.success(this.app.splitLogger("Migrated", file)));
|
|
2071
2230
|
}
|
|
2072
2231
|
/**
|
|
2073
2232
|
* Load all migration classes from the specified directory.
|
|
@@ -2075,7 +2234,7 @@ var MigrateCommand = class extends Command {
|
|
|
2075
2234
|
* @param migrationsDir The directory to load migration classes from.
|
|
2076
2235
|
*/
|
|
2077
2236
|
async loadAllMigrations(migrationsDir) {
|
|
2078
|
-
const files = readdirSync
|
|
2237
|
+
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)));
|
|
2079
2238
|
return (await Promise.all(files.map(async (file) => (await this.loadMigrationClassesFromFile(file)).map((cls) => [cls, file])))).flat();
|
|
2080
2239
|
}
|
|
2081
2240
|
/**
|
|
@@ -2097,7 +2256,7 @@ var MigrateCommand = class extends Command {
|
|
|
2097
2256
|
`${base}Migration.js`,
|
|
2098
2257
|
`${base}Migration.mjs`,
|
|
2099
2258
|
`${base}Migration.cjs`
|
|
2100
|
-
].map((file) => join
|
|
2259
|
+
].map((file) => join(migrationsDir, file)).find((file) => existsSync(file));
|
|
2101
2260
|
if (!target) return [[void 0, name]];
|
|
2102
2261
|
const runtimeTarget = this.app.resolveRuntimeScriptPath(target);
|
|
2103
2262
|
return (await this.loadMigrationClassesFromFile(runtimeTarget)).map((cls) => [cls, runtimeTarget]);
|
|
@@ -2119,6 +2278,147 @@ var MigrateCommand = class extends Command {
|
|
|
2119
2278
|
}
|
|
2120
2279
|
};
|
|
2121
2280
|
|
|
2281
|
+
//#endregion
|
|
2282
|
+
//#region src/cli/commands/MigrateRollbackCommand.ts
|
|
2283
|
+
/**
|
|
2284
|
+
* Rollback migration classes from the Prisma schema and run Prisma workflow.
|
|
2285
|
+
* By default, rolls back classes applied in the last migrate run.
|
|
2286
|
+
*
|
|
2287
|
+
* @author Legacy (3m1n3nc3)
|
|
2288
|
+
* @since 0.2.4
|
|
2289
|
+
*/
|
|
2290
|
+
var MigrateRollbackCommand = class extends Command {
|
|
2291
|
+
signature = `migrate:rollback
|
|
2292
|
+
{--step= : Number of latest applied migration classes to rollback}
|
|
2293
|
+
{--dry-run : Preview rollback targets without applying changes}
|
|
2294
|
+
{--deploy : Use prisma migrate deploy instead of migrate dev}
|
|
2295
|
+
{--skip-generate : Skip prisma generate}
|
|
2296
|
+
{--skip-migrate : Skip prisma migrate command}
|
|
2297
|
+
{--state-file= : Path to applied migration state file}
|
|
2298
|
+
{--schema= : Explicit prisma schema path}
|
|
2299
|
+
{--migration-name= : Name for prisma migrate dev}
|
|
2300
|
+
`;
|
|
2301
|
+
description = "Rollback migration classes from schema.prisma and run Prisma workflow";
|
|
2302
|
+
async handle() {
|
|
2303
|
+
this.app.command = this;
|
|
2304
|
+
const configuredMigrationsDir = this.app.getConfig("paths")?.migrations ?? join(process.cwd(), "database", "migrations");
|
|
2305
|
+
const migrationsDir = this.app.resolveRuntimeDirectoryPath(configuredMigrationsDir);
|
|
2306
|
+
if (!existsSync(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
|
|
2307
|
+
const schemaPath = this.option("schema") ? resolve(String(this.option("schema"))) : join(process.cwd(), "prisma", "schema.prisma");
|
|
2308
|
+
const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
|
|
2309
|
+
let appliedState = readAppliedMigrationsState(stateFilePath);
|
|
2310
|
+
const stepOption = this.option("step");
|
|
2311
|
+
const stepCount = stepOption == null ? void 0 : Number(stepOption);
|
|
2312
|
+
if (stepCount != null && (!Number.isFinite(stepCount) || stepCount <= 0 || !Number.isInteger(stepCount))) return void this.error("Error: --step must be a positive integer.");
|
|
2313
|
+
const targets = stepCount ? getLatestAppliedMigrations(appliedState, stepCount) : (() => {
|
|
2314
|
+
const lastRun = getLastMigrationRun(appliedState);
|
|
2315
|
+
if (!lastRun) return [];
|
|
2316
|
+
return lastRun.migrationIds.map((id) => appliedState.migrations.find((migration) => migration.id === id)).filter((migration) => Boolean(migration));
|
|
2317
|
+
})();
|
|
2318
|
+
if (targets.length === 0) return void this.error("Error: No tracked migrations available to rollback.");
|
|
2319
|
+
const available = await this.loadAllMigrations(migrationsDir);
|
|
2320
|
+
const rollbackClasses = targets.map((target) => {
|
|
2321
|
+
return available.find(([migrationClass, file]) => {
|
|
2322
|
+
return buildMigrationIdentity(file, migrationClass.name) === target.id || migrationClass.name === target.className;
|
|
2323
|
+
});
|
|
2324
|
+
}).filter((entry) => Boolean(entry));
|
|
2325
|
+
if (rollbackClasses.length === 0) return void this.error("Error: Unable to resolve rollback migration classes from tracked history.");
|
|
2326
|
+
if (this.option("dry-run")) {
|
|
2327
|
+
this.success(`Dry run: ${rollbackClasses.length} migration(s) would be rolled back.`);
|
|
2328
|
+
rollbackClasses.forEach(([_, file]) => this.success(this.app.splitLogger("WouldRollback", file)));
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
for (const [MigrationClassItem] of rollbackClasses) await applyMigrationRollbackToPrismaSchema(MigrationClassItem, {
|
|
2332
|
+
schemaPath,
|
|
2333
|
+
write: true
|
|
2334
|
+
});
|
|
2335
|
+
for (const [migrationClass, file] of rollbackClasses) {
|
|
2336
|
+
const identity = buildMigrationIdentity(file, migrationClass.name);
|
|
2337
|
+
appliedState = removeAppliedMigration(appliedState, identity);
|
|
2338
|
+
}
|
|
2339
|
+
writeAppliedMigrationsState(stateFilePath, appliedState);
|
|
2340
|
+
if (!this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
|
|
2341
|
+
if (!this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
|
|
2342
|
+
else runPrismaCommand([
|
|
2343
|
+
"migrate",
|
|
2344
|
+
"dev",
|
|
2345
|
+
"--name",
|
|
2346
|
+
this.option("migration-name") ? String(this.option("migration-name")) : `arkorm_cli_rollback_${Date.now()}`
|
|
2347
|
+
], process.cwd());
|
|
2348
|
+
this.success(`Rolled back ${rollbackClasses.length} migration(s).`);
|
|
2349
|
+
rollbackClasses.forEach(([_, file]) => this.success(this.app.splitLogger("RolledBack", file)));
|
|
2350
|
+
}
|
|
2351
|
+
async loadAllMigrations(migrationsDir) {
|
|
2352
|
+
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)));
|
|
2353
|
+
return (await Promise.all(files.map(async (file) => (await this.loadMigrationClassesFromFile(file)).map((cls) => [cls, file])))).flat();
|
|
2354
|
+
}
|
|
2355
|
+
async loadMigrationClassesFromFile(filePath) {
|
|
2356
|
+
const imported = await import(`${pathToFileURL$1(resolve(filePath)).href}?arkorm_rollback=${Date.now()}`);
|
|
2357
|
+
return Object.values(imported).filter((value) => {
|
|
2358
|
+
if (typeof value !== "function") return false;
|
|
2359
|
+
const candidate = value;
|
|
2360
|
+
const prototype = candidate.prototype;
|
|
2361
|
+
return candidate[MIGRATION_BRAND] === true || typeof prototype?.up === "function" && typeof prototype?.down === "function";
|
|
2362
|
+
});
|
|
2363
|
+
}
|
|
2364
|
+
};
|
|
2365
|
+
|
|
2366
|
+
//#endregion
|
|
2367
|
+
//#region src/cli/commands/MigrationHistoryCommand.ts
|
|
2368
|
+
/**
|
|
2369
|
+
* The MigrationHistoryCommand class manages tracked migration run history.
|
|
2370
|
+
*
|
|
2371
|
+
* @author Legacy (3m1n3nc3)
|
|
2372
|
+
* @since 0.2.4
|
|
2373
|
+
*/
|
|
2374
|
+
var MigrationHistoryCommand = class extends Command {
|
|
2375
|
+
signature = `migrate:history
|
|
2376
|
+
{--state-file= : Path to applied migration state file}
|
|
2377
|
+
{--reset : Clear tracked migration history file}
|
|
2378
|
+
{--delete : Delete tracked migration history file}
|
|
2379
|
+
{--json : Print raw JSON output}
|
|
2380
|
+
`;
|
|
2381
|
+
description = "Inspect or reset tracked migration history";
|
|
2382
|
+
async handle() {
|
|
2383
|
+
this.app.command = this;
|
|
2384
|
+
const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
|
|
2385
|
+
if (this.option("delete")) {
|
|
2386
|
+
if (!existsSync(stateFilePath)) {
|
|
2387
|
+
this.success(`No migration state file found at ${this.app.formatPathForLog(stateFilePath)}`);
|
|
2388
|
+
return;
|
|
2389
|
+
}
|
|
2390
|
+
rmSync(stateFilePath);
|
|
2391
|
+
this.success(`Deleted migration state file: ${this.app.formatPathForLog(stateFilePath)}`);
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
if (this.option("reset")) {
|
|
2395
|
+
writeAppliedMigrationsState(stateFilePath, {
|
|
2396
|
+
version: 1,
|
|
2397
|
+
migrations: []
|
|
2398
|
+
});
|
|
2399
|
+
this.success(`Reset migration state: ${this.app.formatPathForLog(stateFilePath)}`);
|
|
2400
|
+
return;
|
|
2401
|
+
}
|
|
2402
|
+
const state = readAppliedMigrationsState(stateFilePath);
|
|
2403
|
+
if (this.option("json")) {
|
|
2404
|
+
this.success(JSON.stringify({
|
|
2405
|
+
path: stateFilePath,
|
|
2406
|
+
...state
|
|
2407
|
+
}, null, 2));
|
|
2408
|
+
return;
|
|
2409
|
+
}
|
|
2410
|
+
this.success(this.app.splitLogger("State", stateFilePath));
|
|
2411
|
+
this.success(this.app.splitLogger("Tracked", String(state.migrations.length)));
|
|
2412
|
+
if (state.migrations.length === 0) {
|
|
2413
|
+
this.success("No tracked migrations found.");
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
state.migrations.sort((left, right) => left.appliedAt.localeCompare(right.appliedAt)).forEach((migration) => {
|
|
2417
|
+
this.success(this.app.splitLogger("Applied", `${migration.id} @ ${migration.appliedAt}`));
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
|
|
2122
2422
|
//#endregion
|
|
2123
2423
|
//#region src/cli/commands/ModelsSyncCommand.ts
|
|
2124
2424
|
var ModelsSyncCommand = class extends Command {
|
|
@@ -2214,9 +2514,9 @@ var SeedCommand = class extends Command {
|
|
|
2214
2514
|
*/
|
|
2215
2515
|
async handle() {
|
|
2216
2516
|
this.app.command = this;
|
|
2217
|
-
const configuredSeedersDir = this.app.getConfig("paths")?.seeders ?? join
|
|
2517
|
+
const configuredSeedersDir = this.app.getConfig("paths")?.seeders ?? join(process.cwd(), "database", "seeders");
|
|
2218
2518
|
const seedersDir = this.app.resolveRuntimeDirectoryPath(configuredSeedersDir);
|
|
2219
|
-
if (!existsSync
|
|
2519
|
+
if (!existsSync(seedersDir)) return void this.error(`ERROR: Seeders directory not found: ${this.app.formatPathForLog(configuredSeedersDir)}`);
|
|
2220
2520
|
const classes = this.option("all") ? await this.loadAllSeeders(seedersDir) : await this.loadNamedSeeder(seedersDir, this.argument("name") ?? "DatabaseSeeder");
|
|
2221
2521
|
if (classes.length === 0) return void this.error("ERROR: No seeder classes found to run.");
|
|
2222
2522
|
for (const SeederClassItem of classes) await new SeederClassItem().run();
|
|
@@ -2230,7 +2530,7 @@ var SeedCommand = class extends Command {
|
|
|
2230
2530
|
* @returns
|
|
2231
2531
|
*/
|
|
2232
2532
|
async loadAllSeeders(seedersDir) {
|
|
2233
|
-
const files = readdirSync
|
|
2533
|
+
const files = readdirSync(seedersDir).filter((file) => /\.(ts|js|mjs|cjs)$/i.test(file)).map((file) => this.app.resolveRuntimeScriptPath(join(seedersDir, file)));
|
|
2234
2534
|
return (await Promise.all(files.map(async (file) => await this.loadSeederClassesFromFile(file)))).flat();
|
|
2235
2535
|
}
|
|
2236
2536
|
/**
|
|
@@ -2251,7 +2551,7 @@ var SeedCommand = class extends Command {
|
|
|
2251
2551
|
`${base}Seeder.js`,
|
|
2252
2552
|
`${base}Seeder.mjs`,
|
|
2253
2553
|
`${base}Seeder.cjs`
|
|
2254
|
-
].map((file) => join
|
|
2554
|
+
].map((file) => join(seedersDir, file)).find((file) => existsSync(file));
|
|
2255
2555
|
if (!target) return [];
|
|
2256
2556
|
const runtimeTarget = this.app.resolveRuntimeScriptPath(target);
|
|
2257
2557
|
return await this.loadSeederClassesFromFile(runtimeTarget);
|
|
@@ -5087,4 +5387,4 @@ var Model = class Model {
|
|
|
5087
5387
|
};
|
|
5088
5388
|
|
|
5089
5389
|
//#endregion
|
|
5090
|
-
export { ArkormCollection, ArkormException, CliApp, ForeignKeyBuilder, InitCommand, InlineFactory, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, Migration, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_MODEL_REGEX, Paginator, QueryBuilder, SEEDER_BRAND, SchemaBuilder, SeedCommand, Seeder, TableBuilder, URLDriver, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationSource, buildModelBlock, buildRelationLine, configureArkormRuntime, createMigrationTimestamp, createPrismaAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationFieldName, ensureArkormConfigLoading, escapeRegex, findModelBlock, formatDefaultValue, formatRelationAction, generateMigrationFile, getDefaultStubsPath, getMigrationPlan, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, loadArkormConfig, pad, resetArkormRuntimeForTests, resolveCast, resolveMigrationClassName, resolvePrismaType, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName };
|
|
5390
|
+
export { ArkormCollection, ArkormException, CliApp, ForeignKeyBuilder, InitCommand, InlineFactory, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, MigrateRollbackCommand, Migration, MigrationHistoryCommand, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_MODEL_REGEX, Paginator, QueryBuilder, SEEDER_BRAND, SchemaBuilder, SeedCommand, Seeder, TableBuilder, URLDriver, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationRollbackToPrismaSchema, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationIdentity, buildMigrationRunId, buildMigrationSource, buildModelBlock, buildRelationLine, computeMigrationChecksum, configureArkormRuntime, createMigrationTimestamp, createPrismaAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationFieldName, ensureArkormConfigLoading, escapeRegex, findAppliedMigration, findModelBlock, formatDefaultValue, formatRelationAction, generateMigrationFile, getDefaultStubsPath, getLastMigrationRun, getLatestAppliedMigrations, getMigrationPlan, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, isMigrationApplied, loadArkormConfig, markMigrationApplied, markMigrationRun, pad, readAppliedMigrationsState, removeAppliedMigration, resetArkormRuntimeForTests, resolveCast, resolveMigrationClassName, resolveMigrationStateFilePath, resolvePrismaType, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName, writeAppliedMigrationsState };
|