@stacksjs/database 0.70.258 → 0.70.260

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.
Files changed (50) hide show
  1. package/dist/auth-tables.js +18 -137
  2. package/dist/column.js +1 -26
  3. package/dist/custom/audits.js +20 -54
  4. package/dist/custom/errors.js +16 -46
  5. package/dist/custom/index.js +1 -3
  6. package/dist/custom/jobs.js +13 -137
  7. package/dist/database.js +1 -181
  8. package/dist/datetime-columns.js +2 -79
  9. package/dist/ddl-constraints.js +7 -111
  10. package/dist/defaults.js +1 -48
  11. package/dist/dialect.js +1 -79
  12. package/dist/driver-config.js +1 -172
  13. package/dist/drivers/defaults/index.js +1 -1
  14. package/dist/drivers/defaults/traits.js +1 -29
  15. package/dist/drivers/dynamodb.js +1 -607
  16. package/dist/drivers/helpers.js +1 -206
  17. package/dist/drivers/index.js +1 -9
  18. package/dist/drivers/mysql.js +58 -299
  19. package/dist/drivers/postgres.js +78 -368
  20. package/dist/drivers/sqlite.js +61 -379
  21. package/dist/ensure-database.js +1 -145
  22. package/dist/fk-audit.js +3 -187
  23. package/dist/index.js +1 -64
  24. package/dist/managed-columns.js +1 -59
  25. package/dist/migration-dialect.js +4 -107
  26. package/dist/migration-ledger.js +1 -382
  27. package/dist/migration-lock.js +1 -143
  28. package/dist/migrations.js +15 -1118
  29. package/dist/model-sources.js +1 -76
  30. package/dist/notification-tables.js +4 -49
  31. package/dist/query-logger.js +2 -241
  32. package/dist/query-parser.js +1 -93
  33. package/dist/rbac-tables.js +6 -61
  34. package/dist/relation-columns.js +1 -66
  35. package/dist/replicas.js +1 -74
  36. package/dist/safe-migrations.js +2 -52
  37. package/dist/schema.js +1 -10
  38. package/dist/seeder.js +1 -457
  39. package/dist/sql-helpers.js +1 -50
  40. package/dist/table.js +1 -26
  41. package/dist/tools/setup.js +1 -6
  42. package/dist/trait-tables.js +8 -153
  43. package/dist/transaction-context.js +1 -62
  44. package/dist/types.js +1 -98
  45. package/dist/unique-audit.js +3 -155
  46. package/dist/utils.js +1 -285
  47. package/dist/uuid-columns.js +1 -68
  48. package/dist/validators.js +1 -122
  49. package/dist/vschema.js +2 -121
  50. package/package.json +20 -13
