sqlmorpheus 0.2.0 → 0.2.2

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 CHANGED
@@ -20,8 +20,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- DbTable: () => DbTable,
23
+ DataType: () => DataType,
24
24
  ForeignKey: () => ForeignKey,
25
+ TableClass: () => TableClass,
26
+ TableObj: () => TableObj,
25
27
  prepareAndRunMigration: () => prepareAndRunMigration,
26
28
  prepareMigration: () => prepareMigration,
27
29
  rollback: () => rollback,
@@ -53,19 +55,110 @@ var LoggerClass = class {
53
55
  };
54
56
  var Logger = new LoggerClass();
55
57
 
56
- // src/lib/tableInfo/TableInfo.ts
58
+ // src/lib/tableInfo/TableObj.ts
59
+ var TableObj = class _TableObj {
60
+ IS_TABLE_OBJ = true;
61
+ tableName;
62
+ tableStructure = {
63
+ table: "",
64
+ primaryKey: void 0,
65
+ columns: {},
66
+ foreignKeys: []
67
+ };
68
+ static getEmptyColumnEntry(key) {
69
+ return {
70
+ name: key,
71
+ type: "",
72
+ defaultValue: "",
73
+ isPrimaryKey: false
74
+ };
75
+ }
76
+ static getColumnEntry(key, value) {
77
+ let dataType = typeof value;
78
+ switch (typeof value) {
79
+ case "function":
80
+ return null;
81
+ case "object":
82
+ if (value instanceof Date) {
83
+ dataType = "date";
84
+ break;
85
+ } else {
86
+ Logger.warn(`${key} was skipped because its type (${typeof value}) is not supported`);
87
+ return null;
88
+ }
89
+ case "undefined":
90
+ case "symbol":
91
+ Logger.warn(`${key} was skipped because its type (${typeof value}) is not supported`);
92
+ return null;
93
+ }
94
+ return {
95
+ ..._TableObj.getEmptyColumnEntry(key),
96
+ inputType: dataType,
97
+ inputDefaultValue: value
98
+ };
99
+ }
100
+ constructor(tableName, defaultValues) {
101
+ this.tableName = tableName;
102
+ for (const key in defaultValues) {
103
+ const value = defaultValues[key];
104
+ const columnEntry = _TableObj.getColumnEntry(key, value);
105
+ if (!columnEntry)
106
+ continue;
107
+ this.tableStructure.columns[key] = columnEntry;
108
+ }
109
+ }
110
+ primaryKey(key) {
111
+ this.tableStructure.primaryKey = key.toString();
112
+ this.tableStructure.columns[key.toString()].isPrimaryKey = true;
113
+ return this;
114
+ }
115
+ foreignKey(fromColumn, toTable, toColumn, options) {
116
+ this.tableStructure.foreignKeys.push({
117
+ fromTable: this.tableName,
118
+ fromColumn: fromColumn.toString(),
119
+ toTable: toTable.tableName,
120
+ toColumn: toColumn.toString(),
121
+ onUpdate: options?.onUpdate,
122
+ onDelete: options?.onDelete
123
+ });
124
+ return this;
125
+ }
126
+ dataType(column, type) {
127
+ this.tableStructure.columns[column.toString()].type = type;
128
+ return this;
129
+ }
130
+ maxCharacterLength(column, max) {
131
+ this.tableStructure.columns[column.toString()].maxLength = max;
132
+ return this;
133
+ }
134
+ static create(tableName, columns) {
135
+ return new _TableObj(tableName, columns);
136
+ }
137
+ static isTableObj(obj) {
138
+ return !!obj?.IS_TABLE_OBJ;
139
+ }
140
+ };
141
+
142
+ // src/lib/tableInfo/getTableStructure.ts
57
143
  var TABLE_INFO_PROPERTY_NAME = Symbol("tableInfo");
