arkormx 2.0.0-next.2 → 2.0.0-next.4

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
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync
3
3
  import { dirname, extname, join, resolve } from "node:path";
4
4
  import { spawnSync } from "node:child_process";
5
5
  import { str } from "@h3ravel/support";
6
+ import { createHash } from "node:crypto";
6
7
  import path, { dirname as dirname$1, extname as extname$1, join as join$1, relative } from "path";
7
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";
8
9
  import { AsyncLocalStorage } from "async_hooks";
@@ -12,7 +13,6 @@ import { createRequire } from "module";
12
13
  import { fileURLToPath } from "url";
13
14
  import { Logger } from "@h3ravel/shared";
14
15
  import { Command, Kernel } from "@h3ravel/musket";
15
- import { createHash } from "node:crypto";
16
16
 
17
17
  //#region src/Exceptions/ArkormException.ts
18
18
  var ArkormException = class extends Error {
@@ -1371,6 +1371,28 @@ const getMigrationPlan = async (migration, direction = "up") => {
1371
1371
  else await instance.down(schema);
1372
1372
  return schema.getOperations();
1373
1373
  };
1374
+ const supportsDatabaseMigrationExecution = (adapter) => {
1375
+ return typeof adapter?.executeSchemaOperations === "function";
1376
+ };
1377
+ const supportsDatabaseReset = (adapter) => {
1378
+ return typeof adapter?.resetDatabase === "function";
1379
+ };
1380
+ const stripPrismaSchemaModelsAndEnums = (schema) => {
1381
+ const stripped = schema.replace(PRISMA_MODEL_REGEX, "").replace(PRISMA_ENUM_REGEX, "").replace(/\n{3,}/g, "\n\n").trimEnd();
1382
+ return stripped.length > 0 ? `${stripped}\n` : "";
1383
+ };
1384
+ const applyMigrationToDatabase = async (adapter, migration) => {
1385
+ if (!supportsDatabaseMigrationExecution(adapter)) throw new ArkormException("The configured adapter does not support database-backed migration execution.");
1386
+ const operations = await getMigrationPlan(migration, "up");
1387
+ await adapter.executeSchemaOperations(operations);
1388
+ return { operations };
1389
+ };
1390
+ const applyMigrationRollbackToDatabase = async (adapter, migration) => {
1391
+ if (!supportsDatabaseMigrationExecution(adapter)) throw new ArkormException("The configured adapter does not support database-backed migration execution.");
1392
+ const operations = await getMigrationPlan(migration, "down");
1393
+ await adapter.executeSchemaOperations(operations);
1394
+ return { operations };
1395
+ };
1374
1396
  /**
1375
1397
  * Apply the schema operations defined in a migration to a Prisma schema
1376
1398
  * file, updating the file on disk if specified, and return the updated
@@ -1414,6 +1436,377 @@ const applyMigrationRollbackToPrismaSchema = async (migration, options = {}) =>
1414
1436
  };
1415
1437
  };
1416
1438
 
1439
+ //#endregion
1440
+ //#region src/helpers/migration-history.ts
1441
+ const createEmptyAppliedMigrationsState = () => ({
1442
+ version: 1,
1443
+ migrations: [],
1444
+ runs: []
1445
+ });
1446
+ const supportsDatabaseMigrationState = (adapter) => {
1447
+ return typeof adapter?.readAppliedMigrationsState === "function" && typeof adapter?.writeAppliedMigrationsState === "function";
1448
+ };
1449
+ const resolveMigrationStateFilePath = (cwd, configuredPath) => {
1450
+ if (configuredPath && configuredPath.trim().length > 0) return resolve(configuredPath);
1451
+ return join(cwd, ".arkormx", "migrations.applied.json");
1452
+ };
1453
+ const buildMigrationIdentity = (filePath, className) => {
1454
+ const fileName = filePath.split("/").pop()?.split("\\").pop() ?? filePath;
1455
+ return `${fileName.slice(0, fileName.length - extname(fileName).length)}:${className}`;
1456
+ };
1457
+ const computeMigrationChecksum = (filePath) => {
1458
+ const source = readFileSync(filePath, "utf-8");
1459
+ return createHash("sha256").update(source).digest("hex");
1460
+ };
1461
+ const readAppliedMigrationsState = (stateFilePath) => {
1462
+ if (!existsSync(stateFilePath)) return createEmptyAppliedMigrationsState();
1463
+ try {
1464
+ const parsed = JSON.parse(readFileSync(stateFilePath, "utf-8"));
1465
+ if (!Array.isArray(parsed.migrations)) return createEmptyAppliedMigrationsState();
1466
+ return {
1467
+ version: 1,
1468
+ migrations: parsed.migrations.filter((migration) => {
1469
+ 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");
1470
+ }),
1471
+ runs: Array.isArray(parsed.runs) ? parsed.runs.filter((run) => {
1472
+ return typeof run?.id === "string" && typeof run?.appliedAt === "string" && Array.isArray(run?.migrationIds) && run.migrationIds.every((item) => typeof item === "string");
1473
+ }) : []
1474
+ };
1475
+ } catch {
1476
+ return createEmptyAppliedMigrationsState();
1477
+ }
1478
+ };
1479
+ const readAppliedMigrationsStateFromStore = async (adapter, stateFilePath) => {
1480
+ if (supportsDatabaseMigrationState(adapter)) return await adapter.readAppliedMigrationsState();
1481
+ return readAppliedMigrationsState(stateFilePath);
1482
+ };
1483
+ const writeAppliedMigrationsState = (stateFilePath, state) => {
1484
+ const directory = dirname(stateFilePath);
1485
+ if (!existsSync(directory)) mkdirSync(directory, { recursive: true });
1486
+ writeFileSync(stateFilePath, JSON.stringify(state, null, 2));
1487
+ };
1488
+ const writeAppliedMigrationsStateToStore = async (adapter, stateFilePath, state) => {
1489
+ if (supportsDatabaseMigrationState(adapter)) {
1490
+ await adapter.writeAppliedMigrationsState(state);
1491
+ return;
1492
+ }
1493
+ writeAppliedMigrationsState(stateFilePath, state);
1494
+ };
1495
+ const isMigrationApplied = (state, identity, checksum) => {
1496
+ const matched = state.migrations.find((migration) => migration.id === identity);
1497
+ if (!matched) return false;
1498
+ if (checksum && matched.checksum) return matched.checksum === checksum;
1499
+ if (checksum && !matched.checksum) return false;
1500
+ return true;
1501
+ };
1502
+ const findAppliedMigration = (state, identity) => {
1503
+ return state.migrations.find((migration) => migration.id === identity);
1504
+ };
1505
+ const markMigrationApplied = (state, entry) => {
1506
+ const next = state.migrations.filter((migration) => migration.id !== entry.id);
1507
+ next.push(entry);
1508
+ return {
1509
+ version: 1,
1510
+ migrations: next,
1511
+ runs: state.runs ?? []
1512
+ };
1513
+ };
1514
+ const removeAppliedMigration = (state, identity) => {
1515
+ return {
1516
+ version: 1,
1517
+ migrations: state.migrations.filter((migration) => migration.id !== identity),
1518
+ runs: (state.runs ?? []).map((run) => ({
1519
+ ...run,
1520
+ migrationIds: run.migrationIds.filter((id) => id !== identity)
1521
+ })).filter((run) => run.migrationIds.length > 0)
1522
+ };
1523
+ };
1524
+ const buildMigrationRunId = () => {
1525
+ return `run_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
1526
+ };
1527
+ const markMigrationRun = (state, run) => {
1528
+ const nextRuns = (state.runs ?? []).filter((existing) => existing.id !== run.id);
1529
+ nextRuns.push(run);
1530
+ return {
1531
+ version: 1,
1532
+ migrations: state.migrations,
1533
+ runs: nextRuns
1534
+ };
1535
+ };
1536
+ const getLastMigrationRun = (state) => {
1537
+ const runs = state.runs ?? [];
1538
+ if (runs.length === 0) return void 0;
1539
+ return runs.map((run, index) => ({
1540
+ run,
1541
+ index
1542
+ })).sort((left, right) => {
1543
+ const appliedAtOrder = right.run.appliedAt.localeCompare(left.run.appliedAt);
1544
+ if (appliedAtOrder !== 0) return appliedAtOrder;
1545
+ return right.index - left.index;
1546
+ })[0]?.run;
1547
+ };
1548
+ const getLatestAppliedMigrations = (state, steps) => {
1549
+ return state.migrations.map((migration, index) => ({
1550
+ migration,
1551
+ index
1552
+ })).sort((left, right) => {
1553
+ const appliedAtOrder = right.migration.appliedAt.localeCompare(left.migration.appliedAt);
1554
+ if (appliedAtOrder !== 0) return appliedAtOrder;
1555
+ return right.index - left.index;
1556
+ }).slice(0, Math.max(0, steps)).map((entry) => entry.migration);
1557
+ };
1558
+
1559
+ //#endregion
1560
+ //#region src/helpers/column-mappings.ts
1561
+ let cachedColumnMappingsPath;
1562
+ let cachedColumnMappingsState;
1563
+ const resolvePersistedMetadataFeatures = (features) => {
1564
+ return {
1565
+ persistedColumnMappings: features?.persistedColumnMappings !== false,
1566
+ persistedEnums: features?.persistedEnums !== false
1567
+ };
1568
+ };
1569
+ const createEmptyPersistedColumnMappingsState = () => ({
1570
+ version: 1,
1571
+ tables: {}
1572
+ });
1573
+ const resolveColumnMappingsFilePath = (cwd, configuredPath) => {
1574
+ if (configuredPath && configuredPath.trim().length > 0) return resolve(configuredPath);
1575
+ return join(cwd, ".arkormx", "column-mappings.json");
1576
+ };
1577
+ const normalizePersistedEnumValues = (values) => {
1578
+ if (!Array.isArray(values)) return [];
1579
+ return values.filter((value) => typeof value === "string" && value.trim().length > 0);
1580
+ };
1581
+ const normalizeLegacyTableColumns = (columns) => {
1582
+ return Object.entries(columns).reduce((mapped, [attribute, column]) => {
1583
+ if (attribute.trim().length === 0) return mapped;
1584
+ if (typeof column !== "string" || column.trim().length === 0) return mapped;
1585
+ mapped[attribute] = column;
1586
+ return mapped;
1587
+ }, {});
1588
+ };
1589
+ const normalizePersistedTableMetadata = (table) => {
1590
+ if (!table || typeof table !== "object" || Array.isArray(table)) return {
1591
+ columns: {},
1592
+ enums: {}
1593
+ };
1594
+ const candidate = table;
1595
+ if (!(Object.prototype.hasOwnProperty.call(candidate, "columns") || Object.prototype.hasOwnProperty.call(candidate, "enums"))) return {
1596
+ columns: normalizeLegacyTableColumns(candidate),
1597
+ enums: {}
1598
+ };
1599
+ return {
1600
+ columns: normalizeLegacyTableColumns(candidate.columns ?? {}),
1601
+ enums: Object.entries(candidate.enums ?? {}).reduce((all, [columnName, values]) => {
1602
+ if (columnName.trim().length === 0) return all;
1603
+ const normalizedValues = normalizePersistedEnumValues(values);
1604
+ if (normalizedValues.length > 0) all[columnName] = normalizedValues;
1605
+ return all;
1606
+ }, {})
1607
+ };
1608
+ };
1609
+ const normalizePersistedColumnMappingsState = (state) => {
1610
+ return {
1611
+ version: 1,
1612
+ tables: Object.entries(state?.tables ?? {}).reduce((all, [tableName, tableMetadata]) => {
1613
+ if (tableName.trim().length === 0) return all;
1614
+ const normalized = normalizePersistedTableMetadata(tableMetadata);
1615
+ if (Object.keys(normalized.columns).length > 0 || Object.keys(normalized.enums).length > 0) all[tableName] = normalized;
1616
+ return all;
1617
+ }, {})
1618
+ };
1619
+ };
1620
+ const buildPersistedFeatureDisabledError = (feature, table) => {
1621
+ return new ArkormException(`Table [${table}] requires ${feature === "persistedColumnMappings" ? "persisted column mappings" : "persisted enum metadata"}, but ${feature === "persistedColumnMappings" ? "features.persistedColumnMappings" : "features.persistedEnums"} is disabled in arkormx.config.*.`, {
1622
+ operation: "metadata.persisted",
1623
+ meta: {
1624
+ feature,
1625
+ table
1626
+ }
1627
+ });
1628
+ };
1629
+ const assertPersistedTableMetadataEnabled = (table, metadata, features, strict) => {
1630
+ if (!strict) return;
1631
+ if (!features.persistedColumnMappings && Object.keys(metadata.columns).length > 0) throw buildPersistedFeatureDisabledError("persistedColumnMappings", table);
1632
+ if (!features.persistedEnums && Object.keys(metadata.enums).length > 0) throw buildPersistedFeatureDisabledError("persistedEnums", table);
1633
+ };
1634
+ const buildEnumUnionType = (values) => {
1635
+ return values.map((value) => {
1636
+ return `'${value.replace(/'/g, String.raw`\'`)}'`;
1637
+ }).join(" | ");
1638
+ };
1639
+ const resetPersistedColumnMappingsCache = () => {
1640
+ cachedColumnMappingsPath = void 0;
1641
+ cachedColumnMappingsState = void 0;
1642
+ };
1643
+ const readPersistedColumnMappingsState = (filePath) => {
1644
+ if (cachedColumnMappingsPath === filePath && cachedColumnMappingsState) return cachedColumnMappingsState;
1645
+ if (!existsSync(filePath)) {
1646
+ const empty = createEmptyPersistedColumnMappingsState();
1647
+ cachedColumnMappingsPath = filePath;
1648
+ cachedColumnMappingsState = empty;
1649
+ return empty;
1650
+ }
1651
+ try {
1652
+ const normalized = normalizePersistedColumnMappingsState(JSON.parse(readFileSync(filePath, "utf-8")));
1653
+ cachedColumnMappingsPath = filePath;
1654
+ cachedColumnMappingsState = normalized;
1655
+ return normalized;
1656
+ } catch {
1657
+ const empty = createEmptyPersistedColumnMappingsState();
1658
+ cachedColumnMappingsPath = filePath;
1659
+ cachedColumnMappingsState = empty;
1660
+ return empty;
1661
+ }
1662
+ };
1663
+ const writePersistedColumnMappingsState = (filePath, state) => {
1664
+ const normalized = normalizePersistedColumnMappingsState(state);
1665
+ const directory = dirname(filePath);
1666
+ if (!existsSync(directory)) mkdirSync(directory, { recursive: true });
1667
+ writeFileSync(filePath, JSON.stringify(normalized, null, 2));
1668
+ cachedColumnMappingsPath = filePath;
1669
+ cachedColumnMappingsState = normalized;
1670
+ };
1671
+ const deletePersistedColumnMappingsState = (filePath) => {
1672
+ if (existsSync(filePath)) rmSync(filePath, { force: true });
1673
+ resetPersistedColumnMappingsCache();
1674
+ };
1675
+ const getPersistedTableMetadata = (table, options = {}) => {
1676
+ const metadata = readPersistedColumnMappingsState(resolveColumnMappingsFilePath(options.cwd ?? process.cwd(), options.configuredPath)).tables[table] ?? {
1677
+ columns: {},
1678
+ enums: {}
1679
+ };
1680
+ assertPersistedTableMetadataEnabled(table, metadata, options.features ?? resolvePersistedMetadataFeatures(), options.strict ?? false);
1681
+ return {
1682
+ columns: { ...metadata.columns },
1683
+ enums: Object.entries(metadata.enums).reduce((all, [columnName, values]) => {
1684
+ all[columnName] = [...values];
1685
+ return all;
1686
+ }, {})
1687
+ };
1688
+ };
1689
+ const getPersistedEnumMap = (table, options = {}) => {
1690
+ return getPersistedTableMetadata(table, options).enums;
1691
+ };
1692
+ const applyMappedColumn = (tableColumns, column, features, table) => {
1693
+ if (typeof column.map === "string" && column.map.trim().length > 0 && column.map !== column.name) {
1694
+ if (!features.persistedColumnMappings) throw buildPersistedFeatureDisabledError("persistedColumnMappings", table);
1695
+ tableColumns[column.name] = column.map;
1696
+ return;
1697
+ }
1698
+ delete tableColumns[column.name];
1699
+ };
1700
+ const applyEnumColumn = (tableEnums, column, features, table) => {
1701
+ const values = column.enumValues ?? [];
1702
+ if (column.type === "enum" && values.length > 0) {
1703
+ if (!features.persistedEnums) throw buildPersistedFeatureDisabledError("persistedEnums", table);
1704
+ tableEnums[column.name] = [...values];
1705
+ return;
1706
+ }
1707
+ delete tableEnums[column.name];
1708
+ };
1709
+ const removePersistedColumnMetadata = (tableMetadata, columnName) => {
1710
+ delete tableMetadata.columns[columnName];
1711
+ delete tableMetadata.enums[columnName];
1712
+ Object.entries(tableMetadata.columns).forEach(([attribute, mappedColumn]) => {
1713
+ if (mappedColumn === columnName) delete tableMetadata.columns[attribute];
1714
+ });
1715
+ };
1716
+ const applyOperationsToPersistedColumnMappingsState = (state, operations, features = resolvePersistedMetadataFeatures()) => {
1717
+ const nextTables = Object.entries(state.tables).reduce((all, [table, metadata]) => {
1718
+ all[table] = {
1719
+ columns: { ...metadata.columns },
1720
+ enums: Object.entries(metadata.enums).reduce((nextEnums, [columnName, values]) => {
1721
+ nextEnums[columnName] = [...values];
1722
+ return nextEnums;
1723
+ }, {})
1724
+ };
1725
+ return all;
1726
+ }, {});
1727
+ operations.forEach((operation) => {
1728
+ if (operation.type === "createTable") {
1729
+ const tableMetadata = nextTables[operation.table] ?? {
1730
+ columns: {},
1731
+ enums: {}
1732
+ };
1733
+ operation.columns.forEach((column) => {
1734
+ applyMappedColumn(tableMetadata.columns, column, features, operation.table);
1735
+ applyEnumColumn(tableMetadata.enums, column, features, operation.table);
1736
+ });
1737
+ if (Object.keys(tableMetadata.columns).length > 0 || Object.keys(tableMetadata.enums).length > 0) nextTables[operation.table] = tableMetadata;
1738
+ else delete nextTables[operation.table];
1739
+ return;
1740
+ }
1741
+ if (operation.type === "alterTable") {
1742
+ const tableMetadata = nextTables[operation.table] ?? {
1743
+ columns: {},
1744
+ enums: {}
1745
+ };
1746
+ operation.addColumns.forEach((column) => {
1747
+ applyMappedColumn(tableMetadata.columns, column, features, operation.table);
1748
+ applyEnumColumn(tableMetadata.enums, column, features, operation.table);
1749
+ });
1750
+ operation.dropColumns.forEach((columnName) => {
1751
+ removePersistedColumnMetadata(tableMetadata, columnName);
1752
+ });
1753
+ if (Object.keys(tableMetadata.columns).length > 0 || Object.keys(tableMetadata.enums).length > 0) nextTables[operation.table] = tableMetadata;
1754
+ else delete nextTables[operation.table];
1755
+ return;
1756
+ }
1757
+ delete nextTables[operation.table];
1758
+ });
1759
+ return {
1760
+ version: 1,
1761
+ tables: nextTables
1762
+ };
1763
+ };
1764
+ const rebuildPersistedColumnMappingsState = async (state, availableMigrations, features = resolvePersistedMetadataFeatures()) => {
1765
+ const availableByIdentity = new Map(availableMigrations.map(([migrationClass, file]) => [buildMigrationIdentity(file, migrationClass.name), migrationClass]));
1766
+ let nextState = createEmptyPersistedColumnMappingsState();
1767
+ const orderedMigrations = state.migrations.map((migration, index) => ({
1768
+ migration,
1769
+ index
1770
+ })).sort((left, right) => {
1771
+ const appliedAtOrder = left.migration.appliedAt.localeCompare(right.migration.appliedAt);
1772
+ if (appliedAtOrder !== 0) return appliedAtOrder;
1773
+ return left.index - right.index;
1774
+ });
1775
+ for (const { migration } of orderedMigrations) {
1776
+ const migrationClass = availableByIdentity.get(migration.id);
1777
+ if (!migrationClass) throw new ArkormException(`Unable to rebuild persisted column mappings because migration [${migration.id}] could not be resolved from the current migration files.`, {
1778
+ operation: "migration.columnMappings",
1779
+ meta: {
1780
+ migrationId: migration.id,
1781
+ file: migration.file,
1782
+ className: migration.className
1783
+ }
1784
+ });
1785
+ const operations = await getMigrationPlan(migrationClass, "up");
1786
+ nextState = applyOperationsToPersistedColumnMappingsState(nextState, operations, features);
1787
+ }
1788
+ return nextState;
1789
+ };
1790
+ const syncPersistedColumnMappingsFromState = async (cwd, state, availableMigrations, features = resolvePersistedMetadataFeatures()) => {
1791
+ const filePath = resolveColumnMappingsFilePath(cwd);
1792
+ const nextState = await rebuildPersistedColumnMappingsState(state, availableMigrations, features);
1793
+ if (Object.keys(nextState.tables).length === 0) {
1794
+ deletePersistedColumnMappingsState(filePath);
1795
+ return;
1796
+ }
1797
+ writePersistedColumnMappingsState(filePath, nextState);
1798
+ };
1799
+ const validatePersistedMetadataFeaturesForMigrations = async (migrations, features = resolvePersistedMetadataFeatures()) => {
1800
+ let nextState = createEmptyPersistedColumnMappingsState();
1801
+ for (const [migrationClass] of migrations) {
1802
+ const operations = await getMigrationPlan(migrationClass, "up");
1803
+ nextState = applyOperationsToPersistedColumnMappingsState(nextState, operations, features);
1804
+ }
1805
+ };
1806
+ const getPersistedEnumTsType = (values) => {
1807
+ return buildEnumUnionType(values);
1808
+ };
1809
+
1417
1810
  //#endregion
1418
1811
  //#region src/helpers/runtime-module-loader.ts
1419
1812
  var RuntimeModuleLoader = class {
@@ -1441,6 +1834,10 @@ const resolveDefaultStubsPath = () => {
1441
1834
  return path.join(process.cwd(), "stubs");
1442
1835
  };
1443
1836
  const baseConfig = {
1837
+ features: {
1838
+ persistedColumnMappings: true,
1839
+ persistedEnums: true
1840
+ },
1444
1841
  paths: {
1445
1842
  stubs: resolveDefaultStubsPath(),
1446
1843
  seeders: path.join(process.cwd(), "database", "seeders"),
@@ -1453,6 +1850,7 @@ const baseConfig = {
1453
1850
  };
1454
1851
  const userConfig = {
1455
1852
  ...baseConfig,
1853
+ features: { ...baseConfig.features ?? {} },
1456
1854
  paths: { ...baseConfig.paths ?? {} }
1457
1855
  };
1458
1856
  let runtimeConfigLoaded = false;
@@ -1475,6 +1873,15 @@ const mergePathConfig = (paths) => {
1475
1873
  ...incoming
1476
1874
  };
1477
1875
  };
1876
+ const mergeFeatureConfig = (features) => {
1877
+ const defaults = baseConfig.features ?? {};
1878
+ const current = userConfig.features ?? {};
1879
+ return {
1880
+ ...defaults,
1881
+ ...current,
1882
+ ...features ?? {}
1883
+ };
1884
+ };
1478
1885
  const bindAdapterToModels = (adapter, models) => {
1479
1886
  models.forEach((model) => {
1480
1887
  model.setAdapter(adapter);
@@ -1500,6 +1907,7 @@ const getUserConfig = (key) => {
1500
1907
  const configureArkormRuntime = (prisma, options = {}) => {
1501
1908
  const nextConfig = {
1502
1909
  ...userConfig,
1910
+ features: mergeFeatureConfig(options.features),
1503
1911
  paths: mergePathConfig(options.paths)
1504
1912
  };
1505
1913
  nextConfig.prisma = prisma;
@@ -1544,6 +1952,7 @@ const resolveAndApplyConfig = (imported) => {
1544
1952
  configureArkormRuntime(config.prisma, {
1545
1953
  adapter: config.adapter,
1546
1954
  boot: config.boot,
1955
+ features: config.features,
1547
1956
  pagination: config.pagination,
1548
1957
  paths: config.paths,
1549
1958
  outputExt: config.outputExt
@@ -2135,6 +2544,26 @@ var CliApp = class {
2135
2544
  skipped
2136
2545
  };
2137
2546
  }
2547
+ applyPersistedEnumMetadata(structure) {
2548
+ const persistedEnums = getPersistedEnumMap(structure.table, {
2549
+ features: resolvePersistedMetadataFeatures(this.getConfig("features")),
2550
+ strict: true
2551
+ });
2552
+ if (Object.keys(persistedEnums).length === 0) return structure;
2553
+ return {
2554
+ ...structure,
2555
+ fields: structure.fields.map((field) => {
2556
+ const enumValues = persistedEnums[field.name];
2557
+ if (!enumValues || enumValues.length === 0) return field;
2558
+ const enumType = getPersistedEnumTsType(enumValues);
2559
+ const isArray = /^Array<.+>$/.test(field.type);
2560
+ return {
2561
+ ...field,
2562
+ type: isArray ? `Array<${enumType}>` : enumType
2563
+ };
2564
+ })
2565
+ };
2566
+ }
2138
2567
  /**
2139
2568
  * Parse Prisma enum definitions from a schema and return their member names.
2140
2569
  *
@@ -2293,7 +2722,10 @@ var CliApp = class {
2293
2722
  return all;
2294
2723
  }, /* @__PURE__ */ new Map());
