nanodb-orm 0.0.3 → 0.0.5

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 (87) hide show
  1. package/README.md +506 -333
  2. package/dist/cli.d.ts +96 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +348 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/constants/index.d.ts +7 -54
  7. package/dist/constants/index.d.ts.map +1 -1
  8. package/dist/constants/index.js +9 -61
  9. package/dist/constants/index.js.map +1 -1
  10. package/dist/core/config.d.ts +3 -13
  11. package/dist/core/config.d.ts.map +1 -1
  12. package/dist/core/config.js +5 -27
  13. package/dist/core/config.js.map +1 -1
  14. package/dist/core/connection.d.ts +16 -31
  15. package/dist/core/connection.d.ts.map +1 -1
  16. package/dist/core/connection.js +42 -78
  17. package/dist/core/connection.js.map +1 -1
  18. package/dist/core/index.d.ts +2 -3
  19. package/dist/core/index.d.ts.map +1 -1
  20. package/dist/core/index.js +3 -18
  21. package/dist/core/index.js.map +1 -1
  22. package/dist/index.d.ts +235 -12
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +252 -35
  25. package/dist/index.js.map +1 -1
  26. package/dist/init.d.ts +127 -18
  27. package/dist/init.d.ts.map +1 -1
  28. package/dist/init.js +280 -47
  29. package/dist/init.js.map +1 -1
  30. package/dist/jest.setup.d.ts +4 -0
  31. package/dist/jest.setup.d.ts.map +1 -0
  32. package/dist/jest.setup.js +40 -0
  33. package/dist/jest.setup.js.map +1 -0
  34. package/dist/types/errors.d.ts +30 -12
  35. package/dist/types/errors.d.ts.map +1 -1
  36. package/dist/types/errors.js +98 -23
  37. package/dist/types/errors.js.map +1 -1
  38. package/dist/types/index.d.ts +268 -4
  39. package/dist/types/index.d.ts.map +1 -1
  40. package/dist/types/index.js +5 -4
  41. package/dist/types/index.js.map +1 -1
  42. package/dist/utils/error-handler.d.ts +6 -31
  43. package/dist/utils/error-handler.d.ts.map +1 -1
  44. package/dist/utils/error-handler.js +24 -81
  45. package/dist/utils/error-handler.js.map +1 -1
  46. package/dist/utils/index.d.ts +1 -3
  47. package/dist/utils/index.d.ts.map +1 -1
  48. package/dist/utils/index.js +4 -6
  49. package/dist/utils/index.js.map +1 -1
  50. package/dist/utils/logger.d.ts +6 -25
  51. package/dist/utils/logger.d.ts.map +1 -1
  52. package/dist/utils/logger.js +20 -38
  53. package/dist/utils/logger.js.map +1 -1
  54. package/dist/utils/migrations.d.ts +16 -90
  55. package/dist/utils/migrations.d.ts.map +1 -1
  56. package/dist/utils/migrations.js +220 -422
  57. package/dist/utils/migrations.js.map +1 -1
  58. package/dist/utils/schema-introspection.d.ts +30 -169
  59. package/dist/utils/schema-introspection.d.ts.map +1 -1
  60. package/dist/utils/schema-introspection.js +125 -462
  61. package/dist/utils/schema-introspection.js.map +1 -1
  62. package/dist/utils/seeds.d.ts +15 -48
  63. package/dist/utils/seeds.d.ts.map +1 -1
  64. package/dist/utils/seeds.js +108 -186
  65. package/dist/utils/seeds.js.map +1 -1
  66. package/dist/utils/sync.d.ts +16 -41
  67. package/dist/utils/sync.d.ts.map +1 -1
  68. package/dist/utils/sync.js +78 -172
  69. package/dist/utils/sync.js.map +1 -1
  70. package/dist/utils/transactions.d.ts +61 -44
  71. package/dist/utils/transactions.d.ts.map +1 -1
  72. package/dist/utils/transactions.js +103 -137
  73. package/dist/utils/transactions.js.map +1 -1
  74. package/package.json +29 -10
  75. package/dist/example.d.ts +0 -67
  76. package/dist/example.d.ts.map +0 -1
  77. package/dist/example.js +0 -86
  78. package/dist/example.js.map +0 -1
  79. package/dist/types/database.d.ts +0 -74
  80. package/dist/types/database.d.ts.map +0 -1
  81. package/dist/types/database.js +0 -6
  82. package/dist/types/database.js.map +0 -1
  83. package/dist/types/types.d.ts +0 -30
  84. package/dist/types/types.d.ts.map +0 -1
  85. package/dist/types/types.js +0 -6
  86. package/dist/types/types.js.map +0 -1
  87. package/llm.txt +0 -336
