velocious 1.0.2 → 1.0.4

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 (79) hide show
  1. package/.github/dependabot.yml +1 -1
  2. package/README.md +19 -3
  3. package/bin/velocious.mjs +4 -4
  4. package/index.mjs +5 -3
  5. package/package.json +5 -3
  6. package/peak_flow.yml +5 -2
  7. package/spec/cli/commands/db/create-spec.mjs +25 -0
  8. package/spec/cli/commands/db/migrate-spec.mjs +37 -0
  9. package/spec/cli/commands/destroy/migration-spec.mjs +15 -0
  10. package/spec/cli/commands/generate/migration-spec.mjs +18 -0
  11. package/spec/cli/commands/init-spec.mjs +19 -0
  12. package/spec/cli/commands/test/test-files-finder-spec.mjs +12 -0
  13. package/spec/database/drivers/mysql/connection-spec.mjs +2 -2
  14. package/spec/dummy/dummy-directory.mjs +11 -0
  15. package/spec/dummy/index.mjs +22 -26
  16. package/spec/dummy/src/config/configuration.example.mjs +21 -0
  17. package/spec/dummy/src/config/configuration.peakflow.mjs +22 -0
  18. package/spec/dummy/src/database/migrations/20230728075328-create-projects.mjs +11 -0
  19. package/spec/dummy/src/database/migrations/20230728075329-create-tasks.mjs +13 -0
  20. package/spec/http-server/client-spec.mjs +7 -12
  21. package/src/application.mjs +10 -11
  22. package/src/cli/base-command.mjs +11 -0
  23. package/src/cli/commands/db/create.mjs +44 -8
  24. package/src/cli/commands/db/migrate.mjs +105 -0
  25. package/src/cli/commands/destroy/migration.mjs +35 -0
  26. package/src/cli/commands/generate/migration.mjs +31 -7
  27. package/src/cli/commands/generate/model.mjs +36 -0
  28. package/src/cli/commands/init.mjs +60 -0
  29. package/src/cli/commands/server.mjs +15 -0
  30. package/src/cli/commands/test/index.mjs +14 -0
  31. package/src/cli/commands/test/test-files-finder.mjs +99 -0
  32. package/src/cli/commands/test/test-runner.mjs +19 -0
  33. package/src/cli/index.mjs +37 -16
  34. package/src/configuration-resolver.mjs +26 -0
  35. package/src/configuration.mjs +51 -13
  36. package/src/controller.mjs +1 -1
  37. package/src/database/drivers/base.mjs +8 -0
  38. package/src/database/drivers/mysql/index.mjs +25 -0
  39. package/src/database/drivers/mysql/sql/create-database.mjs +4 -0
  40. package/src/database/drivers/mysql/sql/create-table.mjs +4 -0
  41. package/src/database/drivers/sqlite/index.native.mjs +92 -0
  42. package/src/database/drivers/sqlite/index.web.mjs +64 -0
  43. package/src/database/drivers/sqlite/options.mjs +17 -0
  44. package/src/database/drivers/sqlite/query-parser.mjs +25 -0
  45. package/src/database/drivers/sqlite/query.native.mjs +9 -0
  46. package/src/database/drivers/sqlite/query.web.mjs +9 -0
  47. package/src/database/drivers/sqlite/sql/create-table.mjs +4 -0
  48. package/src/database/drivers/sqlite/sql/delete.mjs +19 -0
  49. package/src/database/drivers/sqlite/sql/insert.mjs +29 -0
  50. package/src/database/drivers/sqlite/sql/update.mjs +31 -0
  51. package/src/database/handler.mjs +0 -4
  52. package/src/database/migrate-from-require-context.mjs +53 -0
  53. package/src/database/migration/index.mjs +15 -2
  54. package/src/database/pool/async-tracked-multi-connection.mjs +81 -0
  55. package/src/database/pool/base.mjs +47 -0
  56. package/src/database/pool/single-multi-use.mjs +40 -0
  57. package/src/database/query/base.mjs +11 -0
  58. package/src/database/query/create-database-base.mjs +22 -0
  59. package/src/database/query/create-table-base.mjs +69 -0
  60. package/src/database/query/delete-base.mjs +4 -10
  61. package/src/database/query/from-plain.mjs +3 -5
  62. package/src/database/query/from-table.mjs +2 -2
  63. package/src/database/record/index.mjs +2 -2
  64. package/src/database/table-data/index.mjs +83 -0
  65. package/src/http-server/worker-handler/index.mjs +2 -1
  66. package/src/http-server/worker-handler/worker-thread.mjs +17 -9
  67. package/src/routes/app-routes.mjs +10 -0
  68. package/src/routes/resolver.mjs +4 -2
  69. package/src/spec/index.mjs +5 -0
  70. package/src/templates/configuration.mjs +19 -0
  71. package/src/templates/generate-migration.mjs +11 -0
  72. package/src/templates/generate-model.mjs +4 -0
  73. package/src/templates/routes.mjs +11 -0
  74. package/src/utils/file-exists.mjs +13 -0
  75. package/spec/cli/generate/migration-spec.mjs +0 -9
  76. package/spec/dummy/src/config/database.example.mjs +0 -15
  77. package/spec/dummy/src/config/database.peakflow.mjs +0 -15
  78. package/spec/dummy/src/database/migrations/001-create-tasks.mjs +0 -12
  79. package/src/database/pool/index.mjs +0 -43
