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/index.cjs CHANGED
@@ -26,17 +26,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  }) : target, mod));
27
27
 
28
28
  //#endregion
29
- let fs = require("fs");
30
- let path = require("path");
31
- path = __toESM(path);
32
- let module$1 = require("module");
33
29
  let node_fs = require("node:fs");
34
30
  let node_path = require("node:path");
35
31
  let node_child_process = require("node:child_process");
36
32
  let _h3ravel_support = require("@h3ravel/support");
33
+ let path = require("path");
34
+ path = __toESM(path);
35
+ let fs = require("fs");
37
36
  let url = require("url");
37
+ let module$1 = require("module");
38
38
  let _h3ravel_shared = require("@h3ravel/shared");
39
39
  let _h3ravel_musket = require("@h3ravel/musket");
40
+ let node_crypto = require("node:crypto");
40
41
  let node_url = require("node:url");
41
42
  let _h3ravel_collect_js = require("@h3ravel/collect.js");
42
43
 
@@ -1151,6 +1152,26 @@ const applyMigrationToPrismaSchema = async (migration, options = {}) => {
1151
1152
  };
1152
1153
  };
1153
1154
  /**
1155
+ * Apply the rollback (down) operations defined in a migration to a Prisma schema file.
1156
+ *
1157
+ * @param migration The migration class or instance to rollback.
1158
+ * @param options Options for applying the rollback, including schema path and write flag.
1159
+ * @returns A promise that resolves to an object containing the updated schema, schema path, and rollback operations applied.
1160
+ */
1161
+ const applyMigrationRollbackToPrismaSchema = async (migration, options = {}) => {
1162
+ const schemaPath = options.schemaPath ?? (0, node_path.join)(process.cwd(), "prisma", "schema.prisma");
1163
+ if (!(0, node_fs.existsSync)(schemaPath)) throw new ArkormException(`Prisma schema file not found: ${schemaPath}`);
1164
+ const source = (0, node_fs.readFileSync)(schemaPath, "utf-8");
1165
+ const operations = await getMigrationPlan(migration, "down");
1166
+ const schema = applyOperationsToPrismaSchema(source, operations);
1167
+ if (options.write ?? true) (0, node_fs.writeFileSync)(schemaPath, schema);
1168
+ return {
1169
+ schema,
1170
+ schemaPath,
1171
+ operations
1172
+ };
1173
+ };
1174
+ /**
1154
1175
  * Run a migration by applying its schema operations to a Prisma schema
1155
1176
  * file, optionally generating Prisma client code and running migrations after
1156
1177
  * applying the schema changes.
@@ -1701,7 +1722,9 @@ var CliApp = class {
1701
1722
  name: "id",
1702
1723
  type: "id",
1703
1724
  primary: true
1704
- }]
1725
+ }],
1726
+ indexes: [],
1727
+ foreignKeys: []
1705
1728
  }));
1706
1729
  return {
1707
1730
  path: schemaPath,
@@ -1753,14 +1776,14 @@ var CliApp = class {
1753
1776
  body.split("\n").forEach((rawLine) => {
1754
1777
  const line = rawLine.trim();
1755
1778
  if (!line || line.startsWith("@@") || line.startsWith("//")) return;
1756
- const fieldMatch = line.match(/^(\w+)\s+([A-Za-z]+)(\?)?\b/);
1779
+ const fieldMatch = line.match(/^(\w+)\s+([A-Za-z]+)(\?)?(?:\s|$)/);
1757
1780
  if (!fieldMatch) return;
1758
1781
  const fieldType = fieldMatch[2];
1759
1782
  if (!scalarTypes.has(fieldType)) return;
1760
1783
  fields.push({
1761
1784
  name: fieldMatch[1],
1762
1785
  type: this.prismaTypeToTs(fieldType),
1763
- optional: Boolean(fieldMatch[3])
1786
+ nullable: Boolean(fieldMatch[3])
1764
1787
  });
1765
1788
  });
1766
1789
  models.push({
@@ -1851,7 +1874,7 @@ var CliApp = class {
1851
1874
  skipped.push(filePath);
1852
1875
  return;
1853
1876
  }
1854
- const declarations = prismaModel.fields.map((field) => `declare ${field.name}${field.optional ? "?" : ""}: ${field.type}`);
1877
+ const declarations = prismaModel.fields.map((field) => `declare ${field.name}: ${field.type}${field.nullable ? " | null" : ""}`);
1855
1878
  const synced = this.syncModelDeclarations(source, declarations);
1856
1879
  if (!synced.updated) {
1857
1880
  skipped.push(filePath);
@@ -2031,6 +2054,98 @@ var MakeSeederCommand = class extends _h3ravel_musket.Command {
2031
2054
  }
2032
2055
  };
2033
2056
 
2057
+ //#endregion
2058
+ //#region src/helpers/migration-history.ts
2059
+ const DEFAULT_STATE = {
2060
+ version: 1,
2061
+ migrations: [],
2062
+ runs: []
2063
+ };
2064
+ const resolveMigrationStateFilePath = (cwd, configuredPath) => {
2065
+ if (configuredPath && configuredPath.trim().length > 0) return (0, node_path.resolve)(configuredPath);
2066
+ return (0, node_path.join)(cwd, ".arkormx", "migrations.applied.json");
2067
+ };
2068
+ const buildMigrationIdentity = (filePath, className) => {
2069
+ const fileName = filePath.split("/").pop()?.split("\\").pop() ?? filePath;
2070
+ return `${fileName.slice(0, fileName.length - (0, node_path.extname)(fileName).length)}:${className}`;
2071
+ };
2072
+ const computeMigrationChecksum = (filePath) => {
2073
+ const source = (0, node_fs.readFileSync)(filePath, "utf-8");
2074
+ return (0, node_crypto.createHash)("sha256").update(source).digest("hex");
2075
+ };
2076
+ const readAppliedMigrationsState = (stateFilePath) => {
2077
+ if (!(0, node_fs.existsSync)(stateFilePath)) return { ...DEFAULT_STATE };
2078
+ try {
2079
+ const parsed = JSON.parse((0, node_fs.readFileSync)(stateFilePath, "utf-8"));
2080
+ if (!Array.isArray(parsed.migrations)) return { ...DEFAULT_STATE };
2081
+ return {
2082
+ version: 1,
2083
+ migrations: parsed.migrations.filter((migration) => {
2084
+ 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");
2085
+ }),
2086
+ runs: Array.isArray(parsed.runs) ? parsed.runs.filter((run) => {
2087
+ return typeof run?.id === "string" && typeof run?.appliedAt === "string" && Array.isArray(run?.migrationIds) && run.migrationIds.every((item) => typeof item === "string");
2088
+ }) : []
2089
+ };
2090
+ } catch {
2091
+ return { ...DEFAULT_STATE };
2092
+ }
2093
+ };
2094
+ const writeAppliedMigrationsState = (stateFilePath, state) => {
2095
+ const directory = (0, node_path.dirname)(stateFilePath);
2096
+ if (!(0, node_fs.existsSync)(directory)) (0, node_fs.mkdirSync)(directory, { recursive: true });
2097
+ (0, node_fs.writeFileSync)(stateFilePath, JSON.stringify(state, null, 2));
2098
+ };
2099
+ const isMigrationApplied = (state, identity, checksum) => {
2100
+ const matched = state.migrations.find((migration) => migration.id === identity);
2101
+ if (!matched) return false;
2102
+ if (checksum && matched.checksum) return matched.checksum === checksum;
2103
+ if (checksum && !matched.checksum) return false;
2104
+ return true;
2105
+ };
2106
+ const findAppliedMigration = (state, identity) => {
2107
+ return state.migrations.find((migration) => migration.id === identity);
2108
+ };
2109
+ const markMigrationApplied = (state, entry) => {
2110
+ const next = state.migrations.filter((migration) => migration.id !== entry.id);
2111
+ next.push(entry);
2112
+ return {
2113
+ version: 1,
2114
+ migrations: next,
2115
+ runs: state.runs ?? []
2116
+ };
2117
+ };
2118
+ const removeAppliedMigration = (state, identity) => {
2119
+ return {
2120
+ version: 1,
2121
+ migrations: state.migrations.filter((migration) => migration.id !== identity),
2122
+ runs: (state.runs ?? []).map((run) => ({
2123
+ ...run,
2124
+ migrationIds: run.migrationIds.filter((id) => id !== identity)
2125
+ })).filter((run) => run.migrationIds.length > 0)
2126
+ };
2127
+ };
2128
+ const buildMigrationRunId = () => {
2129
+ return `run_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
2130
+ };
2131
+ const markMigrationRun = (state, run) => {
2132
+ const nextRuns = (state.runs ?? []).filter((existing) => existing.id !== run.id);
2133
+ nextRuns.push(run);
2134
+ return {
2135
+ version: 1,
2136
+ migrations: state.migrations,
2137
+ runs: nextRuns
2138
+ };
2139
+ };
2140
+ const getLastMigrationRun = (state) => {
2141
+ const runs = state.runs ?? [];
2142
+ if (runs.length === 0) return void 0;
2143
+ return [...runs].sort((left, right) => right.appliedAt.localeCompare(left.appliedAt))[0];
2144
+ };
2145
+ const getLatestAppliedMigrations = (state, steps) => {
2146
+ return [...state.migrations].sort((left, right) => right.appliedAt.localeCompare(left.appliedAt)).slice(0, Math.max(0, steps));
2147
+ };
2148
+
2034
2149
  //#endregion
2035
2150
  //#region src/database/Migration.ts
2036
2151
  const MIGRATION_BRAND = Symbol.for("arkormx.migration");
@@ -2062,6 +2177,7 @@ var MigrateCommand = class extends _h3ravel_musket.Command {
2062
2177
  {--deploy : Use prisma migrate deploy instead of migrate dev}
2063
2178
  {--skip-generate : Skip prisma generate}
2064
2179
  {--skip-migrate : Skip prisma migrate command}
2180
+ {--state-file= : Path to applied migration state file}
2065
2181
  {--schema= : Explicit prisma schema path}
2066
2182
  {--migration-name= : Name for prisma migrate dev}
2067
2183
  `;
@@ -2083,10 +2199,53 @@ var MigrateCommand = class extends _h3ravel_musket.Command {
2083
2199
  const schemaPath = this.option("schema") ? (0, node_path.resolve)(String(this.option("schema"))) : (0, node_path.join)(process.cwd(), "prisma", "schema.prisma");
2084
2200
  const classes = this.option("all") || !this.argument("name") ? await this.loadAllMigrations(migrationsDir) : (await this.loadNamedMigration(migrationsDir, this.argument("name"))).filter(([cls]) => cls !== void 0);
2085
2201
  if (classes.length === 0) return void this.error("Error: No migration classes found to run.");
2086
- for (const [MigrationClassItem] of classes) await applyMigrationToPrismaSchema(MigrationClassItem, {
2202
+ const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
2203
+ let appliedState = readAppliedMigrationsState(stateFilePath);
2204
+ const skipped = [];
2205
+ const changed = [];
2206
+ const pending = classes.filter(([migrationClass, file]) => {
2207
+ if (!appliedState) return true;
2208
+ const identity = buildMigrationIdentity(file, migrationClass.name);
2209
+ const checksum = computeMigrationChecksum(file);
2210
+ const alreadyApplied = isMigrationApplied(appliedState, identity, checksum);
2211
+ if (alreadyApplied) skipped.push([migrationClass, file]);
2212
+ else if (findAppliedMigration(appliedState, identity)) changed.push([migrationClass, file]);
2213
+ return !alreadyApplied;
2214
+ });
2215
+ skipped.forEach(([migrationClass, file]) => {
2216
+ this.success(this.app.splitLogger("Skipped", `${file} (${migrationClass.name})`));
2217
+ });
2218
+ changed.forEach(([migrationClass, file]) => {
2219
+ this.success(this.app.splitLogger("Changed", `${file} (${migrationClass.name})`));
2220
+ });
2221
+ if (pending.length === 0) {
2222
+ this.success("No pending migration classes to apply.");
2223
+ return;
2224
+ }
2225
+ for (const [MigrationClassItem] of pending) await applyMigrationToPrismaSchema(MigrationClassItem, {
2087
2226
  schemaPath,
2088
2227
  write: true
2089
2228
  });
2229
+ if (appliedState) {
2230
+ const runAppliedIds = [];
2231
+ for (const [migrationClass, file] of pending) {
2232
+ const identity = buildMigrationIdentity(file, migrationClass.name);
2233
+ appliedState = markMigrationApplied(appliedState, {
2234
+ id: identity,
2235
+ file,
2236
+ className: migrationClass.name,
2237
+ appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
2238
+ checksum: computeMigrationChecksum(file)
2239
+ });
2240
+ runAppliedIds.push(identity);
2241
+ }
2242
+ appliedState = markMigrationRun(appliedState, {
2243
+ id: buildMigrationRunId(),
2244
+ appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
2245
+ migrationIds: runAppliedIds
2246
+ });
2247
+ writeAppliedMigrationsState(stateFilePath, appliedState);
2248
+ }
2090
2249
  if (!this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
2091
2250
  if (!this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
2092
2251
  else runPrismaCommand([
@@ -2095,8 +2254,8 @@ var MigrateCommand = class extends _h3ravel_musket.Command {
2095
2254
  "--name",
2096
2255
  this.option("migration-name") ? String(this.option("migration-name")) : `arkorm_cli_${Date.now()}`
2097
2256
  ], process.cwd());
2098
- this.success(`Applied ${classes.length} migration(s).`);
2099
- classes.forEach(([_, file]) => this.success(this.app.splitLogger("Migrated", file)));
2257
+ this.success(`Applied ${pending.length} migration(s).`);
2258
+ pending.forEach(([_, file]) => this.success(this.app.splitLogger("Migrated", file)));
2100
2259
  }
2101
2260
  /**
2102
2261
  * Load all migration classes from the specified directory.
@@ -2148,6 +2307,147 @@ var MigrateCommand = class extends _h3ravel_musket.Command {
2148
2307
  }
2149
2308
  };
2150
2309
 
2310
+ //#endregion
2311
+ //#region src/cli/commands/MigrateRollbackCommand.ts
2312
+ /**
2313
+ * Rollback migration classes from the Prisma schema and run Prisma workflow.
2314
+ * By default, rolls back classes applied in the last migrate run.
2315
+ *
2316
+ * @author Legacy (3m1n3nc3)
2317
+ * @since 0.2.4
2318
+ */
2319
+ var MigrateRollbackCommand = class extends _h3ravel_musket.Command {
2320
+ signature = `migrate:rollback
2321
+ {--step= : Number of latest applied migration classes to rollback}
2322
+ {--dry-run : Preview rollback targets without applying changes}
2323
+ {--deploy : Use prisma migrate deploy instead of migrate dev}
2324
+ {--skip-generate : Skip prisma generate}
2325
+ {--skip-migrate : Skip prisma migrate command}
2326
+ {--state-file= : Path to applied migration state file}
2327
+ {--schema= : Explicit prisma schema path}
2328
+ {--migration-name= : Name for prisma migrate dev}
2329
+ `;
2330
+ description = "Rollback migration classes from schema.prisma and run Prisma workflow";
2331
+ async handle() {
2332
+ this.app.command = this;
2333
+ const configuredMigrationsDir = this.app.getConfig("paths")?.migrations ?? (0, node_path.join)(process.cwd(), "database", "migrations");
2334
+ const migrationsDir = this.app.resolveRuntimeDirectoryPath(configuredMigrationsDir);
2335
+ if (!(0, node_fs.existsSync)(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
2336
+ const schemaPath = this.option("schema") ? (0, node_path.resolve)(String(this.option("schema"))) : (0, node_path.join)(process.cwd(), "prisma", "schema.prisma");
2337
+ const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
2338
+ let appliedState = readAppliedMigrationsState(stateFilePath);
2339
+ const stepOption = this.option("step");
2340
+ const stepCount = stepOption == null ? void 0 : Number(stepOption);
2341
+ if (stepCount != null && (!Number.isFinite(stepCount) || stepCount <= 0 || !Number.isInteger(stepCount))) return void this.error("Error: --step must be a positive integer.");
2342
+ const targets = stepCount ? getLatestAppliedMigrations(appliedState, stepCount) : (() => {
2343
+ const lastRun = getLastMigrationRun(appliedState);
2344
+ if (!lastRun) return [];
2345
+ return lastRun.migrationIds.map((id) => appliedState.migrations.find((migration) => migration.id === id)).filter((migration) => Boolean(migration));
2346
+ })();
2347
+ if (targets.length === 0) return void this.error("Error: No tracked migrations available to rollback.");
2348
+ const available = await this.loadAllMigrations(migrationsDir);
2349
+ const rollbackClasses = targets.map((target) => {
2350
+ return available.find(([migrationClass, file]) => {
2351
+ return buildMigrationIdentity(file, migrationClass.name) === target.id || migrationClass.name === target.className;
2352
+ });
2353
+ }).filter((entry) => Boolean(entry));
2354
+ if (rollbackClasses.length === 0) return void this.error("Error: Unable to resolve rollback migration classes from tracked history.");
2355
+ if (this.option("dry-run")) {
2356
+ this.success(`Dry run: ${rollbackClasses.length} migration(s) would be rolled back.`);
2357
+ rollbackClasses.forEach(([_, file]) => this.success(this.app.splitLogger("WouldRollback", file)));
2358
+ return;
2359
+ }
2360
+ for (const [MigrationClassItem] of rollbackClasses) await applyMigrationRollbackToPrismaSchema(MigrationClassItem, {
2361
+ schemaPath,
2362
+ write: true
2363
+ });
2364
+ for (const [migrationClass, file] of rollbackClasses) {
2365
+ const identity = buildMigrationIdentity(file, migrationClass.name);
2366
+ appliedState = removeAppliedMigration(appliedState, identity);
2367
+ }
2368
+ writeAppliedMigrationsState(stateFilePath, appliedState);
2369
+ if (!this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
2370
+ if (!this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
2371
+ else runPrismaCommand([
2372
+ "migrate",
2373
+ "dev",
2374
+ "--name",
2375
+ this.option("migration-name") ? String(this.option("migration-name")) : `arkorm_cli_rollback_${Date.now()}`
2376
+ ], process.cwd());
2377
+ this.success(`Rolled back ${rollbackClasses.length} migration(s).`);
2378
+ rollbackClasses.forEach(([_, file]) => this.success(this.app.splitLogger("RolledBack", file)));
2379
+ }
2380
+ async loadAllMigrations(migrationsDir) {
2381
+ const files = (0, node_fs.readdirSync)(migrationsDir).filter((file) => /\.(ts|js|mjs|cjs)$/i.test(file)).sort((left, right) => left.localeCompare(right)).map((file) => this.app.resolveRuntimeScriptPath((0, node_path.join)(migrationsDir, file)));
2382
+ return (await Promise.all(files.map(async (file) => (await this.loadMigrationClassesFromFile(file)).map((cls) => [cls, file])))).flat();
2383
+ }
2384
+ async loadMigrationClassesFromFile(filePath) {
2385
+ const imported = await import(`${(0, node_url.pathToFileURL)((0, node_path.resolve)(filePath)).href}?arkorm_rollback=${Date.now()}`);
2386
+ return Object.values(imported).filter((value) => {
2387
+ if (typeof value !== "function") return false;
2388
+ const candidate = value;
2389
+ const prototype = candidate.prototype;
2390
+ return candidate[MIGRATION_BRAND] === true || typeof prototype?.up === "function" && typeof prototype?.down === "function";
2391
+ });
2392
+ }
2393
+ };
2394
+
2395
+ //#endregion
2396
+ //#region src/cli/commands/MigrationHistoryCommand.ts
2397
+ /**
2398
+ * The MigrationHistoryCommand class manages tracked migration run history.
2399
+ *
2400
+ * @author Legacy (3m1n3nc3)
2401
+ * @since 0.2.4
2402
+ */
2403
+ var MigrationHistoryCommand = class extends _h3ravel_musket.Command {
2404
+ signature = `migrate:history
2405
+ {--state-file= : Path to applied migration state file}
2406
+ {--reset : Clear tracked migration history file}
2407
+ {--delete : Delete tracked migration history file}
2408
+ {--json : Print raw JSON output}
2409
+ `;
2410
+ description = "Inspect or reset tracked migration history";
2411
+ async handle() {
2412
+ this.app.command = this;
2413
+ const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
2414
+ if (this.option("delete")) {
2415
+ if (!(0, node_fs.existsSync)(stateFilePath)) {
2416
+ this.success(`No migration state file found at ${this.app.formatPathForLog(stateFilePath)}`);
2417
+ return;
2418
+ }
2419
+ (0, node_fs.rmSync)(stateFilePath);
2420
+ this.success(`Deleted migration state file: ${this.app.formatPathForLog(stateFilePath)}`);
2421
+ return;
2422
+ }
2423
+ if (this.option("reset")) {
2424
+ writeAppliedMigrationsState(stateFilePath, {
2425
+ version: 1,
2426
+ migrations: []
2427
+ });
2428
+ this.success(`Reset migration state: ${this.app.formatPathForLog(stateFilePath)}`);
2429
+ return;
2430
+ }
2431
+ const state = readAppliedMigrationsState(stateFilePath);
2432
+ if (this.option("json")) {
2433
+ this.success(JSON.stringify({
2434
+ path: stateFilePath,
2435
+ ...state
2436
+ }, null, 2));
2437
+ return;
2438
+ }
2439
+ this.success(this.app.splitLogger("State", stateFilePath));
2440
+ this.success(this.app.splitLogger("Tracked", String(state.migrations.length)));
2441
+ if (state.migrations.length === 0) {
2442
+ this.success("No tracked migrations found.");
2443
+ return;
2444
+ }
2445
+ state.migrations.sort((left, right) => left.appliedAt.localeCompare(right.appliedAt)).forEach((migration) => {
2446
+ this.success(this.app.splitLogger("Applied", `${migration.id} @ ${migration.appliedAt}`));
2447
+ });
2448
+ }
2449
+ };
2450
+
2151
2451
  //#endregion
2152
2452
  //#region src/cli/commands/ModelsSyncCommand.ts
2153
2453
  var ModelsSyncCommand = class extends _h3ravel_musket.Command {
@@ -5129,7 +5429,9 @@ exports.MakeMigrationCommand = MakeMigrationCommand;
5129
5429
  exports.MakeModelCommand = MakeModelCommand;
5130
5430
  exports.MakeSeederCommand = MakeSeederCommand;
5131
5431
  exports.MigrateCommand = MigrateCommand;
5432
+ exports.MigrateRollbackCommand = MigrateRollbackCommand;
5132
5433
  exports.Migration = Migration;
5434
+ exports.MigrationHistoryCommand = MigrationHistoryCommand;
5133
5435
  exports.Model = Model;
5134
5436
  exports.ModelFactory = ModelFactory;
5135
5437
  exports.ModelNotFoundException = ModelNotFoundException;
@@ -5146,14 +5448,18 @@ exports.URLDriver = URLDriver;
5146
5448
  exports.applyAlterTableOperation = applyAlterTableOperation;
5147
5449
  exports.applyCreateTableOperation = applyCreateTableOperation;
5148
5450
  exports.applyDropTableOperation = applyDropTableOperation;
5451
+ exports.applyMigrationRollbackToPrismaSchema = applyMigrationRollbackToPrismaSchema;
5149
5452
  exports.applyMigrationToPrismaSchema = applyMigrationToPrismaSchema;
5150
5453
  exports.applyOperationsToPrismaSchema = applyOperationsToPrismaSchema;
5151
5454
  exports.buildFieldLine = buildFieldLine;
5152
5455
  exports.buildIndexLine = buildIndexLine;
5153
5456
  exports.buildInverseRelationLine = buildInverseRelationLine;
5457
+ exports.buildMigrationIdentity = buildMigrationIdentity;
5458
+ exports.buildMigrationRunId = buildMigrationRunId;
5154
5459
  exports.buildMigrationSource = buildMigrationSource;
5155
5460
  exports.buildModelBlock = buildModelBlock;
5156
5461
  exports.buildRelationLine = buildRelationLine;
5462
+ exports.computeMigrationChecksum = computeMigrationChecksum;
5157
5463
  exports.configureArkormRuntime = configureArkormRuntime;
5158
5464
  exports.createMigrationTimestamp = createMigrationTimestamp;
5159
5465
  exports.createPrismaAdapter = createPrismaAdapter;
@@ -5165,24 +5471,34 @@ exports.deriveInverseRelationAlias = deriveInverseRelationAlias;
5165
5471
  exports.deriveRelationFieldName = deriveRelationFieldName;
5166
5472
  exports.ensureArkormConfigLoading = ensureArkormConfigLoading;
5167
5473
  exports.escapeRegex = escapeRegex;
5474
+ exports.findAppliedMigration = findAppliedMigration;
5168
5475
  exports.findModelBlock = findModelBlock;
5169
5476
  exports.formatDefaultValue = formatDefaultValue;
5170
5477
  exports.formatRelationAction = formatRelationAction;
5171
5478
  exports.generateMigrationFile = generateMigrationFile;
5172
5479
  exports.getDefaultStubsPath = getDefaultStubsPath;
5480
+ exports.getLastMigrationRun = getLastMigrationRun;
5481
+ exports.getLatestAppliedMigrations = getLatestAppliedMigrations;
5173
5482
  exports.getMigrationPlan = getMigrationPlan;
5174
5483
  exports.getRuntimePaginationURLDriverFactory = getRuntimePaginationURLDriverFactory;
5175
5484
  exports.getRuntimePrismaClient = getRuntimePrismaClient;
5176
5485
  exports.getUserConfig = getUserConfig;
5177
5486
  exports.inferDelegateName = inferDelegateName;
5178
5487
  exports.isDelegateLike = isDelegateLike;
5488
+ exports.isMigrationApplied = isMigrationApplied;
5179
5489
  exports.loadArkormConfig = loadArkormConfig;
5490
+ exports.markMigrationApplied = markMigrationApplied;
5491
+ exports.markMigrationRun = markMigrationRun;
5180
5492
  exports.pad = pad;
5493
+ exports.readAppliedMigrationsState = readAppliedMigrationsState;
5494
+ exports.removeAppliedMigration = removeAppliedMigration;
5181
5495
  exports.resetArkormRuntimeForTests = resetArkormRuntimeForTests;
5182
5496
  exports.resolveCast = resolveCast;
5183
5497
  exports.resolveMigrationClassName = resolveMigrationClassName;
5498
+ exports.resolveMigrationStateFilePath = resolveMigrationStateFilePath;
5184
5499
  exports.resolvePrismaType = resolvePrismaType;
5185
5500
  exports.runMigrationWithPrisma = runMigrationWithPrisma;
5186
5501
  exports.runPrismaCommand = runPrismaCommand;
5187
5502
  exports.toMigrationFileSlug = toMigrationFileSlug;
5188
- exports.toModelName = toModelName;
5503
+ exports.toModelName = toModelName;
5504
+ exports.writeAppliedMigrationsState = writeAppliedMigrationsState;
package/dist/index.d.cts CHANGED
@@ -79,6 +79,23 @@ interface PrismaMigrationWorkflowOptions extends PrismaSchemaSyncOptions {
79
79
  migrateMode?: 'dev' | 'deploy';
80
80
  migrationName?: string;
81
81
  }
82
+ interface AppliedMigrationEntry {
83
+ id: string;
84
+ file: string;
85
+ className: string;
86
+ appliedAt: string;
87
+ checksum?: string;
88
+ }
89
+ interface AppliedMigrationRun {
90
+ id: string;
91
+ appliedAt: string;
92
+ migrationIds: string[];
93
+ }
94
+ interface AppliedMigrationsState {
95
+ version: 1;
96
+ migrations: AppliedMigrationEntry[];
97
+ runs?: AppliedMigrationRun[];
98
+ }
82
99
  //#endregion
83
100
  //#region src/Collection.d.ts
84
101
  declare class ArkormCollection<T = any, X = T[]> extends Collection<T, X> {}
@@ -2251,6 +2268,35 @@ declare class MigrateCommand extends Command<CliApp> {
2251
2268
  private loadMigrationClassesFromFile;
2252
2269
  }
2253
2270
  //#endregion
2271
+ //#region src/cli/commands/MigrateRollbackCommand.d.ts
2272
+ /**
2273
+ * Rollback migration classes from the Prisma schema and run Prisma workflow.
2274
+ * By default, rolls back classes applied in the last migrate run.
2275
+ *
2276
+ * @author Legacy (3m1n3nc3)
2277
+ * @since 0.2.4
2278
+ */
2279
+ declare class MigrateRollbackCommand extends Command<CliApp> {
2280
+ protected signature: string;
2281
+ protected description: string;
2282
+ handle(): Promise<undefined>;
2283
+ private loadAllMigrations;
2284
+ private loadMigrationClassesFromFile;
2285
+ }
2286
+ //#endregion
2287
+ //#region src/cli/commands/MigrationHistoryCommand.d.ts
2288
+ /**
2289
+ * The MigrationHistoryCommand class manages tracked migration run history.
2290
+ *
2291
+ * @author Legacy (3m1n3nc3)
2292
+ * @since 0.2.4
2293
+ */
2294
+ declare class MigrationHistoryCommand extends Command<CliApp> {
2295
+ protected signature: string;
2296
+ protected description: string;
2297
+ handle(): Promise<void>;
2298
+ }
2299
+ //#endregion
2254
2300
  //#region src/cli/commands/ModelsSyncCommand.d.ts
2255
2301
  declare class ModelsSyncCommand extends Command<CliApp> {
2256
2302
  protected signature: string;
@@ -2731,6 +2777,21 @@ declare class ModelNotFoundException extends ArkormException {
2731
2777
  getModelName(): string;
2732
2778
  }
2733
2779
  //#endregion
2780
+ //#region src/helpers/migration-history.d.ts
2781
+ declare const resolveMigrationStateFilePath: (cwd: string, configuredPath?: string) => string;
2782
+ declare const buildMigrationIdentity: (filePath: string, className: string) => string;
2783
+ declare const computeMigrationChecksum: (filePath: string) => string;
2784
+ declare const readAppliedMigrationsState: (stateFilePath: string) => AppliedMigrationsState;
2785
+ declare const writeAppliedMigrationsState: (stateFilePath: string, state: AppliedMigrationsState) => void;
2786
+ declare const isMigrationApplied: (state: AppliedMigrationsState, identity: string, checksum?: string) => boolean;
2787
+ declare const findAppliedMigration: (state: AppliedMigrationsState, identity: string) => AppliedMigrationEntry | undefined;
2788
+ declare const markMigrationApplied: (state: AppliedMigrationsState, entry: AppliedMigrationEntry) => AppliedMigrationsState;
2789
+ declare const removeAppliedMigration: (state: AppliedMigrationsState, identity: string) => AppliedMigrationsState;
2790
+ declare const buildMigrationRunId: () => string;
2791
+ declare const markMigrationRun: (state: AppliedMigrationsState, run: AppliedMigrationRun) => AppliedMigrationsState;
2792
+ declare const getLastMigrationRun: (state: AppliedMigrationsState) => AppliedMigrationRun | undefined;
2793
+ declare const getLatestAppliedMigrations: (state: AppliedMigrationsState, steps: number) => AppliedMigrationEntry[];
2794
+ //#endregion
2734
2795
  //#region src/helpers/migrations.d.ts
2735
2796
  declare const PRISMA_MODEL_REGEX: RegExp;
2736
2797
  /**
@@ -2956,6 +3017,18 @@ declare const applyMigrationToPrismaSchema: (migration: Migration | (new () => M
2956
3017
  schemaPath: string;
2957
3018
  operations: SchemaOperation[];
2958
3019
  }>;
3020
+ /**
3021
+ * Apply the rollback (down) operations defined in a migration to a Prisma schema file.
3022
+ *
3023
+ * @param migration The migration class or instance to rollback.
3024
+ * @param options Options for applying the rollback, including schema path and write flag.
3025
+ * @returns A promise that resolves to an object containing the updated schema, schema path, and rollback operations applied.
3026
+ */
3027
+ declare const applyMigrationRollbackToPrismaSchema: (migration: Migration | (new () => Migration), options?: PrismaSchemaSyncOptions) => Promise<{
3028
+ schema: string;
3029
+ schemaPath: string;
3030
+ operations: SchemaOperation[];
3031
+ }>;
2959
3032
  /**
2960
3033
  * Run a migration by applying its schema operations to a Prisma schema
2961
3034
  * file, optionally generating Prisma client code and running migrations after
@@ -3080,4 +3153,4 @@ declare class URLDriver {
3080
3153
  url(page: number): string;
3081
3154
  }
3082
3155
  //#endregion
3083
- export { ArkormCollection, ArkormException, CliApp, ForeignKeyBuilder, InitCommand, InlineFactory, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, Migration, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_MODEL_REGEX, Paginator, PrismaDelegateMap, QueryBuilder, SEEDER_BRAND, SchemaBuilder, SeedCommand, Seeder, SeederCallArgument, SeederConstructor, SeederInput, 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 };
3156
+ 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, PrismaDelegateMap, QueryBuilder, SEEDER_BRAND, SchemaBuilder, SeedCommand, Seeder, SeederCallArgument, SeederConstructor, SeederInput, 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 };