@stacksjs/database 0.61.24 → 0.63.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.
- package/dist/index.js +9566 -7529
- package/dist/index.js.map +307 -0
- package/package.json +6 -6
- package/src/drivers/index.ts +142 -16
- package/src/drivers/mysql.ts +103 -31
- package/src/drivers/postgres.ts +17 -12
- package/src/drivers/sqlite.ts +93 -20
- package/src/migrations.ts +4 -3
- package/src/seeder.ts +103 -26
- package/src/utils.ts +2 -0
package/src/drivers/sqlite.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { italic, log } from '@stacksjs/cli'
|
|
2
2
|
import { db } from '@stacksjs/database'
|
|
3
3
|
import { ok } from '@stacksjs/error-handling'
|
|
4
|
-
import {
|
|
4
|
+
import { getModelName, getTableName } from '@stacksjs/orm'
|
|
5
5
|
import { path } from '@stacksjs/path'
|
|
6
6
|
import { fs, glob } from '@stacksjs/storage'
|
|
7
|
+
import { snakeCase } from '@stacksjs/strings'
|
|
7
8
|
import type { Attribute, Attributes, Model } from '@stacksjs/types'
|
|
8
9
|
import {
|
|
10
|
+
arrangeColumns,
|
|
9
11
|
checkPivotMigration,
|
|
10
12
|
fetchOtherModelRelations,
|
|
13
|
+
findDifferingKeys,
|
|
11
14
|
getLastMigrationFields,
|
|
12
15
|
getPivotTables,
|
|
13
16
|
hasTableBeenMigrated,
|
|
17
|
+
isArrayEqual,
|
|
14
18
|
mapFieldTypeToColumnType,
|
|
19
|
+
pluckChanges,
|
|
15
20
|
} from '.'
|
|
16
21
|
|
|
17
22
|
export async function resetSqliteDatabase() {
|
|
@@ -115,15 +120,17 @@ export async function generateSqliteMigration(modelPath: string) {
|
|
|
115
120
|
else await createTableMigration(modelPath)
|
|
116
121
|
}
|
|
117
122
|
|
|
118
|
-
async function createTableMigration(modelPath: string)
|
|
123
|
+
async function createTableMigration(modelPath: string) {
|
|
119
124
|
log.debug('createTableMigration modelPath:', modelPath)
|
|
120
125
|
|
|
121
126
|
const model = (await import(modelPath)).default as Model
|
|
122
127
|
const tableName = await getTableName(model, modelPath)
|
|
123
128
|
|
|
129
|
+
const twoFactorEnabled = model.traits?.useAuth?.useTwoFactor
|
|
130
|
+
|
|
124
131
|
await createPivotTableMigration(model, modelPath)
|
|
125
132
|
const otherModelRelations = await fetchOtherModelRelations(model, modelPath)
|
|
126
|
-
|
|
133
|
+
|
|
127
134
|
const useTimestamps = model?.traits?.useTimestamps ?? model?.traits?.timestampable ?? true
|
|
128
135
|
const useSoftDeletes = model?.traits?.useSoftDeletes ?? model?.traits?.softDeletable ?? false
|
|
129
136
|
|
|
@@ -134,26 +141,31 @@ async function createTableMigration(modelPath: string): Promise<void> {
|
|
|
134
141
|
migrationContent += ` .createTable('${tableName}')\n`
|
|
135
142
|
migrationContent += ` .addColumn('id', 'integer', col => col.primaryKey().autoIncrement())\n`
|
|
136
143
|
|
|
137
|
-
for (const [fieldName, options] of
|
|
144
|
+
for (const [fieldName, options] of arrangeColumns(model.attributes)) {
|
|
138
145
|
const fieldOptions = options as Attribute
|
|
139
|
-
const
|
|
140
|
-
|
|
146
|
+
const fieldNameFormatted = snakeCase(fieldName)
|
|
147
|
+
const columnType = mapFieldTypeToColumnType(fieldOptions.validation?.rule)
|
|
148
|
+
migrationContent += ` .addColumn('${fieldNameFormatted}', ${columnType}`
|
|
141
149
|
|
|
142
150
|
// Check if there are configurations that require the lambda function
|
|
143
|
-
if (fieldOptions.unique || fieldOptions
|
|
151
|
+
if (fieldOptions.unique || fieldOptions?.required) {
|
|
144
152
|
migrationContent += `, col => col`
|
|
145
153
|
if (fieldOptions.unique) migrationContent += `.unique()`
|
|
146
|
-
if (fieldOptions
|
|
154
|
+
if (fieldOptions?.required) migrationContent += `.notNull()`
|
|
147
155
|
migrationContent += ``
|
|
148
156
|
}
|
|
149
157
|
|
|
150
158
|
migrationContent += `)\n`
|
|
151
159
|
}
|
|
152
160
|
|
|
161
|
+
if (false !== twoFactorEnabled && twoFactorEnabled) {
|
|
162
|
+
migrationContent += ` .addColumn('two_factor_secret', 'varchar(255)')\n`
|
|
163
|
+
}
|
|
164
|
+
|
|
153
165
|
if (otherModelRelations?.length) {
|
|
154
166
|
for (const modelRelation of otherModelRelations) {
|
|
155
167
|
migrationContent += ` .addColumn('${modelRelation.foreignKey}', 'integer', (col) =>
|
|
156
|
-
col.references('${modelRelation.relationTable}.id').onDelete('cascade')
|
|
168
|
+
col.references('${modelRelation.relationTable}.id').onDelete('cascade')
|
|
157
169
|
) \n`
|
|
158
170
|
}
|
|
159
171
|
}
|
|
@@ -174,7 +186,6 @@ async function createTableMigration(modelPath: string): Promise<void> {
|
|
|
174
186
|
const migrationFileName = `${timestamp}-create-${tableName}-table.ts`
|
|
175
187
|
const migrationFilePath = path.userMigrationsPath(migrationFileName)
|
|
176
188
|
|
|
177
|
-
// Assuming fs.writeFileSync is available or use an equivalent method
|
|
178
189
|
Bun.write(migrationFilePath, migrationContent)
|
|
179
190
|
|
|
180
191
|
log.success(`Created migration: ${italic(migrationFileName)}`)
|
|
@@ -204,7 +215,6 @@ async function createPivotTableMigration(model: Model, modelPath: string) {
|
|
|
204
215
|
const migrationFileName = `${timestamp}-create-${pivotTable.table}-table.ts`
|
|
205
216
|
const migrationFilePath = path.userMigrationsPath(migrationFileName)
|
|
206
217
|
|
|
207
|
-
// Assuming fs.writeFileSync is available or use an equivalent method
|
|
208
218
|
Bun.write(migrationFilePath, migrationContent)
|
|
209
219
|
|
|
210
220
|
log.success(`Created pivot migration: ${migrationFileName}`)
|
|
@@ -217,6 +227,7 @@ export async function createAlterTableMigration(modelPath: string) {
|
|
|
217
227
|
const model = (await import(modelPath)).default as Model
|
|
218
228
|
const modelName = getModelName(model, modelPath)
|
|
219
229
|
const tableName = await getTableName(model, modelPath)
|
|
230
|
+
let hasChanged = false
|
|
220
231
|
|
|
221
232
|
// Assuming you have a function to get the fields from the last migration
|
|
222
233
|
// For simplicity, this is not implemented here
|
|
@@ -225,33 +236,95 @@ export async function createAlterTableMigration(modelPath: string) {
|
|
|
225
236
|
const currentFields = model.attributes as Attributes
|
|
226
237
|
|
|
227
238
|
// Determine fields to add and remove
|
|
228
|
-
|
|
229
|
-
const
|
|
239
|
+
|
|
240
|
+
const changes = pluckChanges(Object.keys(lastFields), Object.keys(currentFields))
|
|
241
|
+
|
|
242
|
+
const fieldsToAdd = changes?.added || []
|
|
243
|
+
|
|
244
|
+
const fieldsToRemove = changes?.removed || []
|
|
230
245
|
|
|
231
246
|
let migrationContent = `import type { Database } from '@stacksjs/database'\n`
|
|
232
247
|
migrationContent += `import { sql } from '@stacksjs/database'\n\n`
|
|
233
248
|
migrationContent += `export async function up(db: Database<any>) {\n`
|
|
234
|
-
|
|
249
|
+
|
|
250
|
+
if (fieldsToAdd.length || fieldsToRemove.length) {
|
|
251
|
+
hasChanged = true
|
|
252
|
+
migrationContent += ` await db.schema.alterTable('${tableName}')\n`
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const fieldValidations = findDifferingKeys(lastFields, currentFields)
|
|
256
|
+
|
|
257
|
+
for (const fieldValidation of fieldValidations) {
|
|
258
|
+
hasChanged = true
|
|
259
|
+
const fieldNameFormatted = snakeCase(fieldValidation.key)
|
|
260
|
+
migrationContent += `await sql\`
|
|
261
|
+
ALTER TABLE ${tableName}
|
|
262
|
+
MODIFY COLUMN ${fieldNameFormatted} VARCHAR(${fieldValidation.max})
|
|
263
|
+
\`.execute(db)\n\n`
|
|
264
|
+
}
|
|
235
265
|
|
|
236
266
|
// Add new fields
|
|
237
267
|
for (const fieldName of fieldsToAdd) {
|
|
238
268
|
const options = currentFields[fieldName] as Attribute
|
|
239
|
-
const columnType = mapFieldTypeToColumnType(options.
|
|
240
|
-
|
|
269
|
+
const columnType = mapFieldTypeToColumnType(options.validation?.rule)
|
|
270
|
+
const formattedFieldName = snakeCase(fieldName)
|
|
271
|
+
|
|
272
|
+
migrationContent += ` .addColumn('${formattedFieldName}', ${columnType}`
|
|
273
|
+
|
|
274
|
+
// Check if there are configurations that require the lambda function
|
|
275
|
+
if (options.unique || options?.required) {
|
|
276
|
+
migrationContent += `, col => col`
|
|
277
|
+
if (options.unique) migrationContent += `.unique()`
|
|
278
|
+
if (options?.required) migrationContent += `.notNull()`
|
|
279
|
+
migrationContent += ``
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
migrationContent += `)\n\n`
|
|
241
283
|
}
|
|
242
284
|
|
|
243
285
|
// Remove fields that no longer exist
|
|
244
286
|
for (const fieldName of fieldsToRemove) migrationContent += ` .dropColumn('${fieldName}')\n`
|
|
245
287
|
|
|
246
|
-
migrationContent += ` .execute();\n`
|
|
288
|
+
if (fieldsToAdd.length || fieldsToRemove.length) migrationContent += ` .execute();\n`
|
|
289
|
+
|
|
290
|
+
const lastFieldOrder = Object.values(lastFields).map((attr) => attr.order)
|
|
291
|
+
const currentFieldOrder = Object.values(currentFields).map((attr) => attr.order)
|
|
292
|
+
|
|
293
|
+
if (!isArrayEqual(lastFieldOrder, currentFieldOrder)) {
|
|
294
|
+
hasChanged = true
|
|
295
|
+
migrationContent += reArrangeColumns(model.attributes, tableName)
|
|
296
|
+
}
|
|
297
|
+
|
|
247
298
|
migrationContent += `}\n`
|
|
248
299
|
|
|
249
300
|
const timestamp = new Date().getTime().toString()
|
|
250
301
|
const migrationFileName = `${timestamp}-update-${tableName}-table.ts`
|
|
251
302
|
const migrationFilePath = path.userMigrationsPath(migrationFileName)
|
|
252
303
|
|
|
253
|
-
|
|
254
|
-
|
|
304
|
+
if (hasChanged) {
|
|
305
|
+
Bun.write(migrationFilePath, migrationContent)
|
|
255
306
|
|
|
256
|
-
|
|
307
|
+
log.success(`Created migration: ${italic(migrationFileName)}`)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function reArrangeColumns(attributes: Attributes | undefined, tableName: string): string {
|
|
312
|
+
const fields = arrangeColumns(attributes)
|
|
313
|
+
let migrationContent = ''
|
|
314
|
+
|
|
315
|
+
let previousField = ''
|
|
316
|
+
for (const [fieldName, options] of fields) {
|
|
317
|
+
const fieldNameFormatted = snakeCase(fieldName)
|
|
318
|
+
|
|
319
|
+
if (previousField) {
|
|
320
|
+
migrationContent += `await sql\`
|
|
321
|
+
ALTER TABLE ${tableName}
|
|
322
|
+
MODIFY COLUMN ${fieldNameFormatted} VARCHAR(255) NOT NULL AFTER ${snakeCase(previousField)};
|
|
323
|
+
\`.execute(db)\n\n`
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
previousField = fieldNameFormatted
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return migrationContent
|
|
257
330
|
}
|
package/src/migrations.ts
CHANGED
|
@@ -88,6 +88,7 @@ export async function generateMigrations() {
|
|
|
88
88
|
|
|
89
89
|
for (const file of modelFiles) {
|
|
90
90
|
log.debug('Generating migration for:', file)
|
|
91
|
+
|
|
91
92
|
await generateMigration(file)
|
|
92
93
|
}
|
|
93
94
|
|
|
@@ -99,11 +100,11 @@ export async function generateMigrations() {
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
export async function generateMigration(modelPath: string) {
|
|
102
|
-
if (driver === 'sqlite') generateSqliteMigration(modelPath)
|
|
103
|
+
if (driver === 'sqlite') await generateSqliteMigration(modelPath)
|
|
103
104
|
|
|
104
|
-
if (driver === 'mysql') generateMysqlMigration(modelPath)
|
|
105
|
+
if (driver === 'mysql') await generateMysqlMigration(modelPath)
|
|
105
106
|
|
|
106
|
-
if (driver === 'postgres') generatePostgresMigration(modelPath)
|
|
107
|
+
if (driver === 'postgres') await generatePostgresMigration(modelPath)
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
export async function getExecutedMigrations() {
|
package/src/seeder.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { italic, log } from '@stacksjs/cli'
|
|
2
2
|
import { db } from '@stacksjs/database'
|
|
3
3
|
import { modelTableName } from '@stacksjs/orm'
|
|
4
|
+
import { getModelName, getTableName } from '@stacksjs/orm'
|
|
4
5
|
import { path } from '@stacksjs/path'
|
|
6
|
+
import { makeHash } from '@stacksjs/security'
|
|
5
7
|
import { fs, glob } from '@stacksjs/storage'
|
|
6
|
-
import { snakeCase } from '@stacksjs/strings'
|
|
8
|
+
import { plural, singular, snakeCase } from '@stacksjs/strings'
|
|
7
9
|
import type { Model, RelationConfig } from '@stacksjs/types'
|
|
8
10
|
import { isString } from '@stacksjs/validation'
|
|
11
|
+
|
|
9
12
|
import { generateMigrations, resetDatabase, runDatabaseMigration } from './migrations'
|
|
10
13
|
|
|
11
14
|
async function seedModel(name: string, model?: Model) {
|
|
@@ -25,21 +28,27 @@ async function seedModel(name: string, model?: Model) {
|
|
|
25
28
|
const records = []
|
|
26
29
|
const otherRelations = await fetchOtherModelRelations(model)
|
|
27
30
|
|
|
28
|
-
log.debug(otherRelations)
|
|
29
|
-
|
|
30
31
|
for (let i = 0; i < seedCount; i++) {
|
|
31
32
|
const record: any = {}
|
|
32
33
|
|
|
33
34
|
for (const fieldName in model.attributes) {
|
|
35
|
+
const formattedFieldName = snakeCase(fieldName)
|
|
34
36
|
const field = model.attributes[fieldName]
|
|
37
|
+
|
|
35
38
|
// Use the factory function if available, otherwise leave the field undefined
|
|
36
|
-
|
|
39
|
+
if (formattedFieldName === 'password')
|
|
40
|
+
record[formattedFieldName] = field?.factory ? await makeHash('Test@123', { algorithm: 'bcrypt' }) : undefined
|
|
41
|
+
else record[formattedFieldName] = field?.factory ? field.factory() : undefined
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
if (otherRelations?.length) {
|
|
40
45
|
for (let j = 0; j < otherRelations.length; j++) {
|
|
41
46
|
const relationElement = otherRelations[j] as RelationConfig
|
|
42
47
|
|
|
48
|
+
if (relationElement.relationship === 'belongsToMany') {
|
|
49
|
+
await seedPivotRelation(relationElement)
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
record[relationElement?.foreignKey] = await seedModelRelation(relationElement?.relationModel as string)
|
|
44
53
|
}
|
|
45
54
|
}
|
|
@@ -51,6 +60,51 @@ async function seedModel(name: string, model?: Model) {
|
|
|
51
60
|
await db.insertInto(tableName).values(records).execute()
|
|
52
61
|
}
|
|
53
62
|
|
|
63
|
+
async function seedPivotRelation(relation: RelationConfig): Promise<any> {
|
|
64
|
+
const record: any = {}
|
|
65
|
+
const record2: any = {}
|
|
66
|
+
const pivotRecord: any = {}
|
|
67
|
+
|
|
68
|
+
const modelInstance = (await import(path.userModelsPath(relation?.model))).default
|
|
69
|
+
const relationModelInstance = (await import(path.userModelsPath(relation?.relationModel))).default
|
|
70
|
+
|
|
71
|
+
if (!relationModelInstance) return 1
|
|
72
|
+
|
|
73
|
+
const relationModelTable = relationModelInstance.table
|
|
74
|
+
const relationTable = relation.table
|
|
75
|
+
const pivotTable = relation.pivotTable
|
|
76
|
+
const modelKey = `${singular(relationTable)}_id`
|
|
77
|
+
const foreignKey = relation.foreignKey
|
|
78
|
+
|
|
79
|
+
for (const fieldName in relationModelInstance.attributes) {
|
|
80
|
+
const formattedFieldName = snakeCase(fieldName)
|
|
81
|
+
const field = relationModelInstance.attributes[fieldName]
|
|
82
|
+
// Use the factory function if available, otherwise leave the field undefined
|
|
83
|
+
if (formattedFieldName === 'password')
|
|
84
|
+
record[formattedFieldName] = field?.factory ? await makeHash('Test@123', { algorithm: 'bcrypt' }) : undefined
|
|
85
|
+
else record[formattedFieldName] = field?.factory ? field.factory() : undefined
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const fieldName in modelInstance.attributes) {
|
|
89
|
+
const formattedFieldName = snakeCase(fieldName)
|
|
90
|
+
const field = modelInstance.attributes[fieldName]
|
|
91
|
+
// Use the factory function if available, otherwise leave the field undefined
|
|
92
|
+
if (formattedFieldName === 'password')
|
|
93
|
+
record2[formattedFieldName] = field?.factory ? await makeHash('Test@123', { algorithm: 'bcrypt' }) : undefined
|
|
94
|
+
else record2[formattedFieldName] = field?.factory ? field.factory() : undefined
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const data = await db.insertInto(relationModelTable).values(record).executeTakeFirstOrThrow()
|
|
98
|
+
const data2 = await db.insertInto(relationTable).values(record2).executeTakeFirstOrThrow()
|
|
99
|
+
const relationData = data.insertId || 1
|
|
100
|
+
const modelData = data2.insertId || 1
|
|
101
|
+
|
|
102
|
+
pivotRecord[foreignKey] = relationData
|
|
103
|
+
pivotRecord[modelKey] = modelData
|
|
104
|
+
|
|
105
|
+
if (pivotTable) await db.insertInto(pivotTable).values(pivotRecord).executeTakeFirstOrThrow()
|
|
106
|
+
}
|
|
107
|
+
|
|
54
108
|
async function seedModelRelation(modelName: string): Promise<BigInt | number> {
|
|
55
109
|
const modelInstance = (await import(path.userModelsPath(modelName))).default
|
|
56
110
|
|
|
@@ -60,9 +114,13 @@ async function seedModelRelation(modelName: string): Promise<BigInt | number> {
|
|
|
60
114
|
const table = modelInstance.table
|
|
61
115
|
|
|
62
116
|
for (const fieldName in modelInstance.attributes) {
|
|
117
|
+
const formattedFieldName = snakeCase(fieldName)
|
|
63
118
|
const field = modelInstance.attributes[fieldName]
|
|
64
119
|
// Use the factory function if available, otherwise leave the field undefined
|
|
65
|
-
|
|
120
|
+
|
|
121
|
+
if (formattedFieldName === 'password')
|
|
122
|
+
record[formattedFieldName] = field?.factory ? await makeHash(field.factory(), { algorithm: 'bcrypt' }) : undefined
|
|
123
|
+
else record[formattedFieldName] = field?.factory ? field.factory() : undefined
|
|
66
124
|
}
|
|
67
125
|
|
|
68
126
|
const data = await db.insertInto(table).values(record).executeTakeFirstOrThrow()
|
|
@@ -70,10 +128,13 @@ async function seedModelRelation(modelName: string): Promise<BigInt | number> {
|
|
|
70
128
|
return data.insertId || 1
|
|
71
129
|
}
|
|
72
130
|
|
|
73
|
-
export async function getRelations(model: Model): Promise<RelationConfig[]> {
|
|
131
|
+
export async function getRelations(model: Model, modelPath: string): Promise<RelationConfig[]> {
|
|
74
132
|
const relationsArray = ['hasOne', 'hasMany', 'belongsToMany', 'hasOneThrough']
|
|
75
133
|
const relationships = []
|
|
76
134
|
|
|
135
|
+
const modelName = getModelName(model, modelPath)
|
|
136
|
+
const tableName = getTableName(model, modelPath)
|
|
137
|
+
|
|
77
138
|
for (const relation of relationsArray) {
|
|
78
139
|
if (hasRelations(model, relation)) {
|
|
79
140
|
for (const relationInstance of model[relation]) {
|
|
@@ -85,19 +146,19 @@ export async function getRelations(model: Model): Promise<RelationConfig[]> {
|
|
|
85
146
|
|
|
86
147
|
const modelRelationPath = path.userModelsPath(`${relationModel}.ts`)
|
|
87
148
|
const modelRelation = (await import(modelRelationPath)).default
|
|
88
|
-
const formattedModelName =
|
|
149
|
+
const formattedModelName = modelName?.toLowerCase()
|
|
89
150
|
|
|
90
151
|
relationships.push({
|
|
91
152
|
relationship: relation,
|
|
92
153
|
model: relationModel,
|
|
93
154
|
table: modelRelation.table,
|
|
94
|
-
relationModel:
|
|
95
|
-
relationTable:
|
|
155
|
+
relationModel: modelName,
|
|
156
|
+
relationTable: tableName,
|
|
96
157
|
foreignKey: relationInstance.foreignKey || `${formattedModelName}_id`,
|
|
97
|
-
relationName: relationInstance.relationName
|
|
98
|
-
throughModel: relationInstance.through
|
|
99
|
-
throughForeignKey: relationInstance.throughForeignKey
|
|
100
|
-
pivotTable: relationInstance?.pivotTable ||
|
|
158
|
+
relationName: relationInstance.relationName,
|
|
159
|
+
throughModel: relationInstance.through,
|
|
160
|
+
throughForeignKey: relationInstance.throughForeignKey,
|
|
161
|
+
pivotTable: relationInstance?.pivotTable || getPivotTableName(formattedModelName || '', modelRelation.table),
|
|
101
162
|
})
|
|
102
163
|
}
|
|
103
164
|
}
|
|
@@ -106,6 +167,22 @@ export async function getRelations(model: Model): Promise<RelationConfig[]> {
|
|
|
106
167
|
return relationships
|
|
107
168
|
}
|
|
108
169
|
|
|
170
|
+
function getPivotTableName(formattedModelName: string, modelRelationTable: string): string {
|
|
171
|
+
// Create an array of the model names
|
|
172
|
+
const models = [formattedModelName, modelRelationTable]
|
|
173
|
+
|
|
174
|
+
// Sort the array alphabetically
|
|
175
|
+
models.sort()
|
|
176
|
+
|
|
177
|
+
models[0] = singular(models[0] || '')
|
|
178
|
+
models[1] = plural(models[1] || '')
|
|
179
|
+
|
|
180
|
+
// Join the sorted array with an underscore
|
|
181
|
+
const pivotTableName = models.join('_')
|
|
182
|
+
|
|
183
|
+
return pivotTableName
|
|
184
|
+
}
|
|
185
|
+
|
|
109
186
|
export async function fetchOtherModelRelations(model: Model): Promise<RelationConfig[]> {
|
|
110
187
|
const modelFiles = glob.sync(path.userModelsPath('*.ts'))
|
|
111
188
|
const modelRelations = []
|
|
@@ -116,7 +193,7 @@ export async function fetchOtherModelRelations(model: Model): Promise<RelationCo
|
|
|
116
193
|
|
|
117
194
|
if (model.name === modelFile.default.name) continue
|
|
118
195
|
|
|
119
|
-
const relations = await getRelations(modelFile.default)
|
|
196
|
+
const relations = await getRelations(modelFile.default, modelFileElement)
|
|
120
197
|
|
|
121
198
|
if (!relations.length) continue
|
|
122
199
|
|
|
@@ -134,21 +211,21 @@ function hasRelations(obj: any, key: string): boolean {
|
|
|
134
211
|
|
|
135
212
|
export async function seed() {
|
|
136
213
|
// TODO: need to check other databases too
|
|
137
|
-
const dbPath = path.userDatabasePath('stacks.sqlite')
|
|
214
|
+
// const dbPath = path.userDatabasePath('stacks.sqlite')
|
|
138
215
|
|
|
139
|
-
if (!fs.existsSync(dbPath)) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
216
|
+
// if (!fs.existsSync(dbPath)) {
|
|
217
|
+
// log.warn('No database found, configuring it...')
|
|
218
|
+
// // first, ensure the database is reset
|
|
219
|
+
// await resetDatabase()
|
|
143
220
|
|
|
144
|
-
|
|
145
|
-
|
|
221
|
+
// // then, generate the migrations
|
|
222
|
+
// await generateMigrations()
|
|
146
223
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
} else {
|
|
150
|
-
|
|
151
|
-
}
|
|
224
|
+
// // finally, migrate the database
|
|
225
|
+
// await runDatabaseMigration()
|
|
226
|
+
// } else {
|
|
227
|
+
// log.debug('Database configured...')
|
|
228
|
+
// }
|
|
152
229
|
|
|
153
230
|
// if a custom seeder exists, use it instead
|
|
154
231
|
const customSeederPath = path.userDatabasePath('seeder.ts')
|
package/src/utils.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { database } from '@stacksjs/config'
|
|
2
2
|
import { log } from '@stacksjs/logging'
|
|
3
3
|
import type { Database } from '@stacksjs/orm'
|
|
4
|
+
import { plural, snakeCase } from '@stacksjs/strings'
|
|
5
|
+
import type { Model } from '@stacksjs/types'
|
|
4
6
|
import { Kysely, MysqlDialect, PostgresDialect, sql } from 'kysely'
|
|
5
7
|
import { BunWorkerDialect } from 'kysely-bun-worker'
|
|
6
8
|
import { createPool } from 'mysql2'
|