arkormx 2.0.0-next.2 → 2.0.0-next.3
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 +164 -28
- package/dist/index.cjs +398 -29
- package/dist/index.d.cts +148 -96
- package/dist/index.d.mts +148 -96
- package/dist/index.mjs +387 -29
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -107,6 +107,8 @@ var UnsupportedAdapterFeatureException = class extends ArkormException {
|
|
|
107
107
|
* @since 2.0.0-next.0
|
|
108
108
|
*/
|
|
109
109
|
var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
110
|
+
static migrationStateTable = "arkormx_migrations";
|
|
111
|
+
static migrationRunTable = "arkormx_migration_runs";
|
|
110
112
|
capabilities = {
|
|
111
113
|
transactions: true,
|
|
112
114
|
returning: true,
|
|
@@ -124,6 +126,161 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
124
126
|
this.db = db;
|
|
125
127
|
this.mapping = mapping;
|
|
126
128
|
}
|
|
129
|
+
quoteIdentifier(value) {
|
|
130
|
+
return `"${value.replace(/"/g, "\"\"")}"`;
|
|
131
|
+
}
|
|
132
|
+
quoteLiteral(value) {
|
|
133
|
+
if (value == null) return "null";
|
|
134
|
+
if (typeof value === "number" || typeof value === "bigint") return String(value);
|
|
135
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
136
|
+
if (value instanceof Date) return `'${value.toISOString().replace(/'/g, "''")}'`;
|
|
137
|
+
return `'${String(value).replace(/'/g, "''")}'`;
|
|
138
|
+
}
|
|
139
|
+
async executeRawStatement(statement, executor = this.db) {
|
|
140
|
+
await kysely.sql.raw(statement).execute(executor);
|
|
141
|
+
}
|
|
142
|
+
resolveSchemaColumnName(columnName, columns = []) {
|
|
143
|
+
return columns.find((column) => column.name === columnName)?.map ?? columnName;
|
|
144
|
+
}
|
|
145
|
+
resolveSchemaIndexName(table, index) {
|
|
146
|
+
if (typeof index.name === "string" && index.name.trim().length > 0) return index.name;
|
|
147
|
+
return `${table}_${index.columns.join("_")}_idx`;
|
|
148
|
+
}
|
|
149
|
+
resolveSchemaForeignKeyName(table, foreignKey) {
|
|
150
|
+
return `${table}_${foreignKey.column}_fkey`;
|
|
151
|
+
}
|
|
152
|
+
resolveSchemaColumnType(column) {
|
|
153
|
+
if (column.type === "id") return "integer";
|
|
154
|
+
if (column.type === "uuid") return "uuid";
|
|
155
|
+
if (column.type === "enum") return this.quoteIdentifier(column.enumName ?? `${column.name}_enum`);
|
|
156
|
+
if (column.type === "string") return "varchar(255)";
|
|
157
|
+
if (column.type === "text") return "text";
|
|
158
|
+
if (column.type === "integer") return "integer";
|
|
159
|
+
if (column.type === "bigInteger") return "bigint";
|
|
160
|
+
if (column.type === "float") return "double precision";
|
|
161
|
+
if (column.type === "boolean") return "boolean";
|
|
162
|
+
if (column.type === "json") return "jsonb";
|
|
163
|
+
if (column.type === "date") return "date";
|
|
164
|
+
return "timestamptz";
|
|
165
|
+
}
|
|
166
|
+
resolveSchemaColumnDefault(column) {
|
|
167
|
+
const value = column.default ?? (column.updatedAt ? "now()" : void 0);
|
|
168
|
+
if (value === void 0) return null;
|
|
169
|
+
if (value === "now()") return "now()";
|
|
170
|
+
return this.quoteLiteral(value);
|
|
171
|
+
}
|
|
172
|
+
shouldUseIdentity(column) {
|
|
173
|
+
if (column.autoIncrement === false) return false;
|
|
174
|
+
if (column.type === "id") return column.default === void 0;
|
|
175
|
+
return column.autoIncrement === true;
|
|
176
|
+
}
|
|
177
|
+
buildSchemaColumnDefinition(column) {
|
|
178
|
+
const parts = [this.quoteIdentifier(column.map ?? column.name), this.resolveSchemaColumnType(column)];
|
|
179
|
+
if (this.shouldUseIdentity(column)) parts.push("generated by default as identity");
|
|
180
|
+
const defaultValue = this.resolveSchemaColumnDefault(column);
|
|
181
|
+
if (defaultValue && !this.shouldUseIdentity(column)) parts.push(`default ${defaultValue}`);
|
|
182
|
+
if (column.primary) parts.push("primary key");
|
|
183
|
+
else if (column.unique) parts.push("unique");
|
|
184
|
+
if (!column.nullable && !column.primary) parts.push("not null");
|
|
185
|
+
return parts.join(" ");
|
|
186
|
+
}
|
|
187
|
+
buildSchemaForeignKeyConstraint(table, foreignKey, columns = []) {
|
|
188
|
+
const localColumn = this.resolveSchemaColumnName(foreignKey.column, columns);
|
|
189
|
+
const referencedTable = this.resolveMappedTable(foreignKey.referencesTable);
|
|
190
|
+
const action = foreignKey.onDelete ? ` on delete ${foreignKey.onDelete === "setNull" ? "set null" : foreignKey.onDelete === "setDefault" ? "set default" : foreignKey.onDelete}` : "";
|
|
191
|
+
return `constraint ${this.quoteIdentifier(this.resolveSchemaForeignKeyName(table, foreignKey))} foreign key (${this.quoteIdentifier(localColumn)}) references ${this.quoteIdentifier(referencedTable)} (${this.quoteIdentifier(foreignKey.referencesColumn)})${action}`;
|
|
192
|
+
}
|
|
193
|
+
buildSchemaIndexStatement(table, index, columns = []) {
|
|
194
|
+
const mappedColumns = index.columns.map((column) => this.quoteIdentifier(this.resolveSchemaColumnName(column, columns))).join(", ");
|
|
195
|
+
return `create index if not exists ${this.quoteIdentifier(this.resolveSchemaIndexName(table, index))} on ${this.quoteIdentifier(table)} (${mappedColumns})`;
|
|
196
|
+
}
|
|
197
|
+
async ensureEnumTypes(columns, executor = this.db) {
|
|
198
|
+
for (const column of columns) {
|
|
199
|
+
if (column.type !== "enum") continue;
|
|
200
|
+
const enumName = column.enumName ?? `${column.name}_enum`;
|
|
201
|
+
if ((await kysely.sql`
|
|
202
|
+
select exists(
|
|
203
|
+
select 1
|
|
204
|
+
from pg_type
|
|
205
|
+
where typname = ${enumName}
|
|
206
|
+
) as exists
|
|
207
|
+
`.execute(executor)).rows[0]?.exists) continue;
|
|
208
|
+
const values = column.enumValues ?? [];
|
|
209
|
+
if (values.length === 0) throw new ArkormException(`Enum column [${column.name}] requires enum values for database-backed migrations.`);
|
|
210
|
+
await this.executeRawStatement(`create type ${this.quoteIdentifier(enumName)} as enum (${values.map((value) => this.quoteLiteral(value)).join(", ")})`, executor);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async executeCreateTableOperation(operation, executor) {
|
|
214
|
+
const table = this.resolveMappedTable(operation.table);
|
|
215
|
+
await this.ensureEnumTypes(operation.columns, executor);
|
|
216
|
+
const columnDefinitions = operation.columns.map((column) => this.buildSchemaColumnDefinition(column));
|
|
217
|
+
const foreignKeys = (operation.foreignKeys ?? []).map((foreignKey) => this.buildSchemaForeignKeyConstraint(table, foreignKey, operation.columns));
|
|
218
|
+
const definitions = [...columnDefinitions, ...foreignKeys].join(", ");
|
|
219
|
+
await this.executeRawStatement(`create table if not exists ${this.quoteIdentifier(table)} (${definitions})`, executor);
|
|
220
|
+
for (const index of operation.indexes ?? []) await this.executeRawStatement(this.buildSchemaIndexStatement(table, index, operation.columns), executor);
|
|
221
|
+
}
|
|
222
|
+
async executeAlterTableOperation(operation, executor) {
|
|
223
|
+
const table = this.resolveMappedTable(operation.table);
|
|
224
|
+
await this.ensureEnumTypes(operation.addColumns, executor);
|
|
225
|
+
for (const column of operation.addColumns) await this.executeRawStatement(`alter table ${this.quoteIdentifier(table)} add column if not exists ${this.buildSchemaColumnDefinition(column)}`, executor);
|
|
226
|
+
for (const column of operation.dropColumns) await this.executeRawStatement(`alter table ${this.quoteIdentifier(table)} drop column if exists ${this.quoteIdentifier(column)}`, executor);
|
|
227
|
+
for (const foreignKey of operation.addForeignKeys ?? []) await this.executeRawStatement(`alter table ${this.quoteIdentifier(table)} add ${this.buildSchemaForeignKeyConstraint(table, foreignKey, operation.addColumns)}`, executor);
|
|
228
|
+
for (const index of operation.addIndexes ?? []) await this.executeRawStatement(this.buildSchemaIndexStatement(table, index, operation.addColumns), executor);
|
|
229
|
+
}
|
|
230
|
+
async executeDropTableOperation(operation, executor) {
|
|
231
|
+
const table = this.resolveMappedTable(operation.table);
|
|
232
|
+
await this.executeRawStatement(`drop table if exists ${this.quoteIdentifier(table)} cascade`, executor);
|
|
233
|
+
}
|
|
234
|
+
async ensureMigrationStateTables(executor = this.db) {
|
|
235
|
+
await this.executeRawStatement(`
|
|
236
|
+
create table if not exists ${this.quoteIdentifier(KyselyDatabaseAdapter.migrationStateTable)} (
|
|
237
|
+
id text primary key,
|
|
238
|
+
file text not null,
|
|
239
|
+
class_name text not null,
|
|
240
|
+
applied_at timestamptz not null,
|
|
241
|
+
checksum text null
|
|
242
|
+
)
|
|
243
|
+
`, executor);
|
|
244
|
+
await this.executeRawStatement(`
|
|
245
|
+
create table if not exists ${this.quoteIdentifier(KyselyDatabaseAdapter.migrationRunTable)} (
|
|
246
|
+
id text primary key,
|
|
247
|
+
applied_at timestamptz not null,
|
|
248
|
+
migration_ids jsonb not null
|
|
249
|
+
)
|
|
250
|
+
`, executor);
|
|
251
|
+
}
|
|
252
|
+
async writeAppliedMigrationsStateInternal(state, executor) {
|
|
253
|
+
await this.ensureMigrationStateTables(executor);
|
|
254
|
+
await this.executeRawStatement(`delete from ${this.quoteIdentifier(KyselyDatabaseAdapter.migrationRunTable)}`, executor);
|
|
255
|
+
await this.executeRawStatement(`delete from ${this.quoteIdentifier(KyselyDatabaseAdapter.migrationStateTable)}`, executor);
|
|
256
|
+
for (const migration of state.migrations) await kysely.sql`
|
|
257
|
+
insert into ${kysely.sql.table(KyselyDatabaseAdapter.migrationStateTable)} (id, file, class_name, applied_at, checksum)
|
|
258
|
+
values (${migration.id}, ${migration.file}, ${migration.className}, ${migration.appliedAt}, ${migration.checksum ?? null})
|
|
259
|
+
`.execute(executor);
|
|
260
|
+
for (const run of state.runs ?? []) await kysely.sql`
|
|
261
|
+
insert into ${kysely.sql.table(KyselyDatabaseAdapter.migrationRunTable)} (id, applied_at, migration_ids)
|
|
262
|
+
values (${run.id}, ${run.appliedAt}, cast(${JSON.stringify(run.migrationIds)} as jsonb))
|
|
263
|
+
`.execute(executor);
|
|
264
|
+
}
|
|
265
|
+
async resetDatabaseInternal(executor) {
|
|
266
|
+
const tablesResult = await kysely.sql`
|
|
267
|
+
select table_name, table_schema
|
|
268
|
+
from information_schema.tables
|
|
269
|
+
where table_schema = current_schema()
|
|
270
|
+
and table_type = 'BASE TABLE'
|
|
271
|
+
order by table_name asc
|
|
272
|
+
`.execute(executor);
|
|
273
|
+
for (const row of tablesResult.rows) await this.executeRawStatement(`drop table if exists ${this.quoteIdentifier(row.table_schema)}.${this.quoteIdentifier(row.table_name)} cascade`, executor);
|
|
274
|
+
const enumTypesResult = await kysely.sql`
|
|
275
|
+
select t.typname as enum_name, n.nspname as enum_schema
|
|
276
|
+
from pg_type t
|
|
277
|
+
inner join pg_namespace n on n.oid = t.typnamespace
|
|
278
|
+
where t.typtype = 'e'
|
|
279
|
+
and n.nspname = current_schema()
|
|
280
|
+
order by t.typname asc
|
|
281
|
+
`.execute(executor);
|
|
282
|
+
for (const row of enumTypesResult.rows) await this.executeRawStatement(`drop type if exists ${this.quoteIdentifier(row.enum_schema)}.${this.quoteIdentifier(row.enum_name)} cascade`, executor);
|
|
283
|
+
}
|
|
127
284
|
introspectionTypeToTs(typeName, enumValues) {
|
|
128
285
|
if (enumValues && enumValues.length > 0) return enumValues.map((value) => `'${value.replace(/'/g, "\\'")}'`).join(" | ");
|
|
129
286
|
switch (typeName) {
|
|
@@ -750,6 +907,63 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
750
907
|
});
|
|
751
908
|
return [...models.values()];
|
|
752
909
|
}
|
|
910
|
+
async executeSchemaOperations(operations) {
|
|
911
|
+
if (operations.length === 0) return;
|
|
912
|
+
await this.transaction(async (adapter) => {
|
|
913
|
+
const transactionAdapter = adapter;
|
|
914
|
+
for (const operation of operations) {
|
|
915
|
+
if (operation.type === "createTable") {
|
|
916
|
+
await transactionAdapter.executeCreateTableOperation(operation, transactionAdapter.db);
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
if (operation.type === "alterTable") {
|
|
920
|
+
await transactionAdapter.executeAlterTableOperation(operation, transactionAdapter.db);
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
await transactionAdapter.executeDropTableOperation(operation, transactionAdapter.db);
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
async resetDatabase() {
|
|
928
|
+
await this.transaction(async (adapter) => {
|
|
929
|
+
const transactionAdapter = adapter;
|
|
930
|
+
await transactionAdapter.resetDatabaseInternal(transactionAdapter.db);
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
async readAppliedMigrationsState() {
|
|
934
|
+
await this.ensureMigrationStateTables();
|
|
935
|
+
const migrationsResult = await kysely.sql`
|
|
936
|
+
select id, file, class_name, applied_at, checksum
|
|
937
|
+
from ${kysely.sql.table(KyselyDatabaseAdapter.migrationStateTable)}
|
|
938
|
+
order by applied_at asc, id asc
|
|
939
|
+
`.execute(this.db);
|
|
940
|
+
const runsResult = await kysely.sql`
|
|
941
|
+
select id, applied_at, migration_ids
|
|
942
|
+
from ${kysely.sql.table(KyselyDatabaseAdapter.migrationRunTable)}
|
|
943
|
+
order by applied_at asc, id asc
|
|
944
|
+
`.execute(this.db);
|
|
945
|
+
return {
|
|
946
|
+
version: 1,
|
|
947
|
+
migrations: migrationsResult.rows.map((row) => ({
|
|
948
|
+
id: row.id,
|
|
949
|
+
file: row.file,
|
|
950
|
+
className: row.class_name,
|
|
951
|
+
appliedAt: row.applied_at instanceof Date ? row.applied_at.toISOString() : String(row.applied_at),
|
|
952
|
+
checksum: row.checksum ?? void 0
|
|
953
|
+
})),
|
|
954
|
+
runs: runsResult.rows.map((row) => ({
|
|
955
|
+
id: row.id,
|
|
956
|
+
appliedAt: row.applied_at instanceof Date ? row.applied_at.toISOString() : String(row.applied_at),
|
|
957
|
+
migrationIds: Array.isArray(row.migration_ids) ? row.migration_ids.filter((value) => typeof value === "string") : typeof row.migration_ids === "string" ? JSON.parse(row.migration_ids) : []
|
|
958
|
+
}))
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
async writeAppliedMigrationsState(state) {
|
|
962
|
+
await this.transaction(async (adapter) => {
|
|
963
|
+
const transactionAdapter = adapter;
|
|
964
|
+
await transactionAdapter.writeAppliedMigrationsStateInternal(state, transactionAdapter.db);
|
|
965
|
+
});
|
|
966
|
+
}
|
|
753
967
|
/**
|
|
754
968
|
* Executes a series of database operations within a transaction.
|
|
755
969
|
* The provided callback function is called with a new instance of the
|
|
@@ -2938,6 +3152,28 @@ const getMigrationPlan = async (migration, direction = "up") => {
|
|
|
2938
3152
|
else await instance.down(schema);
|
|
2939
3153
|
return schema.getOperations();
|
|
2940
3154
|
};
|
|
3155
|
+
const supportsDatabaseMigrationExecution = (adapter) => {
|
|
3156
|
+
return typeof adapter?.executeSchemaOperations === "function";
|
|
3157
|
+
};
|
|
3158
|
+
const supportsDatabaseReset = (adapter) => {
|
|
3159
|
+
return typeof adapter?.resetDatabase === "function";
|
|
3160
|
+
};
|
|
3161
|
+
const stripPrismaSchemaModelsAndEnums = (schema) => {
|
|
3162
|
+
const stripped = schema.replace(PRISMA_MODEL_REGEX, "").replace(PRISMA_ENUM_REGEX, "").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
3163
|
+
return stripped.length > 0 ? `${stripped}\n` : "";
|
|
3164
|
+
};
|
|
3165
|
+
const applyMigrationToDatabase = async (adapter, migration) => {
|
|
3166
|
+
if (!supportsDatabaseMigrationExecution(adapter)) throw new ArkormException("The configured adapter does not support database-backed migration execution.");
|
|
3167
|
+
const operations = await getMigrationPlan(migration, "up");
|
|
3168
|
+
await adapter.executeSchemaOperations(operations);
|
|
3169
|
+
return { operations };
|
|
3170
|
+
};
|
|
3171
|
+
const applyMigrationRollbackToDatabase = async (adapter, migration) => {
|
|
3172
|
+
if (!supportsDatabaseMigrationExecution(adapter)) throw new ArkormException("The configured adapter does not support database-backed migration execution.");
|
|
3173
|
+
const operations = await getMigrationPlan(migration, "down");
|
|
3174
|
+
await adapter.executeSchemaOperations(operations);
|
|
3175
|
+
return { operations };
|
|
3176
|
+
};
|
|
2941
3177
|
/**
|
|
2942
3178
|
* Apply the schema operations defined in a migration to a Prisma schema
|
|
2943
3179
|
* file, updating the file on disk if specified, and return the updated
|
|
@@ -3918,10 +4154,13 @@ var MakeSeederCommand = class extends _h3ravel_musket.Command {
|
|
|
3918
4154
|
|
|
3919
4155
|
//#endregion
|
|
3920
4156
|
//#region src/helpers/migration-history.ts
|
|
3921
|
-
const
|
|
4157
|
+
const createEmptyAppliedMigrationsState = () => ({
|
|
3922
4158
|
version: 1,
|
|
3923
4159
|
migrations: [],
|
|
3924
4160
|
runs: []
|
|
4161
|
+
});
|
|
4162
|
+
const supportsDatabaseMigrationState = (adapter) => {
|
|
4163
|
+
return typeof adapter?.readAppliedMigrationsState === "function" && typeof adapter?.writeAppliedMigrationsState === "function";
|
|
3925
4164
|
};
|
|
3926
4165
|
const resolveMigrationStateFilePath = (cwd, configuredPath) => {
|
|
3927
4166
|
if (configuredPath && configuredPath.trim().length > 0) return (0, node_path.resolve)(configuredPath);
|
|
@@ -3936,10 +4175,10 @@ const computeMigrationChecksum = (filePath) => {
|
|
|
3936
4175
|
return (0, node_crypto.createHash)("sha256").update(source).digest("hex");
|
|
3937
4176
|
};
|
|
3938
4177
|
const readAppliedMigrationsState = (stateFilePath) => {
|
|
3939
|
-
if (!(0, node_fs.existsSync)(stateFilePath)) return
|
|
4178
|
+
if (!(0, node_fs.existsSync)(stateFilePath)) return createEmptyAppliedMigrationsState();
|
|
3940
4179
|
try {
|
|
3941
4180
|
const parsed = JSON.parse((0, node_fs.readFileSync)(stateFilePath, "utf-8"));
|
|
3942
|
-
if (!Array.isArray(parsed.migrations)) return
|
|
4181
|
+
if (!Array.isArray(parsed.migrations)) return createEmptyAppliedMigrationsState();
|
|
3943
4182
|
return {
|
|
3944
4183
|
version: 1,
|
|
3945
4184
|
migrations: parsed.migrations.filter((migration) => {
|
|
@@ -3950,14 +4189,34 @@ const readAppliedMigrationsState = (stateFilePath) => {
|
|
|
3950
4189
|
}) : []
|
|
3951
4190
|
};
|
|
3952
4191
|
} catch {
|
|
3953
|
-
return
|
|
4192
|
+
return createEmptyAppliedMigrationsState();
|
|
3954
4193
|
}
|
|
3955
4194
|
};
|
|
4195
|
+
const readAppliedMigrationsStateFromStore = async (adapter, stateFilePath) => {
|
|
4196
|
+
if (supportsDatabaseMigrationState(adapter)) return await adapter.readAppliedMigrationsState();
|
|
4197
|
+
return readAppliedMigrationsState(stateFilePath);
|
|
4198
|
+
};
|
|
3956
4199
|
const writeAppliedMigrationsState = (stateFilePath, state) => {
|
|
3957
4200
|
const directory = (0, node_path.dirname)(stateFilePath);
|
|
3958
4201
|
if (!(0, node_fs.existsSync)(directory)) (0, node_fs.mkdirSync)(directory, { recursive: true });
|
|
3959
4202
|
(0, node_fs.writeFileSync)(stateFilePath, JSON.stringify(state, null, 2));
|
|
3960
4203
|
};
|
|
4204
|
+
const writeAppliedMigrationsStateToStore = async (adapter, stateFilePath, state) => {
|
|
4205
|
+
if (supportsDatabaseMigrationState(adapter)) {
|
|
4206
|
+
await adapter.writeAppliedMigrationsState(state);
|
|
4207
|
+
return;
|
|
4208
|
+
}
|
|
4209
|
+
writeAppliedMigrationsState(stateFilePath, state);
|
|
4210
|
+
};
|
|
4211
|
+
const deleteAppliedMigrationsStateFromStore = async (adapter, stateFilePath) => {
|
|
4212
|
+
if (supportsDatabaseMigrationState(adapter)) {
|
|
4213
|
+
await adapter.writeAppliedMigrationsState(createEmptyAppliedMigrationsState());
|
|
4214
|
+
return "database";
|
|
4215
|
+
}
|
|
4216
|
+
if (!(0, node_fs.existsSync)(stateFilePath)) return "missing-file";
|
|
4217
|
+
writeAppliedMigrationsState(stateFilePath, createEmptyAppliedMigrationsState());
|
|
4218
|
+
return "file";
|
|
4219
|
+
};
|
|
3961
4220
|
const isMigrationApplied = (state, identity, checksum) => {
|
|
3962
4221
|
const matched = state.migrations.find((migration) => migration.id === identity);
|
|
3963
4222
|
if (!matched) return false;
|
|
@@ -4076,7 +4335,9 @@ var MigrateCommand = class extends _h3ravel_musket.Command {
|
|
|
4076
4335
|
const classes = this.option("all") || !this.argument("name") ? await this.loadAllMigrations(migrationsDir) : (await this.loadNamedMigration(migrationsDir, this.argument("name"))).filter(([cls]) => cls !== void 0);
|
|
4077
4336
|
if (classes.length === 0) return void this.error("Error: No migration classes found to run.");
|
|
4078
4337
|
const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
|
|
4079
|
-
let appliedState =
|
|
4338
|
+
let appliedState = await readAppliedMigrationsStateFromStore(this.app.getConfig("adapter"), stateFilePath);
|
|
4339
|
+
const adapter = this.app.getConfig("adapter");
|
|
4340
|
+
const useDatabaseMigrations = supportsDatabaseMigrationExecution(adapter);
|
|
4080
4341
|
const skipped = [];
|
|
4081
4342
|
const changed = [];
|
|
4082
4343
|
const pending = classes.filter(([migrationClass, file]) => {
|
|
@@ -4098,10 +4359,16 @@ var MigrateCommand = class extends _h3ravel_musket.Command {
|
|
|
4098
4359
|
this.success("No pending migration classes to apply.");
|
|
4099
4360
|
return;
|
|
4100
4361
|
}
|
|
4101
|
-
for (const [MigrationClassItem] of pending)
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4362
|
+
for (const [MigrationClassItem] of pending) {
|
|
4363
|
+
if (useDatabaseMigrations) {
|
|
4364
|
+
await applyMigrationToDatabase(adapter, MigrationClassItem);
|
|
4365
|
+
continue;
|
|
4366
|
+
}
|
|
4367
|
+
await applyMigrationToPrismaSchema(MigrationClassItem, {
|
|
4368
|
+
schemaPath,
|
|
4369
|
+
write: true
|
|
4370
|
+
});
|
|
4371
|
+
}
|
|
4105
4372
|
if (appliedState) {
|
|
4106
4373
|
const runAppliedIds = [];
|
|
4107
4374
|
for (const [migrationClass, file] of pending) {
|
|
@@ -4120,10 +4387,10 @@ var MigrateCommand = class extends _h3ravel_musket.Command {
|
|
|
4120
4387
|
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4121
4388
|
migrationIds: runAppliedIds
|
|
4122
4389
|
});
|
|
4123
|
-
|
|
4390
|
+
await writeAppliedMigrationsStateToStore(adapter, stateFilePath, appliedState);
|
|
4124
4391
|
}
|
|
4125
|
-
if (!this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
|
|
4126
|
-
if (!this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
|
|
4392
|
+
if (!useDatabaseMigrations && !this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
|
|
4393
|
+
if (!useDatabaseMigrations && !this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
|
|
4127
4394
|
else runPrismaCommand([
|
|
4128
4395
|
"migrate",
|
|
4129
4396
|
"dev",
|
|
@@ -4183,6 +4450,85 @@ var MigrateCommand = class extends _h3ravel_musket.Command {
|
|
|
4183
4450
|
}
|
|
4184
4451
|
};
|
|
4185
4452
|
|
|
4453
|
+
//#endregion
|
|
4454
|
+
//#region src/cli/commands/MigrateFreshCommand.ts
|
|
4455
|
+
var MigrateFreshCommand = class extends _h3ravel_musket.Command {
|
|
4456
|
+
signature = `migrate:fresh
|
|
4457
|
+
{--skip-generate : Skip prisma generate}
|
|
4458
|
+
{--skip-migrate : Skip prisma database sync}
|
|
4459
|
+
{--state-file= : Path to applied migration state file}
|
|
4460
|
+
{--schema= : Explicit prisma schema path}
|
|
4461
|
+
`;
|
|
4462
|
+
description = "Reset the database and rerun all migration classes";
|
|
4463
|
+
async handle() {
|
|
4464
|
+
this.app.command = this;
|
|
4465
|
+
const configuredMigrationsDir = this.app.getConfig("paths")?.migrations ?? (0, node_path.join)(process.cwd(), "database", "migrations");
|
|
4466
|
+
const migrationsDir = this.app.resolveRuntimeDirectoryPath(configuredMigrationsDir);
|
|
4467
|
+
if (!(0, node_fs.existsSync)(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
|
|
4468
|
+
const adapter = this.app.getConfig("adapter");
|
|
4469
|
+
const useDatabaseMigrations = supportsDatabaseMigrationExecution(adapter);
|
|
4470
|
+
const schemaPath = this.option("schema") ? (0, node_path.resolve)(String(this.option("schema"))) : (0, node_path.join)(process.cwd(), "prisma", "schema.prisma");
|
|
4471
|
+
const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
|
|
4472
|
+
const migrations = await this.loadAllMigrations(migrationsDir);
|
|
4473
|
+
if (migrations.length === 0) return void this.error("Error: No migration classes found to run.");
|
|
4474
|
+
if (supportsDatabaseReset(adapter)) await adapter.resetDatabase();
|
|
4475
|
+
else {
|
|
4476
|
+
if (!(0, node_fs.existsSync)(schemaPath)) return void this.error(`Error: Prisma schema file not found: ${this.app.formatPathForLog(schemaPath)}`);
|
|
4477
|
+
(0, node_fs.writeFileSync)(schemaPath, stripPrismaSchemaModelsAndEnums((0, node_fs.readFileSync)(schemaPath, "utf-8")));
|
|
4478
|
+
}
|
|
4479
|
+
let appliedState = createEmptyAppliedMigrationsState();
|
|
4480
|
+
await writeAppliedMigrationsStateToStore(adapter, stateFilePath, appliedState);
|
|
4481
|
+
for (const [MigrationClassItem] of migrations) {
|
|
4482
|
+
if (useDatabaseMigrations) {
|
|
4483
|
+
await applyMigrationToDatabase(adapter, MigrationClassItem);
|
|
4484
|
+
continue;
|
|
4485
|
+
}
|
|
4486
|
+
await applyMigrationToPrismaSchema(MigrationClassItem, {
|
|
4487
|
+
schemaPath,
|
|
4488
|
+
write: true
|
|
4489
|
+
});
|
|
4490
|
+
}
|
|
4491
|
+
for (const [migrationClass, file] of migrations) appliedState = markMigrationApplied(appliedState, {
|
|
4492
|
+
id: buildMigrationIdentity(file, migrationClass.name),
|
|
4493
|
+
file,
|
|
4494
|
+
className: migrationClass.name,
|
|
4495
|
+
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4496
|
+
checksum: computeMigrationChecksum(file)
|
|
4497
|
+
});
|
|
4498
|
+
appliedState = markMigrationRun(appliedState, {
|
|
4499
|
+
id: buildMigrationRunId(),
|
|
4500
|
+
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4501
|
+
migrationIds: appliedState.migrations.map((migration) => migration.id)
|
|
4502
|
+
});
|
|
4503
|
+
await writeAppliedMigrationsStateToStore(adapter, stateFilePath, appliedState);
|
|
4504
|
+
if (!useDatabaseMigrations) {
|
|
4505
|
+
const schemaArgs = this.option("schema") ? ["--schema", schemaPath] : [];
|
|
4506
|
+
if (!this.option("skip-generate")) runPrismaCommand(["generate", ...schemaArgs], process.cwd());
|
|
4507
|
+
if (!this.option("skip-migrate")) runPrismaCommand([
|
|
4508
|
+
"db",
|
|
4509
|
+
"push",
|
|
4510
|
+
"--force-reset",
|
|
4511
|
+
...schemaArgs
|
|
4512
|
+
], process.cwd());
|
|
4513
|
+
}
|
|
4514
|
+
this.success(`Refreshed database with ${migrations.length} migration(s).`);
|
|
4515
|
+
migrations.forEach(([_, file]) => this.success(this.app.splitLogger("Migrated", file)));
|
|
4516
|
+
}
|
|
4517
|
+
async loadAllMigrations(migrationsDir) {
|
|
4518
|
+
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)));
|
|
4519
|
+
return (await Promise.all(files.map(async (file) => (await this.loadMigrationClassesFromFile(file)).map((cls) => [cls, file])))).flat();
|
|
4520
|
+
}
|
|
4521
|
+
async loadMigrationClassesFromFile(filePath) {
|
|
4522
|
+
const imported = await RuntimeModuleLoader.load(filePath);
|
|
4523
|
+
return Object.values(imported).filter((value) => {
|
|
4524
|
+
if (typeof value !== "function") return false;
|
|
4525
|
+
const candidate = value;
|
|
4526
|
+
const prototype = candidate.prototype;
|
|
4527
|
+
return candidate[MIGRATION_BRAND] === true || typeof prototype?.up === "function" && typeof prototype?.down === "function";
|
|
4528
|
+
});
|
|
4529
|
+
}
|
|
4530
|
+
};
|
|
4531
|
+
|
|
4186
4532
|
//#endregion
|
|
4187
4533
|
//#region src/cli/commands/MigrateRollbackCommand.ts
|
|
4188
4534
|
/**
|
|
@@ -4211,7 +4557,9 @@ var MigrateRollbackCommand = class extends _h3ravel_musket.Command {
|
|
|
4211
4557
|
if (!(0, node_fs.existsSync)(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
|
|
4212
4558
|
const schemaPath = this.option("schema") ? (0, node_path.resolve)(String(this.option("schema"))) : (0, node_path.join)(process.cwd(), "prisma", "schema.prisma");
|
|
4213
4559
|
const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
|
|
4214
|
-
|
|
4560
|
+
const adapter = this.app.getConfig("adapter");
|
|
4561
|
+
const useDatabaseMigrations = supportsDatabaseMigrationExecution(adapter);
|
|
4562
|
+
let appliedState = await readAppliedMigrationsStateFromStore(adapter, stateFilePath);
|
|
4215
4563
|
const stepOption = this.option("step");
|
|
4216
4564
|
const stepCount = stepOption == null ? void 0 : Number(stepOption);
|
|
4217
4565
|
if (stepCount != null && (!Number.isFinite(stepCount) || stepCount <= 0 || !Number.isInteger(stepCount))) return void this.error("Error: --step must be a positive integer.");
|
|
@@ -4233,17 +4581,23 @@ var MigrateRollbackCommand = class extends _h3ravel_musket.Command {
|
|
|
4233
4581
|
rollbackClasses.forEach(([_, file]) => this.success(this.app.splitLogger("WouldRollback", file)));
|
|
4234
4582
|
return;
|
|
4235
4583
|
}
|
|
4236
|
-
for (const [MigrationClassItem] of rollbackClasses)
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4584
|
+
for (const [MigrationClassItem] of rollbackClasses) {
|
|
4585
|
+
if (useDatabaseMigrations) {
|
|
4586
|
+
await applyMigrationRollbackToDatabase(adapter, MigrationClassItem);
|
|
4587
|
+
continue;
|
|
4588
|
+
}
|
|
4589
|
+
await applyMigrationRollbackToPrismaSchema(MigrationClassItem, {
|
|
4590
|
+
schemaPath,
|
|
4591
|
+
write: true
|
|
4592
|
+
});
|
|
4593
|
+
}
|
|
4240
4594
|
for (const [migrationClass, file] of rollbackClasses) {
|
|
4241
4595
|
const identity = buildMigrationIdentity(file, migrationClass.name);
|
|
4242
4596
|
appliedState = removeAppliedMigration(appliedState, identity);
|
|
4243
4597
|
}
|
|
4244
|
-
|
|
4245
|
-
if (!this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
|
|
4246
|
-
if (!this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
|
|
4598
|
+
await writeAppliedMigrationsStateToStore(adapter, stateFilePath, appliedState);
|
|
4599
|
+
if (!useDatabaseMigrations && !this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
|
|
4600
|
+
if (!useDatabaseMigrations && !this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
|
|
4247
4601
|
else runPrismaCommand([
|
|
4248
4602
|
"migrate",
|
|
4249
4603
|
"dev",
|
|
@@ -4287,7 +4641,14 @@ var MigrationHistoryCommand = class extends _h3ravel_musket.Command {
|
|
|
4287
4641
|
async handle() {
|
|
4288
4642
|
this.app.command = this;
|
|
4289
4643
|
const stateFilePath = resolveMigrationStateFilePath(process.cwd(), this.option("state-file") ? String(this.option("state-file")) : void 0);
|
|
4644
|
+
const adapter = this.app.getConfig("adapter");
|
|
4645
|
+
const usesDatabaseState = supportsDatabaseMigrationState(adapter);
|
|
4290
4646
|
if (this.option("delete")) {
|
|
4647
|
+
if (usesDatabaseState) {
|
|
4648
|
+
await adapter.writeAppliedMigrationsState(createEmptyAppliedMigrationsState());
|
|
4649
|
+
this.success("Deleted tracked migration state from database.");
|
|
4650
|
+
return;
|
|
4651
|
+
}
|
|
4291
4652
|
if (!(0, node_fs.existsSync)(stateFilePath)) {
|
|
4292
4653
|
this.success(`No migration state file found at ${this.app.formatPathForLog(stateFilePath)}`);
|
|
4293
4654
|
return;
|
|
@@ -4297,22 +4658,19 @@ var MigrationHistoryCommand = class extends _h3ravel_musket.Command {
|
|
|
4297
4658
|
return;
|
|
4298
4659
|
}
|
|
4299
4660
|
if (this.option("reset")) {
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
migrations: []
|
|
4303
|
-
});
|
|
4304
|
-
this.success(`Reset migration state: ${this.app.formatPathForLog(stateFilePath)}`);
|
|
4661
|
+
await writeAppliedMigrationsStateToStore(adapter, stateFilePath, createEmptyAppliedMigrationsState());
|
|
4662
|
+
this.success(usesDatabaseState ? "Reset migration state in database." : `Reset migration state: ${this.app.formatPathForLog(stateFilePath)}`);
|
|
4305
4663
|
return;
|
|
4306
4664
|
}
|
|
4307
|
-
const state =
|
|
4665
|
+
const state = await readAppliedMigrationsStateFromStore(adapter, stateFilePath);
|
|
4308
4666
|
if (this.option("json")) {
|
|
4309
4667
|
this.success(JSON.stringify({
|
|
4310
|
-
path: stateFilePath,
|
|
4668
|
+
path: usesDatabaseState ? "database" : stateFilePath,
|
|
4311
4669
|
...state
|
|
4312
4670
|
}, null, 2));
|
|
4313
4671
|
return;
|
|
4314
4672
|
}
|
|
4315
|
-
this.success(this.app.splitLogger("State", stateFilePath));
|
|
4673
|
+
this.success(this.app.splitLogger("State", usesDatabaseState ? "database" : stateFilePath));
|
|
4316
4674
|
this.success(this.app.splitLogger("Tracked", String(state.migrations.length)));
|
|
4317
4675
|
if (state.migrations.length === 0) {
|
|
4318
4676
|
this.success("No tracked migrations found.");
|
|
@@ -9295,6 +9653,7 @@ exports.MakeMigrationCommand = MakeMigrationCommand;
|
|
|
9295
9653
|
exports.MakeModelCommand = MakeModelCommand;
|
|
9296
9654
|
exports.MakeSeederCommand = MakeSeederCommand;
|
|
9297
9655
|
exports.MigrateCommand = MigrateCommand;
|
|
9656
|
+
exports.MigrateFreshCommand = MigrateFreshCommand;
|
|
9298
9657
|
exports.MigrateRollbackCommand = MigrateRollbackCommand;
|
|
9299
9658
|
exports.Migration = Migration;
|
|
9300
9659
|
exports.MigrationHistoryCommand = MigrationHistoryCommand;
|
|
@@ -9324,7 +9683,9 @@ exports.UnsupportedAdapterFeatureException = UnsupportedAdapterFeatureException;
|
|
|
9324
9683
|
exports.applyAlterTableOperation = applyAlterTableOperation;
|
|
9325
9684
|
exports.applyCreateTableOperation = applyCreateTableOperation;
|
|
9326
9685
|
exports.applyDropTableOperation = applyDropTableOperation;
|
|
9686
|
+
exports.applyMigrationRollbackToDatabase = applyMigrationRollbackToDatabase;
|
|
9327
9687
|
exports.applyMigrationRollbackToPrismaSchema = applyMigrationRollbackToPrismaSchema;
|
|
9688
|
+
exports.applyMigrationToDatabase = applyMigrationToDatabase;
|
|
9328
9689
|
exports.applyMigrationToPrismaSchema = applyMigrationToPrismaSchema;
|
|
9329
9690
|
exports.applyOperationsToPrismaSchema = applyOperationsToPrismaSchema;
|
|
9330
9691
|
exports.bindAdapterToModels = bindAdapterToModels;
|
|
@@ -9339,6 +9700,7 @@ exports.buildModelBlock = buildModelBlock;
|
|
|
9339
9700
|
exports.buildRelationLine = buildRelationLine;
|
|
9340
9701
|
exports.computeMigrationChecksum = computeMigrationChecksum;
|
|
9341
9702
|
exports.configureArkormRuntime = configureArkormRuntime;
|
|
9703
|
+
exports.createEmptyAppliedMigrationsState = createEmptyAppliedMigrationsState;
|
|
9342
9704
|
exports.createKyselyAdapter = createKyselyAdapter;
|
|
9343
9705
|
exports.createMigrationTimestamp = createMigrationTimestamp;
|
|
9344
9706
|
exports.createPrismaAdapter = createPrismaAdapter;
|
|
@@ -9347,6 +9709,7 @@ exports.createPrismaDatabaseAdapter = createPrismaDatabaseAdapter;
|
|
|
9347
9709
|
exports.createPrismaDelegateMap = createPrismaDelegateMap;
|
|
9348
9710
|
exports.defineConfig = defineConfig;
|
|
9349
9711
|
exports.defineFactory = defineFactory;
|
|
9712
|
+
exports.deleteAppliedMigrationsStateFromStore = deleteAppliedMigrationsStateFromStore;
|
|
9350
9713
|
exports.deriveCollectionFieldName = deriveCollectionFieldName;
|
|
9351
9714
|
exports.deriveInverseRelationAlias = deriveInverseRelationAlias;
|
|
9352
9715
|
exports.deriveRelationAlias = deriveRelationAlias;
|
|
@@ -9380,6 +9743,7 @@ exports.markMigrationApplied = markMigrationApplied;
|
|
|
9380
9743
|
exports.markMigrationRun = markMigrationRun;
|
|
9381
9744
|
exports.pad = pad;
|
|
9382
9745
|
exports.readAppliedMigrationsState = readAppliedMigrationsState;
|
|
9746
|
+
exports.readAppliedMigrationsStateFromStore = readAppliedMigrationsStateFromStore;
|
|
9383
9747
|
exports.removeAppliedMigration = removeAppliedMigration;
|
|
9384
9748
|
exports.resetArkormRuntimeForTests = resetArkormRuntimeForTests;
|
|
9385
9749
|
exports.resolveCast = resolveCast;
|
|
@@ -9390,6 +9754,11 @@ exports.resolvePrismaType = resolvePrismaType;
|
|
|
9390
9754
|
exports.runArkormTransaction = runArkormTransaction;
|
|
9391
9755
|
exports.runMigrationWithPrisma = runMigrationWithPrisma;
|
|
9392
9756
|
exports.runPrismaCommand = runPrismaCommand;
|
|
9757
|
+
exports.stripPrismaSchemaModelsAndEnums = stripPrismaSchemaModelsAndEnums;
|
|
9758
|
+
exports.supportsDatabaseMigrationExecution = supportsDatabaseMigrationExecution;
|
|
9759
|
+
exports.supportsDatabaseMigrationState = supportsDatabaseMigrationState;
|
|
9760
|
+
exports.supportsDatabaseReset = supportsDatabaseReset;
|
|
9393
9761
|
exports.toMigrationFileSlug = toMigrationFileSlug;
|
|
9394
9762
|
exports.toModelName = toModelName;
|
|
9395
|
-
exports.writeAppliedMigrationsState = writeAppliedMigrationsState;
|
|
9763
|
+
exports.writeAppliedMigrationsState = writeAppliedMigrationsState;
|
|
9764
|
+
exports.writeAppliedMigrationsStateToStore = writeAppliedMigrationsStateToStore;
|