58
- function getTableInfo(table) {
144
+ function getInputColumnInfo(table, context) {
145
+ const metadata = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
146
+ const key = context.name ?? context;
147
+ if (!metadata.columns.hasOwnProperty(key))
148
+ metadata.columns[key] = TableObj.getEmptyColumnEntry(key);
149
+ return metadata.columns[key];
150
+ }
151
+ function getTableStructure(table) {
59
152
  let metadata = table[Symbol.metadata] ?? table.prototype?.[Symbol.metadata];
60
153
  if (!metadata) {
61
154
  metadata = {};
62
155
  table[Symbol.metadata] = metadata;
63
156
  }
64
- return getTableInfoFromMetadata(metadata);
157
+ return getTableStructureFromMetadata(metadata);
65
158
  }
66
- function getTableInfoFromMetadata(metadata) {
159
+ function getTableStructureFromMetadata(metadata) {
67
160
  if (!metadata[TABLE_INFO_PROPERTY_NAME])
68
- metadata[TABLE_INFO_PROPERTY_NAME] = { primaryKey: "", foreignKeys: [] };
161
+ metadata[TABLE_INFO_PROPERTY_NAME] = { table: "", primaryKey: "", foreignKeys: [], columns: {} };
69
162
  return metadata[TABLE_INFO_PROPERTY_NAME];
70
163
  }
71
164
 
@@ -77,101 +170,85 @@ var TableStructureGenerator = class {
77
170
  this.dialect = dialect;
78
171
  this.dbInstructions = dbInstructions;
79
172
  }
173
+ /**
174
+ * Generates the structure of tables based on database instructions.
175
+ *
176
+ * @return An object where keys are table names, and values are their corresponding table structures.
177
+ */
80
178
  generateTableStructure() {
81
- if (this.dbInstructions.tables.length) {
82
- const tableObjects = {};
83
- for (const table of this.dbInstructions.tables) {
84
- tableObjects[table.name] = { columns: new table(), tableInfo: getTableInfo(table) };
85
- }
86
- return this.getExpectedStructure(tableObjects);
87
- } else {
88
- const tableObjects = this.dbInstructions.tables;
89
- return this.getExpectedStructure(tableObjects);
90
- }
91
- }
92
- getExpectedStructure(tableObjects) {
93
179
  const tables = {};
94
- for (const tableName in tableObjects) {
95
- const obj = tableObjects[tableName];
96
- const tableInfo = obj.tableInfo;
97
- tables[tableName] = {
98
- table: tableName,
99
- primaryKey: tableInfo?.primaryKey,
100
- columns: this.getColumns(obj.columns, tableInfo?.primaryKey),
101
- foreignKeys: tableInfo?.foreignKeys
102
- };
103
- }
104
- return tables;
105
- }
106
- getColumns(obj, primaryKey) {
107
- const columns = [];
108
- for (const property in obj) {
109
- const propertyKey = property;
110
- const value = obj[propertyKey];
111
- const columnData = {
112
- name: property,
113
- isPrimaryKey: property == primaryKey,
114
- type: "TEXT",
115
- defaultValue: this.dialect.typeNull
116
- };
117
- switch (typeof value) {
118
- case "string":
119
- columnData.type = this.dialect.typeString;
120
- columnData.defaultValue = value === null ? this.dialect.typeNull : `"${this.dialect.formatValueToSql(value)}"`;
121
- break;
122
- case "bigint":
123
- columnData.type = this.dialect.typeBigInt;
124
- columnData.defaultValue = value === null ? this.dialect.typeNull : this.dialect.formatValueToSql(value);
125
- break;
126
- case "number":
127
- columnData.type = this.dialect.typeInt;
128
- columnData.defaultValue = value === null ? this.dialect.typeNull : this.dialect.formatValueToSql(value);
129
- break;
130
- case "boolean":
131
- columnData.type = this.dialect.typeBoolean;
132
- columnData.defaultValue = value === null ? this.dialect.typeNull : this.dialect.formatValueToSql(value);
133
- break;
134
- case "function":
135
- continue;
136
- default:
137
- Logger.warn(`${obj.constructor.name}.${property} was skipped because its type is not supported (${typeof value})`);
138
- continue;
180
+ for (const table of this.dbInstructions.tables) {
181
+ if (TableObj.isTableObj(table)) {
182
+ tables[table.tableName] = table.tableStructure;
183
+ } else {
184
+ const classTable = table;
185
+ const structure = getTableStructure(classTable);
186
+ tables[classTable.name] = structure;
187
+ for (const key in structure.columns) {
188
+ const entry = structure.columns[key];
189
+ entry.defaultValue = entry.defaultValue === null ? this.dialect.types.null : this.dialect.formatValueToSql(entry.inputDefaultValue, entry.inputType);
190
+ entry.type = this.dialect.getSqlType(entry.inputType, entry);
191
+ }
139
192
  }
140
- columns.push(columnData);
141
193
  }
142
- return columns;
194
+ return tables;
143
195
  }
144
196
  };
145
197
 
146
198
  // src/lib/exceptions/NotAllowedException.ts
147
199
  var NotAllowedException = class extends Error {
148
200
  constructor(entries) {
149
- let message = "Some destructive changes need to be confirmed.\n In preMigration() run:\n";
201
+ let message = "Some destructive changes need to be confirmed. To confirm, add to preMigration():\n";
150
202
  for (const entry of entries) {
151
- message += ` migrations.allowMigration(${entry.version}, "${entry.tableName}", "${entry.type}");
203
+ message += ` migrations.allowMigration(${entry.version}, ${entry.tableName}, "${entry.type}");
152
204
  `;
153
205
  }
154
206
  super(message);
155
207
  }
156
208
  };
157
209
 
158
- // src/lib/typings/Migrations.ts
210
+ // src/lib/Migrations.ts
159
211
  var Migrations = class {
160
- toVersion = 0;
161
- alwaysAllowed = {};
162
212
  fromVersion = 0;
213
+ toVersion = 0;
214
+ lastUsedVersion = 0;
163
215
  migrationData = {};
164
216
  migrationDataForOldTableNames = {};
217
+ alwaysAllowed = {};
165
218
  notAllowedChanges = [];
219
+ /**
220
+ * Resets the migration state to its initial values.
221
+ * Only used internally.
222
+ *
223
+ * @param dbInstructions - The instructions containing database metadata and migration details.
224
+ * @param fromVersion - The version of the actual database.
225
+ */
166
226
  reset(dbInstructions, fromVersion) {
167
227
  this.fromVersion = fromVersion;
168
- this.migrationData = {};
169
228
  this.toVersion = dbInstructions.version;
229
+ this.migrationData = {};
230
+ this.migrationDataForOldTableNames = {};
170
231
  this.alwaysAllowed = dbInstructions.alwaysAllowedMigrations ?? {};
171
232
  this.notAllowedChanges = [];
172
233
  }
234
+ /**
235
+ * Retrieves the migration instructions for the specified table.
236
+ * @see getEntryFromTableName
237
+ *
238
+ * @param table - The {@link TableInput} object from which the table name will be retrieved.
239
+ * @return The migration instructions corresponding to the given table input.
240
+ */
173
241
  getEntry(table) {
174
242
  const tableName = this.getTableName(table);
243
+ return this.getEntryFromTableName(tableName);
244
+ }
245
+ /**
246
+ * Retrieves the migration instructions for the specified table.
247
+ *
248
+ * @param tableName - The name of the table for which the entry is to be retrieved or created.
249
+ * @return The migration instructions corresponding to the given table input.
250
+ */
251
+ getEntryFromTableName(tableName) {
175
252
  if (!this.migrationData.hasOwnProperty(tableName)) {
176
253
  this.migrationData[tableName] = {
177
254
  recreate: false,
@@ -182,28 +259,67 @@ var Migrations = class {
182
259
  }
183
260
  return this.migrationData[tableName];
184
261
  }
262
+ /**
263
+ * Retrieves the table name from the given table input.
264
+ *
265
+ * @param table - The {@link TableInput} object.
266
+ * @return The name of the table derived from either {@link TableObj} or a class.
267
+ */
185
268
  getTableName(table) {
186
- return typeof table == "string" ? table : table.name;
269
+ return TableObj.isTableObj(table) ? table.tableName : table.name;
270
+ }
271
+ /**
272
+ * Checks whether a provided version is relevant based on database version ({@link fromVersion}) and target version ({@link toVersion}).
273
+ * Also makes sure that version checks happen in order.
274
+ *
275
+ * @param version - The version to check for relevance.
276
+ * @return Returns true if the version is relevant, otherwise false.
277
+ * @throws Error if versions are not properly ordered
278
+ */
279
+ versionIsRelevant(version) {
280
+ if (version <= this.fromVersion || version > this.toVersion)
281
+ return false;
282
+ if (version < this.lastUsedVersion)
283
+ throw new Error("Migrations in preMigration() have to be ordered by version (beginning with the lowest version).");
284
+ this.lastUsedVersion = version;
285
+ return true;
187
286
  }
287
+ /**
288
+ * Verifies the renaming tasks specified in {@link DatabaseInstructions.preMigration} that were considered are valid.
289
+ * Called after all SQL queries have been generated.
290
+ * Only used internally.
291
+ *
292
+ * @param newTables - The table structures extracted from {@link DatabaseInstructions.tables}.
293
+ * @throws Error if a renaming task is invalid.
294
+ */
188
295
  verifyRenamingTasks(newTables) {
189
296
  for (const tableName in this.migrationData) {
190
297
  const migrationEntry = this.migrationData[tableName];
191
- if (migrationEntry.tableRenaming?.oldName == tableName)
192
- return new Error(`You set table "${tableName}" to be renamed to itself!`);
193
- if (migrationEntry.tableRenaming && migrationEntry.tableRenaming.newName != tableName)
194
- return new Error(`You set table "${tableName}" to be renamed to a different name than the one in your structure (${migrationEntry.tableRenaming.newName})!`);
195
- else if (!newTables[tableName])
196
- return new Error(`You set table "${migrationEntry.tableRenaming}" migrations for "${tableName}". But "${tableName}" does not exist in your structure.`);
298
+ if (migrationEntry.tableRenaming) {
299
+ if (migrationEntry.tableRenaming.oldName == tableName)
300
+ return new Error(`You set table "${tableName}" to be renamed to itself!`);
301
+ if (migrationEntry.tableRenaming.newName != tableName)
302
+ return new Error(`You set table "${tableName}" to be renamed to a different name than the one in your structure (${migrationEntry.tableRenaming.newName})!`);
303
+ else if (!newTables[tableName])
304
+ return new Error(`You set table "${migrationEntry.tableRenaming}" migrations for "${tableName}". But "${tableName}" does not exist in your structure.`);
305
+ }
197
306
  for (const renamingData of migrationEntry.renamedColumns) {
198
307
  const oldColumnName = renamingData.oldName;
199
308
  const newColumnName = renamingData.newName;
200
309
  if (oldColumnName == newColumnName)
201
310
  return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to itself!`);
202
- else if (!newTables[tableName]?.columns.find((column) => column.name == newColumnName))
311
+ else if (!newTables[tableName]?.columns[newColumnName])
203
312
  return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to ${tableName}.${newColumnName}. But ${tableName}.${newColumnName} does not exist in your structure.`);
204
313
  }
205
314
  }
206
315
  }
316
+ /**
317
+ * Making sure all considered calls to {@link allowMigration()} where necessary to prevent input mistakes.
318
+ * Called after all SQL queries have been generated.
319
+ * Only used internally.
320
+ *
321
+ * @throws Error if unnecessary calls were discovered. All discovered calls are collected and thrown as one Error.
322
+ */
207
323
  verifyAllowedMigrations() {
208
324
  if (this.notAllowedChanges.length)
209
325
  return new NotAllowedException(this.notAllowedChanges);
@@ -219,6 +335,14 @@ var Migrations = class {
219
335
  if (errorMsg)
220
336
  return new Error(errorMsg);
221
337
  }
338
+ /**
339
+ * Called by {@link MigrationManager} to check if a specific destructive query is allowed.
340
+ * Only used internally.
341
+ *
342
+ * @see allowMigration()
343
+ * @param tableName - The name of the table to check the migration against.
344
+ * @param type - The type of destructive migration being checked.
345
+ */
222
346
  compareWithAllowedMigration(tableName, type) {
223
347
  const migrationEntry = this.migrationData[tableName];
224
348
  if (!migrationEntry?.allowedMigrations[type]) {
@@ -227,22 +351,118 @@ var Migrations = class {
227
351
  } else if (migrationEntry)
228
352
  migrationEntry.usedMigrations[type] = true;
229
353
  }
354
+ /**
355
+ * Retrieves the migration data containing instructions for data migration.
356
+ * Only used internally.
357
+ *
358
+ * @return An object where each key corresponds to the table name,
359
+ * and the value is the associated migration instructions.
360
+ */
361
+ getMigrationData() {
362
+ return this.migrationData;
363
+ }
364
+ /**
365
+ * Iterates over renamed columns for the specified table and executes a callback for each pair of old and new column names.
366
+ * Only used internally.
367
+ *
368
+ * @param tableName - Name of the table whose renamed columns should be processed.
369
+ * @param callback - A function to execute for each renamed column, receiving the old and new column names as arguments.
370
+ */
371
+ loopRenamedColumns(tableName, callback) {
372
+ const migrationEntry = this.migrationData[tableName];
373
+ for (const renamingData of migrationEntry.renamedColumns) {
374
+ callback(renamingData.oldName, renamingData.newName);
375
+ }
376
+ }
377
+ /**
378
+ * Considers the renaming of a table and returns its (newest) name after the migration or
379
+ * the original name if no renaming is queued.
380
+ * Only used internally.
381
+ *
382
+ * @see getOldTableName()
383
+ * @param tableName - The current table name in the database.
384
+ * @return The newest table name if available, otherwise the current table name.
385
+ */
386
+ getNewestTableName(tableName) {
387
+ return this.migrationDataForOldTableNames[tableName]?.tableRenaming?.newName ?? tableName;
388
+ }
389
+ /**
390
+ * Retrieves the current table name in the database.
391
+ * Only used internally.
392
+ *
393
+ * @see getNewestTableName()
394
+ * @param tableName - The name of the newest table name.
395
+ * @return The current table name in the database.
396
+ */
397
+ getOldTableName(tableName) {
398
+ return this.migrationData[tableName]?.tableRenaming?.oldName ?? tableName;
399
+ }
400
+ /**
401
+ * Considers the renaming of a column in a table and returns its (newest) name after the migration or
402
+ * the original name if no renaming is queued.
403
+ * Only used internally.
404
+ *
405
+ * @param tableName - The name of the table containing the column.
406
+ * @param oldColumnName - The current name of the column in the database.
407
+ * @return The newest column name if available, otherwise the original column name.
408
+ */
409
+ getNewestColumnName(tableName, oldColumnName) {
410
+ const columnNames = this.migrationData[tableName]?.renamedColumns.find((columns) => columns.oldName == oldColumnName);
411
+ return columnNames ? columnNames.newName : oldColumnName;
412
+ }
413
+ /**
414
+ * Determines if the specified table is marked to be recreated.
415
+ * Only used internally.
416
+ *
417
+ * @param tableName The name of the table to check.
418
+ * @return Returns true if the table will be recreated, false otherwise.
419
+ */
420
+ willBeRecreated(tableName) {
421
+ return this.migrationData[tableName]?.recreate;
422
+ }
423
+ /**
424
+ * Marks a table to be recreated and logs the recreation reason for the user.
425
+ * Only used internally.
426
+ *
427
+ * @param tableName - The name of the table to be marked for recreation.
428
+ * @param reason - The reason why the table is being marked for recreation.
429
+ */
430
+ internalRecreate(tableName, reason) {
431
+ const entry = this.getEntryFromTableName(tableName);
432
+ entry.recreate = true;
433
+ Logger.log(`Table ${tableName} will be recreated(${reason})!`);
434
+ }
435
+ /**
436
+ * SqlMorpheus only allows destructive statements if it was specifically allowed in {@link DatabaseInstructions.preMigration()}.
437
+ * Use this method to allow a destructive statement for a specific version.
438
+ * This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
439
+ *
440
+ * @param version - The target version for which the specific migration is allowed. Must be provided to make sure this method will only have an effect when specified.
441
+ * @param table - Either a {@link TableObj} or a class decorated with @TableClass.
442
+ * @param allowedMigrations - The keys representing the migrations to be allowed. Multiple migrations can be provided
443
+ */
230
444
  allowMigration(version, table, ...allowedMigrations) {
231
- if (version < this.fromVersion || version > this.toVersion)
445
+ if (!this.versionIsRelevant(version))
232
446
  return;
233
447
  const entry = this.getEntry(table);
234
448
  for (const key of allowedMigrations) {
235
449
  entry.allowedMigrations[key] = true;
236
450
  }
237
451
  }
452
+ /**
453
+ * When you change the name of a table, SqlMorpheus will assume the original table was meant to be dropped
454
+ * and the new table was meant to be added, which would lead to data loss.
455
+ * Use this method to mark a table to be renamed instead.
456
+ * This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
457
+ *
458
+ * @param version - The target version for which the table should be renamed. Must be provided to make sure this method will only have an effect when specified.
459
+ * @param oldTableName - The current name in the database at the time of the migration.
460
+ * @param newTableName - The new table name or its equivalent input format.
461
+ */
238
462
  renameTable(version, oldTableName, newTableName) {
239
- if (version < this.fromVersion || version > this.toVersion)
463
+ if (!this.versionIsRelevant(version))
240
464
  return;
241
465
  const newTableNameString = this.getTableName(newTableName);
242
- if (this.migrationData.hasOwnProperty(oldTableName)) {
243
- this.migrationData[newTableNameString] = this.getEntry(oldTableName);
244
- delete this.migrationData[oldTableName];
245
- }
246
466
  const entry = this.getEntry(newTableName);
247
467
  this.migrationDataForOldTableNames[oldTableName] = entry;
248
468
  if (!entry.tableRenaming)
@@ -250,8 +470,19 @@ var Migrations = class {
250
470
  else
251
471
  entry.tableRenaming.newName = newTableNameString;
252
472
  }
473
+ /**
474
+ * When you change the name of a column, SqlMorpheus will assume the original column was meant to be removed
475
+ * and the new column was meant to be added, which would lead to data loss.
476
+ * Use this method to mark a column to be renamed instead.
477
+ * This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
478
+ *
479
+ * @param version - The target version for which the table should be renamed. Must be provided to make sure this method will only have an effect when specified.
480
+ * @param table Either a {@link TableObj} or a class decorated with @TableClass containing the column to be renamed.
481
+ * @param oldColumn The current name in the database at the time of the migration.
482
+ * @param newColumn The new name to assign to the column.
483
+ */
253
484
  renameColumn(version, table, oldColumn, newColumn) {
254
- if (version < this.fromVersion || version > this.toVersion)
485
+ if (!this.versionIsRelevant(version))
255
486
  return;
256
487
  const entry = this.getEntry(table);
257
488
  const existingColumnEntry = entry.renamedColumns.find((entry2) => entry2.newName == oldColumn);
@@ -260,36 +491,21 @@ var Migrations = class {
260
491
  else
261
492
  entry.renamedColumns.push({ oldName: oldColumn, newName: newColumn });
262
493
  }
263
- recreateTableImp(table) {
264
- const entry = this.getEntry(table);
265
- entry.recreate = true;
266
- }
494
+ /**
495
+ * Should not be needed most of the time.
496
+ * Marks a table to be recreated
497
+ * (a copy of the table will be created, all data will be moved over, the original table will be dropped and the new table will be renamed to the original name).
498
+ * This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
499
+ *
500
+ * @param version - The target version for which the table should be renamed. Must be provided to make sure this method will only have an effect when specified.
501
+ * @param table Either a {@link TableObj} or a class decorated with @TableClass.
502
+ */
267
503
  recreateTable(version, table) {
268
- if (version < this.fromVersion || version > this.toVersion)
504
+ if (!this.versionIsRelevant(version))
269
505
  return;
270
- this.recreateTableImp(table);
271
- }
272
- getMigrationData() {
273
- return this.migrationData;
274
- }
275
- loopRenamedColumns(tableName, callback) {
276
- const migrationEntry = this.migrationData[tableName];
277
- for (const renamingData of migrationEntry.renamedColumns) {
278
- callback(renamingData.oldName, renamingData.newName);
279
- }
280
- }
281
- getNewestTableName(tableName) {
282
- return this.migrationDataForOldTableNames[tableName]?.tableRenaming?.newName ?? tableName;
283
- }
284
- getOldTableName(tableName) {
285
- return this.migrationData[tableName]?.tableRenaming?.oldName ?? tableName;
286
- }
287
- getNewestColumnName(tableName, oldColumnName) {
288
- const columnNames = this.migrationData[tableName]?.renamedColumns.find((columns) => columns.oldName == oldColumnName);
289
- return columnNames ? columnNames.newName : oldColumnName;
290
- }
291
- willBeRecreated(tableName) {
292
- return this.migrationData[tableName]?.recreate;
506
+ const entry = this.getEntry(table);
507
+ entry.recreate = true;
508
+ Logger.log(`Table ${this.getTableName(table)} will be recreated!`);
293
509
  }
294
510
  };
295
511
 
@@ -472,8 +688,9 @@ ${changes.up}
472
688
  */
473
689
  createTableSql(tableName, columns, foreignKeys) {
474
690
  const queryLines = [];
475
- for (const columnInfo of columns) {
476
- queryLines.push(this.dialect.columnDefinition(columnInfo.name, columnInfo.type, columnInfo.defaultValue, columnInfo.isPrimaryKey));
691
+ for (const key in columns) {
692
+ const columnInfo = columns[key];
693
+ queryLines.push(this.dialect.columnDefinition(columnInfo.name, columnInfo.type, columnInfo.isPrimaryKey, columnInfo.defaultValue));
477
694
  }
478
695
  if (foreignKeys) {
479
696
  for (const foreignKeyInfo of foreignKeys) {
@@ -506,13 +723,16 @@ ${changes.up}
506
723
  const migrationEntry = migrationData[tableName];
507
724
  if (!migrationEntry.recreate)
508
725
  continue;
509
- Logger.log(`Table ${tableName} will be recreated!`);
510
726
  this.migrations.compareWithAllowedMigration(tableName, "recreateTable");
511
727
  if (!this.dialect.canInspectForeignKeys || !this.dialect.canInspectPrimaryKey)
512
728
  this.migrations.compareWithAllowedMigration(tableName, "continueWithoutRollback");
513
729
  const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
514
730
  const newColumnList = this.newTables[tableName].columns;
515
- const moveableColumns = newColumnList.filter((entry) => oldColumnList.find((oldEntry) => oldEntry.name == entry.name) != void 0).map((entry) => entry.name);
731
+ const moveableColumns = [];
732
+ for (const key in newColumnList) {
733
+ if (oldColumnList.hasOwnProperty(key))
734
+ moveableColumns.push(newColumnList[key].name);
735
+ }
516
736
  const backupTableName = `${tableName}__backup`;
517
737
  const insertQuery = this.dialect.insert(
518
738
  backupTableName,
@@ -520,7 +740,7 @@ ${changes.up}
520
740
  );
521
741
  const moveDataQuery = insertQuery + "\n" + this.dialect.dropTable(tableName) + "\n" + this.dialect.renameTable(backupTableName, tableName);
522
742
  const structure = this.newTables[tableName];
523
- changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n";
743
+ changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n\n";
524
744
  const dbTableName = this.migrations.getOldTableName(tableName);
525
745
  changes.down += this.dialect.canInspectForeignKeys && this.dialect.canInspectPrimaryKey ? this.createTableSql(backupTableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName)) + "\n" + moveDataQuery : "\n\n-- Cannot recreate foreign keys or load primary keys with this database! Table will not be recreated!\n";
526
746
  }
@@ -553,7 +773,7 @@ ${changes.up}
553
773
  this.dialect.foreignKey(oldForeignKey.fromColumn, oldForeignKey.toTable, oldForeignKey.toColumn, oldForeignKey.onUpdate, oldForeignKey.onDelete)
554
774
  );
555
775
  } else {
556
- this.migrations.recreateTableImp(structure.table);
776
+ this.migrations.internalRecreate(structure.table, "Dialect does not support altering foreign keys");
557
777
  checkForNewForeignKeys = false;
558
778
  }
559
779
  } else {
@@ -570,7 +790,7 @@ ${changes.up}
570
790
  this.dialect.foreignKey(oldForeignKey.fromColumn, oldForeignKey.toTable, oldForeignKey.toColumn, oldForeignKey.onUpdate, oldForeignKey.onDelete)
571
791
  ) + this.dialect.removeForeignKey(newForeignKey.fromTable, newForeignKey.fromColumn);
572
792
  } else {
573
- this.migrations.recreateTableImp(structure.table);
793
+ this.migrations.internalRecreate(structure.table, "Dialect does not support altering foreign keys");
574
794
  checkForNewForeignKeys = false;
575
795
  }
576
796
  }
@@ -589,7 +809,7 @@ ${changes.up}
589
809
  );
590
810
  changes.down += this.dialect.removeForeignKey(newForeignKey.fromTable, newForeignKey.fromColumn);
591
811
  } else {
592
- this.migrations.recreateTableImp(structure.table);
812
+ this.migrations.internalRecreate(structure.table, "Dialect does not support altering foreign keys");
593
813
  break;
594
814
  }
595
815
  }
@@ -612,6 +832,10 @@ ${changes.up}
612
832
  const newTableDefinition = this.newTables[tableName];
613
833
  const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
614
834
  const oldPrimaryKey = this.dialect.canInspectPrimaryKey ? this.getPrimaryKeyColumn(oldColumnList) : false;
835
+ const oldColumnNewNameIndex = {};
836
+ for (const key in oldColumnList) {
837
+ oldColumnNewNameIndex[this.migrations.getNewestColumnName(tableName, key)] = oldColumnList[key];
838
+ }
615
839
  const newColumnList = newTableDefinition.columns;
616
840
  const newPrimaryKey = newTableDefinition.primaryKey;
617
841
  if (this.dialect.canInspectPrimaryKey && oldPrimaryKey != newPrimaryKey) {
@@ -627,28 +851,26 @@ ${changes.up}
627
851
  changes.down += this.dialect.removePrimaryKey(tableName, newPrimaryKey.toString());
628
852
  }
629
853
  } else
630
- this.migrations.recreateTableImp(newTableDefinition.table);
854
+ this.migrations.internalRecreate(newTableDefinition.table, "Dialect does not support altering primary keys");
631
855
  continue;
632
856
  }
633
- for (const newColumn of newColumnList) {
634
- const oldColumn = oldColumnList.find(
635
- (entry) => this.migrations.getNewestColumnName(tableName, entry.name) == newColumn.name
636
- );
857
+ for (const key in newColumnList) {
858
+ const newColumn = newColumnList[key];
859
+ const oldColumn = oldColumnNewNameIndex[newColumn.name];
637
860
  if (oldColumn == void 0) {
638
- changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.type, newColumn.defaultValue, newColumn.isPrimaryKey)) + "\n";
861
+ changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.type, newColumn.isPrimaryKey, newColumn.defaultValue)) + "\n";
639
862
  changes.down += this.dialect.dropColumn(tableName, newColumn.name) + "\n";
640
863
  } else if (newColumn.type != oldColumn.type || newColumn.defaultValue != oldColumn.defaultValue) {
641
- this.migrations.recreateTableImp(newTableDefinition.table);
864
+ this.migrations.internalRecreate(newTableDefinition.table, "Table structure was changed");
642
865
  }
643
866
  }
644
- for (const oldColumn of oldColumnList) {
645
- const newColumn = newColumnList.find(
646
- (entry) => entry.name == this.migrations.getNewestColumnName(tableName, oldColumn.name)
647
- );
867
+ for (const key in oldColumnList) {
868
+ const oldColumn = oldColumnList[key];
869
+ const newColumn = newColumnList[this.migrations.getNewestColumnName(tableName, oldColumn.name)];
648
870
  if (newColumn == void 0) {
649
871
  this.migrations.compareWithAllowedMigration(tableName, "dropColumn");
650
872
  changes.up += this.dialect.dropColumn(tableName, oldColumn.name) + "\n";
651
- changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.type, oldColumn.defaultValue, oldColumn.isPrimaryKey)) + "\n";
873
+ changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.type, oldColumn.isPrimaryKey, oldColumn.defaultValue)) + "\n";
652
874
  }
653
875
  }
654
876
  }
@@ -657,11 +879,12 @@ ${changes.up}
657
879
  /**
658
880
  * Retrieves the name of the primary key column from a list of column information.
659
881
  *
660
- * @param columnInfoList - An array of ColumnInfo objects containing details about each column.
882
+ * @param columnInfoEntries - An array of ColumnInfo objects containing details about each column.
661
883
  * @return The name of the primary key column if found; otherwise, undefined.
662
884
  */
663
- getPrimaryKeyColumn(columnInfoList) {
664
- for (const columnInfo of columnInfoList) {
885
+ getPrimaryKeyColumn(columnInfoEntries) {
886
+ for (const key in columnInfoEntries) {
887
+ const columnInfo = columnInfoEntries[key];
665
888
  if (columnInfo.isPrimaryKey)
666
889
  return columnInfo.name;
667
890
  }
@@ -747,26 +970,49 @@ ${changes.down}`, { encoding: "utf-8" });
747
970
  }
748
971
  };
749
972
 
750
- // src/lib/tableInfo/decorators/DbTable.ts
973
+ // src/lib/tableInfo/decorators/TableClass.ts
751
974
  var import_polyfill_symbol_metadata = require("polyfill-symbol-metadata");
752
- function DbTable(tableName, primaryKey) {
975
+ function TableClass(tableName, primaryKey) {
753
976
  return (table, context) => {
754
- Object.defineProperty(table, "name", { value: tableName, writable: false });
755
- const tableInfo = context?.metadata ? getTableInfoFromMetadata(context.metadata) : getTableInfo(table);
756
- tableInfo.primaryKey = primaryKey?.toString();
757
- if (tableInfo.foreignKeys) {
758
- for (const foreignKey of tableInfo.foreignKeys) {
977
+ const tableStructure = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
978
+ tableStructure.primaryKey = primaryKey?.toString();
979
+ tableStructure.table = tableName;
980
+ if (tableStructure.foreignKeys) {
981
+ for (const foreignKey of tableStructure.foreignKeys) {
759
982
  foreignKey.fromTable = tableName;
983
+ if (foreignKey.toTable == table.name)
984
+ foreignKey.toTable = tableName;
760
985
  }
761
986
  }
987
+ for (const key in table) {
988
+ const value = table[key];
989
+ const existingEntry = tableStructure.columns[key];
990
+ const defaultEntry = TableObj.getColumnEntry(key, value);
991
+ if (!defaultEntry)
992
+ continue;
993
+ tableStructure.columns[key] = {
994
+ ...defaultEntry,
995
+ ...existingEntry
996
+ };
997
+ }
998
+ Object.defineProperty(table, "name", { value: tableName, writable: false });
762
999
  };
763
1000
  }
764
1001
 
765
- // src/lib/tableInfo/decorators/ForeignKey.ts
1002
+ // src/lib/tableInfo/decorators/DataType.ts
766
1003
  var import_polyfill_symbol_metadata2 = require("polyfill-symbol-metadata");
767
- function ForeignKey(toTable, toColumn, onDelete, onUpdate) {
1004
+ function DataType(type) {
1005
+ return (table, context) => {
1006
+ const entry = getInputColumnInfo(table, context);
1007
+ entry.inputType = type;
1008
+ };
1009
+ }
1010
+
1011
+ // src/lib/tableInfo/decorators/ForeignKey.ts
1012
+ var import_polyfill_symbol_metadata3 = require("polyfill-symbol-metadata");
1013
+ function ForeignKey(toTable, toColumn, options) {
768
1014
  return (table, context) => {
769
- const metadata = context?.metadata ? getTableInfoFromMetadata(context.metadata) : getTableInfo(table);
1015
+ const metadata = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
770
1016
  if (!metadata.foreignKeys)
771
1017
  metadata.foreignKeys = [];
772
1018
  metadata.foreignKeys.push({
@@ -775,8 +1021,8 @@ function ForeignKey(toTable, toColumn, onDelete, onUpdate) {
775
1021
  //to stay compatible with old decorator structure (see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#decorator-metadata)
776
1022
  toTable: toTable.name,
777
1023
  toColumn: toColumn.toString(),
778
- onDelete,
779
- onUpdate
1024
+ onDelete: options?.onDelete,
1025
+ onUpdate: options?.onUpdate
780
1026
  });
781
1027
  };
782
1028
  }
@@ -789,32 +1035,111 @@ var DefaultSql = class {
789
1035
  canAlterPrimaryKey = false;
790
1036
  canInspectForeignKeys = false;
791
1037
  canInspectPrimaryKey = false;
792
- typeString = "TEXT";
793
- typeInt = "INTEGER";
794
- typeBigInt = "BIGINT";
795
- typeBoolean = "BOOLEAN";
796
- typeNull = "NULL";
1038
+ types = {
1039
+ string: "TEXT",
1040
+ number: "INTEGER",
1041
+ bigint: "BIGINT",
1042
+ boolean: "BOOLEAN",
1043
+ date: "DATE",
1044
+ time: "TIME",
1045
+ dateTime: "DATETIME",
1046
+ null: "NULL"
1047
+ };
797
1048
  constructor(db) {
798
1049
  this.db = db;
799
1050
  }
800
- formatValueToSql(value) {
801
- return value.toString();
1051
+ /**
1052
+ * Formats a given value to a SQL-equivalent string based on the provided data type.
1053
+ *
1054
+ * @param value - The value to be formatted.
1055
+ * @param type - The data type of the value. Either determined through typeof or {@link TableStructure} setting
1056
+ * @return A string representation of the value formatted for SQL.
1057
+ */
1058
+ formatValueToSql(value, type) {
1059
+ function toDate(value2) {
1060
+ return value2 instanceof Date ? value2 : new Date(value2);
1061
+ }
1062
+ switch (type) {
1063
+ case "string":
1064
+ return `"${value}"`;
1065
+ case "time":
1066
+ return `"${toDate(value).toISOString().slice(11, 19)}"`;
1067
+ case "date":
1068
+ return `"${toDate(value).toISOString().slice(0, 10)}"`;
1069
+ case "dateTime":
1070
+ return `"${toDate(value).toISOString().slice(0, 19).replace("T", " ")}"`;
1071
+ default:
1072
+ return value.toString();
1073
+ }
1074
+ }
1075
+ getSqlType(dataType, _) {
1076
+ return this.types[dataType];
802
1077
  }
1078
+ /**
1079
+ * Statement to enable / disable foreign keys in the database.
1080
+ * Is dialect specific.
1081
+ *
1082
+ * @param enabled - A boolean indicating whether to enable or disable foreign key constraints.
1083
+ * @return The SQL statement as a string.
1084
+ */
803
1085
  changeForeignKeysState(enabled) {
804
1086
  return "";
805
1087
  }
1088
+ /**
1089
+ * Statement to add a foreign key to a specified table.
1090
+ * Is only used if {@link canAlterForeignKeys} is true.
1091
+ *
1092
+ * @param fromTableName - The name of the table to which the foreign key will be added.
1093
+ * @param foreignKey - The foreign key definition to be added, see {@link foreignKey()}.
1094
+ * @return The SQL statement as a string.
1095
+ */
806
1096
  addForeignKey(fromTableName, foreignKey) {
807
1097
  throw new Error("Adding foreign keys is not supported!");
808
1098
  }
1099
+ /**
1100
+ * Statement to remove a foreign key from a specified table.
1101
+ * Is only used if {@link canAlterForeignKeys} is true.
1102
+ *
1103
+ * @param tableName - The name of the table to which the foreign key will be added.
1104
+ * @param foreignKeyName - The name of the foreign key definition.
1105
+ * @return The SQL statement as a string.
1106
+ */
809
1107
  removeForeignKey(tableName, foreignKeyName) {
810
1108
  throw new Error("Foreign key removal is not supported!");
811
1109
  }
1110
+ /**
1111
+ * Statement to add a primary key to the specified table.
1112
+ * Is only used if {@link canAlterPrimaryKey} is true.
1113
+ *
1114
+ * @param tableName - The name of the table to which the primary key will be added.
1115
+ * @param columnName - The name of the column that will be set as the primary key.
1116
+ * @return The SQL statement as a string.
1117
+ */
812
1118
  addPrimaryKey(tableName, columnName) {
813
1119
  throw new Error("Adding foreign keys is not supported!");
814
1120
  }
1121
+ /**
1122
+ * Statement to remove a primary key from the specified table.
1123
+ * Is only used if {@link canAlterPrimaryKey} is true.
1124
+ *
1125
+ * @param tableName - The name of the table to which the primary key will be added.
1126
+ * @param columnName - The name of the column that will be set as the primary key.
1127
+ * @return The SQL statement as a string.
1128
+ */
815
1129
  removePrimaryKey(tableName, columnName) {
816
1130
  throw new Error("Foreign key removal is not supported!");
817
1131
  }
1132
+ /**
1133
+ * Constructs a SQL FOREIGN KEY constraint clause.
1134
+ * Used in combination with {@link addForeignKey} or {@link createTable}
1135
+ *
1136
+ * @param column - The name of the column in the current table to apply the foreign key constraint.
1137
+ * @param foreignTable - The name of the referenced table.
1138
+ * @param foreignColumn - The name of the referenced column in the foreign table.
1139
+ * @param onUpdate - Optional action to specify the behavior on update (e.g., CASCADE, SET NULL).
1140
+ * @param onDelete - Optional action to specify the behavior on delete (e.g., CASCADE, SET NULL).
1141
+ * @return The SQL string representing the foreign key constraint.
1142
+ */
818
1143
  foreignKey(column, foreignTable, foreignColumn, onUpdate, onDelete) {
819
1144
  let query = `FOREIGN KEY (${column}) REFERENCES ${foreignTable} (${foreignColumn})`;
820
1145
  if (onUpdate)
@@ -823,64 +1148,164 @@ var DefaultSql = class {
823
1148
  query += ` ON DELETE ${onDelete}`;
824
1149
  return query;
825
1150
  }
1151
+ /**
1152
+ * Statement to create a table if it does not already exist.
1153
+ *
1154
+ * @param tableName - The name of the table to be created.
1155
+ * @param entries - An array containing the column definitions for the table.
1156
+ * @return The SQL statement as a string.
1157
+ */
826
1158
  createTable(tableName, entries) {
827
1159
  return `CREATE TABLE IF NOT EXISTS ${tableName} (
828
1160
  ${entries.join(",\n ")}
829
1161
  );`;
830
1162
  }
1163
+ /**
1164
+ * Statement to rename a table.
1165
+ *
1166
+ * @param tableName - The name of the existing table to be renamed.
1167
+ * @param newTableName - The new name for the table.
1168
+ * @return The SQL statement as a string.
1169
+ */
831
1170
  renameTable(tableName, newTableName) {
832
1171
  return `ALTER TABLE ${tableName} RENAME TO ${newTableName};`;
833
1172
  }
1173
+ /**
1174
+ * Statement to drop a table if it exists.
1175
+ *
1176
+ * @param tableName - The name of the table to be dropped.
1177
+ * @return The SQL statement as a string.
1178
+ */
834
1179
  dropTable(tableName) {
835
1180
  return `DROP TABLE IF EXISTS ${tableName};`;
836
1181
  }
837
- columnDefinition(columnName, type, defaultValue, isPrimaryKey) {
838
- const query = `${columnName} ${type} DEFAULT ${defaultValue}`;
1182
+ /**
1183
+ * Constructs a SQL column definition based on the provided parameters.
1184
+ *
1185
+ * @param columnName - The name of the column.
1186
+ * @param type - The data type of the column.
1187
+ * @param defaultValue - The default value for the column.
1188
+ * @param isPrimaryKey - Indicates whether the column is a primary key.
1189
+ * @return The constructed SQL column definition string.
1190
+ */
1191
+ columnDefinition(columnName, type, isPrimaryKey, defaultValue) {
1192
+ const query = `${columnName} ${type}${defaultValue ? ` DEFAULT ${defaultValue}` : ""}`;
839
1193
  return isPrimaryKey ? `${query} PRIMARY KEY` : query;
840
1194
  }
1195
+ /**
1196
+ * Statement to add a new column to a specified table.
1197
+ *
1198
+ * @param columnTable - The name of the table to which the column will be added.
1199
+ * @param entry - The column definition including the column name and its data type. See {@link columnDefinition()}
1200
+ * @return The SQL statement as a string.
1201
+ */
841
1202
  createColumn(columnTable, entry) {
842
1203
  return `ALTER TABLE ${columnTable} ADD ${entry};`;
843
1204
  }
1205
+ /**
1206
+ * Statement to rename a column in an existing table.
1207
+ *
1208
+ * @param tableName - The name of the table containing the column to rename.
1209
+ * @param oldColumnName - The current name of the column to be renamed.
1210
+ * @param newColumnName - The new name for the column.
1211
+ * @return The SQL statement as a string.
1212
+ */
844
1213
  renameColumn(tableName, oldColumnName, newColumnName) {
845
1214
  return `ALTER TABLE ${tableName} RENAME COLUMN ${oldColumnName} TO ${newColumnName};`;
846
1215
  }
847
- copyColumn(tableName, oldColumnName, newColumnName) {
848
- return `UPDATE ${tableName} SET ${newColumnName} = ${oldColumnName};`;
849
- }
1216
+ /**
1217
+ * Statement to drop a column from a specified table.
1218
+ *
1219
+ * @param tableName - The name of the table from which the column will be dropped.
1220
+ * @param columnName - The name of the column to be dropped.
1221
+ * @return The SQL statement as a string.
1222
+ */
850
1223
  dropColumn(tableName, columnName) {
851
1224
  return `ALTER TABLE ${tableName} DROP COLUMN ${columnName};`;
852
1225
  }
1226
+ /**
1227
+ * Statement to return data from a table.
1228
+ *
1229
+ * @param tableName - The name of the database table to select data from.
1230
+ * @param select - An array of column names to include in the SELECT clause.
1231
+ * @param where - An optional condition.
1232
+ * @return The SQL statement as a string.
1233
+ */
853
1234
  select(tableName, select, where) {
854
1235
  let query = `SELECT ${select.join(",")} FROM ${tableName}`;
855
1236
  if (where)
856
1237
  query += ` WHERE ${where}`;
857
1238
  return `${query};`;
858
1239
  }
1240
+ /**
1241
+ * Statement to insert data into a table.
1242
+ *
1243
+ * @param tableName - The name of the table where the data will be inserted.
1244
+ * @param content - The value query. See {@link insertValues()}.
1245
+ * @return The SQL statement as a string.
1246
+ */
859
1247
  insert(tableName, content) {
860
1248
  return `INSERT INTO ${tableName} ${content};`;
861
1249
  }
1250
+ /**
1251
+ * A SQL insert query string. See {@link insert()}.
1252
+ *
1253
+ * @param keys - An array of column names to be used in the query.
1254
+ * @param [valueString] - An optional preformatted value string to be included in the query.
1255
+ * If not provided, the method generates a default placeholder string with "?" for each key.
1256
+ * @return The SQL statement as a string.
1257
+ */
862
1258
  insertValues(keys, valueString) {
863
1259
  return `(${keys}) ${valueString ?? `VALUES (${keys.map(() => "?").join(",")})`}`;
864
1260
  }
1261
+ /**
1262
+ * Returns a SQL query string to create a migration table.
1263
+ * The table is used to store migration data, specifically the version of the migration.
1264
+ *
1265
+ * @return The SQL query string for creating the migration table.
1266
+ */
865
1267
  migrationTableQuery() {
866
1268
  return this.createTable(MIGRATION_DATA_TABLE_NAME, [
867
- this.columnDefinition("version", this.typeInt, "0", false)
1269
+ this.columnDefinition("version", this.types.number, false, "0")
868
1270
  ]);
869
1271
  }
1272
+ /**
1273
+ * Fetches the foreign keys for the specified table.
1274
+ * This method is only used if {@link canInspectForeignKeys} is true
1275
+ *
1276
+ * @param tableName - The name of the table for which to retrieve foreign key information.
1277
+ * @return {Promise<ForeignKeyInfo[]>} A promise that resolves to an array of {@link ForeignKeyInfo}.
1278
+ */
870
1279
  async getForeignKeys(tableName) {
871
1280
  throw new Error("Inspecting foreign keys is not supported!");
872
1281
  }
1282
+ /**
1283
+ * Ensures that the migration table exists in the database.
1284
+ * If the table does not exist, it creates the table using the defined migration table query.
1285
+ *
1286
+ * @return A promise that resolves when the migration table is created successfully.
1287
+ */
873
1288
  async createMigrationTableIfNeeded() {
874
1289
  await this.db.runMultipleWriteStatements(this.migrationTableQuery());
875
1290
  }
876
- //TODO: untested
1291
+ /**
1292
+ * Retrieves the current version from the migration data table.
1293
+ *
1294
+ * @return A promise that resolves to the current version number. Returns 0 if no version is found.
1295
+ */
877
1296
  async getVersion() {
878
1297
  await this.createMigrationTableIfNeeded();
879
1298
  const query = this.select(MIGRATION_DATA_TABLE_NAME, ["version"]);
880
1299
  const data = await this.db.runGetStatement(query);
881
1300
  return data.length ? data[0].version : 0;
882
1301
  }
883
- //TODO: untested
1302
+ /**
1303
+ * Updates the migration version in the database. If the version entry does not exist, it inserts a new one;
1304
+ * if it exists, it updates the version to the specified value.
1305
+ *
1306
+ * @param newVersion - The new migration version to set in the database.
1307
+ * @return A promise that resolves when the operation is complete.
1308
+ */
884
1309
  async setVersion(newVersion) {
885
1310
  const query = `${this.migrationTableQuery()};
886
1311
  INSERT INTO ${MIGRATION_DATA_TABLE_NAME} (version) VALUES ${newVersion} WHERE NOT EXISTS (SELECT 1 FROM ${MIGRATION_DATA_TABLE_NAME})
@@ -893,8 +1318,16 @@ UPDATE ${MIGRATION_DATA_TABLE_NAME} SET version = ${newVersion} WHERE EXISTS (SE
893
1318
 
894
1319
  // src/lib/dialects/SqliteDialect.ts
895
1320
  var SqliteDialect = class extends DefaultSql {
896
- typeBoolean = "INTEGER";
897
- typeBigInt = "INTEGER";
1321
+ types = {
1322
+ string: "TEXT",
1323
+ number: "INTEGER",
1324
+ bigint: "INTEGER",
1325
+ boolean: "INTEGER",
1326
+ date: "INTEGER",
1327
+ time: "INTEGER",
1328
+ dateTime: "INTEGER",
1329
+ null: "NULL"
1330
+ };
898
1331
  canAlterForeignKeys = false;
899
1332
  canAlterPrimaryKey = false;
900
1333
  canInspectForeignKeys = true;
@@ -907,21 +1340,30 @@ var SqliteDialect = class extends DefaultSql {
907
1340
  }
908
1341
  async getColumnInformation(tableName) {
909
1342
  const data = await this.db.runGetStatement(`PRAGMA table_info(${tableName})`);
910
- return data.map((entry) => {
911
- return {
1343
+ const output = {};
1344
+ for (const entry of data) {
1345
+ output[entry["name"]] = {
912
1346
  name: entry["name"],
913
1347
  type: entry["type"],
914
1348
  defaultValue: entry["dflt_value"],
915
1349
  isPrimaryKey: entry["pk"] == "1"
916
1350
  };
917
- });
1351
+ }
1352
+ return output;
918
1353
  }
919
- formatValueToSql(value) {
920
- switch (typeof value) {
1354
+ formatValueToSql(value, type) {
1355
+ function toDate(value2) {
1356
+ return value2 instanceof Date ? value2 : new Date(value2);
1357
+ }
1358
+ switch (type) {
921
1359
  case "boolean":
922
1360
  return value ? "1" : "0";
1361
+ case "date":
1362
+ case "time":
1363
+ case "dateTime":
1364
+ return toDate(value).getTime().toString();
923
1365
  default:
924
- return value.toString();
1366
+ return super.formatValueToSql(value, type);
925
1367
  }
926
1368
  }
927
1369
  async getForeignKeys(tableName) {
@@ -932,8 +1374,8 @@ var SqliteDialect = class extends DefaultSql {
932
1374
  fromColumn: entry["from"],
933
1375
  toTable: entry["table"],
934
1376
  toColumn: entry["to"],
935
- on_update: entry["on_update"],
936
- on_delete: entry["on_delete"]
1377
+ onUpdate: entry["on_update"] ?? "NO ACTION",
1378
+ onDelete: entry["on_delete"] ?? "NO ACTION"
937
1379
  };
938
1380
  });
939
1381
  }
@@ -966,14 +1408,17 @@ var PostgresDialect = class extends DefaultSql {
966
1408
  }
967
1409
  async getColumnInformation(tableName) {
968
1410
  const data = await this.db.runGetStatement(`SELECT column_name, data_type, column_default FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${tableName}';`);
969
- return data.map((entry) => {
970
- return {
1411
+ const output = {};
1412
+ for (const entry of data) {
1413
+ output[entry["name"]] = {
971
1414
  name: entry["column_name"],
972
1415
  type: entry["data_type"],
1416
+ maxLength: 0,
973
1417
  defaultValue: entry["column_default"],
974
1418
  isPrimaryKey: false
975
1419
  };
976
- });
1420
+ }
1421
+ return output;
977
1422
  }
978
1423
  addPrimaryKey(tableName, columnName) {
979
1424
  return `ALTER TABLE ${tableName} ADD PRIMARY KEY (${columnName});
@@ -1018,18 +1463,21 @@ var MsSqlDialect = class extends DefaultSql {
1018
1463
  });
1019
1464
  }
1020
1465
  async getTableNames() {
1021
- return await this.db.runGetStatement("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'");
1466
+ return (await this.db.runGetStatement("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'")).map((entry) => entry.name);
1022
1467
  }
1023
1468
  async getColumnInformation(tableName) {
1024
1469
  const data = await this.db.runGetStatement(`SELECT COLUMN_NAME, DATA_TYPE, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = N'${tableName};'`);
1025
- return data.map((entry) => {
1026
- return {
1027
- name: entry["COLUMN_NAME"],
1470
+ const output = {};
1471
+ for (const entry of data) {
1472
+ output[entry["name"]] = {
1473
+ name: entry["name"],
1028
1474
  type: entry["DATA_TYPE"],
1475
+ maxLength: 0,
1029
1476
  defaultValue: entry["COLUMN_DEFAULT"],
1030
- isPrimaryKey: false
1477
+ isPrimaryKey: entry["pk"] == "1"
1031
1478
  };
1032
- });
1479
+ }
1480
+ return output;
1033
1481
  }
1034
1482
  };
1035
1483
 
@@ -1039,9 +1487,26 @@ var MySqlDialect = class extends DefaultSql {
1039
1487
  canAlterPrimaryKey = true;
1040
1488
  canInspectForeignKeys = true;
1041
1489
  canInspectPrimaryKey = true;
1490
+ types = {
1491
+ string: "VARCHAR",
1492
+ number: "INT",
1493
+ bigint: "BIGINT",
1494
+ boolean: "BOOLEAN",
1495
+ date: "DATE",
1496
+ time: "TIME",
1497
+ dateTime: "DATETIME",
1498
+ null: "NULL"
1499
+ };
1500
+ getSqlType(dataType, columnInfo) {
1501
+ switch (dataType) {
1502
+ case "string":
1503
+ return this.types[dataType] + `(${columnInfo?.maxLength})`;
1504
+ default:
1505
+ return super.getSqlType(dataType, columnInfo);
1506
+ }
1507
+ }
1042
1508
  changeForeignKeysState(enabled) {
1043
- return `SET FOREIGN_KEY_CHECKS ${enabled ? "1" : "0"}
1044
- ;`;
1509
+ return `SET FOREIGN_KEY_CHECKS ${enabled ? "1" : "0"};`;
1045
1510
  }
1046
1511
  addForeignKey(fromTableName, foreignKey) {
1047
1512
  return `ALTER TABLE ${fromTableName} ADD ${foreignKey};`;
@@ -1067,18 +1532,22 @@ var MySqlDialect = class extends DefaultSql {
1067
1532
  });
1068
1533
  }
1069
1534
  async getTableNames() {
1070
- return await this.db.runGetStatement("SHOW TABLES;");
1535
+ const t = await this.db.runGetStatement("SELECT table_name FROM information_schema.tables;");
1536
+ return (await this.db.runGetStatement("SELECT table_name FROM information_schema.tables;")).map((entry) => entry.name);
1071
1537
  }
1072
1538
  async getColumnInformation(tableName) {
1073
1539
  const data = await this.db.runGetStatement(`SHOW COLUMNS FROM ${tableName};`);
1074
- return data.map((entry) => {
1075
- return {
1076
- name: entry["Field"],
1077
- type: entry["Type"],
1078
- defaultValue: entry["Default"],
1079
- isPrimaryKey: entry["Key"] == "PRI"
1540
+ const output = {};
1541
+ for (const entry of data) {
1542
+ output[entry["name"]] = {
1543
+ name: entry["name"],
1544
+ type: entry["type"],
1545
+ maxLength: 0,
1546
+ defaultValue: entry["dflt_value"],
1547
+ isPrimaryKey: entry["pk"] == "1"
1080
1548
  };
1081
- });
1549
+ }
1550
+ return output;
1082
1551
  }
1083
1552
  };
1084
1553
 
@@ -1159,8 +1628,10 @@ async function rollback(db, dbInstructions, toVersion) {
1159
1628
  }
1160
1629
  // Annotate the CommonJS export names for ESM import in node:
1161
1630
  0 && (module.exports = {
1162
- DbTable,
1631
+ DataType,
1163
1632
  ForeignKey,
1633
+ TableClass,
1634
+ TableObj,
1164
1635
  prepareAndRunMigration,
1165
1636
  prepareMigration,
1166
1637
  rollback,