@stacksjs/database 0.64.6 → 0.66.0

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.
@@ -1,82 +1,108 @@
1
+ import type { Attribute, Attributes, Model } from '@stacksjs/types'
1
2
  import { italic, log } from '@stacksjs/cli'
3
+ import { app } from '@stacksjs/config'
2
4
  import { db } from '@stacksjs/database'
3
- import { ok } from '@stacksjs/error-handling'
4
- import { getModelName, getTableName } from '@stacksjs/orm'
5
+ import { type Ok, ok } from '@stacksjs/error-handling'
6
+ import { fetchOtherModelRelations, getModelName, getPivotTables, getTableName } from '@stacksjs/orm'
5
7
  import { path } from '@stacksjs/path'
6
- import { fs, glob } from '@stacksjs/storage'
8
+ import { fs, globSync } from '@stacksjs/storage'
7
9
  import { snakeCase } from '@stacksjs/strings'
8
- import type { Attribute, Attributes, Model } from '@stacksjs/types'
9
10
  import {
10
11
  arrangeColumns,
11
12
  checkPivotMigration,
12
- fetchOtherModelRelations,
13
+ fetchTables,
13
14
  findDifferingKeys,
14
15
  getLastMigrationFields,
15
- getPivotTables,
16
16
  hasTableBeenMigrated,
17
17
  isArrayEqual,
18
18
  mapFieldTypeToColumnType,
19
19
  pluckChanges,
20
20
  } from '.'
21
21
 
