velocious 1.0.2 → 1.0.3

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 (67) hide show
  1. package/README.md +12 -2
  2. package/bin/velocious.mjs +3 -3
  3. package/index.mjs +5 -1
  4. package/package.json +4 -3
  5. package/peak_flow.yml +5 -2
  6. package/spec/cli/commands/db/create-spec.mjs +25 -0
  7. package/spec/cli/commands/db/migrate-spec.mjs +37 -0
  8. package/spec/cli/commands/destroy/migration-spec.mjs +15 -0
  9. package/spec/cli/commands/generate/migration-spec.mjs +18 -0
  10. package/spec/cli/commands/init-spec.mjs +19 -0
  11. package/spec/cli/commands/test/test-files-finder-spec.mjs +12 -0
  12. package/spec/database/drivers/mysql/connection-spec.mjs +2 -2
  13. package/spec/dummy/dummy-directory.mjs +11 -0
  14. package/spec/dummy/index.mjs +18 -24
  15. package/spec/dummy/src/config/configuration.example.mjs +19 -0
  16. package/spec/dummy/src/config/configuration.peakflow.mjs +20 -0
  17. package/spec/dummy/src/database/migrations/20230728075328-create-projects.mjs +11 -0
  18. package/spec/dummy/src/database/migrations/20230728075329-create-tasks.mjs +13 -0
  19. package/spec/http-server/client-spec.mjs +3 -13
  20. package/src/application.mjs +6 -12
  21. package/src/cli/base-command.mjs +11 -0
  22. package/src/cli/commands/db/create.mjs +44 -8
  23. package/src/cli/commands/db/migrate.mjs +58 -0
  24. package/src/cli/commands/destroy/migration.mjs +35 -0
  25. package/src/cli/commands/generate/migration.mjs +31 -7
  26. package/src/cli/commands/init.mjs +60 -0
  27. package/src/cli/commands/test/index.mjs +14 -0
  28. package/src/cli/commands/test/test-files-finder.mjs +99 -0
  29. package/src/cli/commands/test/test-runner.mjs +19 -0
  30. package/src/cli/index.mjs +36 -16
  31. package/src/configuration-resolver.mjs +26 -0
  32. package/src/configuration.mjs +24 -2
  33. package/src/database/drivers/base.mjs +8 -0
  34. package/src/database/drivers/mysql/index.mjs +25 -0
  35. package/src/database/drivers/mysql/sql/create-database.mjs +4 -0
  36. package/src/database/drivers/mysql/sql/create-table.mjs +4 -0
  37. package/src/database/drivers/sqlite/options.mjs +17 -0
  38. package/src/database/drivers/sqlite/query-parser.mjs +25 -0
  39. package/src/database/drivers/sqlite/sql/create-database.mjs +4 -0
  40. package/src/database/drivers/sqlite/sql/create-table.mjs +4 -0
  41. package/src/database/drivers/sqlite/sql/delete.mjs +19 -0
  42. package/src/database/drivers/sqlite/sql/insert.mjs +29 -0
  43. package/src/database/drivers/sqlite/sql/update.mjs +31 -0
  44. package/src/database/drivers/sqlite-expo/index.mjs +100 -0
  45. package/src/database/drivers/sqlite-expo/query.mjs +9 -0
  46. package/src/database/handler.mjs +0 -4
  47. package/src/database/migration/index.mjs +15 -2
  48. package/src/database/pool/index.mjs +87 -18
  49. package/src/database/query/base.mjs +11 -0
  50. package/src/database/query/create-database-base.mjs +20 -0
  51. package/src/database/query/create-table-base.mjs +69 -0
  52. package/src/database/query/delete-base.mjs +4 -10
  53. package/src/database/query/from-plain.mjs +3 -5
  54. package/src/database/query/from-table.mjs +2 -2
  55. package/src/database/record/index.mjs +1 -1
  56. package/src/database/table-data/index.mjs +83 -0
  57. package/src/http-server/worker-handler/worker-thread.mjs +17 -8
  58. package/src/routes/resolver.mjs +3 -1
  59. package/src/spec/index.mjs +5 -0
  60. package/src/templates/configuration.mjs +17 -0
  61. package/src/templates/generate-migration.mjs +11 -0
  62. package/src/templates/routes.mjs +11 -0
  63. package/src/utils/file-exists.mjs +13 -0
  64. package/spec/cli/generate/migration-spec.mjs +0 -9
  65. package/spec/dummy/src/config/database.example.mjs +0 -15
  66. package/spec/dummy/src/config/database.peakflow.mjs +0 -15
  67. package/spec/dummy/src/database/migrations/001-create-tasks.mjs +0 -12
