sqlmorpheus 0.2.7 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,124 +1,124 @@
1
- # SqlMorpheus
2
-
3
- SQLMorpheus is a standalone SQL migration tool that automatically calculates the differences between a database and a defined state and updates the database structure accordingly. It is not bundled with any ORM or limited to a specific SQL dialect or SQL library, making it very flexible and easy to integrate into any (new or preexisting) project.
4
-
5
- Its stand-out features are:
6
- - Database **changes are calculated at runtime**, allowing SqlMorpheus to be used with different database states without expecting any existing structure or having to recompile your app.
7
- - SQLMorpheus **does not bundle its own SQL library** but allows you to define database access yourself using any library you want. This not only prevents unnecessary overhead, it also makes SQLMorpheus very flexible and easy to migrate between database flavours.
8
- - While SQLMorpheus can work without (almost) any manual input, it optionally **allows for destructive changes to be confirmed** to prevent possible data loss.
9
- - It **keeps a history of changes** (and adds an interface for backups before migrations) and allows viewing all changes made and even roll back to a specific version.
10
-
11
-
12
- ## How to install?
13
- To install SqlMorpheus, run
14
- ```bash
15
- npm install sqlmorpheus
16
- ```
17
-
18
- ## How to use?
19
-
20
- Because SqlMorpheus relies on the framework you choose for database access, it needs a little bit more setup code. But you only need to copy & paste a few simple lines of code:
21
-
22
- ### 1. Access the database
23
- For SqlMorpheus to be able to access your database, you need to provide its `runMigration` call (see bellow) with a `dbAccess` object with three callback functions: `runReadStatement()`, `runWriteStatement()` and `runTransaction()`.
24
- Here is a full example for SQLite using better-sqlite3
25
- For other SQL types you can copy one of the provided examples (see [MySQL](/examples/DatabaseAccess_for_MySQL.md), [Postgres](/examples/DatabaseAccess_for_Postgres.md), [SQLite](/examples/DatabaseAccess_for_SQLite.md))
26
-
27
- ```typescript
28
- import {DatabaseAccess, runMigration} from "sqlmorpheus";
29
- import BetterSqlite3 from "better-sqlite3";
30
-
31
- const db = new BetterSqlite3(":memory:");
32
- const dbAccess: DatabaseAccess = {
33
- runReadStatement: async (query: string) => db.prepare(query).all(),
34
- runWriteStatement: async (query: string) => db.prepare(query).run(),
35
- runTransaction: async (query: string) => {
36
- const transaction = db.transaction(() => db.exec(query));
37
- transaction();
38
- }
39
- }
40
- ```
41
- <details>
42
- <summary>Optional: `createBackup()` in DatabaseAccess.</summary>
43
-
44
- On top of the three mandatory methods, you can also define `createBackup()`. This callback is called whenever the `version` in `DatabaseInstructions` has changed and SqlMorpheus needs to change the database. An example implementation for a backup in SQLite would be:
45
-
46
- ```typescript
47
- const dbAccess: DatabaseAccess = {
48
- ...,
49
- createBackup: async (backupName: string) => {
50
- await db.backup(`backups/${backupName}.sqlite`)
51
- }
52
- }
53
- ```
54
- </details>
55
-
56
-
57
- ### 2. Define the database structure
58
- This part might require the most work from you, but should be familiar to you if you have already used other migration libraries: You need to tell SqlMorpheus what structure it should mold your database into. To make this process as simple as possible, SqlMorpheus provides a convenient `TableObj.create()` builder (see [TableObj example](/examples/database_structure_using_TableObj.md)) or, if you prefer, you can also use decorators (see [decorator example](/examples/database_structure_using_decorators.md)).
59
- Here is an example using `TableObj.create()`:
60
- ```typescript
61
- import {DatabaseAccess, runMigration} from "sqlmorpheus";
62
- const usersTable = TableObj.create("User", {
63
- userId: [0, {primaryKey: true}],
64
- name: "Default name",
65
- email: [null, {dataType: "string"}]
66
- });
67
- const messagesTable = TableObj.create("Message", {
68
- messageId: [0, {primaryKey: true}],
69
- from: 0,
70
- to: 0,
71
- content: ""
72
- })
73
- .foreignKey("from", usersTable, "userId", {onDelete: "CASCADE"})
74
- .foreignKey("to", usersTable, "userId", {onDelete: "CASCADE"});
75
- ```
76
-
77
- ### 3. Create the database instructions and run the migration
78
- Now we combine everything and call `runMigration()`.
79
-
80
- Example:
81
- ```typescript
82
- const instructions: DatabaseInstructions = {
83
- dialect: "Sqlite",
84
- tables: [userTable, messagesTable], // usersTable and messagesTable are defined above
85
- version: 1
86
- };
87
-
88
- await runMigration(dbAccess, instructions); // dbAccess is defined above
89
- ```
90
- <details>
91
- <summary>More information about `DatabaseInstructions`.</summary>
92
-
93
- #### `dialect`
94
- This property tells SqlMorpheus which SQL dialect your database uses.
95
-
96
- #### `tables`
97
- This property contains all tables that should be created in the database.
98
-
99
- #### `version`
100
- SqlMorpheus will only compare the database structure when the database version is different to the version property defined in DatabaseInstructions. The version property is also used to decide which manual migrations defined in `preMigration()` should be executed.
101
-
102
- #### `preMigration()`
103
- This method is run before database migrations (and comparisons) are executed. This is also the place where you allow specific destructive migrations or tell SqlMorpheus that you want to rename a table or column (rather than removing and adding it – which would be the default behaviour).
104
-
105
- #### `postMigration()`
106
- This method is run after the migration was finished. You will very rarely need it.
107
-
108
- > All properties are documented in [DatabaseInstructions](/src/lib/typings/DatabaseInstructions.ts).
109
- </details>
110
-
111
- ## When does SqlMorpheus check for migrations?
112
- SqlMorpheus will only compare the database when the [version property](#version) is different to the version saved in the database itself (or if the database has no version defined at all).
113
-
114
- ## How to rename a table or column?
115
- SqlMorpheus has no way of telling if you want to rename a table / column or want to remove it and add a completely new one. To solve this, it relies on your input to tell it what to do (by default it just removes and adds a table / column).
116
- To tell it to rename a table / column, you call `migration.renameTable(VERSION, "OLD_TABLENAME", "NEW_TABLENAME");` in `preMigration()` ([example](/examples/example_renaming.md))
117
-
118
- ## rollback your database
119
- SqlMorpheus saves its changes into the database (into a table called `__sqlmorpheus_migrations`) which allows you to roll back the database to a previous state with a single command:
120
-
121
- ```typescript
122
- // `dbAccess` and `instructions` are defined above
123
- await rollback(dbAccess, TARGET_VERSION, instructions.dialect);
1
+ # SqlMorpheus
2
+
3
+ SQLMorpheus is a standalone SQL migration tool that automatically calculates the differences between a database and a defined state and updates the database structure accordingly. It is not bundled with any ORM or limited to a specific SQL dialect or SQL library, making it very flexible and easy to integrate into any (new or preexisting) project.
4
+
5
+ Its stand-out features are:
6
+ - Database **changes are calculated at runtime**, allowing SqlMorpheus to be used with different database states without expecting any existing structure or having to recompile your app.
7
+ - SQLMorpheus **does not bundle its own SQL library** but allows you to define database access yourself using any library you want. This not only prevents unnecessary overhead, it also makes SQLMorpheus very flexible and easy to migrate between database flavours.
8
+ - While SQLMorpheus can work without (almost) any manual input, it optionally **allows for destructive changes to be confirmed** to prevent possible data loss.
9
+ - It **keeps a history of changes** (and adds an interface for backups before migrations) and allows viewing all changes made and even roll back to a specific version.
10
+
11
+
12
+ ## How to install?
13
+ To install SqlMorpheus, run
14
+ ```bash
15
+ npm install sqlmorpheus
16
+ ```
17
+
18
+ ## How to use?
19
+
20
+ Because SqlMorpheus relies on the framework you choose for database access, it needs a little bit more setup code. But you only need to copy & paste a few simple lines of code:
21
+
22
+ ### 1. Access the database
23
+ For SqlMorpheus to be able to access your database, you need to provide its `runMigration` call (see bellow) with a `dbAccess` object with three callback functions: `runReadStatement()`, `runWriteStatement()` and `runTransaction()`.
24
+ Here is a full example for SQLite using better-sqlite3
25
+ For other SQL types you can copy one of the provided examples (see [MySQL](/examples/DatabaseAccess_for_MySQL.md), [Postgres](/examples/DatabaseAccess_for_Postgres.md), [SQLite](/examples/DatabaseAccess_for_SQLite.md))
26
+
27
+ ```typescript
28
+ import {DatabaseAccess, runMigration} from "sqlmorpheus";
29
+ import BetterSqlite3 from "better-sqlite3";
30
+
31
+ const db = new BetterSqlite3(":memory:");
32
+ const dbAccess: DatabaseAccess = {
33
+ runReadStatement: async (query: string) => db.prepare(query).all(),
34
+ runWriteStatement: async (query: string) => db.prepare(query).run(),
35
+ runTransaction: async (query: string) => {
36
+ const transaction = db.transaction(() => db.exec(query));
37
+ transaction();
38
+ }
39
+ }
40
+ ```
41
+ <details>
42
+ <summary>Optional: `createBackup()` in DatabaseAccess.</summary>
43
+
44
+ On top of the three mandatory methods, you can also define `createBackup()`. This callback is called whenever the `version` in `DatabaseInstructions` has changed and SqlMorpheus needs to change the database. An example implementation for a backup in SQLite would be:
45
+
46
+ ```typescript
47
+ const dbAccess: DatabaseAccess = {
48
+ ...,
49
+ createBackup: async (backupName: string) => {
50
+ await db.backup(`backups/${backupName}.sqlite`)
51
+ }
52
+ }
53
+ ```
54
+ </details>
55
+
56
+
57
+ ### 2. Define the database structure
58
+ This part might require the most work from you, but should be familiar to you if you have already used other migration libraries: You need to tell SqlMorpheus what structure it should mold your database into. To make this process as simple as possible, SqlMorpheus provides a convenient `TableObj.create()` builder (see [TableObj example](/examples/database_structure_using_TableObj.md)) or, if you prefer, you can also use decorators (see [decorator example](/examples/database_structure_using_decorators.md)).
59
+ Here is an example using `TableObj.create()`:
60
+ ```typescript
61
+ import {DatabaseAccess, runMigration} from "sqlmorpheus";
62
+ const usersTable = TableObj.create("User", {
63
+ userId: [0, {primaryKey: true}],
64
+ name: "Default name",
65
+ email: [null, {dataType: "string"}]
66
+ });
67
+ const messagesTable = TableObj.create("Message", {
68
+ messageId: [0, {primaryKey: true}],
69
+ from: 0,
70
+ to: 0,
71
+ content: ""
72
+ })
73
+ .foreignKey("from", usersTable, "userId", {onDelete: "CASCADE"})
74
+ .foreignKey("to", usersTable, "userId", {onDelete: "CASCADE"});
75
+ ```
76
+
77
+ ### 3. Create the database instructions and run the migration
78
+ Now we combine everything and call `runMigration()`.
79
+
80
+ Example:
81
+ ```typescript
82
+ const instructions: DatabaseInstructions = {
83
+ dialect: "Sqlite",
84
+ tables: [userTable, messagesTable], // usersTable and messagesTable are defined above
85
+ version: 1
86
+ };
87
+
88
+ await runMigration(dbAccess, instructions); // dbAccess is defined above
89
+ ```
90
+ <details>
91
+ <summary>More information about `DatabaseInstructions`.</summary>
92
+
93
+ #### `dialect`
94
+ This property tells SqlMorpheus which SQL dialect your database uses.
95
+
96
+ #### `tables`
97
+ This property contains all tables that should be created in the database.
98
+
99
+ #### `version`
100
+ SqlMorpheus will only compare the database structure when the database version is different to the version property defined in DatabaseInstructions. The version property is also used to decide which manual migrations defined in `preMigration()` should be executed.
101
+
102
+ #### `preMigration()`
103
+ This method is run before database migrations (and comparisons) are executed. This is also the place where you allow specific destructive migrations or tell SqlMorpheus that you want to rename a table or column (rather than removing and adding it – which would be the default behaviour).
104
+
105
+ #### `postMigration()`
106
+ This method is run after the migration was finished. You will very rarely need it.
107
+
108
+ > All properties are documented in [DatabaseInstructions](/src/lib/typings/DatabaseInstructions.ts).
109
+ </details>
110
+
111
+ ## When does SqlMorpheus check for migrations?
112
+ SqlMorpheus will only compare the database when the [version property](#version) is different to the version saved in the database itself (or if the database has no version defined at all).
113
+
114
+ ## How to rename a table or column?
115
+ SqlMorpheus has no way of telling if you want to rename a table / column or want to remove it and add a completely new one. To solve this, it relies on your input to tell it what to do (by default it just removes and adds a table / column).
116
+ To tell it to rename a table / column, you call `migration.renameTable(VERSION, "OLD_TABLENAME", "NEW_TABLENAME");` in `preMigration()` ([example](/examples/example_renaming.md))
117
+
118
+ ## Roll back your database
119
+ SqlMorpheus saves its changes into the database (into a table called `__sqlmorpheus_migrations`) which allows you to roll back the database to a previous state with a single command:
120
+
121
+ ```typescript
122
+ // `dbAccess` and `instructions` are defined above
123
+ await rollback(dbAccess, TARGET_VERSION, instructions.dialect);
124
124
  ```
package/dist/index.js CHANGED
@@ -1719,7 +1719,7 @@ async function rollback(dbAccess, toVersion, dialect, loggerMode) {
1719
1719
  while (version > toVersion) {
1720
1720
  const changes = await sql.getChanges(version);
1721
1721
  if (!changes) {
1722
- throw new Error(`No changes found for version ${version}`);
1722
+ throw new Error(`Unexpected error! No changes were found from version ${version} (fromVersion: ${fromVersion}, toVersion: ${toVersion})`);
1723
1723
  } else if (changes.fromVersion == version) {
1724
1724
  throw new Error(`Saved changes do not change the database version (${version})`);
1725
1725
  }
package/dist/index.mjs CHANGED
@@ -1678,7 +1678,7 @@ async function rollback(dbAccess, toVersion, dialect, loggerMode) {
1678
1678
  while (version > toVersion) {
1679
1679
  const changes = await sql.getChanges(version);
1680
1680
  if (!changes) {
1681
- throw new Error(`No changes found for version ${version}`);
1681
+ throw new Error(`Unexpected error! No changes were found from version ${version} (fromVersion: ${fromVersion}, toVersion: ${toVersion})`);
1682
1682
  } else if (changes.fromVersion == version) {
1683
1683
  throw new Error(`Saved changes do not change the database version (${version})`);
1684
1684
  }
package/package.json CHANGED
@@ -1,46 +1,46 @@
1
- {
2
- "name": "sqlmorpheus",
3
- "version": "0.2.7",
4
- "author": "JodliDev",
5
- "keywords": [],
6
- "license": "MIT",
7
- "description": "",
8
- "repository": {
9
- "type": "git",
10
- "url": "git://github.com/JodliDev/SqlMorpheus.git"
11
- },
12
- "bugs": {
13
- "url": "https://github.com/JodliDev/SqlMorpheus/issues"
14
- },
15
- "homepage": "https://github.com/JodliDev/SqlMorpheus#readme",
16
- "main": "dist/index.js",
17
- "types": "dist/index.d.ts",
18
- "files": [
19
- "/dist"
20
- ],
21
- "scripts": {
22
- "build": "tsup",
23
- "dev": "tsup --watch",
24
- "test": "vitest run",
25
- "test:watch": "vitest",
26
- "build_and_publish": "npm run build && npm publish"
27
- },
28
- "devDependencies": {
29
- "@testcontainers/mysql": "^11.7.1",
30
- "@testcontainers/postgresql": "^11.10.0",
31
- "@types/better-sqlite3": "^7.6.13",
32
- "@types/node": "^24.0.13",
33
- "@types/pg": "^8.16.0",
34
- "@vitest/coverage-v8": "^3.2.4",
35
- "better-sqlite3": "^12.2.0",
36
- "mysql2": "^3.15.2",
37
- "pg": "^8.16.3",
38
- "tsup": "^8.5.0",
39
- "typescript": "^5.8.3",
40
- "vitest": "^3.2.4"
41
- },
42
- "dependencies": {
43
- "escape-sql-string": "^1.2.2",
44
- "polyfill-symbol-metadata": "^0.0.2"
45
- }
46
- }
1
+ {
2
+ "name": "sqlmorpheus",
3
+ "version": "1.0.0",
4
+ "author": "JodliDev",
5
+ "keywords": [],
6
+ "license": "MIT",
7
+ "description": "",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git://github.com/JodliDev/SqlMorpheus.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/JodliDev/SqlMorpheus/issues"
14
+ },
15
+ "homepage": "https://github.com/JodliDev/SqlMorpheus#readme",
16
+ "main": "dist/index.js",
17
+ "types": "dist/index.d.ts",
18
+ "files": [
19
+ "/dist"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "dev": "tsup --watch",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "build_and_publish": "npm run build && npm publish"
27
+ },
28
+ "devDependencies": {
29
+ "@testcontainers/mysql": "^11.7.1",
30
+ "@testcontainers/postgresql": "^11.10.0",
31
+ "@types/better-sqlite3": "^7.6.13",
32
+ "@types/node": "^24.0.13",
33
+ "@types/pg": "^8.16.0",
34
+ "@vitest/coverage-v8": "^3.2.4",
35
+ "better-sqlite3": "^12.2.0",
36
+ "mysql2": "^3.15.2",
37
+ "pg": "^8.16.3",
38
+ "tsup": "^8.5.0",
39
+ "typescript": "^5.8.3",
40
+ "vitest": "^3.2.4"
41
+ },
42
+ "dependencies": {
43
+ "escape-sql-string": "^1.2.2",
44
+ "polyfill-symbol-metadata": "^0.0.2"
45
+ }
46
+ }