22
- export async function resetSqliteDatabase() {
23
- const dbPath = path.userDatabasePath('stacks.sqlite')
22
+ export async function resetSqliteDatabase(): Promise<Ok<string, never>> {
23
+ await deleteFrameworkModels()
24
+ await deleteMigrationFiles()
25
+ await dropSqliteTables()
24
26
 
25
- if (fs.existsSync(dbPath)) await Bun.$`rm ${dbPath}`
27
+ return ok('All tables dropped successfully!')
28
+ }
26
29
 
30
+ export async function deleteMigrationFiles(): Promise<void> {
27
31
  const files = await fs.readdir(path.userMigrationsPath())
28
- const modelFiles = await fs.readdir(path.frameworkPath('database/models'))
29
-
30
- const userModelFiles = glob.sync(path.userModelsPath('*.ts'))
31
32
 
32
- for (const userModel of userModelFiles) {
33
- const userModelPath = (await import(userModel)).default
34
-
35
- const pivotTables = await getPivotTables(userModelPath, userModel)
33
+ if (files.length) {
34
+ for (const file of files) {
35
+ if (file.endsWith('.ts')) {
36
+ const migrationPath = path.userMigrationsPath(`${file}`)
36
37
 
37
- for (const pivotTable of pivotTables) await db.schema.dropTable(pivotTable.table).ifExists().execute()
38
+ if (fs.existsSync(migrationPath))
39
+ await Bun.$`rm ${migrationPath}`
40
+ }
41
+ }
38
42
  }
43
+ }
44
+
45
+ export async function deleteFrameworkModels(): Promise<void> {
46
+ const modelFiles = await fs.readdir(path.frameworkPath('database/models'))
39
47
 
40
48
  if (modelFiles.length) {
41
49
  for (const modelFile of modelFiles) {
42
50
  if (modelFile.endsWith('.ts')) {
43
51
  const modelPath = path.frameworkPath(`database/models/${modelFile}`)
44
52
 
45
- if (fs.existsSync(modelPath)) await Bun.$`rm ${modelPath}`
53
+ if (fs.existsSync(modelPath))
54
+ await Bun.$`rm ${modelPath}`
46
55
  }
47
56
  }
48
57
  }
58
+ }
49
59
 
50
- if (files.length) {
51
- for (const file of files) {
52
- if (file.endsWith('.ts')) {
53
- const migrationPath = path.userMigrationsPath(`${file}`)
60
+ export async function dropSqliteTables(): Promise<void> {
61
+ const userModelFiles = globSync([path.userModelsPath('*.ts')], { absolute: true })
62
+ const tables = await fetchTables()
54
63
 
55
- if (fs.existsSync(migrationPath)) await Bun.$`rm ${migrationPath}`
56
- }
57
- }
64
+ for (const table of tables) await db.schema.dropTable(table).ifExists().execute()
65
+ await db.schema.dropTable('migrations').ifExists().execute()
66
+ await db.schema.dropTable('migration_locks').ifExists().execute()
67
+ await db.schema.dropTable('passkeys').ifExists().execute()
68
+
69
+ for (const userModel of userModelFiles) {
70
+ const userModelPath = (await import(userModel)).default
71
+ const pivotTables = await getPivotTables(userModelPath, userModel)
72
+
73
+ for (const pivotTable of pivotTables) await db.schema.dropTable(pivotTable.table).ifExists().execute()
58
74
  }
75
+ }
59
76
 
60
- return ok('All tables dropped successfully!')
77
+ export function fetchSqliteFile(): string {
78
+ if (app.env === 'testing') {
79
+ return fetchTestSqliteFile()
80
+ }
81
+
82
+ return path.userDatabasePath('stacks.sqlite')
83
+ }
84
+
85
+ export function fetchTestSqliteFile(): string {
86
+ return path.userDatabasePath('stacks_testing.sqlite')
61
87
  }
62
88
 
63
- export async function generateSqliteMigration(modelPath: string) {
89
+ export async function generateSqliteMigration(modelPath: string): Promise<void> {
64
90
  // check if any files are in the database folder
65
- const files = await fs.readdir(path.userMigrationsPath())
91
+ // const files = await fs.readdir(path.userMigrationsPath())
66
92
 
67
- if (files.length === 0) {
68
- log.debug('No migrations found in the database folder, deleting all framework/database/*.json files...')
93
+ // if (files.length === 0) {
94
+ // log.debug('No migrations found in the database folder, deleting all framework/database/*.json files...')
69
95
 
70
- // delete the *.ts files in the database/models folder
71
- const modelFiles = await fs.readdir(path.frameworkPath('database/models'))
96
+ // // delete the *.ts files in the database/models folder
97
+ // const modelFiles = await fs.readdir(path.frameworkPath('database/models'))
72
98
 
73
- if (modelFiles.length) {
74
- log.debug('No existing model files in framework path...')
99
+ // if (modelFiles.length) {
100
+ // log.debug('No existing model files in framework path...')
75
101
 
76
- for (const file of modelFiles)
77
- if (file.endsWith('.ts')) await fs.unlink(path.frameworkPath(`database/models/${file}`))
78
- }
79
- }
102
+ // for (const file of modelFiles)
103
+ // if (file.endsWith('.ts')) await fs.unlink(path.frameworkPath(`database/models/${file}`))
104
+ // }
105
+ // }
80
106
 
81
107
  const model = (await import(modelPath)).default as Model
82
108
  const fileName = path.basename(modelPath)
@@ -101,7 +127,8 @@ export async function generateSqliteMigration(modelPath: string) {
101
127
 
102
128
  haveFieldsChanged = true
103
129
  log.debug(`Fields have changed for ${tableName}`)
104
- } else {
130
+ }
131
+ else {
105
132
  log.debug(`Fields have not been generated for ${tableName}`)
106
133
  }
107
134
 
@@ -116,17 +143,43 @@ export async function generateSqliteMigration(modelPath: string) {
116
143
 
117
144
  log.debug(`Has ${tableName} been migrated? ${hasBeenMigrated}`)
118
145
 
119
- if (haveFieldsChanged) await createAlterTableMigration(modelPath)
146
+ if (haveFieldsChanged)
147
+ await createAlterTableMigration(modelPath)
120
148
  else await createTableMigration(modelPath)
121
149
  }
122
150
 
151
+ export async function copyModelFiles(modelPath: string): Promise<void> {
152
+ const model = (await import(modelPath)).default as Model
153
+ const fileName = path.basename(modelPath)
154
+ const tableName = await getTableName(model, modelPath)
155
+
156
+ const fieldsString = JSON.stringify(model.attributes, null, 2) // Pretty print the JSON
157
+ const copiedModelPath = path.frameworkPath(`database/models/${fileName}`)
158
+
159
+ // if the file exists, we need to check if the fields have changed
160
+ if (fs.existsSync(copiedModelPath)) {
161
+ log.debug(`Fields have already been generated for ${tableName}`)
162
+
163
+ const previousFields = await getLastMigrationFields(fileName)
164
+ const previousFieldsString = JSON.stringify(previousFields, null, 2) // Convert to string for comparison
165
+
166
+ if (previousFieldsString === fieldsString) {
167
+ log.debug(`Fields have not changed for ${tableName}`)
168
+ return
169
+ }
170
+ }
171
+
172
+ // store the fields of the model to a file
173
+ await Bun.$`cp ${modelPath} ${copiedModelPath}`
174
+ }
123
175
  async function createTableMigration(modelPath: string) {
124
176
  log.debug('createTableMigration modelPath:', modelPath)
125
177
 
126
178
  const model = (await import(modelPath)).default as Model
127
- const tableName = await getTableName(model, modelPath)
179
+ const tableName = getTableName(model, modelPath)
128
180
 
129
- const twoFactorEnabled = model.traits?.useAuth?.useTwoFactor
181
+ const twoFactorEnabled
182
+ = model.traits?.useAuth && typeof model.traits.useAuth !== 'boolean' ? model.traits.useAuth.useTwoFactor : false
130
183
 
131
184
  await createPivotTableMigration(model, modelPath)
132
185
  const otherModelRelations = await fetchOtherModelRelations(model, modelPath)
@@ -134,6 +187,11 @@ async function createTableMigration(modelPath: string) {
134
187
  const useTimestamps = model?.traits?.useTimestamps ?? model?.traits?.timestampable ?? true
135
188
  const useSoftDeletes = model?.traits?.useSoftDeletes ?? model?.traits?.softDeletable ?? false
136
189
 
190
+ const usePasskey = (typeof model.traits?.useAuth === 'object' && model.traits.useAuth.usePasskey) ?? false
191
+
192
+ if (usePasskey)
193
+ await createPasskeyMigration()
194
+
137
195
  let migrationContent = `import type { Database } from '@stacksjs/database'\n`
138
196
  migrationContent += `import { sql } from '@stacksjs/database'\n\n`
139
197
  migrationContent += `export async function up(db: Database<any>) {\n`
@@ -144,21 +202,23 @@ async function createTableMigration(modelPath: string) {
144
202
  for (const [fieldName, options] of arrangeColumns(model.attributes)) {
145
203
  const fieldOptions = options as Attribute
146
204
  const fieldNameFormatted = snakeCase(fieldName)
147
- const columnType = mapFieldTypeToColumnType(fieldOptions.validation?.rule)
205
+ const columnType = mapFieldTypeToColumnType(fieldOptions.validation?.rule, 'sqlite')
148
206
  migrationContent += ` .addColumn('${fieldNameFormatted}', ${columnType}`
149
207
 
150
208
  // Check if there are configurations that require the lambda function
151
209
  if (fieldOptions.unique || fieldOptions?.required) {
152
210
  migrationContent += `, col => col`
153
- if (fieldOptions.unique) migrationContent += `.unique()`
154
- if (fieldOptions?.required) migrationContent += `.notNull()`
211
+ if (fieldOptions.unique)
212
+ migrationContent += `.unique()`
213
+ if (fieldOptions?.required)
214
+ migrationContent += `.notNull()`
155
215
  migrationContent += ``
156
216
  }
157
217
 
158
218
  migrationContent += `)\n`
159
219
  }
160
220
 
161
- if (false !== twoFactorEnabled && twoFactorEnabled) {
221
+ if (twoFactorEnabled !== false && twoFactorEnabled) {
162
222
  migrationContent += ` .addColumn('two_factor_secret', 'varchar(255)')\n`
163
223
  }
164
224
 
@@ -170,14 +230,18 @@ async function createTableMigration(modelPath: string) {
170
230
  }
171
231
  }
172
232
 
233
+ if (usePasskey)
234
+ migrationContent += ` .addColumn('public_passkey', 'text')\n`
235
+
173
236
  // Append created_at and updated_at columns if useTimestamps is true
174
237
  if (useTimestamps) {
175
- migrationContent += ` .addColumn('created_at', 'timestamp', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))\n`
176
- migrationContent += ` .addColumn('updated_at', 'timestamp')\n`
238
+ migrationContent
239
+ += ' .addColumn(\'created_at\', \'timestamp\', col => col.notNull().defaultTo(sql`CURRENT_TIMESTAMP`))\n'
240
+ migrationContent += ' .addColumn(\'updated_at\', \'timestamp\')\n'
177
241
  }
178
242
 
179
- // Append deleted_at column if useSoftDeletes is true
180
- if (useSoftDeletes) migrationContent += ` .addColumn('deleted_at', 'timestamp')\n`
243
+ if (useSoftDeletes)
244
+ migrationContent += ` .addColumn('deleted_at', 'text')\n`
181
245
 
182
246
  migrationContent += ` .execute()\n`
183
247
  migrationContent += `}\n`
@@ -194,12 +258,14 @@ async function createTableMigration(modelPath: string) {
194
258
  async function createPivotTableMigration(model: Model, modelPath: string) {
195
259
  const pivotTables = await getPivotTables(model, modelPath)
196
260
 
197
- if (!pivotTables.length) return
261
+ if (!pivotTables.length)
262
+ return
198
263
 
199
264
  for (const pivotTable of pivotTables) {
200
265
  const hasBeenMigrated = await checkPivotMigration(pivotTable.table)
201
266
 
202
- if (hasBeenMigrated) return
267
+ if (hasBeenMigrated)
268
+ return
203
269
 
204
270
  let migrationContent = `import type { Database } from '@stacksjs/database'\n`
205
271
  migrationContent += `export async function up(db: Database<any>) {\n`
@@ -213,6 +279,7 @@ async function createPivotTableMigration(model: Model, modelPath: string) {
213
279
 
214
280
  const timestamp = new Date().getTime().toString()
215
281
  const migrationFileName = `${timestamp}-create-${pivotTable.table}-table.ts`
282
+
216
283
  const migrationFilePath = path.userMigrationsPath(migrationFileName)
217
284
 
218
285
  Bun.write(migrationFilePath, migrationContent)
@@ -221,12 +288,45 @@ async function createPivotTableMigration(model: Model, modelPath: string) {
221
288
  }
222
289
  }
223
290
 
224
- export async function createAlterTableMigration(modelPath: string) {
225
- console.log('createAlterTableMigration')
291
+ async function createPasskeyMigration() {
292
+ const hasBeenMigrated = await hasTableBeenMigrated('users')
293
+
294
+ if (hasBeenMigrated)
295
+ return
226
296
 
297
+ let migrationContent = `import type { Database } from '@stacksjs/database'\n import { sql } from '@stacksjs/database'\n\n`
298
+ migrationContent += `export async function up(db: Database<any>) {\n`
299
+ migrationContent += ` await db.schema\n`
300
+ migrationContent += ` .createTable('passkeys')\n`
301
+ migrationContent += ` .addColumn('id', 'text')\n`
302
+ migrationContent += ` .addColumn('cred_public_key', 'text')\n`
303
+ migrationContent += ` .addColumn('user_id', 'integer')\n`
304
+ migrationContent += ` .addColumn('webauthn_user_id', 'varchar(255)')\n`
305
+ migrationContent += ` .addColumn('counter', 'integer')\n`
306
+ migrationContent += ` .addColumn('device_type', 'varchar(255)')\n`
307
+ migrationContent += ` .addColumn('credential_type', 'varchar(255)')\n`
308
+ migrationContent += ` .addColumn('backup_eligible', 'boolean')\n`
309
+ migrationContent += ` .addColumn('backup_status', 'boolean')\n`
310
+ migrationContent += ` .addColumn('transports', 'varchar(255)')\n`
311
+ migrationContent += ` .addColumn('last_used_at', 'text')\n`
312
+ migrationContent += ` .addColumn('created_at', 'timestamp', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))\n`
313
+ migrationContent += ` .execute()\n`
314
+ migrationContent += ` }\n`
315
+
316
+ const timestamp = new Date().getTime().toString()
317
+ const migrationFileName = `${timestamp}-create-passkeys-table.ts`
318
+
319
+ const migrationFilePath = path.userMigrationsPath(migrationFileName)
320
+
321
+ Bun.write(migrationFilePath, migrationContent)
322
+
323
+ log.success(`Created pivot migration: ${migrationFileName}`)
324
+ }
325
+
326
+ async function createAlterTableMigration(modelPath: string) {
227
327
  const model = (await import(modelPath)).default as Model
228
328
  const modelName = getModelName(model, modelPath)
229
- const tableName = await getTableName(model, modelPath)
329
+ const tableName = getTableName(model, modelPath)
230
330
  let hasChanged = false
231
331
 
232
332
  // Assuming you have a function to get the fields from the last migration
@@ -266,7 +366,7 @@ export async function createAlterTableMigration(modelPath: string) {
266
366
  // Add new fields
267
367
  for (const fieldName of fieldsToAdd) {
268
368
  const options = currentFields[fieldName] as Attribute
269
- const columnType = mapFieldTypeToColumnType(options.validation?.rule)
369
+ const columnType = mapFieldTypeToColumnType(options.validation?.rule, 'sqlite')
270
370
  const formattedFieldName = snakeCase(fieldName)
271
371
 
272
372
  migrationContent += ` .addColumn('${formattedFieldName}', ${columnType}`
@@ -274,8 +374,10 @@ export async function createAlterTableMigration(modelPath: string) {
274
374
  // Check if there are configurations that require the lambda function
275
375
  if (options.unique || options?.required) {
276
376
  migrationContent += `, col => col`
277
- if (options.unique) migrationContent += `.unique()`
278
- if (options?.required) migrationContent += `.notNull()`
377
+ if (options.unique)
378
+ migrationContent += `.unique()`
379
+ if (options?.required)
380
+ migrationContent += `.notNull()`
279
381
  migrationContent += ``
280
382
  }
281
383
 
@@ -285,10 +387,11 @@ export async function createAlterTableMigration(modelPath: string) {
285
387
  // Remove fields that no longer exist
286
388
  for (const fieldName of fieldsToRemove) migrationContent += ` .dropColumn('${fieldName}')\n`
287
389
 
288
- if (fieldsToAdd.length || fieldsToRemove.length) migrationContent += ` .execute();\n`
390
+ if (fieldsToAdd.length || fieldsToRemove.length)
391
+ migrationContent += ` .execute();\n`
289
392
 
290
- const lastFieldOrder = Object.values(lastFields).map((attr) => attr.order)
291
- const currentFieldOrder = Object.values(currentFields).map((attr) => attr.order)
393
+ const lastFieldOrder = Object.values(lastFields).map(attr => attr.order)
394
+ const currentFieldOrder = Object.values(currentFields).map(attr => attr.order)
292
395
 
293
396
  if (!isArrayEqual(lastFieldOrder, currentFieldOrder)) {
294
397
  hasChanged = true
@@ -313,7 +416,7 @@ function reArrangeColumns(attributes: Attributes | undefined, tableName: string)
313
416
  let migrationContent = ''
314
417
 
315
418
  let previousField = ''
316
- for (const [fieldName, options] of fields) {
419
+ for (const [fieldName] of fields) {
317
420
  const fieldNameFormatted = snakeCase(fieldName)
318
421
 
319
422
  if (previousField) {
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
- export * from './schema'
1
+ export * from './drivers'
2
2
  export * from './migrations'
3
+ export * from './schema'
3
4
  export * from './seeder'
4
5
  export * from './types'
5
6
  export * from './utils'
package/src/migrations.ts CHANGED
@@ -1,20 +1,16 @@
1
- import { dim, italic, log } from '@stacksjs/cli'
1
+ import { $ } from 'bun'
2
+ import { log } from '@stacksjs/cli'
2
3
  import { database } from '@stacksjs/config'
3
- import { err, ok } from '@stacksjs/error-handling'
4
- import { extractFieldsFromModel } from '@stacksjs/orm'
4
+ import { type Err, err, handleError, type Ok, ok, type Result } from '@stacksjs/error-handling'
5
5
  import { path } from '@stacksjs/path'
6
- import { fs, glob } from '@stacksjs/storage'
7
- import type { Attribute, Attributes } from '@stacksjs/types'
8
- import { $ } from 'bun'
9
- import { FileMigrationProvider, Migrator } from 'kysely'
10
- import { generateMysqlMigration, resetMysqlDatabase } from './drivers'
11
- import { generatePostgresMigration, resetPostgresDatabase } from './drivers'
12
- import { generateSqliteMigration, resetSqliteDatabase } from './drivers'
6
+ import { fs, globSync } from '@stacksjs/storage'
7
+ import { FileMigrationProvider, type MigrationResult, Migrator } from 'kysely'
8
+ import { generateMysqlMigration, generatePostgresMigration, generateSqliteMigration, resetMysqlDatabase, resetPostgresDatabase, resetSqliteDatabase } from './drivers'
13
9
  import { db } from './utils'
14
10
 
15
11
  const driver = database.default || ''
16
12
 
17
- export const migrator = new Migrator({
13
+ export const migrator: Migrator = new Migrator({
18
14
  db,
19
15
 
20
16
  provider: new FileMigrationProvider({
@@ -39,15 +35,14 @@ export const migrator = new Migrator({
39
35
  // }),
40
36
  // })
41
37
 
42
- export async function runDatabaseMigration() {
38
+ export async function runDatabaseMigration(): Promise<Result<MigrationResult[] | string, Error>> {
43
39
  try {
44
40
  log.info('Migrating database...')
45
41
 
46
42
  const { error, results } = await migrator.migrateToLatest()
47
43
 
48
44
  if (error) {
49
- log.error(error)
50
- return err(error)
45
+ return err(handleError(error))
51
46
  }
52
47
 
53
48
  if (results?.length === 0) {
@@ -55,13 +50,14 @@ export async function runDatabaseMigration() {
55
50
  return ok('No new migrations were executed')
56
51
  }
57
52
 
58
- if (results) return ok(results)
53
+ if (results)
54
+ return ok(results)
59
55
 
60
56
  log.success('Database migration completed with no new migrations.')
61
57
  return ok('Database migration completed with no new migrations.')
62
- } catch (error) {
63
- console.error('Migration failed:', error)
64
- return err(error)
58
+ }
59
+ catch (error) {
60
+ return err(handleError('Migration failed', error))
65
61
  }
66
62
  }
67
63
 
@@ -70,21 +66,22 @@ export interface MigrationOptions {
70
66
  up: string
71
67
  }
72
68
 
73
- export async function resetDatabase() {
74
- if (driver === 'sqlite') return resetSqliteDatabase()
75
-
76
- if (driver === 'mysql') return resetMysqlDatabase()
77
-
78
- if (driver === 'postgres') return resetPostgresDatabase()
69
+ export async function resetDatabase(): Promise<Ok<string, never>> {
70
+ if (driver === 'sqlite')
71
+ return await resetSqliteDatabase()
72
+ if (driver === 'mysql')
73
+ return await resetMysqlDatabase()
74
+ if (driver === 'postgres')
75
+ return await resetPostgresDatabase()
79
76
 
80
77
  throw new Error('Unsupported database driver in resetDatabase')
81
78
  }
82
79
 
83
- export async function generateMigrations() {
80
+ export async function generateMigrations(): Promise<Ok<string, never> | Err<string, any>> {
84
81
  try {
85
82
  log.info('Generating migrations...')
86
83
 
87
- const modelFiles = glob.sync(path.userModelsPath('*.ts'))
84
+ const modelFiles = globSync([path.userModelsPath('*.ts')], { absolute: true })
88
85
 
89
86
  for (const file of modelFiles) {
90
87
  log.debug('Generating migration for:', file)
@@ -94,29 +91,24 @@ export async function generateMigrations() {
94
91
 
95
92
  log.success('Migrations generated')
96
93
  return ok('Migrations generated')
97
- } catch (error) {
94
+ }
95
+ catch (error) {
98
96
  return err(error)
99
97
  }
100
98
  }
101
99
 
102
- export async function generateMigration(modelPath: string) {
103
- if (driver === 'sqlite') await generateSqliteMigration(modelPath)
100
+ export async function generateMigration(modelPath: string): Promise<void> {
101
+ if (driver === 'sqlite')
102
+ await generateSqliteMigration(modelPath)
104
103
 
105
- if (driver === 'mysql') await generateMysqlMigration(modelPath)
106
-
107
- if (driver === 'postgres') await generatePostgresMigration(modelPath)
108
- }
104
+ if (driver === 'mysql')
105
+ await generateMysqlMigration(modelPath)
109
106
 
110
- export async function getExecutedMigrations() {
111
- try {
112
- // @ts-expect-error the migrations table is not typed yet
113
- return await db.selectFrom('migrations').select('name').execute()
114
- } catch (error) {
115
- return []
116
- }
107
+ if (driver === 'postgres')
108
+ await generatePostgresMigration(modelPath)
117
109
  }
118
110
 
119
- export async function haveModelFieldsChangedSinceLastMigration(modelPath: string) {
111
+ export async function haveModelFieldsChangedSinceLastMigration(modelPath: string): Promise<boolean> {
120
112
  log.debug(`haveModelFieldsChangedSinceLastMigration for model: ${modelPath}`)
121
113
 
122
114
  // const model = await import(modelPath)
@@ -134,11 +126,11 @@ export async function haveModelFieldsChangedSinceLastMigration(modelPath: string
134
126
  return !!gitHistory
135
127
  }
136
128
 
137
- export async function lastMigration() {
129
+ export async function lastMigration(): Promise<any> {
138
130
  try {
139
- // @ts-expect-error the migrations table is not typed yet
140
131
  return await db.selectFrom('migrations').selectAll().orderBy('timestamp', 'desc').limit(1).execute()
141
- } catch (error) {
132
+ }
133
+ catch (error) {
142
134
  console.error('Failed to get last migration:', error)
143
135
  return { error }
144
136
  }
@@ -146,28 +138,13 @@ export async function lastMigration() {
146
138
 
147
139
  export async function lastMigrationDate(): Promise<string | undefined> {
148
140
  try {
149
- // @ts-expect-error the migrations table is not typed yet
150
141
  return (await db.selectFrom('migrations').select('timestamp').orderBy('timestamp', 'desc').limit(1).execute())[0]
151
142
  .timestamp
152
- } catch (error) {
143
+ }
144
+ catch (error) {
153
145
  console.error('Failed to get last migration date:', error)
154
146
  return undefined
155
147
  }
156
148
  }
157
149
 
158
- // This is a placeholder function. You need to implement the logic to
159
- // read the last migration file and extract the fields that were modified.
160
- export async function getLastMigrationFields(modelName: string): Promise<Attribute> {
161
- const oldModelPath = path.frameworkPath(`database/models/${modelName}`)
162
- const model = (await import(oldModelPath)).default as Model
163
- let fields = {} as Attributes
164
-
165
- if (typeof model.attributes === 'object') fields = model.attributes
166
- else fields = JSON.parse(model.attributes) as Attributes
167
-
168
- return fields
169
- }
170
-
171
- export async function getCurrentMigrationFields(modelPath: string): Promise<Attribute | undefined> {
172
- return extractFieldsFromModel(modelPath)
173
- }
150
+ export type { MigrationResult }