@@ -1,378 +1,88 @@
1
- import { log } from "@stacksjs/logging";
2
- function italic(str) {
3
- return `\x1B[3m${str}\x1B[23m`;
4
- }
5
- import { db } from "../utils";
6
- import { ok } from "@stacksjs/error-handling";
7
- import { fetchOtherModelRelations, getModelName, getPivotTables, getTableName } from "@stacksjs/orm";
8
- import { path } from "@stacksjs/path";
9
- import { fs, globSync } from "@stacksjs/storage";
10
- import { plural, snakeCase } from "@stacksjs/strings";
11
- import {
12
- arrangeColumns,
13
- checkPivotMigration,
14
- deleteFrameworkModels,
15
- deleteMigrationFiles,
16
- findDifferingKeys,
17
- getLastMigrationFields,
18
- getLikeableForeignKey,
19
- getUpvoteTableName,
20
- hasTableBeenMigrated,
21
- isArrayEqual,
22
- mapFieldTypeToColumnType,
23
- pluckChanges
24
- } from "./helpers";
25
- import { dropCommonTables, dropMigrationTables } from "./defaults/traits";
26
- export async function dropPostgresTables() {
27
- const tables = await fetchPostgresTables(), userModelFiles = globSync([path.userModelsPath("*.ts"), path.storagePath("framework/defaults/app/Models/**/*.ts")], { absolute: !0 });
28
- await dropMigrationTables();
29
- for (const table of tables)
30
- await db.unsafe(`DROP TABLE IF EXISTS "${table}" CASCADE`).execute();
31
- await dropCommonTables();
32
- for (const userModel of userModelFiles) {
33
- const userModelPath = (await import(userModel)).default, pivotTables = await getPivotTables(userModelPath, userModel);
34
- for (const pivotTable of pivotTables)
35
- await db.unsafe(`DROP TABLE IF EXISTS "${pivotTable.table}" CASCADE`).execute();
36
- }
37
- }
38
- export async function resetPostgresDatabase() {
39
- await dropPostgresTables();
40
- await deleteFrameworkModels();
41
- await deleteMigrationFiles();
42
- await db.unsafe('CREATE TABLE IF NOT EXISTS "migrations" (id SERIAL PRIMARY KEY)').execute();
43
- await db.unsafe('CREATE TABLE IF NOT EXISTS "migration_locks" (id SERIAL PRIMARY KEY)').execute();
44
- await db.unsafe('CREATE TABLE IF NOT EXISTS "activities" (id SERIAL PRIMARY KEY)').execute();
45
- return ok("All tables dropped successfully!");
46
- }
47
- export async function generatePostgresMigration(modelPath) {
48
- if ((await fs.promises.readdir(path.userMigrationsPath(""))).length === 0) {
49
- log.debug("No migrations found in the database folder, clearing the model snapshot cache...");
50
- const cacheDir = path.frameworkPath("cache/models");
51
- if (fs.existsSync(cacheDir)) {
52
- const modelFiles = await fs.promises.readdir(cacheDir);
53
- if (modelFiles.length) {
54
- for (const file of modelFiles)
55
- if (file.endsWith(".ts"))
56
- await fs.promises.unlink(path.frameworkPath(`cache/models/${file}`));
57
- }
58
- }
59
- }
60
- const model = (await import(modelPath)).default, fileName = path.basename(modelPath), tableName = getTableName(model, modelPath), fieldsString = JSON.stringify(model.attributes, null, 2), copiedModelPath = path.frameworkPath(`cache/models/${fileName}`);
61
- let haveFieldsChanged = !1;
62
- if (fs.existsSync(copiedModelPath)) {
63
- log.info(`Fields have already been generated for ${tableName}`);
64
- const previousFields = await getLastMigrationFields(fileName);
65
- if (JSON.stringify(previousFields, null, 2) === fieldsString) {
66
- log.debug(`Fields have not changed for ${tableName}`);
67
- return;
68
- }
69
- haveFieldsChanged = !0;
70
- log.debug(`Fields have changed for ${tableName}`);
71
- } else
72
- log.debug(`Fields have not been generated for ${tableName}`);
73
- await Bun.$`mkdir -p ${path.frameworkPath("cache/models")} && cp ${modelPath} ${copiedModelPath}`;
74
- const hasBeenMigrated = await hasTableBeenMigrated(tableName);
75
- log.debug(`Has ${tableName} been migrated? ${hasBeenMigrated}`);
76
- if ((model.traits?.billable || !1) && tableName === "users")
77
- await createTableMigration(path.frameworkPath("defaults/app/Models/Subscription.ts"));
78
- if (haveFieldsChanged)
79
- await createAlterTableMigration(modelPath);
80
- else
81
- await createTableMigration(modelPath);
82
- }
83
- async function createTableMigration(modelPath) {
84
- log.debug("createTableMigration modelPath:", modelPath);
85
- const model = (await import(modelPath)).default, tableName = getTableName(model, modelPath), twoFactorEnabled = model.traits?.useAuth && typeof model.traits.useAuth !== "boolean" ? model.traits.useAuth.useTwoFactor : !1;
86
- await createPivotTableMigration(model, modelPath);
87
- const useTimestamps = model.traits?.useTimestamps ?? model.traits?.timestampable ?? !0, useSocials = model?.traits?.useSocials && Array.isArray(model.traits.useSocials) && model.traits.useSocials.length > 0, useLikeable = Array.isArray(model?.traits?.likeable) ? model.traits.likeable.length > 0 : Boolean(model?.traits?.likeable), useSoftDeletes = model.traits?.useSoftDeletes ?? model.traits?.softDeletable ?? !1, usePasskey = (typeof model.traits?.useAuth === "object" && model.traits.useAuth.usePasskey) ?? !1, useBillable = model.traits?.billable || !1, useUuid = model.traits?.useUuid || !1;
88
- if (useBillable && tableName === "users")
89
- await createTableMigration(path.frameworkPath("defaults/app/Models/Subscription.ts"));
90
- let migrationContent = `import type { Database } from '@stacksjs/database'
91
- `;
92
- migrationContent += `import { sql } from '@stacksjs/database'
1
+ import{log}from"@stacksjs/logging";function italic(str){return`\x1B[3m${str}\x1B[23m`}import{db}from"../utils";import{ok}from"@stacksjs/error-handling";import{fetchOtherModelRelations,getModelName,getPivotTables,getTableName}from"@stacksjs/orm";import{path}from"@stacksjs/path";import{fs,globSync}from"@stacksjs/storage";import{plural,snakeCase}from"@stacksjs/strings";import{arrangeColumns,checkPivotMigration,deleteFrameworkModels,deleteMigrationFiles,findDifferingKeys,getLastMigrationFields,getLikeableForeignKey,getUpvoteTableName,hasTableBeenMigrated,isArrayEqual,mapFieldTypeToColumnType,pluckChanges}from"./helpers";import{dropCommonTables,dropMigrationTables}from"./defaults/traits";export async function dropPostgresTables(){const tables=await fetchPostgresTables(),userModelFiles=globSync([path.userModelsPath("*.ts"),path.storagePath("framework/defaults/app/Models/**/*.ts")],{absolute:!0});await dropMigrationTables();for(const table of tables)await db.unsafe(`DROP TABLE IF EXISTS "${table}" CASCADE`).execute();await dropCommonTables();for(const userModel of userModelFiles){const userModelPath=(await import(userModel)).default,pivotTables=await getPivotTables(userModelPath,userModel);for(const pivotTable of pivotTables)await db.unsafe(`DROP TABLE IF EXISTS "${pivotTable.table}" CASCADE`).execute()}}export async function resetPostgresDatabase(){await dropPostgresTables();await deleteFrameworkModels();await deleteMigrationFiles();await db.unsafe('CREATE TABLE IF NOT EXISTS "migrations" (id SERIAL PRIMARY KEY)').execute();await db.unsafe('CREATE TABLE IF NOT EXISTS "migration_locks" (id SERIAL PRIMARY KEY)').execute();await db.unsafe('CREATE TABLE IF NOT EXISTS "activities" (id SERIAL PRIMARY KEY)').execute();return ok("All tables dropped successfully!")}export async function generatePostgresMigration(modelPath){if((await fs.promises.readdir(path.userMigrationsPath(""))).length===0){log.debug("No migrations found in the database folder, clearing the model snapshot cache...");const cacheDir=path.frameworkPath("cache/models");if(fs.existsSync(cacheDir)){const modelFiles=await fs.promises.readdir(cacheDir);if(modelFiles.length){for(const file of modelFiles)if(file.endsWith(".ts"))await fs.promises.unlink(path.frameworkPath(`cache/models/${file}`))}}}const model=(await import(modelPath)).default,fileName=path.basename(modelPath),tableName=getTableName(model,modelPath),fieldsString=JSON.stringify(model.attributes,null,2),copiedModelPath=path.frameworkPath(`cache/models/${fileName}`);let haveFieldsChanged=!1;if(fs.existsSync(copiedModelPath)){log.info(`Fields have already been generated for ${tableName}`);const previousFields=await getLastMigrationFields(fileName);if(JSON.stringify(previousFields,null,2)===fieldsString){log.debug(`Fields have not changed for ${tableName}`);return}haveFieldsChanged=!0;log.debug(`Fields have changed for ${tableName}`)}else log.debug(`Fields have not been generated for ${tableName}`);await Bun.$`mkdir -p ${path.frameworkPath("cache/models")} && cp ${modelPath} ${copiedModelPath}`;const hasBeenMigrated=await hasTableBeenMigrated(tableName);log.debug(`Has ${tableName} been migrated? ${hasBeenMigrated}`);if((model.traits?.billable||!1)&&tableName==="users")await createTableMigration(path.frameworkPath("defaults/app/Models/Subscription.ts"));if(haveFieldsChanged)await createAlterTableMigration(modelPath);else await createTableMigration(modelPath)}async function createTableMigration(modelPath){log.debug("createTableMigration modelPath:",modelPath);const model=(await import(modelPath)).default,tableName=getTableName(model,modelPath),twoFactorEnabled=model.traits?.useAuth&&typeof model.traits.useAuth!=="boolean"?model.traits.useAuth.useTwoFactor:!1;await createPivotTableMigration(model,modelPath);const useTimestamps=model.traits?.useTimestamps??model.traits?.timestampable??!0,useSocials=model?.traits?.useSocials&&Array.isArray(model.traits.useSocials)&&model.traits.useSocials.length>0,useLikeable=Array.isArray(model?.traits?.likeable)?model.traits.likeable.length>0:Boolean(model?.traits?.likeable),useSoftDeletes=model.traits?.useSoftDeletes??model.traits?.softDeletable??!1,usePasskey=(typeof model.traits?.useAuth==="object"&&model.traits.useAuth.usePasskey)??!1,useBillable=model.traits?.billable||!1,useUuid=model.traits?.useUuid||!1;if(useBillable&&tableName==="users")await createTableMigration(path.frameworkPath("defaults/app/Models/Subscription.ts"));let migrationContent=`import type { Database } from '@stacksjs/database'
2
+ `;migrationContent+=`import { sql } from '@stacksjs/database'
93
3
 
94
- `;
95
- migrationContent += `export async function up(db: Database<any>) {
96
- `;
97
- migrationContent += ` await (db as any).schema
98
- `;
99
- migrationContent += ` .createTable('${tableName}')
100
- `;
101
- migrationContent += ` .addColumn('id', 'serial', (col) => col.primaryKey())
102
- `;
103
- if (useUuid)
104
- migrationContent += ` .addColumn('uuid', 'uuid', (col) => col.defaultTo(sql.raw('gen_random_uuid()')))
105
- `;
106
- for (const [fieldName, options] of arrangeColumns(model.attributes)) {
107
- const fieldOptions = options, fieldNameFormatted = snakeCase(fieldName), columnType = mapFieldTypeToColumnType(fieldOptions.validation?.rule, "postgres"), isRequired = "isRequired" in (fieldOptions.validation?.rule ?? {}) ? (fieldOptions.validation?.rule).isRequired : !1;
108
- migrationContent += ` .addColumn('${fieldNameFormatted}', ${columnType}`;
109
- if (isRequired || fieldOptions.unique || fieldOptions.default !== void 0) {
110
- migrationContent += ", col => col";
111
- if (isRequired)
112
- migrationContent += ".notNull()";
113
- if (fieldOptions.unique)
114
- migrationContent += ".unique()";
115
- if (fieldOptions.default !== void 0)
116
- if (typeof fieldOptions.default === "string")
117
- migrationContent += `.defaultTo('${fieldOptions.default.replace(/'/g, "\\'")}')`;
118
- else if (fieldOptions.default === null)
119
- migrationContent += ".defaultTo(null)";
120
- else
121
- migrationContent += `.defaultTo(${fieldOptions.default})`;
122
- migrationContent += "";
123
- }
124
- migrationContent += `)
125
- `;
126
- }
127
- if (useSocials) {
128
- const socials = model.traits?.useSocials || [];
129
- if (socials.includes("google"))
130
- migrationContent += ` .addColumn('google_id', 'text')
131
- `;
132
- if (socials.includes("github"))
133
- migrationContent += ` .addColumn('github_id', 'text')
134
- `;
135
- if (socials.includes("apple"))
136
- migrationContent += ` .addColumn('apple_id', 'text')
137
- `;
138
- if (socials.includes("twitter"))
139
- migrationContent += ` .addColumn('twitter_id', 'text')
140
- `;
141
- if (socials.includes("facebook"))
142
- migrationContent += ` .addColumn('facebook_id', 'text')
143
- `;
144
- }
145
- if (useLikeable) {
146
- const likeables = Array.isArray(model.traits?.likeable) ? model.traits.likeable : [];
147
- for (const likeable of likeables)
148
- migrationContent += ` .addColumn('${likeable}_count', 'integer', (col) => col.defaultTo(0))
149
- `;
150
- }
151
- if (twoFactorEnabled !== !1 && twoFactorEnabled)
152
- migrationContent += ` .addColumn('two_factor_secret', 'text')
153
- `;
154
- if (useBillable)
155
- migrationContent += ` .addColumn('stripe_id', 'text')
156
- `;
157
- if (usePasskey)
158
- migrationContent += ` .addColumn('public_passkey', 'text')
159
- `;
160
- if (useTimestamps) {
161
- migrationContent += ` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
162
- `;
163
- migrationContent += ` .addColumn('updated_at', 'timestamptz')
164
- `;
165
- }
166
- if (useSoftDeletes)
167
- migrationContent += ` .addColumn('deleted_at', 'timestamptz')
168
- `;
169
- migrationContent += ` .execute()
170
- `;
171
- migrationContent += generatePrimaryKeyIndexSQL(tableName);
172
- if (useLikeable) {
173
- const upvoteTable = getUpvoteTableName(model, tableName);
174
- if (upvoteTable) {
175
- const foreignKey = getLikeableForeignKey(model, tableName);
176
- migrationContent += `
4
+ `;migrationContent+=`export async function up(db: Database<any>) {
5
+ `;migrationContent+=` await (db as any).schema
6
+ `;migrationContent+=` .createTable('${tableName}')
7
+ `;migrationContent+=` .addColumn('id', 'serial', (col) => col.primaryKey())
8
+ `;if(useUuid)migrationContent+=` .addColumn('uuid', 'uuid', (col) => col.defaultTo(sql.raw('gen_random_uuid()')))
9
+ `;for(const[fieldName,options]of arrangeColumns(model.attributes)){const fieldOptions=options,fieldNameFormatted=snakeCase(fieldName),columnType=mapFieldTypeToColumnType(fieldOptions.validation?.rule,"postgres"),isRequired="isRequired"in(fieldOptions.validation?.rule??{})?(fieldOptions.validation?.rule).isRequired:!1;migrationContent+=` .addColumn('${fieldNameFormatted}', ${columnType}`;if(isRequired||fieldOptions.unique||fieldOptions.default!==void 0){migrationContent+=", col => col";if(isRequired)migrationContent+=".notNull()";if(fieldOptions.unique)migrationContent+=".unique()";if(fieldOptions.default!==void 0)if(typeof fieldOptions.default==="string")migrationContent+=`.defaultTo('${fieldOptions.default.replace(/'/g,"\\'")}')`;else if(fieldOptions.default===null)migrationContent+=".defaultTo(null)";else migrationContent+=`.defaultTo(${fieldOptions.default})`;migrationContent+=""}migrationContent+=`)
10
+ `}if(useSocials){const socials=model.traits?.useSocials||[];if(socials.includes("google"))migrationContent+=` .addColumn('google_id', 'text')
11
+ `;if(socials.includes("github"))migrationContent+=` .addColumn('github_id', 'text')
12
+ `;if(socials.includes("apple"))migrationContent+=` .addColumn('apple_id', 'text')
13
+ `;if(socials.includes("twitter"))migrationContent+=` .addColumn('twitter_id', 'text')
14
+ `;if(socials.includes("facebook"))migrationContent+=` .addColumn('facebook_id', 'text')
15
+ `}if(useLikeable){const likeables=Array.isArray(model.traits?.likeable)?model.traits.likeable:[];for(const likeable of likeables)migrationContent+=` .addColumn('${likeable}_count', 'integer', (col) => col.defaultTo(0))
16
+ `}if(twoFactorEnabled!==!1&&twoFactorEnabled)migrationContent+=` .addColumn('two_factor_secret', 'text')
17
+ `;if(useBillable)migrationContent+=` .addColumn('stripe_id', 'text')
18
+ `;if(usePasskey)migrationContent+=` .addColumn('public_passkey', 'text')
19
+ `;if(useTimestamps){migrationContent+=` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
20
+ `;migrationContent+=` .addColumn('updated_at', 'timestamptz')
21
+ `}if(useSoftDeletes)migrationContent+=` .addColumn('deleted_at', 'timestamptz')
22
+ `;migrationContent+=` .execute()
23
+ `;migrationContent+=generatePrimaryKeyIndexSQL(tableName);if(useLikeable){const upvoteTable=getUpvoteTableName(model,tableName);if(upvoteTable){const foreignKey=getLikeableForeignKey(model,tableName);migrationContent+=`
177
24
  // Create upvote table
178
- `;
179
- migrationContent += ` await (db as any).schema
180
- `;
181
- migrationContent += ` .createTable('${upvoteTable}')
182
- `;
183
- migrationContent += ` .addColumn('id', 'serial', (col) => col.primaryKey())
184
- `;
185
- migrationContent += ` .addColumn('${foreignKey}', 'integer', (col) => col.notNull())
186
- `;
187
- migrationContent += ` .addColumn('user_id', 'integer', (col) => col.notNull())
188
- `;
189
- migrationContent += ` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
190
- `;
191
- migrationContent += ` .addColumn('updated_at', 'timestamptz')
192
- `;
193
- migrationContent += ` .execute()
25
+ `;migrationContent+=` await (db as any).schema
26
+ `;migrationContent+=` .createTable('${upvoteTable}')
27
+ `;migrationContent+=` .addColumn('id', 'serial', (col) => col.primaryKey())
28
+ `;migrationContent+=` .addColumn('${foreignKey}', 'integer', (col) => col.notNull())
29
+ `;migrationContent+=` .addColumn('user_id', 'integer', (col) => col.notNull())
30
+ `;migrationContent+=` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
31
+ `;migrationContent+=` .addColumn('updated_at', 'timestamptz')
32
+ `;migrationContent+=` .execute()
194
33
 
195
- `;
196
- migrationContent += ` // Add indexes for upvote table
197
- `;
198
- migrationContent += ` await (db as any).schema.createIndex('${upvoteTable}_${foreignKey}_index').on('${upvoteTable}').column('${foreignKey}').execute()
199
- `;
200
- migrationContent += ` await (db as any).schema.createIndex('${upvoteTable}_user_${foreignKey}_unique').on('${upvoteTable}').columns(['user_id', '${foreignKey}']).unique().execute()
201
- `;
202
- migrationContent += ` await (db as any).schema.createIndex('${upvoteTable}_id_index').on('${upvoteTable}').column('id').execute()
203
- `;
204
- }
205
- }
206
- migrationContent += `}
207
- `;
208
- const migrationFileName = `${new Date().getTime().toString()}-create-${tableName}-table.ts`, migrationFilePath = path.userMigrationsPath(migrationFileName);
209
- await Bun.write(migrationFilePath, migrationContent);
210
- log.success(`Created migration: ${italic(migrationFileName)}`);
211
- }
212
- async function createPivotTableMigration(model, modelPath) {
213
- const pivotTables = await getPivotTables(model, modelPath), processedPivotTables = new Set;
214
- if (!pivotTables.length)
215
- return;
216
- for (const pivotTable of pivotTables) {
217
- if (processedPivotTables.has(pivotTable.table))
218
- continue;
219
- if (await checkPivotMigration(pivotTable.table)) {
220
- processedPivotTables.add(pivotTable.table);
221
- continue;
222
- }
223
- let migrationContent = `import type { Database } from '@stacksjs/database'
224
- `;
225
- migrationContent += `import { sql } from '@stacksjs/database'
34
+ `;migrationContent+=` // Add indexes for upvote table
35
+ `;migrationContent+=` await (db as any).schema.createIndex('${upvoteTable}_${foreignKey}_index').on('${upvoteTable}').column('${foreignKey}').execute()
36
+ `;migrationContent+=` await (db as any).schema.createIndex('${upvoteTable}_user_${foreignKey}_unique').on('${upvoteTable}').columns(['user_id', '${foreignKey}']).unique().execute()
37
+ `;migrationContent+=` await (db as any).schema.createIndex('${upvoteTable}_id_index').on('${upvoteTable}').column('id').execute()
38
+ `}}migrationContent+=`}
39
+ `;const migrationFileName=`${new Date().getTime().toString()}-create-${tableName}-table.ts`,migrationFilePath=path.userMigrationsPath(migrationFileName);await Bun.write(migrationFilePath,migrationContent);log.success(`Created migration: ${italic(migrationFileName)}`)}async function createPivotTableMigration(model,modelPath){const pivotTables=await getPivotTables(model,modelPath),processedPivotTables=new Set;if(!pivotTables.length)return;for(const pivotTable of pivotTables){if(processedPivotTables.has(pivotTable.table))continue;if(await checkPivotMigration(pivotTable.table)){processedPivotTables.add(pivotTable.table);continue}let migrationContent=`import type { Database } from '@stacksjs/database'
40
+ `;migrationContent+=`import { sql } from '@stacksjs/database'
226
41
 
227
- `;
228
- migrationContent += `export async function up(db: Database<any>) {
229
- `;
230
- migrationContent += ` await (db as any).schema
231
- `;
232
- migrationContent += ` .createTable('${pivotTable.table}')
233
- `;
234
- migrationContent += ` .addColumn('id', 'serial', (col) => col.primaryKey())
235
- `;
236
- migrationContent += ` .addColumn('${pivotTable.firstForeignKey}', 'integer', (col) => col.notNull())
237
- `;
238
- migrationContent += ` .addColumn('${pivotTable.secondForeignKey}', 'integer', (col) => col.notNull())
239
- `;
240
- migrationContent += ` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
241
- `;
242
- migrationContent += ` .execute()
42
+ `;migrationContent+=`export async function up(db: Database<any>) {
43
+ `;migrationContent+=` await (db as any).schema
44
+ `;migrationContent+=` .createTable('${pivotTable.table}')
45
+ `;migrationContent+=` .addColumn('id', 'serial', (col) => col.primaryKey())
46
+ `;migrationContent+=` .addColumn('${pivotTable.firstForeignKey}', 'integer', (col) => col.notNull())
47
+ `;migrationContent+=` .addColumn('${pivotTable.secondForeignKey}', 'integer', (col) => col.notNull())
48
+ `;migrationContent+=` .addColumn('created_at', 'timestamptz', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
49
+ `;migrationContent+=` .execute()
243
50
 
244
- `;
245
- migrationContent += ` await (db as any).schema
246
- `;
247
- migrationContent += ` .alterTable('${pivotTable.table}')
248
- `;
249
- migrationContent += ` .addForeignKeyConstraint('${pivotTable.table}_${pivotTable.firstForeignKey}_fkey', ['${pivotTable.firstForeignKey}'], '${plural(pivotTable.firstForeignKey?.replace(/_id$/, "") || "")}', ['id'], (cb) => cb.onDelete('cascade'))
250
- `;
251
- migrationContent += ` .execute()
51
+ `;migrationContent+=` await (db as any).schema
52
+ `;migrationContent+=` .alterTable('${pivotTable.table}')
53
+ `;migrationContent+=` .addForeignKeyConstraint('${pivotTable.table}_${pivotTable.firstForeignKey}_fkey', ['${pivotTable.firstForeignKey}'], '${plural(pivotTable.firstForeignKey?.replace(/_id$/,"")||"")}', ['id'], (cb) => cb.onDelete('cascade'))
54
+ `;migrationContent+=` .execute()
252
55
 
253
- `;
254
- migrationContent += ` await (db as any).schema
255
- `;
256
- migrationContent += ` .alterTable('${pivotTable.table}')
257
- `;
258
- migrationContent += ` .addUniqueConstraint('${pivotTable.table}_unique', ['${pivotTable.firstForeignKey}', '${pivotTable.secondForeignKey}'])
259
- `;
260
- migrationContent += ` .execute()
56
+ `;migrationContent+=` await (db as any).schema
57
+ `;migrationContent+=` .alterTable('${pivotTable.table}')
58
+ `;migrationContent+=` .addUniqueConstraint('${pivotTable.table}_unique', ['${pivotTable.firstForeignKey}', '${pivotTable.secondForeignKey}'])
59
+ `;migrationContent+=` .execute()
261
60
 
262
- `;
263
- migrationContent += ` await (db as any).schema
264
- `;
265
- migrationContent += ` .createIndex('${pivotTable.table}_${pivotTable.firstForeignKey}_idx')
266
- `;
267
- migrationContent += ` .on('${pivotTable.table}')
268
- `;
269
- migrationContent += ` .column('${pivotTable.firstForeignKey}')
270
- `;
271
- migrationContent += ` .execute()
61
+ `;migrationContent+=` await (db as any).schema
62
+ `;migrationContent+=` .createIndex('${pivotTable.table}_${pivotTable.firstForeignKey}_idx')
63
+ `;migrationContent+=` .on('${pivotTable.table}')
64
+ `;migrationContent+=` .column('${pivotTable.firstForeignKey}')
65
+ `;migrationContent+=` .execute()
272
66
 
273
- `;
274
- migrationContent += ` await (db as any).schema
275
- `;
276
- migrationContent += ` .createIndex('${pivotTable.table}_${pivotTable.secondForeignKey}_idx')
277
- `;
278
- migrationContent += ` .on('${pivotTable.table}')
279
- `;
280
- migrationContent += ` .column('${pivotTable.secondForeignKey}')
281
- `;
282
- migrationContent += ` .execute()
283
- `;
284
- migrationContent += `}
285
- `;
286
- const migrationFileName = `${new Date().getTime().toString()}-create-${pivotTable.table}-table.ts`, migrationFilePath = path.userMigrationsPath(migrationFileName);
287
- await Bun.write(migrationFilePath, migrationContent);
288
- processedPivotTables.add(pivotTable.table);
289
- log.success(`Created migration: ${italic(migrationFileName)}`);
290
- }
291
- }
292
- async function createAlterTableMigration(modelPath) {
293
- const model = (await import(modelPath)).default, modelName = getModelName(model, modelPath), tableName = getTableName(model, modelPath);
294
- let hasChanged = !1;
295
- const lastFields = await getLastMigrationFields(modelName) ?? {}, currentFields = model.attributes, changes = pluckChanges(Object.keys(lastFields), Object.keys(currentFields)), fieldsToAdd = changes?.added || [], fieldsToRemove = changes?.removed || [];
296
- let migrationContent = `import type { Database } from '@stacksjs/database'
297
- `;
298
- migrationContent += `import { sql } from '@stacksjs/database'
67
+ `;migrationContent+=` await (db as any).schema
68
+ `;migrationContent+=` .createIndex('${pivotTable.table}_${pivotTable.secondForeignKey}_idx')
69
+ `;migrationContent+=` .on('${pivotTable.table}')
70
+ `;migrationContent+=` .column('${pivotTable.secondForeignKey}')
71
+ `;migrationContent+=` .execute()
72
+ `;migrationContent+=`}
73
+ `;const migrationFileName=`${new Date().getTime().toString()}-create-${pivotTable.table}-table.ts`,migrationFilePath=path.userMigrationsPath(migrationFileName);await Bun.write(migrationFilePath,migrationContent);processedPivotTables.add(pivotTable.table);log.success(`Created migration: ${italic(migrationFileName)}`)}}async function createAlterTableMigration(modelPath){const model=(await import(modelPath)).default,modelName=getModelName(model,modelPath),tableName=getTableName(model,modelPath);let hasChanged=!1;const lastFields=await getLastMigrationFields(modelName)??{},currentFields=model.attributes,changes=pluckChanges(Object.keys(lastFields),Object.keys(currentFields)),fieldsToAdd=changes?.added||[],fieldsToRemove=changes?.removed||[];let migrationContent=`import type { Database } from '@stacksjs/database'
74
+ `;migrationContent+=`import { sql } from '@stacksjs/database'
299
75
 
300
- `;
301
- migrationContent += `export async function up(db: Database<any>) {
302
- `;
303
- if (fieldsToAdd.length || fieldsToRemove.length) {
304
- hasChanged = !0;
305
- migrationContent += ` await (db as any).schema.alterTable('${tableName}')
306
- `;
307
- }
308
- for (const fieldName of fieldsToAdd) {
309
- const options = currentFields[fieldName], columnType = mapFieldTypeToColumnType(options.validation?.rule, "postgres"), formattedFieldName = snakeCase(fieldName), isRequired = "isRequired" in (options.validation?.rule ?? {}) ? (options.validation?.rule).isRequired : !1;
310
- migrationContent += ` .addColumn('${formattedFieldName}', '${columnType}'`;
311
- if (isRequired || options.unique || options.default !== void 0) {
312
- migrationContent += ", col => col";
313
- if (isRequired)
314
- migrationContent += ".notNull()";
315
- if (options.unique)
316
- migrationContent += ".unique()";
317
- if (options.default !== void 0)
318
- if (typeof options.default === "string")
319
- migrationContent += `.defaultTo('${options.default}')`;
320
- else if (options.default === null)
321
- migrationContent += ".defaultTo(null)";
322
- else
323
- migrationContent += `.defaultTo(${options.default})`;
324
- migrationContent += "";
325
- }
326
- migrationContent += `)
327
- `;
328
- }
329
- for (const fieldName of fieldsToRemove)
330
- migrationContent += ` .dropColumn('${fieldName}')
331
- `;
332
- const fieldValidations = findDifferingKeys(lastFields, currentFields);
333
- for (const fieldValidation of fieldValidations) {
334
- hasChanged = !0;
335
- const fieldNameFormatted = snakeCase(fieldValidation.key);
336
- migrationContent += ` .alterColumn('${fieldNameFormatted}', (col) => col.setDataType('text'))
337
- `;
338
- }
339
- const lastFieldOrder = Object.values(lastFields).map((attr) => attr.order), currentFieldOrder = Object.values(currentFields).map((attr) => attr.order);
340
- if (!isArrayEqual(lastFieldOrder, currentFieldOrder))
341
- hasChanged = !0;
342
- if (hasChanged) {
343
- migrationContent += ` .execute()
344
- `;
345
- migrationContent += `}
346
- `;
347
- const migrationFileName = `${new Date().getTime().toString()}-alter-${tableName}-table.ts`, migrationFilePath = path.userMigrationsPath(migrationFileName);
348
- await Bun.write(migrationFilePath, migrationContent);
349
- log.success(`Created alter migration: ${italic(migrationFileName)}`);
350
- }
351
- }
352
- export function generateIndexCreationSQL(tableName, index) {
353
- if (index.unique || index.where) {
354
- const unique = index.unique ? "UNIQUE " : "", cols = index.columns.map((col) => snakeCase(col)).join(", "), whereClause = index.where ? ` WHERE ${index.where}` : "";
355
- return ` await db.unsafe(\`CREATE ${unique}INDEX IF NOT EXISTS "${index.name}" ON "${tableName}" (${cols})${whereClause}\`).execute()
356
- `;
357
- }
358
- const columnsStr = index.columns.map((col) => `'${snakeCase(col)}'`).join(", ");
359
- return ` await (db as any).schema.createIndex('${index.name}').on('${tableName}').columns([${columnsStr}]).execute()
360
- `;
361
- }
362
- function generatePrimaryKeyIndexSQL(tableName) {
363
- return ` await (db as any).schema.createIndex('${tableName}_id_index').on('${tableName}').column('id').execute()
364
- `;
365
- }
366
- function generateForeignKeyIndexSQL(tableName, foreignKey) {
367
- return ` await (db as any).schema.createIndex('${tableName}_${foreignKey}_index').on('${tableName}').column('${foreignKey}').execute()
76
+ `;migrationContent+=`export async function up(db: Database<any>) {
77
+ `;if(fieldsToAdd.length||fieldsToRemove.length){hasChanged=!0;migrationContent+=` await (db as any).schema.alterTable('${tableName}')
78
+ `}for(const fieldName of fieldsToAdd){const options=currentFields[fieldName],columnType=mapFieldTypeToColumnType(options.validation?.rule,"postgres"),formattedFieldName=snakeCase(fieldName),isRequired="isRequired"in(options.validation?.rule??{})?(options.validation?.rule).isRequired:!1;migrationContent+=` .addColumn('${formattedFieldName}', '${columnType}'`;if(isRequired||options.unique||options.default!==void 0){migrationContent+=", col => col";if(isRequired)migrationContent+=".notNull()";if(options.unique)migrationContent+=".unique()";if(options.default!==void 0)if(typeof options.default==="string")migrationContent+=`.defaultTo('${options.default}')`;else if(options.default===null)migrationContent+=".defaultTo(null)";else migrationContent+=`.defaultTo(${options.default})`;migrationContent+=""}migrationContent+=`)
79
+ `}for(const fieldName of fieldsToRemove)migrationContent+=` .dropColumn('${fieldName}')
80
+ `;const fieldValidations=findDifferingKeys(lastFields,currentFields);for(const fieldValidation of fieldValidations){hasChanged=!0;const fieldNameFormatted=snakeCase(fieldValidation.key);migrationContent+=` .alterColumn('${fieldNameFormatted}', (col) => col.setDataType('text'))
81
+ `}const lastFieldOrder=Object.values(lastFields).map((attr)=>attr.order),currentFieldOrder=Object.values(currentFields).map((attr)=>attr.order);if(!isArrayEqual(lastFieldOrder,currentFieldOrder))hasChanged=!0;if(hasChanged){migrationContent+=` .execute()
82
+ `;migrationContent+=`}
83
+ `;const migrationFileName=`${new Date().getTime().toString()}-alter-${tableName}-table.ts`,migrationFilePath=path.userMigrationsPath(migrationFileName);await Bun.write(migrationFilePath,migrationContent);log.success(`Created alter migration: ${italic(migrationFileName)}`)}}export function generateIndexCreationSQL(tableName,index){if(index.unique||index.where){const unique=index.unique?"UNIQUE ":"",cols=index.columns.map((col)=>snakeCase(col)).join(", "),whereClause=index.where?` WHERE ${index.where}`:"";return` await db.unsafe(\`CREATE ${unique}INDEX IF NOT EXISTS "${index.name}" ON "${tableName}" (${cols})${whereClause}\`).execute()
84
+ `}const columnsStr=index.columns.map((col)=>`'${snakeCase(col)}'`).join(", ");return` await (db as any).schema.createIndex('${index.name}').on('${tableName}').columns([${columnsStr}]).execute()
85
+ `}function generatePrimaryKeyIndexSQL(tableName){return` await (db as any).schema.createIndex('${tableName}_id_index').on('${tableName}').column('id').execute()
86
+ `}function generateForeignKeyIndexSQL(tableName,foreignKey){return` await (db as any).schema.createIndex('${tableName}_${foreignKey}_index').on('${tableName}').column('${foreignKey}').execute()
368
87
 
369
- `;
370
- }
371
- export async function fetchPostgresTables() {
372
- const modelFiles = globSync([path.userModelsPath("*.ts"), path.storagePath("framework/defaults/app/Models/**/*.ts")], { absolute: !0 }), tables = [];
373
- for (const modelPath of modelFiles) {
374
- const model = (await import(modelPath)).default, tableName = getTableName(model, modelPath);
375
- tables.push(tableName);
376
- }
377
- return tables;
378
- }
88
+ `}export async function fetchPostgresTables(){const modelFiles=globSync([path.userModelsPath("*.ts"),path.storagePath("framework/defaults/app/Models/**/*.ts")],{absolute:!0}),tables=[];for(const modelPath of modelFiles){const model=(await import(modelPath)).default,tableName=getTableName(model,modelPath);tables.push(tableName)}return tables}