@@ -1,457 +1,255 @@
1
1
  "use strict";
2
- /**
3
- * Completely Generic Database Migration Utilities
4
- * Uses Drizzle to auto-create tables from schema
5
- * No hardcoded table names or structures - completely dynamic!
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.DatabaseMigrations = void 0;
9
4
  const drizzle_orm_1 = require("drizzle-orm");
10
5
  const connection_1 = require("../core/connection");
11
6
  const logger_1 = require("./logger");
12
- const errors_1 = require("../types/errors");
13
7
  const error_handler_1 = require("./error-handler");
14
8
  const transactions_1 = require("./transactions");
15
- class DatabaseMigrations {
16
- /**
17
- * Initialize with schema data
18
- */
19
- static initialize(schemaData) {
20
- this.tables = schemaData.tables;
21
- this.migrationConfig = {
22
- preserveData: true, // Always preserve data by default
23
- autoMigrate: true, // Default to auto-migration
24
- dropTables: false, // Default to not dropping tables
25
- ...schemaData.migrationConfig, // Override with any provided config
26
- };
27
- }
28
- /**
29
- * Initialize database schema using Drizzle's auto table creation
30
- * Completely generic - works with any table structure defined in schema.ts
31
- */
32
- static async initializeSchema() {
33
- return error_handler_1.ErrorHandler.wrapAsync(async () => {
34
- logger_1.logger.info('Auto-creating database schema using Drizzle...');
35
- const db = await connection_1.DatabaseConnection.getInstance();
36
- // Get all tables from the provided schema
37
- const tableNames = Object.keys(this.tables);
38
- logger_1.logger.info(`Found ${tableNames.length} tables in schema: ${tableNames.join(', ')}`);
39
- // Use Drizzle to auto-create tables
40
- for (const tableName of tableNames) {
41
- await this.createTableWithDrizzle(tableName, db);
42
- }
43
- logger_1.logger.info('Database schema initialization completed!');
44
- }, {
45
- operation: 'init-schema',
46
- context: 'Schema initialization failed'
47
- });
48
- }
49
- /**
50
- * Create a table using Drizzle's table definition
51
- * Uses the actual Drizzle table object to ensure correct SQL generation
52
- */
53
- static async createTableWithDrizzle(tableName, db) {
54
- try {
55
- const table = this.tables[tableName];
56
- if (!table) {
57
- throw new Error(`Table ${tableName} not found in provided schema`);
58
- }
59
- logger_1.logger.info(`Creating table ${tableName} using Drizzle definition`);
60
- // Check if table exists and has correct schema
61
- try {
62
- const hasCorrectSchema = await this.validateTableSchema(tableName, table, db);
63
- if (!hasCorrectSchema) {
64
- if (!this.migrationConfig.autoMigrate) {
65
- logger_1.logger.warn(`Table ${tableName} needs schema update but auto-migration is disabled`);
66
- return;
67
- }
68
- const hasData = await this.tableHasData(tableName, db);
69
- if (hasData && this.migrationConfig.preserveData && !this.migrationConfig.dropTables) {
70
- logger_1.logger.warn(`Table ${tableName} has data and preserveData is enabled. Skipping schema update.`);
71
- logger_1.logger.warn(`To update schema, set dropTables: true or preserveData: false in migration config`);
72
- return;
73
- }
74
- if (hasData) {
75
- logger_1.logger.info(`Table ${tableName} has data but will be recreated due to configuration`);
76
- }
77
- logger_1.logger.info(`Table ${tableName} needs schema update, recreating...`);
78
- // Use transaction to ensure atomic table recreation
79
- const result = await transactions_1.TransactionManager.recreateTable(tableName, async (db) => {
80
- const basicCreateSql = this.generateBasicCreateSql(tableName, table);
81
- await db.run(basicCreateSql);
82
- });
83
- if (!result.success) {
84
- throw new errors_1.DatabaseError(`Failed to recreate table ${tableName}: ${result.error?.message}`, 'recreate-table', result.error);
85
- }
86
- logger_1.logger.info(`Table ${tableName} recreated with correct schema`);
87
- }
88
- else {
89
- logger_1.logger.info(`Table ${tableName} already has correct schema`);
90
- }
91
- }
92
- catch (error) {
93
- error_handler_1.ErrorHandler.handleError(error, {
94
- operation: 'create-table',
95
- context: `Error ensuring table ${tableName}`
96
- });
97
- }
98
- }
99
- catch (error) {
100
- error_handler_1.ErrorHandler.handleError(error, {
101
- operation: 'create-table',
102
- context: `Failed to create table ${tableName}`
103
- });
104
- }
105
- }
106
- /**
107
- * Validate if table exists and has the correct schema
108
- */
109
- static async validateTableSchema(tableName, table, db) {
110
- try {
111
- // First check if table exists
112
- const checkResult = await db.run((0, drizzle_orm_1.sql) `
113
- SELECT name FROM sqlite_master
114
- WHERE type='table' AND name=${tableName}
115
- `);
116
- if (!checkResult.rows || checkResult.rows.length === 0) {
117
- logger_1.logger.info(`Table ${tableName} does not exist, needs creation`);
118
- return false;
119
- }
120
- // Get expected columns from the Drizzle table
121
- const expectedColumns = this.getExpectedColumns(table);
122
- // Get actual columns from the database
123
- const actualColumns = await this.getActualColumns(tableName, db);
124
- logger_1.logger.info(`Expected columns for ${tableName}:`, expectedColumns);
125
- logger_1.logger.info(`Actual columns for ${tableName}:`, actualColumns);
126
- // Check if all expected columns exist
127
- for (const expectedCol of expectedColumns) {
128
- if (!actualColumns.includes(expectedCol)) {
129
- logger_1.logger.info(`Table ${tableName} missing column: ${expectedCol}`);
130
- return false;
131
- }
132
- }
133
- // Check if there are extra columns that shouldn't exist
134
- for (const actualCol of actualColumns) {
135
- if (!expectedColumns.includes(actualCol)) {
136
- logger_1.logger.info(`Table ${tableName} has extra column that should be removed: ${actualCol}`);
137
- return false;
138
- }
139
- }
140
- return true;
141
- }
142
- catch (error) {
143
- error_handler_1.ErrorHandler.handleNonThrowingError(error, 'validate-schema', `Error validating schema for table ${tableName}`);
144
- return false; // If we can't validate, assume it needs recreation
145
- }
146
- }
147
- /**
148
- * Get expected column names from Drizzle table definition
149
- */
150
- static getExpectedColumns(table) {
151
- const columns = {};
152
- // Use the same logic as generateBasicCreateSql
153
- if (table._ && table._.columns) {
154
- Object.assign(columns, table._.columns);
155
- }
156
- else if (table.columns) {
157
- Object.assign(columns, table.columns);
158
- }
159
- else {
160
- const tableKeys = Object.keys(table).filter(key => !key.startsWith('_') &&
161
- typeof table[key] === 'object' &&
162
- table[key] !== null &&
163
- !Array.isArray(table[key]));
164
- for (const key of tableKeys) {
165
- columns[key] = table[key];
166
- }
167
- }
168
- const expectedColumns = [];
169
- for (const [colName, column] of Object.entries(columns)) {
170
- const col = column;
171
- const dbColumnName = col.name || col.columnName || colName;
172
- expectedColumns.push(dbColumnName);
173
- }
174
- return expectedColumns;
175
- }
176
- /**
177
- * Get actual column names from database table
178
- */
179
- static async getActualColumns(tableName, db) {
180
- try {
181
- const result = await db.run((0, drizzle_orm_1.sql) `PRAGMA table_info(${drizzle_orm_1.sql.identifier(tableName)})`);
182
- if (result.rows) {
183
- return result.rows.map((row) => row.name);
184
- }
185
- return [];
186
- }
187
- catch (error) {
188
- error_handler_1.ErrorHandler.handleNonThrowingError(error, 'get-table-info', `Error getting table info for ${tableName}`);
189
- return [];
190
- }
191
- }
192
- /**
193
- * Check if table has any data
194
- */
195
- static async tableHasData(tableName, db) {
196
- try {
197
- const result = await db.run((0, drizzle_orm_1.sql) `SELECT COUNT(*) as count FROM ${drizzle_orm_1.sql.identifier(tableName)}`);
198
- if (result.rows && result.rows.length > 0) {
199
- const count = result.rows[0].count;
200
- return count > 0;
201
- }
202
- return false;
203
- }
204
- catch (error) {
205
- error_handler_1.ErrorHandler.handleNonThrowingError(error, 'check-table-data', `Error checking data for table ${tableName}`);
206
- return false; // If we can't check, assume no data
207
- }
208
- }
209
- /**
210
- * Generate basic CREATE TABLE SQL for SQLite
211
- * This is a simplified approach that works for most common table structures
212
- */
213
- static generateBasicCreateSql(tableName, table) {
214
- try {
215
- // Extract column information from Drizzle table with multiple fallback options
216
- let columns = {};
217
- if (table._ && table._.columns) {
218
- columns = table._.columns;
219
- }
220
- else if (table.columns) {
221
- columns = table.columns;
222
- }
223
- else {
224
- // Try to extract columns directly from the table object
225
- const tableKeys = Object.keys(table).filter(key => !key.startsWith('_') &&
226
- typeof table[key] === 'object' &&
227
- table[key] !== null &&
228
- !Array.isArray(table[key]));
229
- for (const key of tableKeys) {
230
- columns[key] = table[key];
231
- }
232
- }
233
- const columnDefs = [];
234
- const foreignKeys = [];
235
- for (const [colName, column] of Object.entries(columns)) {
236
- const col = column;
237
- // Extract the actual database column name from the Drizzle column
238
- const dbColumnName = col.name || col.columnName || colName;
239
- let columnDef = `"${dbColumnName}"`;
240
- // Determine column type
241
- if (col.dataType === 'integer' || col.columnType === 'SQLiteInteger' || col.type === 'integer') {
242
- columnDef += ' INTEGER';
243
- }
244
- else if (col.dataType === 'real' || col.columnType === 'SQLiteReal' || col.type === 'real') {
245
- columnDef += ' REAL';
246
- }
247
- else if (col.dataType === 'text' || col.columnType === 'SQLiteText' || col.type === 'text') {
248
- columnDef += ' TEXT';
249
- }
250
- else {
251
- columnDef += ' TEXT';
252
- }
253
- // Add constraints
254
- if (col.primary) {
255
- columnDef += ' PRIMARY KEY';
256
- // Add AUTOINCREMENT for primary key if specified
257
- if (col.autoIncrement) {
258
- columnDef += ' AUTOINCREMENT';
259
- }
260
- }
261
- if (col.notNull) {
262
- columnDef += ' NOT NULL';
263
- }
264
- if (col.unique) {
265
- columnDef += ' UNIQUE';
266
- }
267
- if (col.default !== undefined) {
268
- if (typeof col.default === 'string') {
269
- columnDef += ` DEFAULT '${col.default}'`;
270
- }
271
- else if (col.default?.sql) {
272
- // Handle SQL expressions like sql`(datetime('now'))`
273
- columnDef += ` DEFAULT ${col.default.sql}`;
274
- }
275
- else if (col.default?.name === 'sql') {
276
- columnDef += ` DEFAULT ${col.default.value}`;
277
- }
278
- else if (typeof col.default === 'number') {
279
- columnDef += ` DEFAULT ${col.default}`;
280
- }
281
- else {
282
- columnDef += ` DEFAULT ${col.default}`;
283
- }
284
- }
285
- columnDefs.push(columnDef);
286
- // Handle foreign key references
287
- if (col.references) {
288
- const foreignTable = col.references.table || col.references.foreignTable?.tableName || 'unknown';
289
- const foreignColumn = col.references.column || col.references.columnName || 'id';
290
- const onDelete = col.references.onDelete || 'CASCADE';
291
- foreignKeys.push(`FOREIGN KEY ("${dbColumnName}") REFERENCES "${foreignTable}"("${foreignColumn}") ON DELETE ${onDelete}`);
292
- }
293
- }
294
- // Combine column definitions and foreign keys
295
- const allConstraints = [...columnDefs, ...foreignKeys];
296
- return `CREATE TABLE IF NOT EXISTS "${tableName}" (
297
- ${allConstraints.join(',\n ')}
298
- )`;
299
- }
300
- catch (error) {
301
- error_handler_1.ErrorHandler.handleError(error, {
302
- operation: 'generate-sql',
303
- context: `Error generating SQL for table ${tableName}`
304
- });
305
- return ''; // This will never be reached due to handleError throwing
306
- }
307
- }
308
- /**
309
- * Drop all tables
310
- * Uses the provided schema tables
311
- */
312
- static async dropAllTables() {
313
- return error_handler_1.ErrorHandler.wrapAsync(async () => {
314
- logger_1.logger.warn('Dropping all tables...');
9
+ // ============================================================================
10
+ // Module State
11
+ // ============================================================================
12
+ let tables = {};
13
+ let config = {
14
+ preserveData: true,
15
+ autoMigrate: true,
16
+ dropTables: false,
17
+ };
18
+ // ============================================================================
19
+ // Public API
20
+ // ============================================================================
21
+ exports.DatabaseMigrations = {
22
+ /** Initialize with schema data */
23
+ initialize(data) {
24
+ tables = data.tables;
25
+ config = { ...config, ...data.migrationConfig };
26
+ },
27
+ /** Create all tables from schema */
28
+ async initializeSchema() {
29
+ return (0, error_handler_1.withErrorHandling)('init-schema', async () => {
315
30
  const db = await connection_1.DatabaseConnection.getInstance();
316
- // Get all tables from provided schema
317
- const tableNames = Object.keys(this.tables);
318
- // Use transaction to ensure atomic table dropping
319
- const result = await transactions_1.TransactionManager.execute(async (db) => {
320
- // Drop tables in reverse order to handle foreign key constraints
321
- const reversedTableNames = [...tableNames].reverse();
322
- for (const tableName of reversedTableNames) {
323
- await db.run(`DROP TABLE IF EXISTS ${tableName}`);
324
- logger_1.logger.info(`Dropped table ${tableName}`);
325
- }
326
- });
327
- if (!result.success) {
328
- throw new errors_1.DatabaseError(`Failed to drop tables: ${result.error?.message}`, 'drop-tables', result.error);
329
- }
330
- logger_1.logger.info('All tables dropped successfully');
331
- }, {
332
- operation: 'drop-tables',
333
- context: 'Drop tables failed'
31
+ const tableNames = Object.keys(tables);
32
+ logger_1.logger.info(`Initializing ${tableNames.length} tables...`);
33
+ for (const name of tableNames) {
34
+ const table = tables[name];
35
+ if (table)
36
+ await ensureTable(name, table, db);
37
+ }
38
+ logger_1.logger.info('Schema initialization complete');
334
39
  });
335
- }
336
- /**
337
- * Reset database (drop and recreate all tables)
338
- * Completely generic - works with any table structure defined in schema.ts
339
- */
340
- static async resetDatabase() {
341
- try {
40
+ },
41
+ /** Drop all tables and recreate */
42
+ async resetDatabase() {
43
+ return (0, error_handler_1.withErrorHandling)('reset-db', async () => {
342
44
  logger_1.logger.warn('Resetting database...');
343
45
  await this.dropAllTables();
344
46
  await this.initializeSchema();
345
- logger_1.logger.info('Database reset completed');
346
- }
347
- catch (error) {
348
- error_handler_1.ErrorHandler.handleError(error, {
349
- operation: 'reset-db',
350
- context: 'Database reset failed'
351
- });
352
- }
353
- }
354
- /**
355
- * Check if tables exist in the database
356
- * Uses the provided schema tables
357
- */
358
- static async checkTableExistence() {
359
- try {
47
+ });
48
+ },
49
+ /** Drop all tables */
50
+ async dropAllTables() {
51
+ return (0, error_handler_1.withErrorHandling)('drop-tables', async () => {
360
52
  const db = await connection_1.DatabaseConnection.getInstance();
361
- const tableNames = Object.keys(this.tables);
362
- const existence = {};
363
- for (const tableName of tableNames) {
364
- try {
365
- // Check if table exists by querying sqlite_master
366
- const result = await db.run((0, drizzle_orm_1.sql) `
367
- SELECT name FROM sqlite_master
368
- WHERE type='table' AND name=${tableName}
369
- `);
370
- existence[tableName] = (result.rows && result.rows.length > 0);
371
- }
372
- catch (error) {
373
- error_handler_1.ErrorHandler.handleOptionalError(error, 'check-table-existence', `Could not check existence of table ${tableName}`);
374
- existence[tableName] = false;
375
- }
53
+ const tableNames = Object.keys(tables).reverse();
54
+ for (const name of tableNames) {
55
+ await db.run((0, drizzle_orm_1.sql) `DROP TABLE IF EXISTS ${drizzle_orm_1.sql.identifier(name)}`);
56
+ logger_1.logger.debug(`Dropped: ${name}`);
376
57
  }
377
- return existence;
378
- }
379
- catch (error) {
380
- error_handler_1.ErrorHandler.handleNonThrowingError(error, 'check-table-existence', 'Error checking table existence');
381
- return {};
382
- }
383
- }
384
- /**
385
- * Validate that database schema matches provided schema
386
- * Uses the provided schema tables
387
- */
388
- static async validateSchema() {
58
+ });
59
+ },
60
+ /** Check which tables exist */
61
+ async checkTableExistence() {
62
+ const db = await connection_1.DatabaseConnection.getInstance();
63
+ const result = {};
64
+ for (const name of Object.keys(tables)) {
65
+ result[name] = await tableExists(name, db);
66
+ }
67
+ return result;
68
+ },
69
+ /** Validate schema matches database */
70
+ async validateSchema() {
389
71
  try {
390
- const tableNames = Object.keys(this.tables);
391
72
  const existence = await this.checkTableExistence();
392
- const missingTables = [];
393
- const extraTables = [];
394
- const errors = [];
395
- // Check for missing tables
396
- for (const tableName of tableNames) {
397
- if (!existence[tableName]) {
398
- missingTables.push(tableName);
399
- }
400
- }
401
- // Check for extra tables (tables in DB but not in schema.ts)
402
- // This would require querying the database for all tables
403
- // For now, we'll focus on missing tables
404
- const isValid = missingTables.length === 0 && errors.length === 0;
73
+ const expected = Object.keys(tables);
74
+ const missingTables = expected.filter((t) => !existence[t]);
405
75
  return {
406
- isValid,
76
+ isValid: missingTables.length === 0,
407
77
  missingTables,
408
- extraTables,
409
- errors
78
+ extraTables: [],
79
+ errors: [],
410
80
  };
411
81
  }
412
82
  catch (error) {
413
- error_handler_1.ErrorHandler.handleNonThrowingError(error, 'validate-schema', 'Error validating schema');
83
+ (0, error_handler_1.logError)('validate-schema', error);
414
84
  return {
415
85
  isValid: false,
416
86
  missingTables: [],
417
87
  extraTables: [],
418
- errors: [error.message]
88
+ errors: [error instanceof Error ? error.message : String(error)],
419
89
  };
420
90
  }
91
+ },
92
+ };
93
+ // ============================================================================
94
+ // Internal Functions
95
+ // ============================================================================
96
+ async function ensureTable(name, table, db) {
97
+ const exists = await tableExists(name, db);
98
+ const expectedCols = getColumnNames(table);
99
+ if (!exists) {
100
+ logger_1.logger.info(`Creating table: ${name}`);
101
+ await db.run(drizzle_orm_1.sql.raw(generateCreateSql(name, table)));
102
+ return;
421
103
  }
422
- /**
423
- * Get migration status
424
- * Uses the provided schema tables
425
- */
426
- static async getMigrationStatus() {
427
- try {
428
- const tableNames = Object.keys(this.tables);
429
- const existence = await this.checkTableExistence();
430
- const missingTables = tableNames.filter(name => !existence[name]);
431
- const isValid = missingTables.length === 0;
432
- return {
433
- tablesInSchema: tableNames,
434
- tablesInDatabase: existence,
435
- missingTables,
436
- isValid
104
+ const actualCols = await getActualColumns(name, db);
105
+ const schemaMatches = columnsMatch(expectedCols, actualCols);
106
+ if (schemaMatches) {
107
+ logger_1.logger.debug(`Table ${name} is up to date`);
108
+ return;
109
+ }
110
+ if (!config.autoMigrate) {
111
+ logger_1.logger.warn(`Table ${name} needs update but auto-migrate is disabled`);
112
+ return;
113
+ }
114
+ const hasData = await tableHasData(name, db);
115
+ if (hasData && config.preserveData && !config.dropTables) {
116
+ logger_1.logger.warn(`Table ${name} has data, skipping update (set dropTables: true to force)`);
117
+ return;
118
+ }
119
+ logger_1.logger.info(`Recreating table: ${name}`);
120
+ const result = await (0, transactions_1.recreateTable)(name, generateCreateSql(name, table));
121
+ if (!result.success) {
122
+ throw result.error;
123
+ }
124
+ }
125
+ async function tableExists(name, db) {
126
+ try {
127
+ const result = await db.run((0, drizzle_orm_1.sql) `
128
+ SELECT 1 FROM sqlite_master WHERE type='table' AND name=${name}
129
+ `);
130
+ return (result.rows?.length ?? 0) > 0;
131
+ }
132
+ catch {
133
+ return false;
134
+ }
135
+ }
136
+ async function tableHasData(name, db) {
137
+ try {
138
+ const result = await db.run((0, drizzle_orm_1.sql) `SELECT COUNT(*) as c FROM ${drizzle_orm_1.sql.identifier(name)}`);
139
+ const row = result.rows?.[0];
140
+ const count = row ? row.c : 0;
141
+ return (count ?? 0) > 0;
142
+ }
143
+ catch {
144
+ return false;
145
+ }
146
+ }
147
+ async function getActualColumns(name, db) {
148
+ try {
149
+ const result = await db.run((0, drizzle_orm_1.sql) `PRAGMA table_info(${drizzle_orm_1.sql.identifier(name)})`);
150
+ return result.rows?.map((r) => r.name) ?? [];
151
+ }
152
+ catch {
153
+ return [];
154
+ }
155
+ }
156
+ function getColumnNames(table) {
157
+ const cols = extractColumns(table);
158
+ return Object.keys(cols);
159
+ }
160
+ /**
161
+ * Extract column information from a Drizzle table.
162
+ * Handles both real Drizzle tables and simple objects.
163
+ */
164
+ function extractColumns(table) {
165
+ const cols = {};
166
+ for (const [key, val] of Object.entries(table)) {
167
+ if (key.startsWith('_') || key === 'Symbol(drizzle:Name)')
168
+ continue;
169
+ if (typeof val !== 'object' || val === null)
170
+ continue;
171
+ const column = val;
172
+ // Check if it's a Drizzle column (has .name property that's a string)
173
+ const columnName = typeof column.name === 'string' ? column.name : key;
174
+ if (!column.name && !column.dataType)
175
+ continue;
176
+ // Determine SQL type
177
+ let dataType = 'TEXT';
178
+ const rawType = column.dataType ?? column.columnType ?? '';
179
+ const sqliteType = column.getSQLType?.() ?? '';
180
+ if (rawType.includes('integer') || sqliteType.includes('integer')) {
181
+ dataType = 'INTEGER';
182
+ }
183
+ else if (rawType.includes('real') || sqliteType.includes('real')) {
184
+ dataType = 'REAL';
185
+ }
186
+ else if (rawType.includes('blob') || sqliteType.includes('blob')) {
187
+ dataType = 'BLOB';
188
+ }
189
+ const info = {
190
+ name: columnName,
191
+ dataType,
192
+ isPrimary: column.primary === true || column.primaryKey === true,
193
+ hasAutoIncrement: column.autoIncrement === true || column.hasAutoIncrement === true,
194
+ isNotNull: column.notNull === true || column.isNotNull === true,
195
+ isUnique: column.unique === true || column.isUnique === true,
196
+ defaultValue: column.default ?? column.defaultValue,
197
+ };
198
+ if (column.references) {
199
+ const ref = column.references;
200
+ info.references = {
201
+ table: ref.table ?? ref.foreignTable?.tableName ?? 'unknown',
202
+ column: ref.column ?? ref.columnName ?? 'id',
203
+ onDelete: ref.onDelete ?? 'CASCADE',
437
204
  };
438
205
  }
439
- catch (error) {
440
- error_handler_1.ErrorHandler.handleNonThrowingError(error, 'get-migration-status', 'Error getting migration status');
441
- return {
442
- tablesInSchema: [],
443
- tablesInDatabase: {},
444
- missingTables: [],
445
- isValid: false
446
- };
206
+ cols[columnName] = info;
207
+ }
208
+ return cols;
209
+ }
210
+ function columnsMatch(expected, actual) {
211
+ if (expected.length !== actual.length)
212
+ return false;
213
+ const sortedExpected = [...expected].sort();
214
+ const sortedActual = [...actual].sort();
215
+ return sortedExpected.every((col, i) => col === sortedActual[i]);
216
+ }
217
+ function generateCreateSql(tableName, table) {
218
+ const cols = extractColumns(table);
219
+ const columnDefs = [];
220
+ const foreignKeys = [];
221
+ for (const [colName, info] of Object.entries(cols)) {
222
+ const parts = [`"${colName}"`, info.dataType];
223
+ if (info.isPrimary) {
224
+ parts.push('PRIMARY KEY');
225
+ if (info.hasAutoIncrement)
226
+ parts.push('AUTOINCREMENT');
227
+ }
228
+ if (info.isNotNull && !info.isPrimary)
229
+ parts.push('NOT NULL');
230
+ if (info.isUnique)
231
+ parts.push('UNIQUE');
232
+ // Default value
233
+ if (info.defaultValue !== undefined) {
234
+ const defaultVal = info.defaultValue;
235
+ if (typeof defaultVal === 'string') {
236
+ parts.push(`DEFAULT '${defaultVal}'`);
237
+ }
238
+ else if (typeof defaultVal === 'number') {
239
+ parts.push(`DEFAULT ${defaultVal}`);
240
+ }
241
+ else if (typeof defaultVal === 'object' && defaultVal !== null && 'sql' in defaultVal) {
242
+ parts.push(`DEFAULT ${defaultVal.sql}`);
243
+ }
244
+ }
245
+ columnDefs.push(parts.join(' '));
246
+ // Foreign key
247
+ if (info.references) {
248
+ const { table: refTable, column: refCol, onDelete } = info.references;
249
+ foreignKeys.push(`FOREIGN KEY ("${colName}") REFERENCES "${refTable}"("${refCol}") ON DELETE ${onDelete}`);
447
250
  }
448
251
  }
252
+ const allDefs = [...columnDefs, ...foreignKeys].join(',\n ');
253
+ return `CREATE TABLE IF NOT EXISTS "${tableName}" (\n ${allDefs}\n)`;
449
254
  }
450
- exports.DatabaseMigrations = DatabaseMigrations;
451
- DatabaseMigrations.tables = {};
452
- DatabaseMigrations.migrationConfig = {
453
- preserveData: true,
454
- autoMigrate: true,
455
- dropTables: false,
456
- };
457
255
  //# sourceMappingURL=migrations.js.map