2295
2724
  const discovered = await adapter.introspectModels({ tables: [...new Set([...sources.values()].map((source) => source.table))] });
2296
- const structuresByTable = new Map(discovered.map((model) => [model.table, model]));
2725
+ const structuresByTable = new Map(discovered.map((model) => {
2726
+ const enriched = this.applyPersistedEnumMetadata(model);
2727
+ return [enriched.table, enriched];
2728
+ }));
2297
2729
  const result = this.syncModelFiles(modelFiles, (filePath) => {
2298
2730
  const parsed = sources.get(filePath);
2299
2731
  return parsed ? structuresByTable.get(parsed.table) : void 0;
@@ -2507,112 +2939,6 @@ var MakeSeederCommand = class extends Command {
2507
2939
  }
2508
2940
  };
2509
2941
 
2510
- //#endregion
2511
- //#region src/helpers/migration-history.ts
2512
- const DEFAULT_STATE = {
2513
- version: 1,
2514
- migrations: [],
2515
- runs: []
2516
- };
2517
- const resolveMigrationStateFilePath = (cwd, configuredPath) => {
2518
- if (configuredPath && configuredPath.trim().length > 0) return resolve(configuredPath);
2519
- return join(cwd, ".arkormx", "migrations.applied.json");
2520
- };
2521
- const buildMigrationIdentity = (filePath, className) => {
2522
- const fileName = filePath.split("/").pop()?.split("\\").pop() ?? filePath;
2523
- return `${fileName.slice(0, fileName.length - extname(fileName).length)}:${className}`;
2524
- };
2525
- const computeMigrationChecksum = (filePath) => {
2526
- const source = readFileSync(filePath, "utf-8");
2527
- return createHash("sha256").update(source).digest("hex");
2528
- };
2529
- const readAppliedMigrationsState = (stateFilePath) => {
2530
- if (!existsSync(stateFilePath)) return { ...DEFAULT_STATE };
2531
- try {
2532
- const parsed = JSON.parse(readFileSync(stateFilePath, "utf-8"));
2533
- if (!Array.isArray(parsed.migrations)) return { ...DEFAULT_STATE };
2534
- return {
2535
- version: 1,
2536
- migrations: parsed.migrations.filter((migration) => {
2537
- 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");
2538
- }),
2539
- runs: Array.isArray(parsed.runs) ? parsed.runs.filter((run) => {
2540
- return typeof run?.id === "string" && typeof run?.appliedAt === "string" && Array.isArray(run?.migrationIds) && run.migrationIds.every((item) => typeof item === "string");
2541
- }) : []
2542
- };
2543
- } catch {
2544
- return { ...DEFAULT_STATE };
2545
- }
2546
- };
2547
- const writeAppliedMigrationsState = (stateFilePath, state) => {
2548
- const directory = dirname(stateFilePath);
2549
- if (!existsSync(directory)) mkdirSync(directory, { recursive: true });
2550
- writeFileSync(stateFilePath, JSON.stringify(state, null, 2));
2551
- };
2552
- const isMigrationApplied = (state, identity, checksum) => {
2553
- const matched = state.migrations.find((migration) => migration.id === identity);
2554
- if (!matched) return false;
2555
- if (checksum && matched.checksum) return matched.checksum === checksum;
2556
- if (checksum && !matched.checksum) return false;
2557
- return true;
2558
- };
2559
- const findAppliedMigration = (state, identity) => {
2560
- return state.migrations.find((migration) => migration.id === identity);
2561
- };
2562
- const markMigrationApplied = (state, entry) => {
2563
- const next = state.migrations.filter((migration) => migration.id !== entry.id);
2564
- next.push(entry);
2565
- return {
2566
- version: 1,
2567
- migrations: next,
2568
- runs: state.runs ?? []
2569
- };
2570
- };
2571
- const removeAppliedMigration = (state, identity) => {
2572
- return {
2573
- version: 1,
2574
- migrations: state.migrations.filter((migration) => migration.id !== identity),
2575
- runs: (state.runs ?? []).map((run) => ({
2576
- ...run,
2577
- migrationIds: run.migrationIds.filter((id) => id !== identity)
2578
- })).filter((run) => run.migrationIds.length > 0)
2579
- };
2580
- };
2581
- const buildMigrationRunId = () => {
2582
- return `run_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
2583
- };
2584
- const markMigrationRun = (state, run) => {
2585
- const nextRuns = (state.runs ?? []).filter((existing) => existing.id !== run.id);
2586
- nextRuns.push(run);
2587
- return {
2588
- version: 1,
2589
- migrations: state.migrations,
2590
- runs: nextRuns
2591
- };
2592
- };
2593
- const getLastMigrationRun = (state) => {
2594
- const runs = state.runs ?? [];
2595
- if (runs.length === 0) return void 0;
2596
- return runs.map((run, index) => ({
2597
- run,
2598
- index
2599
- })).sort((left, right) => {
2600
- const appliedAtOrder = right.run.appliedAt.localeCompare(left.run.appliedAt);
2601
- if (appliedAtOrder !== 0) return appliedAtOrder;
2602
- return right.index - left.index;
2603
- })[0]?.run;
2604
- };
2605
- const getLatestAppliedMigrations = (state, steps) => {
2606
- return state.migrations.map((migration, index) => ({
2607
- migration,
2608
- index
2609
- })).sort((left, right) => {
2610
- const appliedAtOrder = right.migration.appliedAt.localeCompare(left.migration.appliedAt);
2611
- if (appliedAtOrder !== 0) return appliedAtOrder;
2612
- return right.index - left.index;
2613
- }).slice(0, Math.max(0, steps)).map((entry) => entry.migration);
2614
- };
2615
-
2616
2942
  //#endregion
2617
2943
  //#region src/database/Migration.ts
2618
2944
  const MIGRATION_BRAND = Symbol.for("arkormx.migration");
@@ -2667,7 +2993,10 @@ var MigrateCommand = class extends Command {
2667
2993
  const classes = this.option("all") || !this.argument("name") ? await this.loadAllMigrations(migrationsDir) : (await this.loadNamedMigration(migrationsDir, this.argument("name"))).filter(([cls]) => cls !== void 0);
2668
2994
  if (classes.length === 0) return void this.error("Error: No migration classes found to run.");
2669
2995
  const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
2670
- let appliedState = readAppliedMigrationsState(stateFilePath);
2996
+ let appliedState = await readAppliedMigrationsStateFromStore(this.app.getConfig("adapter"), stateFilePath);
2997
+ const adapter = this.app.getConfig("adapter");
2998
+ const useDatabaseMigrations = supportsDatabaseMigrationExecution(adapter);
2999
+ const persistedFeatures = resolvePersistedMetadataFeatures(this.app.getConfig("features"));
2671
3000
  const skipped = [];
2672
3001
  const changed = [];
2673
3002
  const pending = classes.filter(([migrationClass, file]) => {
@@ -2686,13 +3015,31 @@ var MigrateCommand = class extends Command {
2686
3015
  this.success(this.app.splitLogger("Changed", `${file} (${migrationClass.name})`));
2687
3016
  });
2688
3017
  if (pending.length === 0) {
3018
+ if (appliedState) try {
3019
+ await syncPersistedColumnMappingsFromState(process.cwd(), appliedState, await this.loadAllMigrations(migrationsDir), persistedFeatures);
3020
+ } catch (error) {
3021
+ this.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
3022
+ return;
3023
+ }
2689
3024
  this.success("No pending migration classes to apply.");
2690
3025
  return;
2691
3026
  }
2692
- for (const [MigrationClassItem] of pending) await applyMigrationToPrismaSchema(MigrationClassItem, {
2693
- schemaPath,
2694
- write: true
2695
- });
3027
+ if (useDatabaseMigrations) try {
3028
+ await validatePersistedMetadataFeaturesForMigrations(pending, persistedFeatures);
3029
+ } catch (error) {
3030
+ this.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
3031
+ return;
3032
+ }
3033
+ for (const [MigrationClassItem] of pending) {
3034
+ if (useDatabaseMigrations) {
3035
+ await applyMigrationToDatabase(adapter, MigrationClassItem);
3036
+ continue;
3037
+ }
3038
+ await applyMigrationToPrismaSchema(MigrationClassItem, {
3039
+ schemaPath,
3040
+ write: true
3041
+ });
3042
+ }
2696
3043
  if (appliedState) {
2697
3044
  const runAppliedIds = [];
2698
3045
  for (const [migrationClass, file] of pending) {
@@ -2711,10 +3058,16 @@ var MigrateCommand = class extends Command {
2711
3058
  appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
2712
3059
  migrationIds: runAppliedIds
2713
3060
  });
2714
- writeAppliedMigrationsState(stateFilePath, appliedState);
3061
+ await writeAppliedMigrationsStateToStore(adapter, stateFilePath, appliedState);
3062
+ try {
3063
+ await syncPersistedColumnMappingsFromState(process.cwd(), appliedState, await this.loadAllMigrations(migrationsDir), persistedFeatures);
3064
+ } catch (error) {
3065
+ this.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
3066
+ return;
3067
+ }
2715
3068
  }
2716
- if (!this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
2717
- if (!this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
3069
+ if (!useDatabaseMigrations && !this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
3070
+ if (!useDatabaseMigrations && !this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
2718
3071
  else runPrismaCommand([
2719
3072
  "migrate",
2720
3073
  "dev",
@@ -2774,6 +3127,103 @@ var MigrateCommand = class extends Command {
2774
3127
  }
2775
3128
  };
2776
3129
 
3130
+ //#endregion
3131
+ //#region src/cli/commands/MigrateFreshCommand.ts
3132
+ var MigrateFreshCommand = class extends Command {
3133
+ signature = `migrate:fresh
3134
+ {--skip-generate : Skip prisma generate}
3135
+ {--skip-migrate : Skip prisma database sync}
3136
+ {--state-file= : Path to applied migration state file}
3137
+ {--schema= : Explicit prisma schema path}
3138
+ `;
3139
+ description = "Reset the database and rerun all migration classes";
3140
+ async handle() {
3141
+ this.app.command = this;
3142
+ const configuredMigrationsDir = this.app.getConfig("paths")?.migrations ?? join(process.cwd(), "database", "migrations");
3143
+ const migrationsDir = this.app.resolveRuntimeDirectoryPath(configuredMigrationsDir);
3144
+ if (!existsSync(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
3145
+ const adapter = this.app.getConfig("adapter");
3146
+ const useDatabaseMigrations = supportsDatabaseMigrationExecution(adapter);
3147
+ const persistedFeatures = resolvePersistedMetadataFeatures(this.app.getConfig("features"));
3148
+ const schemaPath = this.option("schema") ? resolve(String(this.option("schema"))) : join(process.cwd(), "prisma", "schema.prisma");
3149
+ const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
3150
+ const migrations = await this.loadAllMigrations(migrationsDir);
3151
+ if (migrations.length === 0) return void this.error("Error: No migration classes found to run.");
3152
+ if (useDatabaseMigrations) try {
3153
+ await validatePersistedMetadataFeaturesForMigrations(migrations, persistedFeatures);
3154
+ } catch (error) {
3155
+ this.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
3156
+ return;
3157
+ }
3158
+ if (useDatabaseMigrations) {
3159
+ if (!supportsDatabaseReset(adapter)) {
3160
+ this.error("Error: Your current database adapter does not support database reset.");
3161
+ return;
3162
+ }
3163
+ await adapter.resetDatabase();
3164
+ } else {
3165
+ if (!existsSync(schemaPath)) return void this.error(`Error: Prisma schema file not found: ${this.app.formatPathForLog(schemaPath)}`);
3166
+ writeFileSync(schemaPath, stripPrismaSchemaModelsAndEnums(readFileSync(schemaPath, "utf-8")));
3167
+ }
3168
+ let appliedState = createEmptyAppliedMigrationsState();
3169
+ await writeAppliedMigrationsStateToStore(adapter, stateFilePath, appliedState);
3170
+ for (const [MigrationClassItem] of migrations) {
3171
+ if (useDatabaseMigrations) {
3172
+ await applyMigrationToDatabase(adapter, MigrationClassItem);
3173
+ continue;
3174
+ }
3175
+ await applyMigrationToPrismaSchema(MigrationClassItem, {
3176
+ schemaPath,
3177
+ write: true
3178
+ });
3179
+ }
3180
+ for (const [migrationClass, file] of migrations) appliedState = markMigrationApplied(appliedState, {
3181
+ id: buildMigrationIdentity(file, migrationClass.name),
3182
+ file,
3183
+ className: migrationClass.name,
3184
+ appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
3185
+ checksum: computeMigrationChecksum(file)
3186
+ });
3187
+ appliedState = markMigrationRun(appliedState, {
3188
+ id: buildMigrationRunId(),
3189
+ appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
3190
+ migrationIds: appliedState.migrations.map((migration) => migration.id)
3191
+ });
3192
+ await writeAppliedMigrationsStateToStore(adapter, stateFilePath, appliedState);
3193
+ try {
3194
+ await syncPersistedColumnMappingsFromState(process.cwd(), appliedState, migrations, persistedFeatures);
3195
+ } catch (error) {
3196
+ this.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
3197
+ return;
3198
+ }
3199
+ if (!useDatabaseMigrations) {
3200
+ const schemaArgs = this.option("schema") ? ["--schema", schemaPath] : [];
3201
+ if (!this.option("skip-generate")) runPrismaCommand(["generate", ...schemaArgs], process.cwd());
3202
+ if (!this.option("skip-migrate")) runPrismaCommand([
3203
+ "db",
3204
+ "push",
3205
+ "--force-reset",
3206
+ ...schemaArgs
3207
+ ], process.cwd());
3208
+ }
3209
+ this.success(`Refreshed database with ${migrations.length} migration(s).`);
3210
+ migrations.forEach(([_, file]) => this.success(this.app.splitLogger("Migrated", file)));
3211
+ }
3212
+ async loadAllMigrations(migrationsDir) {
3213
+ 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)));
3214
+ return (await Promise.all(files.map(async (file) => (await this.loadMigrationClassesFromFile(file)).map((cls) => [cls, file])))).flat();
3215
+ }
3216
+ async loadMigrationClassesFromFile(filePath) {
3217
+ const imported = await RuntimeModuleLoader.load(filePath);
3218
+ return Object.values(imported).filter((value) => {
3219
+ if (typeof value !== "function") return false;
3220
+ const candidate = value;
3221
+ const prototype = candidate.prototype;
3222
+ return candidate[MIGRATION_BRAND] === true || typeof prototype?.up === "function" && typeof prototype?.down === "function";
3223
+ });
3224
+ }
3225
+ };
3226
+
2777
3227
  //#endregion
2778
3228
  //#region src/cli/commands/MigrateRollbackCommand.ts
2779
3229
  /**
@@ -2802,7 +3252,10 @@ var MigrateRollbackCommand = class extends Command {
2802
3252
  if (!existsSync(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
2803
3253
  const schemaPath = this.option("schema") ? resolve(String(this.option("schema"))) : join(process.cwd(), "prisma", "schema.prisma");
2804
3254
  const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
2805
- let appliedState = readAppliedMigrationsState(stateFilePath);
3255
+ const adapter = this.app.getConfig("adapter");
3256
+ const useDatabaseMigrations = supportsDatabaseMigrationExecution(adapter);
3257
+ const persistedFeatures = resolvePersistedMetadataFeatures(this.app.getConfig("features"));
3258
+ let appliedState = await readAppliedMigrationsStateFromStore(adapter, stateFilePath);
2806
3259
  const stepOption = this.option("step");
2807
3260
  const stepCount = stepOption == null ? void 0 : Number(stepOption);
2808
3261
  if (stepCount != null && (!Number.isFinite(stepCount) || stepCount <= 0 || !Number.isInteger(stepCount))) return void this.error("Error: --step must be a positive integer.");
@@ -2824,17 +3277,29 @@ var MigrateRollbackCommand = class extends Command {
2824
3277
  rollbackClasses.forEach(([_, file]) => this.success(this.app.splitLogger("WouldRollback", file)));
2825
3278
  return;
2826
3279
  }
2827
- for (const [MigrationClassItem] of rollbackClasses) await applyMigrationRollbackToPrismaSchema(MigrationClassItem, {
2828
- schemaPath,
2829
- write: true
2830
- });
3280
+ for (const [MigrationClassItem] of rollbackClasses) {
3281
+ if (useDatabaseMigrations) {
3282
+ await applyMigrationRollbackToDatabase(adapter, MigrationClassItem);
3283
+ continue;
3284
+ }
3285
+ await applyMigrationRollbackToPrismaSchema(MigrationClassItem, {
3286
+ schemaPath,
3287
+ write: true
3288
+ });
3289
+ }
2831
3290
  for (const [migrationClass, file] of rollbackClasses) {
2832
3291
  const identity = buildMigrationIdentity(file, migrationClass.name);
2833
3292
  appliedState = removeAppliedMigration(appliedState, identity);
2834
3293
  }
2835
- writeAppliedMigrationsState(stateFilePath, appliedState);
2836
- if (!this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
2837
- if (!this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
3294
+ await writeAppliedMigrationsStateToStore(adapter, stateFilePath, appliedState);
3295
+ try {
3296
+ await syncPersistedColumnMappingsFromState(process.cwd(), appliedState, available, persistedFeatures);
3297
+ } catch (error) {
3298
+ this.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
3299
+ return;
3300
+ }
3301
+ if (!useDatabaseMigrations && !this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
3302
+ if (!useDatabaseMigrations && !this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
2838
3303
  else runPrismaCommand([
2839
3304
  "migrate",
2840
3305
  "dev",
@@ -2878,32 +3343,39 @@ var MigrationHistoryCommand = class extends Command {
2878
3343
  async handle() {
2879
3344
  this.app.command = this;
2880
3345
  const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
3346
+ const adapter = this.app.getConfig("adapter");
3347
+ const usesDatabaseState = supportsDatabaseMigrationState(adapter);
2881
3348
  if (this.option("delete")) {
3349
+ if (usesDatabaseState) {
3350
+ await adapter.writeAppliedMigrationsState(createEmptyAppliedMigrationsState());
3351
+ deletePersistedColumnMappingsState(resolveColumnMappingsFilePath(process.cwd()));
3352
+ this.success("Deleted tracked migration state from database.");
3353
+ return;
3354
+ }
2882
3355
  if (!existsSync(stateFilePath)) {
2883
3356
  this.success(`No migration state file found at ${this.app.formatPathForLog(stateFilePath)}`);
2884
3357
  return;
2885
3358
  }
2886
3359
  rmSync(stateFilePath);
3360
+ deletePersistedColumnMappingsState(resolveColumnMappingsFilePath(process.cwd()));
2887
3361
  this.success(`Deleted migration state file: ${this.app.formatPathForLog(stateFilePath)}`);
2888
3362
  return;
2889
3363
  }
2890
3364
  if (this.option("reset")) {
2891
- writeAppliedMigrationsState(stateFilePath, {
2892
- version: 1,
2893
- migrations: []
2894
- });
2895
- this.success(`Reset migration state: ${this.app.formatPathForLog(stateFilePath)}`);
3365
+ await writeAppliedMigrationsStateToStore(adapter, stateFilePath, createEmptyAppliedMigrationsState());
3366
+ deletePersistedColumnMappingsState(resolveColumnMappingsFilePath(process.cwd()));
3367
+ this.success(usesDatabaseState ? "Reset migration state in database." : `Reset migration state: ${this.app.formatPathForLog(stateFilePath)}`);
2896
3368
  return;
2897
3369
  }
2898
- const state = readAppliedMigrationsState(stateFilePath);
3370
+ const state = await readAppliedMigrationsStateFromStore(adapter, stateFilePath);
2899
3371
  if (this.option("json")) {
2900
3372
  this.success(JSON.stringify({
2901
- path: stateFilePath,
3373
+ path: usesDatabaseState ? "database" : stateFilePath,
2902
3374
  ...state
2903
3375
  }, null, 2));
2904
3376
  return;
2905
3377
  }
2906
- this.success(this.app.splitLogger("State", stateFilePath));
3378
+ this.success(this.app.splitLogger("State", usesDatabaseState ? "database" : stateFilePath));
2907
3379
  this.success(this.app.splitLogger("Tracked", String(state.migrations.length)));
2908
3380
  if (state.migrations.length === 0) {
2909
3381
  this.success("No tracked migrations found.");
@@ -2925,10 +3397,16 @@ var ModelsSyncCommand = class extends Command {
2925
3397
  description = "Sync model declare attributes from the active adapter when supported, otherwise fall back to the Prisma schema";
2926
3398
  async handle() {
2927
3399
  this.app.command = this;
2928
- const result = await this.app.syncModels({
2929
- schemaPath: this.option("schema") ? resolve(String(this.option("schema"))) : void 0,
2930
- modelsDir: this.option("models") ? resolve(String(this.option("models"))) : void 0
2931
- });
3400
+ let result;
3401
+ try {
3402
+ result = await this.app.syncModels({
3403
+ schemaPath: this.option("schema") ? resolve(String(this.option("schema"))) : void 0,
3404
+ modelsDir: this.option("models") ? resolve(String(this.option("models"))) : void 0
3405
+ });
3406
+ } catch (error) {
3407
+ this.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
3408
+ return;
3409
+ }
2932
3410
  const updatedLines = result.updated.length === 0 ? [this.app.splitLogger("Updated", "none")] : result.updated.map((path) => this.app.splitLogger("Updated", path));
2933
3411
  this.success("SUCCESS: Model sync completed with the following results:");
2934
3412
  [
@@ -3096,6 +3574,7 @@ await Kernel.init(app, {
3096
3574
  ModelsSyncCommand,
3097
3575
  SeedCommand,
3098
3576
  MigrateCommand,
3577
+ MigrateFreshCommand,
3099
3578
  MigrateRollbackCommand,
3100
3579
  MigrationHistoryCommand
3101
3580
  ],