@stacksjs/database 0.70.88 → 0.70.91

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