@@ -0,0 +1,105 @@
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 = this.configuration.getDirectory()
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 executeRequireContext(requireContext) {
38
+ const migrationFiles = requireContext.keys()
39
+
40
+ files = migrationFiles
41
+ .map((file) => {
42
+ const match = file.match(/^(\d{14})-(.+)\.mjs$/)
43
+
44
+ if (!match) return null
45
+
46
+ const date = parseInt(match[1])
47
+ const migrationName = match[2]
48
+ const migrationClassName = inflection.camelize(migrationName)
49
+
50
+ return {
51
+ file,
52
+ fullPath: `${migrationsPath}/${file}`,
53
+ date,
54
+ migrationClassName
55
+ }
56
+ })
57
+ .filter((migration) => Boolean(migration))
58
+ .sort((migration1, migration2) => migration1.date - migration2.date)
59
+
60
+ for (const migration of files) {
61
+ await this.runMigrationFileFromRequireContext(migration, requireContext)
62
+ }
63
+ }
64
+
65
+ async runMigrationFileFromRequireContext(migration, requireContext) {
66
+ if (!this.configuration) throw new Error("No configuration set")
67
+ if (!this.configuration.isDatabasePoolInitialized()) await this.configuration.initializeDatabasePool()
68
+
69
+ await this.configuration.getDatabasePool().withConnection(async () => {
70
+ const MigrationClass = requireContext(migration.file).default
71
+ const migrationInstance = new MigrationClass({
72
+ configuration: this.configuration
73
+ })
74
+
75
+ if (migrationInstance.change) {
76
+ await migrationInstance.change()
77
+ } else if (migrationInstance.up) {
78
+ await migrationInstance.up()
79
+ } else {
80
+ throw new Error(`'change' or 'up' didn't exist on migration: ${migration.file}`)
81
+ }
82
+ })
83
+ }
84
+
85
+ async runMigrationFile(migration) {
86
+ if (!this.configuration) throw new Error("No configuration set")
87
+ if (!this.configuration.isDatabasePoolInitialized()) await this.configuration.initializeDatabasePool()
88
+
89
+ await this.configuration.getDatabasePool().withConnection(async () => {
90
+ const migrationImport = await import(migration.fullPath)
91
+ const MigrationClass = migrationImport.default
92
+ const migrationInstance = new MigrationClass({
93
+ configuration: this.configuration
94
+ })
95
+
96
+ if (migrationInstance.change) {
97
+ await migrationInstance.change()
98
+ } else if (migrationInstance.up) {
99
+ await migrationInstance.up()
100
+ } else {
101
+ throw new Error(`'change' or 'up' didn't exist on migration: ${migration.file}`)
102
+ }
103
+ })
104
+ }
105
+ }
@@ -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.getDirectory()}/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
+ }
@@ -1,12 +1,36 @@
1
- export default class DbGenerateMigration {
2
- constructor({args}) {
3
- this.args = args
4
- }
1
+ import BaseCommand from "../../base-command.mjs"
2
+ import {dirname} from "path"
3
+ import {fileURLToPath} from "url"
4
+ import fileExists from "../../../utils/file-exists.mjs"
5
+ import fs from "node:fs/promises"
6
+ import inflection from "inflection"
7
+ import strftime from "strftime"
5
8
 
6
- execute() {
7
- const migrationName = this.args[1]
9
+ export default class DbGenerateMigration extends BaseCommand {
10
+ async execute() {
11
+ const migrationName = this.processArgs[1]
12
+ const migrationNameCamelized = inflection.camelize(migrationName.replaceAll("-", "_"))
8
13
  const date = new Date()
14
+ const migrationNumber = strftime("%Y%m%d%H%M%S")
15
+ const migrationFileName = `${migrationNumber}-${migrationName}.mjs`
16
+ const __filename = fileURLToPath(`${import.meta.url}/../../..`)
17
+ const __dirname = dirname(__filename)
18
+ const templateFilePath = `${__dirname}/templates/generate-migration.mjs`
19
+ const migrationContentBuffer = await fs.readFile(templateFilePath)
20
+ const migrationContent = migrationContentBuffer.toString().replaceAll("__MIGRATION_NAME__", migrationNameCamelized)
21
+ const migrationDir = `${process.cwd()}/src/database/migrations`
22
+ const migrationPath = `${migrationDir}/${migrationFileName}`
23
+
24
+ if (this.args.testing) {
25
+ return {date, migrationContent, migrationName, migrationNameCamelized, migrationNumber, migrationPath}
26
+ } else {
27
+ if (!await fileExists(migrationDir)) {
28
+ await fs.mkdir(migrationDir, {recursive: true})
29
+ }
30
+
31
+ await fs.writeFile(migrationPath, migrationContent)
9
32
 
10
- console.log({ migrationName, date })
33
+ console.log(`create src/database/migrations/${migrationFileName}`)
34
+ }
11
35
  }
12
36
  }
@@ -0,0 +1,36 @@
1
+ import BaseCommand from "../../base-command.mjs"
2
+ import {dirname} from "path"
3
+ import {fileURLToPath} from "url"
4
+ import fileExists from "../../../utils/file-exists.mjs"
5
+ import fs from "node:fs/promises"
6
+ import inflection from "inflection"
7
+
8
+ export default class DbGenerateModel extends BaseCommand {
9
+ async execute() {
10
+ const modelName = this.processArgs[1]
11
+ const modelNameCamelized = inflection.camelize(modelName.replaceAll("-", "_"))
12
+ const date = new Date()
13
+ const modelFileName = `${inflection.dasherize(inflection.underscore(modelName))}.mjs`
14
+ const __filename = fileURLToPath(`${import.meta.url}/../../..`)
15
+ const __dirname = dirname(__filename)
16
+ const templateFilePath = `${__dirname}/templates/generate-model.mjs`
17
+ const modelContentBuffer = await fs.readFile(templateFilePath)
18
+ const modelContent = modelContentBuffer.toString().replaceAll("__MODEL_NAME__", modelNameCamelized)
19
+ const modelsDir = `${process.cwd()}/src/models`
20
+ const modelPath = `${modelsDir}/${modelFileName}`
21
+
22
+ if (await fileExists(modelPath)) throw new Error(`Model file already exists: ${modelPath}`)
23
+
24
+ if (this.args.testing) {
25
+ return {date, modelContent, modelName, modelNameCamelized, modelPath}
26
+ } else {
27
+ if (!await fileExists(modelsDir)) {
28
+ await fs.mkdir(modelsDir, {recursive: true})
29
+ }
30
+
31
+ await fs.writeFile(modelPath, modelContent)
32
+
33
+ console.log(`create src/models/${modelFileName}`)
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,60 @@
1
+ import BaseCommand from "../base-command.mjs"
2
+ import {dirname} from "path"
3
+ import fileExists from "../../utils/file-exists.mjs"
4
+ import {fileURLToPath} from "url"
5
+ import fs from "node:fs/promises"
6
+
7
+ export default class VelociousCliCommandsInit extends BaseCommand {
8
+ async execute() {
9
+ const __filename = fileURLToPath(`${import.meta.url}/../../..`)
10
+ const velocipusPath = dirname(__filename)
11
+ const projectPath = this.configuration?.getDirectory() || process.cwd()
12
+ const projectConfigPath = `${projectPath}/src/config`
13
+ const fileMappings = [
14
+ {
15
+ source: `${velocipusPath}/src/templates/configuration.mjs`,
16
+ target: `${projectConfigPath}/configuration.mjs`
17
+ },
18
+ {
19
+ source: `${velocipusPath}/src/templates/routes.mjs`,
20
+ target: `${projectConfigPath}/routes.mjs`
21
+ }
22
+ ]
23
+ const paths = [
24
+ projectConfigPath,
25
+ `${projectPath}/database/migrations`
26
+ ]
27
+
28
+ if (this.args.testing) {
29
+ return {
30
+ fileMappings
31
+ }
32
+ }
33
+
34
+ for (const path of paths) {
35
+ if (await fileExists(path)) {
36
+ console.log(`Config dir already exists: ${path}`)
37
+ } else {
38
+ console.log(`Config dir doesn't exists: ${path}`)
39
+ await fs.mkdir(path, {recursive: true})
40
+ }
41
+ }
42
+
43
+ for (const fileMapping of fileMappings) {
44
+ if (!await fileExists(fileMapping.source)) {
45
+ throw new Error(`Template doesn't exist: ${fileMapping.source}`)
46
+ }
47
+
48
+ if (await fileExists(fileMapping.target)) {
49
+ console.log(`File already exists: ${fileMapping.target}`)
50
+ } else {
51
+ console.log(`File doesnt exist: ${fileMapping.target}`)
52
+ await fs.copyFile(fileMapping.source, fileMapping.target)
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ const dontLoadConfiguration = true
59
+
60
+ export {dontLoadConfiguration}
@@ -0,0 +1,15 @@
1
+ import BaseCommand from "../base-command.mjs"
2
+
3
+ export default class DbCreate extends BaseCommand{
4
+ async execute() {
5
+ this.databasePool = this.configuration.getDatabasePool()
6
+ this.newConfiguration = Object.assign({}, this.databasePool.getConfiguration())
7
+
8
+ if (this.args.testing) this.result = []
9
+
10
+ this.databaseConnection = await this.databasePool.spawnConnectionWithConfiguration(this.newConfiguration)
11
+ await this.databaseConnection.connect()
12
+
13
+ throw new Error("stub")
14
+ }
15
+ }
@@ -0,0 +1,14 @@
1
+ import BaseCommand from "../../base-command.mjs"
2
+ import TestFilesFinder from "./test-files-finder.mjs"
3
+ import TestRunner from "./test-runner.mjs"
4
+
5
+ export default class VelociousCliCommandsInit extends BaseCommand {
6
+ async execute() {
7
+ const testFilesFinder = new TestFilesFinder({directory: this.directory(), processArgs: this.processArgs})
8
+ const testFiles = await testFilesFinder.findTestFiles()
9
+
10
+ const testRunner = new TestRunner(testFiles)
11
+
12
+ await testRunner.run()
13
+ }
14
+ }
@@ -0,0 +1,99 @@
1
+ import fs from "fs/promises"
2
+
3
+ // Incredibly complex class to find files in multiple simultanious running promises to do it as fast as possible.
4
+ export default class TestFilesFinder {
5
+ static IGNORED_NAMES = [".git", "node_modules"]
6
+
7
+ constructor({directory, processArgs}) {
8
+ this.directory = directory
9
+ this.foundFiles = []
10
+ this.findingCount = 0
11
+ this.findingPromises = {}
12
+ this.processArgs = processArgs
13
+ this.testArgs = this.processArgs.filter((processArg, index) => index != 0)
14
+ }
15
+
16
+ async findTestFiles() {
17
+ await this.withFindingCount(async () => {
18
+ await this.findTestFilesInDir(this.directory)
19
+ })
20
+
21
+ await this.waitForFindingPromises()
22
+
23
+ return this.foundFiles
24
+ }
25
+
26
+ findingPromisesLength = () => Object.keys(this.findingPromises).length
27
+
28
+ async waitForFindingPromises() {
29
+ while (this.findingPromisesLength() > 0) {
30
+ await this.waitForFindingPromisesIteration()
31
+ }
32
+ }
33
+
34
+ async waitForFindingPromisesIteration() {
35
+ const unfinishedPromises = []
36
+
37
+ for (const findingPromiseId in this.findingPromises) {
38
+ const findingPromise = this.findingPromises[findingPromiseId]
39
+
40
+ unfinishedPromises.push(findingPromise)
41
+ }
42
+
43
+ await Promise.all(unfinishedPromises)
44
+ }
45
+
46
+ withFindingCount(callback) {
47
+ return new Promise((resolve) => {
48
+ const findingPromise = callback()
49
+ const findingCount = this.findingCount
50
+
51
+ this.findingCount += 1
52
+ this.findingPromises[findingCount] = findingPromise
53
+
54
+ findingPromise.finally(() => {
55
+ delete this.findingPromises[findingCount]
56
+
57
+ resolve()
58
+ })
59
+ })
60
+ }
61
+
62
+ async findTestFilesInDir(dir) {
63
+ await this.withFindingCount(async () => {
64
+ const files = await fs.readdir(dir)
65
+
66
+ for (const file of files) {
67
+ if (TestFilesFinder.IGNORED_NAMES.includes(file)) {
68
+ continue
69
+ }
70
+
71
+ const fullPath = `${dir}/${file}`
72
+ const localPath = fullPath.replace(`${this.directory}/`, "")
73
+ const isDir = (await fs.stat(fullPath)).isDirectory()
74
+
75
+ if (isDir) {
76
+ this.findTestFilesInDir(fullPath)
77
+ } else {
78
+ if (this.isFileMatchingRequirements(file, localPath, fullPath)) {
79
+ this.foundFiles.push(fullPath)
80
+ }
81
+ }
82
+ }
83
+ })
84
+ }
85
+
86
+ isFileMatchingRequirements(file, localPath, fullPath) {
87
+ if (this.testArgs.length > 0) {
88
+ for (const testArg of this.testArgs) {
89
+ if (testArg == localPath) {
90
+ return true
91
+ }
92
+ }
93
+ } else if (file.match(/-spec\.mjs/)) {
94
+ return true
95
+ }
96
+
97
+ return false
98
+ }
99
+ }
@@ -0,0 +1,19 @@
1
+ export default class TestRunner {
2
+ constructor(testFiles) {
3
+ this.testFiles = testFiles
4
+ }
5
+
6
+ async importTestFiles() {
7
+ for (const testFile of this.testFiles) {
8
+ const importTestFile = await import(testFile)
9
+ }
10
+ }
11
+
12
+ async run() {
13
+ await this.importTestFiles()
14
+
15
+ console.log({foundTestFiles: this.testFiles})
16
+
17
+ throw new Error("stub")
18
+ }
19
+ }
package/src/cli/index.mjs CHANGED
@@ -1,39 +1,60 @@
1
+ import configurationResolver from "../configuration-resolver.mjs"
1
2
  import {dirname} from "path"
2
3
  import {fileURLToPath} from "url"
3
- import fs from "node:fs/promises"
4
+ import fileExists from "../utils/file-exists.mjs"
4
5
 
5
- const fileExists = async (path) => {
6
- try {
7
- await fs.access(path)
8
-
9
- return true
10
- } catch (error) {
11
- return false
6
+ export default class VelociousCli {
7
+ constructor(args = {}) {
8
+ this.args = args
12
9
  }
13
- }
14
10
 
15
- export default class VelociousCli {
16
- async execute({args}) {
11
+ async execute() {
17
12
  const __filename = fileURLToPath(`${import.meta.url}/../..`)
18
13
  const __dirname = dirname(__filename)
19
- const commandParts = args[0].split(":")
14
+ const commandParts = this.args.processArgs[0].split(":")
15
+ const filePaths = []
20
16
  let filePath = `${__dirname}/src/cli/commands`
21
17
 
22
18
  for (let commandPart of commandParts) {
23
19
  if (commandPart == "d") commandPart = "destroy"
24
20
  if (commandPart == "g") commandPart = "generate"
21
+ if (commandPart == "s") commandPart = "server"
25
22
 
26
23
  filePath += `/${commandPart}`
27
24
  }
28
25
 
26
+ filePaths.push(`${filePath}/index.mjs`)
29
27
  filePath += ".mjs"
28
+ filePaths.push(filePath)
29
+
30
+ let fileFound
31
+
32
+ for (const aFilePath of filePaths) {
33
+ if (await fileExists(aFilePath)) {
34
+ fileFound = aFilePath
35
+ break
36
+ }
37
+ }
30
38
 
31
- if (!fileExists(filePath)) throw new Error(`Unknown command: ${args[0]} which should have been in ${filePath}`)
39
+ if (!fileFound) throw new Error(`Unknown command: ${this.args.processArgs[0]} which should have been one of ${filePaths.join(", ")}`)
32
40
 
33
- const commandClassImport = await import(filePath)
41
+ const commandClassImport = await import(fileFound)
34
42
  const CommandClass = commandClassImport.default
35
- const commandInstance = new CommandClass({args})
36
43
 
37
- await commandInstance.execute()
44
+ await this.loadConfiguration()
45
+
46
+ const commandInstance = new CommandClass(this.args)
47
+
48
+ if (commandInstance.initialize) {
49
+ await commandInstance.initialize()
50
+ }
51
+
52
+ return await commandInstance.execute()
53
+ }
54
+
55
+ async loadConfiguration() {
56
+ this.configuration = await configurationResolver({directory: this.args.directory})
57
+ this.configuration.setCurrent()
58
+ this.args.configuration = this.configuration
38
59
  }
39
60
  }
@@ -0,0 +1,26 @@
1
+ import Configuration from "./configuration.mjs"
2
+
3
+ const configurationResolver = async (args) => {
4
+ if (Configuration.current(false)) {
5
+ return Configuration.current()
6
+ }
7
+
8
+ const directory = args.directory || process.cwd()
9
+ const configurationPath = `${directory}/src/config/configuration.mjs`
10
+ let configuration
11
+
12
+ try {
13
+ const configurationImport = await import(configurationPath)
14
+
15
+ configuration = configurationImport.default
16
+ } catch (error) {
17
+ // This might happen during an "init" CLI command where we copy a sample configuration file.
18
+ if (error.code != "ERR_MODULE_NOT_FOUND") throw error
19
+
20
+ configuration = new Configuration(args)
21
+ }
22
+
23
+ return configuration
24
+ }
25
+
26
+ export default configurationResolver
@@ -1,27 +1,65 @@
1
1
  import {digg} from "diggerize"
2
2
 
3
3
  export default class VelociousConfiguration {
4
- static current() {
5
- if (!global.velociousConfiguration) throw new Error("A Velocious configuration hasn't been set")
4
+ static current(throwError = true) {
5
+ if (!this.velociousConfiguration && throwError) throw new Error("A Velocious configuration hasn't been set")
6
6
 
7
- return global.velociousConfiguration
7
+ return this.velociousConfiguration
8
8
  }
9
9
 
10
- constructor({debug, directory}) {
11
- if (!directory) throw new Error("No directory given")
12
-
10
+ constructor({database, debug, directory}) {
11
+ this.database = database
13
12
  this.debug = debug
14
- this.directory = directory
13
+ this._directory = directory
15
14
  }
16
15
 
17
- async initialize() {
18
- await this.initializeRoutes()
16
+ getDatabasePool() {
17
+ if (!this.isDatabasePoolInitialized()) {
18
+ this.initializeDatabasePool()
19
+ }
20
+
21
+ return this.databasePool
22
+ }
23
+
24
+ getDatabasePoolType = () => {
25
+ const poolTypeClass = digg(this, "database", "default", "master", "poolType")
26
+
27
+ if (!poolTypeClass) {
28
+ throw new Error("No poolType given in database configuration")
29
+ }
30
+
31
+ return poolTypeClass
32
+ }
33
+
34
+ getDirectory = () => {
35
+ if (!this._directory) {
36
+ this._directory = process.cwd()
37
+ }
38
+
39
+ return this._directory
19
40
  }
20
41
 
21
- async initializeRoutes() {
22
- // Every client need to make their own routes because they probably can't be shared across different worker threads
23
- const routesImport = await import(`${this.directory}/src/config/routes.mjs`)
42
+ initializeDatabasePool() {
43
+ if (!this.database) throw new Error("No 'database' was given")
44
+ if (this.databasePool) throw new Error("DatabasePool has already been initialized")
45
+
46
+ const PoolType = this.getDatabasePoolType()
47
+
48
+ this.databasePool = new PoolType({configuration: this})
49
+ this.databasePool.setCurrent()
50
+ }
51
+
52
+ isDatabasePoolInitialized = () => Boolean(this.databasePool)
53
+
54
+ initialize() {
55
+ // Doesn't currently do anything.
56
+ }
57
+
58
+ setCurrent() {
59
+ this.constructor.velociousConfiguration = this
60
+ }
24
61
 
25
- this.routes = digg(routesImport, "default", "routes")
62
+ setRoutes(newRoutes) {
63
+ this.routes = newRoutes
26
64
  }
27
65
  }
@@ -36,7 +36,7 @@ export default class VelociousController {
36
36
  return new Promise((resolve, reject) => {
37
37
  const actionName = digg(this, "_params", "action")
38
38
  const controllerName = digg(this, "_params", "controller")
39
- const directory = digg(this, "_configuration", "directory")
39
+ const directory = digg(this, "_configuration").getDirectory()
40
40
  const viewPath = `${directory}/src/routes/${controllerName}/${actionName}.ejs`
41
41
  const {viewParams} = digs(this, "viewParams")
42
42
 
@@ -6,4 +6,12 @@ export default class VelociousDatabaseDriversBase {
6
6
  getArgs() {
7
7
  return this._args
8
8
  }
9
+
10
+ getIdSeq() {
11
+ return this.idSeq
12
+ }
13
+
14
+ setIdSeq(id) {
15
+ this.idSeq = id
16
+ }
9
17
  }
@@ -1,5 +1,7 @@
1
1
  import Base from "../base.mjs"
2
2
  import connectConnection from "./connect-connection.mjs"
3
+ import CreateDatabase from "./sql/create-database.mjs"
4
+ import CreateTable from "./sql/create-table.mjs"
3
5
  import Delete from "./sql/delete.mjs"
4
6
  import {digg} from "diggerize"
5
7
  import Insert from "./sql/insert.mjs"
@@ -35,6 +37,25 @@ export default class VelociousDatabaseDriversMysql extends Base{
35
37
  return connectArgs
36
38
  }
37
39
 
40
+ async close() {
41
+ await this.connection.end()
42
+ this.connection = undefined
43
+ }
44
+
45
+ createDatabaseSql(databaseName, args) {
46
+ const createArgs = Object.assign({databaseName, driver: this}, args)
47
+ const createDatabase = new CreateDatabase(createArgs)
48
+
49
+ return createDatabase.toSql()
50
+ }
51
+
52
+ createTableSql(tableData) {
53
+ const createArgs = Object.assign({tableData, driver: this})
54
+ const createTable = new CreateTable(createArgs)
55
+
56
+ return createTable.toSql()
57
+ }
58
+
38
59
  async query(sql) {
39
60
  return await query(this.connection, sql)
40
61
  }
@@ -49,6 +70,10 @@ export default class VelociousDatabaseDriversMysql extends Base{
49
70
  return this.connection.escape(string)
50
71
  }
51
72
 
73
+ quoteColumn(string) {
74
+ return `\`${string}\``
75
+ }
76
+
52
77
  deleteSql({tableName, conditions}) {
53
78
  const deleteInstruction = new Delete({conditions, driver: this, tableName})
54
79