package/README.md CHANGED
@@ -9,7 +9,17 @@ This is still work in progress.
9
9
  * Migrations ala Rails
10
10
  * Controllers and views ala Rails
11
11
 
12
- ## Migrations
12
+ # Setup
13
+
14
+ Make a new NPM project.
15
+ ```bash
16
+ mkdir project
17
+ cd project
18
+ npm install velocious
19
+ npx velocious init
20
+ ```
21
+
22
+ # Migrations
13
23
 
14
24
  ```bash
15
25
  npx velocious db:g:migration create_tasks
@@ -19,7 +29,7 @@ npx velocious db:g:migration create_tasks
19
29
  npx velocious db:migrate
20
30
  ```
21
31
 
22
- ## Testing
32
+ # Testing
23
33
 
24
34
  ```bash
25
35
  npm test
package/bin/velocious.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import Cli from "../src/cli/index.mjs"
4
4
 
5
- const cli = new Cli()
6
- const args = process.argv.slice(2)
5
+ const processArgs = process.argv.slice(2)
6
+ const cli = new Cli({processArgs})
7
7
 
8
- cli.execute({args})
8
+ cli.execute()
package/index.mjs CHANGED
@@ -1,17 +1,21 @@
1
1
  import Application from "./src/application.mjs"
2
2
  import Cli from "./src/cli/index.mjs"
3
+ import Configuration from "./src/configuration.mjs"
3
4
  import Controller from "./src/controller.mjs"
4
5
  import Database from "./src/database/index.mjs"
5
6
  import DatabasePool from "./src/database/pool/index.mjs"
6
7
  import HttpServer from "./src/http-server/index.mjs"
7
8
  import Routes from "./src/routes/index.mjs"
9
+ import Spec from "./src/spec/index.mjs"
8
10
 
9
11
  export {
10
12
  Application,
11
13
  Cli,
14
+ Configuration,
12
15
  Controller,
13
16
  Database,
14
17
  DatabasePool,
15
18
  HttpServer,
16
- Routes
19
+ Routes,
20
+ Spec
17
21
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "velocious": "bin/velocious.mjs"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.2",
6
+ "version": "1.0.3",
7
7
  "main": "index.mjs",
8
8
  "scripts": {
9
9
  "test": "jasmine",
@@ -27,10 +27,11 @@
27
27
  "node-fetch": "^3.3.1"
28
28
  },
29
29
  "dependencies": {
30
- "diggerize": "^1.0.3",
30
+ "diggerize": "^1.0.5",
31
31
  "ejs": "^3.1.6",
32
32
  "escape-string-regexp": "^1.0.5",
33
33
  "incorporator": "^1.0.2",
34
- "inflection": "^2.0.0"
34
+ "inflection": "^3.0.0",
35
+ "strftime": "^0.10.2"
35
36
  }
36
37
  }
package/peak_flow.yml CHANGED
@@ -1,8 +1,11 @@
1
1
  before_install:
2
- - curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
2
+ - sudo mkdir -p /etc/apt/keyrings
3
+ - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
4
+ - echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
5
+ - sudo apt-get update
3
6
  - sudo apt-get install -y nodejs
4
7
  before_script:
5
- - cp spec/dummy/src/config/database.peakflow.mjs spec/dummy/src/config/database.mjs
8
+ - cp spec/dummy/src/config/configuration.peakflow.mjs spec/dummy/src/config/configuration.mjs
6
9
  - npm install
7
10
  - wait-for-it mariadb:3306
8
11
  services:
@@ -0,0 +1,25 @@
1
+ import Cli from "../../../../src/cli/index.mjs"
2
+ import dummyDirectory from "../../../dummy/dummy-directory.mjs"
3
+
4
+ describe("Cli - Commands - db:create", () => {
5
+ it("generates SQL to create a new database", async () => {
6
+ const cli = new Cli({
7
+ directory: dummyDirectory(),
8
+ processArgs: ["db:create"],
9
+ testing: true
10
+ })
11
+ const result = await cli.execute()
12
+
13
+ expect(result).toEqual(
14
+ [
15
+ {
16
+ databaseName: 'velocious_test',
17
+ sql: 'CREATE DATABASE IF NOT EXISTS velocious_test'
18
+ },
19
+ {
20
+ createSchemaMigrationsTableSql: 'CREATE TABLE IF NOT EXISTS schema_migrations (`version` varchar(255) PRIMARY KEY)'
21
+ }
22
+ ]
23
+ )
24
+ })
25
+ })
@@ -0,0 +1,37 @@
1
+ import Cli from "../../../../src/cli/index.mjs"
2
+ import dummyDirectory from "../../../dummy/dummy-directory.mjs"
3
+
4
+ describe("Cli - Commands - db:migrate", () => {
5
+ it("runs migrations", async () => {
6
+ const directory = dummyDirectory()
7
+ const cli = new Cli({
8
+ directory,
9
+ processArgs: ["db:migrate"],
10
+ testing: true
11
+ })
12
+
13
+ await cli.loadConfiguration()
14
+
15
+ const db = cli.configuration.getDatabasePool()
16
+
17
+ await db.withConnection(async () => {
18
+ await db.query("DROP TABLE IF EXISTS tasks")
19
+ await db.query("DROP TABLE IF EXISTS projects")
20
+ })
21
+
22
+ await cli.execute()
23
+
24
+ let tablesResult
25
+
26
+ await db.withConnection(async () => {
27
+ tablesResult = await db.query("SHOW TABLES")
28
+ })
29
+
30
+ expect(tablesResult).toEqual(
31
+ [
32
+ {Tables_in_velocious_test: "projects"},
33
+ {Tables_in_velocious_test: "tasks"}
34
+ ]
35
+ )
36
+ })
37
+ })
@@ -0,0 +1,15 @@
1
+ import Cli from "../../../../src/cli/index.mjs"
2
+ import dummyDirectory from "../../../dummy/dummy-directory.mjs"
3
+
4
+ describe("Cli - destroy - migration", () => {
5
+ it("destroys an existing migration", async () => {
6
+ const cli = new Cli({
7
+ directory: dummyDirectory(),
8
+ processArgs: ["d:migration", "create-tasks"],
9
+ testing: true
10
+ })
11
+ const result = await cli.execute()
12
+
13
+ expect(result.destroyed).toEqual(["create-tasks"])
14
+ })
15
+ })
@@ -0,0 +1,18 @@
1
+ import Cli from "../../../../src/cli/index.mjs"
2
+ import dummyDirectory from "../../../dummy/dummy-directory.mjs"
3
+
4
+ describe("Cli - generate - migration", () => {
5
+ it("generates a new migration", async () => {
6
+ const cli = new Cli({
7
+ directory: dummyDirectory(),
8
+ processArgs: ["g:migration", "create-tasks"],
9
+ testing: true
10
+ })
11
+ const result = await cli.execute()
12
+
13
+ expect(result.migrationName).toEqual("create-tasks")
14
+ expect(result.migrationNameCamelized).toEqual("CreateTasks")
15
+ expect(result.migrationNumber).toMatch(/^\d+$/)
16
+ expect(result.migrationPath).toMatch(/-create-tasks\.mjs$/)
17
+ })
18
+ })
@@ -0,0 +1,19 @@
1
+ import Cli from "../../../src/cli/index.mjs"
2
+ import dummyDirectory from "../../dummy/dummy-directory.mjs"
3
+
4
+ describe("Cli - Commands - init", () => {
5
+ it("inits files and dirs", async () => {
6
+ const cli = new Cli({
7
+ directory: dummyDirectory(),
8
+ processArgs: ["init"],
9
+ testing: true
10
+ })
11
+ const result = await cli.execute()
12
+
13
+ expect(result.fileMappings.length).toEqual(2)
14
+ expect(result.fileMappings[0].source).toContain("/src/templates/configuration.mjs")
15
+ expect(result.fileMappings[0].target).toContain("/spec/dummy/src/config/configuration.mjs")
16
+ expect(result.fileMappings[1].source).toContain("/src/templates/routes.mjs")
17
+ expect(result.fileMappings[1].target).toContain("/spec/dummy/src/config/routes.mjs")
18
+ })
19
+ })
@@ -0,0 +1,12 @@
1
+ import TestFilesFinder from "../../../../src/cli/commands/test/test-files-finder.mjs"
2
+
3
+ describe("Cli - Commands - test - TestFilesFinder", () => {
4
+ it("finds the correct test files", async () => {
5
+ const testFilesFinder = new TestFilesFinder({directory: process.cwd(), processArgs: ["test"]})
6
+ const testFiles = await testFilesFinder.findTestFiles()
7
+
8
+ const sampleTestFilePath = `${process.cwd()}/spec/cli/commands/destroy/migration-spec.mjs`
9
+
10
+ expect(testFiles.includes(sampleTestFilePath)).toBe(true)
11
+ })
12
+ })
@@ -1,8 +1,8 @@
1
1
  import Database from "../../../../src/database/index.mjs"
2
- import {databaseConfiguration} from "../../../dummy/src/config/database.mjs"
2
+ import configuration from "../../../dummy/src/config/configuration.mjs"
3
3
  import {digg} from "diggerize"
4
4
 
5
- const mysqlConfig = digg(databaseConfiguration(), "default", "master")
5
+ const mysqlConfig = digg(configuration, "database", "default", "master")
6
6
  const Mysql = Database.Drivers.Mysql
7
7
 
8
8
  describe("Database - Drivers - Mysql - Connection", () => {
@@ -0,0 +1,11 @@
1
+ import {dirname} from "path"
2
+ import {fileURLToPath} from "url"
3
+
4
+ const dummyDirectory = () => {
5
+ const __filename = fileURLToPath(import.meta.url)
6
+ const __dirname = dirname(__filename)
7
+
8
+ return __dirname
9
+ }
10
+
11
+ export default dummyDirectory
@@ -1,7 +1,5 @@
1
1
  import Application from "../../src/application.mjs"
2
- import DatabasePool from "../../src/database/pool/index.mjs"
3
- import {dirname} from "path"
4
- import {fileURLToPath} from "url"
2
+ import dummyConfiguration from "./src/config/configuration.mjs"
5
3
 
6
4
  export default class Dummy {
7
5
  static current() {
@@ -13,10 +11,12 @@ export default class Dummy {
13
11
  }
14
12
 
15
13
  static async prepare() {
16
- const connection = DatabasePool.current().singleConnection()
14
+ const db = dummyConfiguration.getDatabasePool()
17
15
 
18
- await connection.query("DROP TABLE IF EXISTS tasks")
19
- await connection.query("CREATE TABLE tasks (id MEDIUMINT NOT NULL AUTO_INCREMENT, name VARCHAR(255), description TEXT, PRIMARY KEY (id))")
16
+ await db.withConnection(async () => {
17
+ await db.query("DROP TABLE IF EXISTS tasks")
18
+ await db.query("CREATE TABLE tasks (id MEDIUMINT NOT NULL AUTO_INCREMENT, name VARCHAR(255), description TEXT, PRIMARY KEY (id))")
19
+ })
20
20
  }
21
21
 
22
22
  static async run(callback) {
@@ -24,21 +24,23 @@ export default class Dummy {
24
24
  }
25
25
 
26
26
  async run(callback) {
27
- await this.start()
28
-
29
- try {
30
- await Dummy.prepare()
31
- await callback()
32
- } finally {
33
- await this.stop()
34
- }
27
+ await dummyConfiguration.getDatabasePool().withConnection(async () => {
28
+ await this.start()
29
+
30
+ try {
31
+ await Dummy.prepare()
32
+ await callback()
33
+ } finally {
34
+ await this.stop()
35
+ }
36
+ })
35
37
  }
36
38
 
37
39
  async start() {
38
- const __filename = fileURLToPath(import.meta.url)
39
- const __dirname = dirname(__filename)
40
+ if (!dummyConfiguration.isDatabasePoolInitialized()) await dummyConfiguration.initializeDatabasePool()
40
41
 
41
42
  this.application = new Application({
43
+ configuration: dummyConfiguration,
42
44
  databases: {
43
45
  default: {
44
46
  host: "mysql",
@@ -46,19 +48,11 @@ export default class Dummy {
46
48
  password: ""
47
49
  }
48
50
  },
49
- debug: false,
50
- directory: __dirname,
51
51
  httpServer: {port: 3006}
52
52
  })
53
53
 
54
54
  await this.application.initialize()
55
55
  await this.application.startHttpServer()
56
-
57
- const databasePool = DatabasePool.current()
58
-
59
- if (!databasePool.isConnected()) {
60
- await databasePool.connect()
61
- }
62
56
  }
63
57
 
64
58
  async stop() {
@@ -0,0 +1,19 @@
1
+ import Configuration from "../../../../src/configuration.mjs"
2
+ import dummyDirectory from "../../dummy-directory.mjs"
3
+
4
+ const configuration = new Configuration({
5
+ database: {
6
+ default: {
7
+ master: {
8
+ type: "mysql",
9
+ host: "mariadb",
10
+ username: "username",
11
+ password: "password",
12
+ database: "velocious_test"
13
+ }
14
+ }
15
+ },
16
+ directory: dummyDirectory()
17
+ })
18
+
19
+ export default configuration
@@ -0,0 +1,20 @@
1
+ import Configuration from "../../../../src/configuration.mjs"
2
+ import dummyDirectory from "../../dummy-directory.mjs"
3
+
4
+ const configuration = new Configuration({
5
+ database: {
6
+ default: {
7
+ master: {
8
+ type: "mysql",
9
+ host: "mariadb",
10
+ username: "peakflow",
11
+ password: "password",
12
+ database: "velocious_test",
13
+ useDatabase: "velocious_test"
14
+ }
15
+ }
16
+ },
17
+ directory: dummyDirectory()
18
+ })
19
+
20
+ export default configuration
@@ -0,0 +1,11 @@
1
+ import Migration from "../../../../../src/database/migration/index.mjs"
2
+
3
+ export default class CreateProjects extends Migration {
4
+ async change() {
5
+ await this.createTable("projects", (table) => {
6
+ table.bigint("id", {autoIncrement: true, primaryKey: true})
7
+ table.string("name", {maxLength: 100, null: false})
8
+ table.timestamps()
9
+ })
10
+ }
11
+ }
@@ -0,0 +1,13 @@
1
+ import Migration from "../../../../../src/database/migration/index.mjs"
2
+
3
+ export default class CreateTasks extends Migration {
4
+ async change() {
5
+ await this.createTable("tasks", (table) => {
6
+ table.bigint("id", {autoIncrement: true, primaryKey: true})
7
+ table.references("project")
8
+ table.string("name")
9
+ table.text("description")
10
+ table.timestamps()
11
+ })
12
+ }
13
+ }
@@ -1,24 +1,14 @@
1
1
  import Client from "../../src/http-server/client/index.mjs"
2
- import Configuration from "../../src/configuration.mjs"
3
2
  import {digg} from "diggerize"
4
- import {dirname} from "path"
5
- import {fileURLToPath} from "url"
6
- import path from "path"
3
+ import dummyConfiguration from "../dummy/src/config/configuration.mjs"
7
4
 
8
5
  describe("http server - client", () => {
9
6
  it("spawns a request for each that it is fed", async () => {
10
- const __filename = fileURLToPath(import.meta.url)
11
- const __dirname = dirname(__filename)
12
- const dummyDirectory = path.join(__dirname, "../dummy")
13
- const configuration = new Configuration({
14
- directory: dummyDirectory
15
- })
16
-
17
- await configuration.initialize()
7
+ await dummyConfiguration.initialize()
18
8
 
19
9
  const client = new Client({
20
10
  clientCount: 0,
21
- configuration
11
+ configuration: dummyConfiguration
22
12
  })
23
13
 
24
14
  const strings = [
@@ -1,22 +1,19 @@
1
1
  import {digs} from "diggerize"
2
- import Configuration from "./configuration.mjs"
3
2
  import logger from "./logger.mjs"
4
3
  import HttpServer from "./http-server/index.mjs"
5
4
 
6
5
  export default class VelociousApplication {
7
- constructor({debug, directory, httpServer}) {
8
- this.configuration = new Configuration({debug, directory})
6
+ constructor({configuration, httpServer}) {
7
+ this.configuration = configuration
9
8
  this.httpServerConfiguration = httpServer ?? {}
10
9
  }
11
10
 
12
11
  async initialize() {
13
- if (global.velociousApplication) throw new Error("A Velocious application is already running")
14
- if (global.velociousConfiguration) throw new Error("A Velocious configuration has already been set")
15
-
16
- global.velociousApplication = this
17
- global.velociousConfiguration = this.configuration
18
-
19
12
  await this.configuration.initialize()
13
+
14
+ if (!this.configuration.isDatabasePoolInitialized()) {
15
+ await this.configuration.initializeDatabasePool()
16
+ }
20
17
  }
21
18
 
22
19
  isActive() {
@@ -49,8 +46,5 @@ export default class VelociousApplication {
49
46
  logger(this, "Stopping server")
50
47
 
51
48
  await this.httpServer.stop()
52
-
53
- global.velociousApplication = undefined
54
- global.velociousConfiguration = undefined
55
49
  }
56
50
  }
@@ -0,0 +1,11 @@
1
+ import {digg} from "diggerize"
2
+
3
+ export default class VelociousCliBaseCommand {
4
+ constructor(args) {
5
+ this.args = args
6
+ this.configuration = this.args.configuration
7
+ this.processArgs = args.processArgs
8
+ }
9
+
10
+ directory = () => digg(this, "configuration", "directory")
11
+ }
@@ -1,14 +1,50 @@
1
- export default class DbCreate {
2
- constructor({args}) {
3
- this.args = args
1
+ import BaseCommand from "../../base-command.mjs"
2
+ import {digg} from "diggerize"
3
+ import TableData from "../../../database/table-data/index.mjs"
4
+
5
+ export default class DbCreate extends BaseCommand{
6
+ async execute() {
7
+ this.databasePool = this.configuration.getDatabasePool()
8
+ this.newConfiguration = Object.assign({}, this.databasePool.getConfiguration())
9
+
10
+ if (this.args.testing) this.result = []
11
+
12
+ // Use a database known to exist. Since we are creating the database, it shouldn't actually exist which would make connecting fail.
13
+ this.newConfiguration.database = this.newConfiguration.useDatabase || "mysql"
14
+
15
+ this.databaseConnection = await this.databasePool.spawnConnectionWithConfiguration(this.newConfiguration)
16
+ await this.databaseConnection.connect()
17
+
18
+ this.createDatabase()
19
+ await this.createSchemaMigrationsTable()
20
+
21
+ await this.databaseConnection.close()
22
+
23
+ if (this.args.testing) return this.result
4
24
  }
5
25
 
6
- execute() {
7
- const migrationName = this.args[2]
8
- const date = new Date()
26
+ async createDatabase() {
27
+ const databaseName = digg(this.databasePool.getConfiguration(), "database")
28
+ const sql = this.databaseConnection.createDatabaseSql(databaseName, {ifNotExists: true})
29
+
30
+ if (this.args.testing) {
31
+ this.result.push({databaseName, sql})
32
+ } else {
33
+ await this.databaseConnection.query(sql)
34
+ }
35
+ }
36
+
37
+ async createSchemaMigrationsTable() {
38
+ const schemaMigrationsTable = new TableData("schema_migrations", {ifNotExists: true})
39
+
40
+ schemaMigrationsTable.string("version", {null: false, primaryKey: true})
9
41
 
10
- console.log({ migrationName, date })
42
+ const createSchemaMigrationsTableSql = this.databaseConnection.createTableSql(schemaMigrationsTable)
11
43
 
12
- throw new Error("stub")
44
+ if (this.args.testing) {
45
+ this.result.push({createSchemaMigrationsTableSql})
46
+ } else {
47
+ await this.databaseConnection.query(createSchemaMigrationsTableSql)
48
+ }
13
49
  }
14
50
  }
@@ -0,0 +1,58 @@
1
+ import BaseCommand from "../../base-command.mjs"
2
+ import {digg} from "diggerize"
3
+ import fs from "node:fs/promises"
4
+ import inflection from "inflection"
5
+
6
+ export default class DbMigrate extends BaseCommand {
7
+ async execute() {
8
+ const projectPath = digg(this.configuration, "directory")
9
+ const migrationsPath = `${projectPath}/src/database/migrations`
10
+ let files = await fs.readdir(migrationsPath)
11
+
12
+ files = files
13
+ .map((file) => {
14
+ const match = file.match(/^(\d{14})-(.+)\.mjs$/)
15
+
16
+ if (!match) return null
17
+
18
+ const date = parseInt(match[1])
19
+ const migrationName = match[2]
20
+ const migrationClassName = inflection.camelize(migrationName)
21
+
22
+ return {
23
+ file,
24
+ fullPath: `${migrationsPath}/${file}`,
25
+ date,
26
+ migrationClassName
27
+ }
28
+ })
29
+ .filter((migration) => Boolean(migration))
30
+ .sort((migration1, migration2) => migration1.date - migration2.date)
31
+
32
+ for (const migration of files) {
33
+ await this.runMigrationFile(migration)
34
+ }
35
+ }
36
+
37
+ async runMigrationFile(migration) {
38
+ if (!this.configuration.isDatabasePoolInitialized()) {
39
+ await this.configuration.initializeDatabasePool()
40
+ }
41
+
42
+ await this.configuration.databasePool.withConnection(async () => {
43
+ const migrationImport = await import(migration.fullPath)
44
+ const MigrationClass = migrationImport.default
45
+ const migrationInstance = new MigrationClass({
46
+ configuration: this.configuration
47
+ })
48
+
49
+ if (migrationInstance.change) {
50
+ await migrationInstance.change()
51
+ } else if (migrationInstance.up) {
52
+ await migrationInstance.up()
53
+ } else {
54
+ throw new Error(`'change' or 'up' didn't exist on migration: ${migration.file}`)
55
+ }
56
+ })
57
+ }
58
+ }
@@ -0,0 +1,35 @@
1
+ import BaseCommand from "../../base-command.mjs"
2
+ import fs from "node:fs/promises"
3
+
4
+ export default class DbDestroyMigration extends BaseCommand {
5
+ async execute() {
6
+ const migrationName = this.processArgs[1]
7
+ const migrationDir = `${this.configuration.directory}/src/database/migrations`
8
+ const migrationFiles = await fs.readdir(migrationDir)
9
+ const destroyed = []
10
+
11
+ for (const migrationFile of migrationFiles) {
12
+ const match = migrationFile.match(/^(\d{14})-(.+)\.mjs$/)
13
+
14
+ if (!match) {
15
+ continue
16
+ }
17
+
18
+ const fileName = match[2]
19
+
20
+ if (fileName != migrationName) continue
21
+
22
+ const fullFilePath = `${migrationDir}/${migrationFile}`
23
+ destroyed.push(fileName)
24
+
25
+ if (!this.args.testing) {
26
+ console.log(`Destroy src/database/migrations/${migrationFile}`)
27
+ await fs.unlink(fullFilePath)
28
+ }
29
+ }
30
+
31
+ if (this.args.testing) {
32
+ return {destroyed}
33
+ }
34
+ }
35
+ }