velocious 1.0.3 → 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 (44) hide show
  1. package/.github/dependabot.yml +1 -1
  2. package/README.md +7 -1
  3. package/bin/velocious.mjs +2 -2
  4. package/index.mjs +0 -2
  5. package/package.json +2 -1
  6. package/peak_flow.yml +1 -1
  7. package/spec/cli/commands/db/create-spec.mjs +1 -1
  8. package/spec/dummy/index.mjs +6 -4
  9. package/spec/dummy/src/config/configuration.example.mjs +5 -3
  10. package/spec/dummy/src/config/configuration.peakflow.mjs +5 -3
  11. package/spec/http-server/client-spec.mjs +5 -0
  12. package/src/application.mjs +5 -0
  13. package/src/cli/base-command.mjs +1 -1
  14. package/src/cli/commands/db/migrate.mjs +52 -5
  15. package/src/cli/commands/destroy/migration.mjs +1 -1
  16. package/src/cli/commands/generate/migration.mjs +1 -1
  17. package/src/cli/commands/generate/model.mjs +36 -0
  18. package/src/cli/commands/init.mjs +1 -1
  19. package/src/cli/commands/server.mjs +15 -0
  20. package/src/cli/index.mjs +1 -0
  21. package/src/configuration-resolver.mjs +2 -2
  22. package/src/configuration.mjs +35 -19
  23. package/src/controller.mjs +1 -1
  24. package/src/database/drivers/{sqlite-expo/index.mjs → sqlite/index.native.mjs} +1 -9
  25. package/src/database/drivers/sqlite/index.web.mjs +64 -0
  26. package/src/database/drivers/sqlite/query.web.mjs +9 -0
  27. package/src/database/migrate-from-require-context.mjs +53 -0
  28. package/src/database/migration/index.mjs +1 -1
  29. package/src/database/pool/async-tracked-multi-connection.mjs +81 -0
  30. package/src/database/pool/base.mjs +47 -0
  31. package/src/database/pool/single-multi-use.mjs +40 -0
  32. package/src/database/query/create-database-base.mjs +3 -1
  33. package/src/database/record/index.mjs +2 -2
  34. package/src/http-server/worker-handler/index.mjs +2 -1
  35. package/src/http-server/worker-handler/worker-thread.mjs +0 -1
  36. package/src/routes/app-routes.mjs +10 -0
  37. package/src/routes/resolver.mjs +1 -1
  38. package/src/templates/configuration.mjs +6 -4
  39. package/src/templates/generate-migration.mjs +2 -2
  40. package/src/templates/generate-model.mjs +4 -0
  41. package/src/templates/routes.mjs +1 -1
  42. package/src/database/drivers/sqlite/sql/create-database.mjs +0 -4
  43. package/src/database/pool/index.mjs +0 -112
  44. /package/src/database/drivers/{sqlite-expo/query.mjs → sqlite/query.native.mjs} +0 -0
@@ -5,5 +5,5 @@ updates:
5
5
  schedule:
6
6
  interval: weekly
7
7
  time: "01:00"
8
- timezone: Europe/Copenhagen
8
+ timezone: Europe/Berlin
9
9
  open-pull-requests-limit: 99
package/README.md CHANGED
@@ -22,13 +22,19 @@ npx velocious init
22
22
  # Migrations
23
23
 
24
24
  ```bash
25
- npx velocious db:g:migration create_tasks
25
+ npx velocious g:migration create_tasks
26
26
  ```
27
27
 
28
28
  ```bash
29
29
  npx velocious db:migrate
30
30
  ```
31
31
 
32
+ # Models
33
+
34
+ ```bash
35
+ npx velocious g:model Option
36
+ ```
37
+
32
38
  # Testing
33
39
 
34
40
  ```bash
package/bin/velocious.mjs CHANGED
@@ -1,8 +1,8 @@
1
- #!/usr/bin/node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import Cli from "../src/cli/index.mjs"
4
4
 
5
5
  const processArgs = process.argv.slice(2)
6
6
  const cli = new Cli({processArgs})
7
7
 
8
- cli.execute()
8
+ await cli.execute()
package/index.mjs CHANGED
@@ -3,7 +3,6 @@ import Cli from "./src/cli/index.mjs"
3
3
  import Configuration from "./src/configuration.mjs"
4
4
  import Controller from "./src/controller.mjs"
5
5
  import Database from "./src/database/index.mjs"
6
- import DatabasePool from "./src/database/pool/index.mjs"
7
6
  import HttpServer from "./src/http-server/index.mjs"
8
7
  import Routes from "./src/routes/index.mjs"
9
8
  import Spec from "./src/spec/index.mjs"
@@ -14,7 +13,6 @@ export {
14
13
  Configuration,
15
14
  Controller,
16
15
  Database,
17
- DatabasePool,
18
16
  HttpServer,
19
17
  Routes,
20
18
  Spec
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "velocious": "bin/velocious.mjs"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.3",
6
+ "version": "1.0.4",
7
7
  "main": "index.mjs",
8
8
  "scripts": {
9
9
  "test": "jasmine",
@@ -32,6 +32,7 @@
32
32
  "escape-string-regexp": "^1.0.5",
33
33
  "incorporator": "^1.0.2",
34
34
  "inflection": "^3.0.0",
35
+ "sql.js": "^1.12.0",
35
36
  "strftime": "^0.10.2"
36
37
  }
37
38
  }
package/peak_flow.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  before_install:
2
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
3
+ - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --no-tty --batch --yes --dearmor -o /etc/apt/keyrings/nodesource.gpg
4
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
5
  - sudo apt-get update
6
6
  - sudo apt-get install -y nodejs
@@ -14,7 +14,7 @@ describe("Cli - Commands - db:create", () => {
14
14
  [
15
15
  {
16
16
  databaseName: 'velocious_test',
17
- sql: 'CREATE DATABASE IF NOT EXISTS velocious_test'
17
+ sql: 'CREATE DATABASE IF NOT EXISTS `velocious_test`'
18
18
  },
19
19
  {
20
20
  createSchemaMigrationsTableSql: 'CREATE TABLE IF NOT EXISTS schema_migrations (`version` varchar(255) PRIMARY KEY)'
@@ -3,11 +3,11 @@ import dummyConfiguration from "./src/config/configuration.mjs"
3
3
 
4
4
  export default class Dummy {
5
5
  static current() {
6
- if (!global.velociousDummy) {
7
- global.velociousDummy = new Dummy()
6
+ if (!this.velociousDummy) {
7
+ this.velociousDummy = new Dummy()
8
8
  }
9
9
 
10
- return global.velociousDummy
10
+ return this.velociousDummy
11
11
  }
12
12
 
13
13
  static async prepare() {
@@ -37,7 +37,9 @@ export default class Dummy {
37
37
  }
38
38
 
39
39
  async start() {
40
- if (!dummyConfiguration.isDatabasePoolInitialized()) await dummyConfiguration.initializeDatabasePool()
40
+ if (!dummyConfiguration.isDatabasePoolInitialized()) {
41
+ await dummyConfiguration.initializeDatabasePool()
42
+ }
41
43
 
42
44
  this.application = new Application({
43
45
  configuration: dummyConfiguration,
@@ -1,10 +1,14 @@
1
+ import AsyncTrackedMultiConnection from "../../../../src/database/pool/async-tracked-multi-connection.mjs"
1
2
  import Configuration from "../../../../src/configuration.mjs"
2
3
  import dummyDirectory from "../../dummy-directory.mjs"
4
+ import MysqlDriver from "../../../../src/database/drivers/mysql/index.mjs"
3
5
 
4
- const configuration = new Configuration({
6
+ export default new Configuration({
5
7
  database: {
6
8
  default: {
7
9
  master: {
10
+ driver: MysqlDriver,
11
+ poolType: AsyncTrackedMultiConnection,
8
12
  type: "mysql",
9
13
  host: "mariadb",
10
14
  username: "username",
@@ -15,5 +19,3 @@ const configuration = new Configuration({
15
19
  },
16
20
  directory: dummyDirectory()
17
21
  })
18
-
19
- export default configuration
@@ -1,10 +1,14 @@
1
+ import AsyncTrackedMultiConnection from "../../../../src/database/pool/async-tracked-multi-connection.mjs"
1
2
  import Configuration from "../../../../src/configuration.mjs"
2
3
  import dummyDirectory from "../../dummy-directory.mjs"
4
+ import MysqlDriver from "../../../../src/database/drivers/mysql/index.mjs"
3
5
 
4
- const configuration = new Configuration({
6
+ export default new Configuration({
5
7
  database: {
6
8
  default: {
7
9
  master: {
10
+ driver: MysqlDriver,
11
+ poolType: AsyncTrackedMultiConnection,
8
12
  type: "mysql",
9
13
  host: "mariadb",
10
14
  username: "peakflow",
@@ -16,5 +20,3 @@ const configuration = new Configuration({
16
20
  },
17
21
  directory: dummyDirectory()
18
22
  })
19
-
20
- export default configuration
@@ -1,3 +1,4 @@
1
+ import AppRoutes from "../../src/routes/app-routes.mjs"
1
2
  import Client from "../../src/http-server/client/index.mjs"
2
3
  import {digg} from "diggerize"
3
4
  import dummyConfiguration from "../dummy/src/config/configuration.mjs"
@@ -6,6 +7,10 @@ describe("http server - client", () => {
6
7
  it("spawns a request for each that it is fed", async () => {
7
8
  await dummyConfiguration.initialize()
8
9
 
10
+ const routes = await AppRoutes.getRoutes(dummyConfiguration)
11
+
12
+ dummyConfiguration.setRoutes(routes)
13
+
9
14
  const client = new Client({
10
15
  clientCount: 0,
11
16
  configuration: dummyConfiguration
@@ -1,3 +1,4 @@
1
+ import AppRoutes from "../src/routes/app-routes.mjs"
1
2
  import {digs} from "diggerize"
2
3
  import logger from "./logger.mjs"
3
4
  import HttpServer from "./http-server/index.mjs"
@@ -9,8 +10,12 @@ export default class VelociousApplication {
9
10
  }
10
11
 
11
12
  async initialize() {
13
+ const routes = await AppRoutes.getRoutes(this.configuration)
14
+
12
15
  await this.configuration.initialize()
13
16
 
17
+ this.configuration.setRoutes(routes)
18
+
14
19
  if (!this.configuration.isDatabasePoolInitialized()) {
15
20
  await this.configuration.initializeDatabasePool()
16
21
  }
@@ -7,5 +7,5 @@ export default class VelociousCliBaseCommand {
7
7
  this.processArgs = args.processArgs
8
8
  }
9
9
 
10
- directory = () => digg(this, "configuration", "directory")
10
+ directory = () => digg(this, "configuration").getDirectory()
11
11
  }
@@ -5,7 +5,7 @@ import inflection from "inflection"
5
5
 
6
6
  export default class DbMigrate extends BaseCommand {
7
7
  async execute() {
8
- const projectPath = digg(this.configuration, "directory")
8
+ const projectPath = this.configuration.getDirectory()
9
9
  const migrationsPath = `${projectPath}/src/database/migrations`
10
10
  let files = await fs.readdir(migrationsPath)
11
11
 
@@ -34,12 +34,59 @@ export default class DbMigrate extends BaseCommand {
34
34
  }
35
35
  }
36
36
 
37
- async runMigrationFile(migration) {
38
- if (!this.configuration.isDatabasePoolInitialized()) {
39
- await this.configuration.initializeDatabasePool()
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)
40
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()
41
88
 
42
- await this.configuration.databasePool.withConnection(async () => {
89
+ await this.configuration.getDatabasePool().withConnection(async () => {
43
90
  const migrationImport = await import(migration.fullPath)
44
91
  const MigrationClass = migrationImport.default
45
92
  const migrationInstance = new MigrationClass({
@@ -4,7 +4,7 @@ import fs from "node:fs/promises"
4
4
  export default class DbDestroyMigration extends BaseCommand {
5
5
  async execute() {
6
6
  const migrationName = this.processArgs[1]
7
- const migrationDir = `${this.configuration.directory}/src/database/migrations`
7
+ const migrationDir = `${this.configuration.getDirectory()}/src/database/migrations`
8
8
  const migrationFiles = await fs.readdir(migrationDir)
9
9
  const destroyed = []
10
10
 
@@ -22,7 +22,7 @@ export default class DbGenerateMigration extends BaseCommand {
22
22
  const migrationPath = `${migrationDir}/${migrationFileName}`
23
23
 
24
24
  if (this.args.testing) {
25
- return {date, migrationContent, migrationName, migrationNameCamelized, migrationNumber, migrationPath }
25
+ return {date, migrationContent, migrationName, migrationNameCamelized, migrationNumber, migrationPath}
26
26
  } else {
27
27
  if (!await fileExists(migrationDir)) {
28
28
  await fs.mkdir(migrationDir, {recursive: true})
@@ -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
+ }
@@ -8,7 +8,7 @@ export default class VelociousCliCommandsInit extends BaseCommand {
8
8
  async execute() {
9
9
  const __filename = fileURLToPath(`${import.meta.url}/../../..`)
10
10
  const velocipusPath = dirname(__filename)
11
- const projectPath = this.configuration?.directory || process.cwd()
11
+ const projectPath = this.configuration?.getDirectory() || process.cwd()
12
12
  const projectConfigPath = `${projectPath}/src/config`
13
13
  const fileMappings = [
14
14
  {
@@ -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
+ }
package/src/cli/index.mjs CHANGED
@@ -18,6 +18,7 @@ export default class VelociousCli {
18
18
  for (let commandPart of commandParts) {
19
19
  if (commandPart == "d") commandPart = "destroy"
20
20
  if (commandPart == "g") commandPart = "generate"
21
+ if (commandPart == "s") commandPart = "server"
21
22
 
22
23
  filePath += `/${commandPart}`
23
24
  }
@@ -1,8 +1,8 @@
1
1
  import Configuration from "./configuration.mjs"
2
2
 
3
3
  const configurationResolver = async (args) => {
4
- if (global.velociousConfiguration) {
5
- return global.velociousConfiguration
4
+ if (Configuration.current(false)) {
5
+ return Configuration.current()
6
6
  }
7
7
 
8
8
  const directory = args.directory || process.cwd()
@@ -1,49 +1,65 @@
1
- import DatabasePool from "./database/pool/index.mjs"
2
1
  import {digg} from "diggerize"
3
2
 
4
3
  export default class VelociousConfiguration {
5
- static current() {
6
- 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")
7
6
 
8
- return global.velociousConfiguration
7
+ return this.velociousConfiguration
9
8
  }
10
9
 
11
10
  constructor({database, debug, directory}) {
12
- if (!directory) directory = process.cwd()
13
-
14
11
  this.database = database
15
12
  this.debug = debug
16
- this.directory = directory
17
- }
18
-
19
- async initialize() {
20
- await this.initializeRoutes()
13
+ this._directory = directory
21
14
  }
22
15
 
23
16
  getDatabasePool() {
24
- if (!this.isDatabasePoolInitialized()) this.initializeDatabasePool()
17
+ if (!this.isDatabasePoolInitialized()) {
18
+ this.initializeDatabasePool()
19
+ }
25
20
 
26
21
  return this.databasePool
27
22
  }
28
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
40
+ }
41
+
29
42
  initializeDatabasePool() {
30
43
  if (!this.database) throw new Error("No 'database' was given")
31
44
  if (this.databasePool) throw new Error("DatabasePool has already been initialized")
32
45
 
33
- this.databasePool = new DatabasePool({configuration: this})
46
+ const PoolType = this.getDatabasePoolType()
47
+
48
+ this.databasePool = new PoolType({configuration: this})
34
49
  this.databasePool.setCurrent()
35
50
  }
36
51
 
37
52
  isDatabasePoolInitialized = () => Boolean(this.databasePool)
38
53
 
39
- async initializeRoutes() {
40
- // Every client need to make their own routes because they probably can't be shared across different worker threads
41
- const routesImport = await import(`${this.directory}/src/config/routes.mjs`)
42
-
43
- this.routes = digg(routesImport, "default", "routes")
54
+ initialize() {
55
+ // Doesn't currently do anything.
44
56
  }
45
57
 
46
58
  setCurrent() {
47
- global.velociousConfiguration = this
59
+ this.constructor.velociousConfiguration = this
60
+ }
61
+
62
+ setRoutes(newRoutes) {
63
+ this.routes = newRoutes
48
64
  }
49
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
 
@@ -1,5 +1,4 @@
1
1
  import Base from "../base.mjs"
2
- import CreateDatabase from "../sqlite/sql/create-database.mjs"
3
2
  import CreateTable from "../sqlite/sql/create-table.mjs"
4
3
  import Delete from "../sqlite/sql/delete.mjs"
5
4
  import {digg} from "diggerize"
@@ -10,7 +9,7 @@ import QueryParser from "../sqlite/query-parser.mjs"
10
9
  import * as SQLite from "expo-sqlite"
11
10
  import Update from "../sqlite/sql/update.mjs"
12
11
 
13
- export default class VelociousDatabaseDriversMysql extends Base{
12
+ export default class VelociousDatabaseDriversSqliteNative extends Base{
14
13
  async connect() {
15
14
  const connection = await SQLite.openDatabaseAsync(digg(this.connectArgs(), "name"))
16
15
 
@@ -40,13 +39,6 @@ export default class VelociousDatabaseDriversMysql extends Base{
40
39
  this.connection = undefined
41
40
  }
42
41
 
43
- createDatabaseSql(databaseName, args) {
44
- const createArgs = Object.assign({databaseName, driver: this}, args)
45
- const createDatabase = new CreateDatabase(createArgs)
46
-
47
- return createDatabase.toSql()
48
- }
49
-
50
42
  createTableSql(tableData) {
51
43
  const createArgs = Object.assign({tableData, driver: this})
52
44
  const createTable = new CreateTable(createArgs)
@@ -0,0 +1,64 @@
1
+ import Base from "../base.mjs"
2
+ import CreateTable from "../sqlite/sql/create-table.mjs"
3
+ import Delete from "../sqlite/sql/delete.mjs"
4
+ import {digg} from "diggerize"
5
+ import Insert from "../sqlite/sql/insert.mjs"
6
+ import Options from "../sqlite/options.mjs"
7
+ import query from "./query"
8
+ import QueryParser from "../sqlite/query-parser.mjs"
9
+ import Update from "../sqlite/sql/update.mjs"
10
+
11
+ import initSqlJs from "sql.js"
12
+
13
+ export default class VelociousDatabaseDriversSqliteWeb extends Base{
14
+ async connect() {
15
+ const SQL = await initSqlJs({
16
+ // Required to load the wasm binary asynchronously. Of course, you can host it wherever you want you can omit locateFile completely when running in Node.
17
+ locateFile: (file) => `https://sql.js.org/dist/${file}`
18
+ })
19
+
20
+ const databaseContent = localStorage.getItem(this.localStorageName())
21
+
22
+ this.connection = new SQL.Database(databaseContent)
23
+ }
24
+
25
+ localStorageName = () => `VelociousDatabaseDriversSqliteWeb---${digg(this.getArgs(), "name")}`
26
+ disconnect = () => this.saveDatabase()
27
+ saveDatabase = () => localStorage.setItem(this.localStorageName(), this.connection.export())
28
+
29
+ async close() {
30
+ this.saveDatabase()
31
+ await this.connection.end()
32
+ this.connection = undefined
33
+ }
34
+
35
+ createTableSql(tableData) {
36
+ const createArgs = Object.assign({tableData, driver: this})
37
+ const createTable = new CreateTable(createArgs)
38
+
39
+ return createTable.toSql()
40
+ }
41
+
42
+ query = async (sql) => await query(this.connection, sql)
43
+ queryToSql = (query) => new QueryParser({query}).toSql()
44
+
45
+ quote(string) {
46
+ if (!this.connection) throw new Error("Can't escape before connected")
47
+
48
+ return this.connection.escape(string)
49
+ }
50
+
51
+ quoteColumn = (string) => `\`${string}\``
52
+ deleteSql = ({tableName, conditions}) => new Delete({conditions, driver: this, tableName}).toSql()
53
+ insertSql = ({tableName, data}) => new Insert({driver: this, tableName, data}).toSql()
54
+
55
+ options() {
56
+ if (!this._options) {
57
+ this._options = new Options({driver: this})
58
+ }
59
+
60
+ return this._options
61
+ }
62
+
63
+ updateSql = ({conditions, data, tableName}) => new Update({conditions, data, driver: this, tableName}).toSql()
64
+ }
@@ -0,0 +1,9 @@
1
+ export default async function query(connection, sql) {
2
+ const rows = []
3
+
4
+ for await (const entry of connection.exec(sql)) {
5
+ rows.push(entry)
6
+ }
7
+
8
+ return rows
9
+ }
@@ -0,0 +1,53 @@
1
+ import Configuration from "../configuration.mjs"
2
+ import * as inflection from "inflection"
3
+
4
+ export default class VelociousDatabaseMigrateFromRequireContext {
5
+ constructor(configuration) {
6
+ this.configuration = configuration || Configuration.current()
7
+ }
8
+
9
+ async execute(requireContext) {
10
+ const files = requireContext.keys()
11
+ .map((file) => {
12
+ const match = file.match(/^\.\/(\d{14})-(.+)\.mjs$/)
13
+
14
+ if (!match) return null
15
+
16
+ const date = parseInt(match[1])
17
+ const migrationName = match[2]
18
+ const migrationClassName = inflection.camelize(migrationName)
19
+
20
+ return {
21
+ file,
22
+ date,
23
+ migrationClassName
24
+ }
25
+ })
26
+ .filter((migration) => Boolean(migration))
27
+ .sort((migration1, migration2) => migration1.date - migration2.date)
28
+
29
+ for (const migration of files) {
30
+ await this.runMigrationFile(migration, requireContext)
31
+ }
32
+ }
33
+
34
+ async runMigrationFile(migration, requireContext) {
35
+ if (!this.configuration) throw new Error("No configuration set")
36
+ if (!this.configuration.isDatabasePoolInitialized()) await this.configuration.initializeDatabasePool()
37
+
38
+ await this.configuration.getDatabasePool().withConnection(async () => {
39
+ const MigrationClass = requireContext(migration.file).default
40
+ const migrationInstance = new MigrationClass({
41
+ configuration: this.configuration
42
+ })
43
+
44
+ if (migrationInstance.change) {
45
+ await migrationInstance.change()
46
+ } else if (migrationInstance.up) {
47
+ await migrationInstance.up()
48
+ } else {
49
+ throw new Error(`'change' or 'up' didn't exist on migration: ${migration.file}`)
50
+ }
51
+ })
52
+ }
53
+ }
@@ -10,7 +10,7 @@ export default class VelociousDatabaseMigration {
10
10
 
11
11
  callback(tableData)
12
12
 
13
- const databasePool = this.configuration.databasePool
13
+ const databasePool = this.configuration.getDatabasePool()
14
14
  const sql = databasePool.createTableSql(tableData)
15
15
 
16
16
  await databasePool.query(sql)
@@ -0,0 +1,81 @@
1
+ import {AsyncLocalStorage} from "async_hooks"
2
+ import BasePool from "./base.mjs"
3
+
4
+ let idSeq = 0
5
+
6
+ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends BasePool {
7
+ static current() {
8
+ if (!this.velociousDatabasePoolAsyncTrackedMultiConnection) {
9
+ this.velociousDatabasePoolAsyncTrackedMultiConnection = new VelociousDatabasePoolAsyncTrackedMultiConnection()
10
+ }
11
+
12
+ return this.velociousDatabasePoolAsyncTrackedMultiConnection
13
+ }
14
+
15
+ constructor(args = {}) {
16
+ super(args)
17
+ this.connections = []
18
+ this.connectionsInUse = {}
19
+ this.asyncLocalStorage = new AsyncLocalStorage()
20
+ }
21
+
22
+ checkin = (connection) => {
23
+ const id = connection.getIdSeq()
24
+
25
+ if (id in this.connectionsInUse) {
26
+ delete this.connectionsInUse[id]
27
+ }
28
+
29
+ connection.setIdSeq(undefined)
30
+
31
+ this.connections.push(connection)
32
+ }
33
+
34
+ async checkout() {
35
+ let connection = this.connections.shift()
36
+
37
+ if (!connection) {
38
+ connection = await this.spawnConnection()
39
+ }
40
+
41
+ if (connection.getIdSeq() !== undefined) throw new Error(`Connection already has an ID-seq - is it in use? ${connection.getIdSeq()}`)
42
+
43
+ const id = idSeq++
44
+
45
+ connection.setIdSeq(id)
46
+ this.connectionsInUse[id] = connection
47
+
48
+ return connection
49
+ }
50
+
51
+ setCurrent() {
52
+ this.constructor.velociousDatabasePoolAsyncTrackedMultiConnection = this
53
+ }
54
+
55
+ async withConnection(callback) {
56
+ const connection = await this.checkout()
57
+ const id = connection.getIdSeq()
58
+
59
+ await this.asyncLocalStorage.run(id, async () => {
60
+ try {
61
+ await callback()
62
+ } finally {
63
+ this.checkin(connection)
64
+ }
65
+ })
66
+ }
67
+
68
+ getCurrentConnection() {
69
+ const id = this.asyncLocalStorage.getStore()
70
+
71
+ if (id === undefined) {
72
+ throw new Error("ID hasn't been set for this async context")
73
+ }
74
+
75
+ if (!(id in this.connectionsInUse)) {
76
+ throw new Error(`Connection ${id} doesn't exist any more - has it been checked in again?`)
77
+ }
78
+
79
+ return this.connectionsInUse[id]
80
+ }
81
+ }
@@ -0,0 +1,47 @@
1
+ import Configuration from "../../configuration.mjs"
2
+ import {digg} from "diggerize"
3
+
4
+ class VelociousDatabasePoolBase {
5
+ constructor(args = {}) {
6
+ this.configuration = args.configuration || Configuration.current()
7
+ this.connections = []
8
+ this.connectionsInUse = {}
9
+ }
10
+
11
+ getConfiguration = () => digg(this, "configuration", "database", "default", "master")
12
+
13
+ setDriverClass(driverClass) {
14
+ this.driverClass = driverClass
15
+ }
16
+
17
+ async spawnConnection() {
18
+ const defaultConfig = this.getConfiguration()
19
+ const connection = await this.spawnConnectionWithConfiguration(defaultConfig)
20
+
21
+ return connection
22
+ }
23
+
24
+ async spawnConnectionWithConfiguration(config) {
25
+ const DriverClass = config.driver || this.driverClass
26
+
27
+ if (!DriverClass) throw new Error("No driver class set in database pool or in given config")
28
+
29
+ const connection = new DriverClass(config)
30
+
31
+ await connection.connect()
32
+
33
+ return connection
34
+ }
35
+ }
36
+
37
+ const forwardMethods = ["createTableSql", "deleteSql", "insertSql", "query", "quote", "updateSql"]
38
+
39
+ for (const forwardMethod of forwardMethods) {
40
+ VelociousDatabasePoolBase.prototype[forwardMethod] = function(...args) {
41
+ const connection = this.getCurrentConnection()
42
+
43
+ return connection[forwardMethod](...args)
44
+ }
45
+ }
46
+
47
+ export default VelociousDatabasePoolBase
@@ -0,0 +1,40 @@
1
+ import BasePool from "./base.mjs"
2
+
3
+ export default class VelociousDatabasePoolSingleMultiUser extends BasePool {
4
+ static current() {
5
+ if (!this.velociousDatabasePoolSingleMultiUser) {
6
+ this.velociousDatabasePoolSingleMultiUser = new VelociousDatabasePoolSingleMultiUser()
7
+ }
8
+
9
+ return this.velociousDatabasePoolSingleMultiUser
10
+ }
11
+
12
+ checkin = (connection) => {
13
+ // Do nothing
14
+ }
15
+
16
+ async checkout() {
17
+ if (!this.connection) {
18
+ this.connection = await this.spawnConnection()
19
+ }
20
+
21
+ return this.connection
22
+ }
23
+
24
+ setCurrent() {
25
+ this.constructor.velociousDatabasePoolSingleMultiUser = this
26
+ }
27
+
28
+ async withConnection(callback) {
29
+ await this.checkout() // Ensure a connection is present
30
+ await callback()
31
+ }
32
+
33
+ getCurrentConnection() {
34
+ if (!this.connection) {
35
+ throw new Error("A connection hasn't been made yet")
36
+ }
37
+
38
+ return this.connection
39
+ }
40
+ }
@@ -1,3 +1,4 @@
1
+ import {digs} from "diggerize"
1
2
  import QueryBase from "./base.mjs"
2
3
 
3
4
  export default class VelociousDatabaseQueryCreateDatabaseBase extends QueryBase {
@@ -9,11 +10,12 @@ export default class VelociousDatabaseQueryCreateDatabaseBase extends QueryBase
9
10
 
10
11
  toSql() {
11
12
  const {databaseName} = this
13
+ const {tableQuote} = digs(this.getOptions(), "tableQuote")
12
14
  let sql = "CREATE DATABASE"
13
15
 
14
16
  if (this.ifNotExists) sql += " IF NOT EXISTS"
15
17
 
16
- sql += ` ${databaseName}`
18
+ sql += ` ${tableQuote}${databaseName}${tableQuote}`
17
19
 
18
20
  return sql
19
21
  }
@@ -1,4 +1,4 @@
1
- import DatabasePool from "../pool/index.mjs"
1
+ import Configuration from "../../configuration.mjs"
2
2
  import Handler from "../handler.mjs"
3
3
  import inflection from "inflection"
4
4
  import Query from "../query/index.mjs"
@@ -6,7 +6,7 @@ import RecordNotFoundError from "./record-not-found-error.mjs"
6
6
 
7
7
  export default class VelociousDatabaseRecord {
8
8
  static connection() {
9
- const connection = DatabasePool.current().getCurrentConnection()
9
+ const connection = Configuration.current().getDatabasePoolType().current().getCurrentConnection()
10
10
 
11
11
  if (!connection) throw new Error("No connection?")
12
12
 
@@ -13,7 +13,8 @@ export default class VelociousHttpServerWorker {
13
13
 
14
14
  async start() {
15
15
  return new Promise((resolve) => {
16
- const {debug, directory} = digs(this.configuration, "debug", "directory")
16
+ const {debug} = digs(this.configuration, "debug")
17
+ const directory = this.configuration.getDirectory()
17
18
  const __filename = fileURLToPath(import.meta.url)
18
19
  const __dirname = dirname(__filename)
19
20
 
@@ -1,6 +1,5 @@
1
1
  import Application from "../../application.mjs"
2
2
  import Client from "../client/index.mjs"
3
- import DatabasePool from "../../database/pool/index.mjs"
4
3
  import {digg, digs} from "diggerize"
5
4
  import errorLogger from "../../error-logger.mjs"
6
5
  import logger from "../../logger.mjs"
@@ -0,0 +1,10 @@
1
+ import {digg} from "diggerize"
2
+
3
+ export default class VelociousRoutesAppRoutes {
4
+ static async getRoutes(configuration) {
5
+ // Every client need to make their own routes because they probably can't be shared across different worker threads
6
+ const routesImport = await import(`${configuration.getDirectory()}/src/config/routes.mjs`)
7
+
8
+ return digg(routesImport, "default", "routes")
9
+ }
10
+ }
@@ -21,7 +21,7 @@ export default class VelociousRoutesResolver {
21
21
  if (!matchResult) throw new Error(`Couldn't match a route with the given path: ${currentPath}`)
22
22
 
23
23
  if (this.params.action && this.params.controller) {
24
- const controllerPath = `${digg(this, "configuration", "directory")}/src/routes/${digg(this, "params", "controller")}/controller.mjs`
24
+ const controllerPath = `${this.configuration.getDirectory()}/src/routes/${digg(this, "params", "controller")}/controller.mjs`
25
25
  const controllerClassImport = await import(controllerPath)
26
26
  const controllerClass = controllerClassImport.default
27
27
  const controllerInstance = new controllerClass({
@@ -1,9 +1,13 @@
1
- import {Configuration} from "velocious"
1
+ import AsyncTrackedMultiConnection from "velocious/src/database/pool/async-tracked-multi-connection.mjs"
2
+ import Configuration from "velocious/src/configuration.mjs"
3
+ import MysqlDriver from "velocious/src/database/drivers/mysql/index.mjs"
2
4
 
3
- const configuration = new Configuration({
5
+ export default new Configuration({
4
6
  database: {
5
7
  default: {
6
8
  master: {
9
+ driver: MysqlDriver,
10
+ poolType: AsyncTrackedMultiConnection,
7
11
  type: "mysql",
8
12
  host: "mariadb",
9
13
  username: "username",
@@ -13,5 +17,3 @@ const configuration = new Configuration({
13
17
  }
14
18
  }
15
19
  })
16
-
17
- export default configuration
@@ -1,6 +1,6 @@
1
- import {Database} from "velocious"
1
+ import Migration from "velocious/src/database/migration/index.mjs"
2
2
 
3
- export default class __MIGRATION_NAME__ extends Database.Migration {
3
+ export default class __MIGRATION_NAME__ extends Migration {
4
4
  async up() {
5
5
  await this.connection().execute("...")
6
6
  }
@@ -0,0 +1,4 @@
1
+ import Record from "velocious/src/database/record/index.mjs"
2
+
3
+ export default class __MODEL_NAME__ extends Record {
4
+ }
@@ -1,4 +1,4 @@
1
- import {Routes} from "velocious"
1
+ import Routes from "velocious/src/routes/index.mjs"
2
2
 
3
3
  const routes = new Routes()
4
4
 
@@ -1,4 +0,0 @@
1
- import CreateDatabaseBase from "../../../query/create-database-base.mjs"
2
-
3
- export default class VelociousDatabaseConnectionDriversMysqlSqlCreateDatabase extends CreateDatabaseBase {
4
- }
@@ -1,112 +0,0 @@
1
- import {AsyncLocalStorage} from "node:async_hooks"
2
- import Configuration from "../../configuration.mjs"
3
- import {digg} from "diggerize"
4
-
5
- const asyncLocalStorage = new AsyncLocalStorage()
6
- let idSeq = 0
7
-
8
- class VelociousDatabasePool {
9
- static current() {
10
- if (!global.velociousDatabasePool) global.velociousDatabasePool = new VelociousDatabasePool()
11
-
12
- return global.velociousDatabasePool
13
- }
14
-
15
- constructor(args = {}) {
16
- this.configuration = args.configuration || Configuration.current()
17
- this.connections = []
18
- this.connectionsInUse = {}
19
- }
20
-
21
- checkin = (connection) => {
22
- const id = connection.getIdSeq()
23
-
24
- if (id in this.connectionsInUse) {
25
- delete this.connectionsInUse[id]
26
- }
27
-
28
- connection.setIdSeq(undefined)
29
-
30
- this.connections.push(connection)
31
- }
32
-
33
- async checkout() {
34
- let connection = this.connections.shift()
35
-
36
- if (!connection) {
37
- connection = await this.spawnConnection()
38
- }
39
-
40
- if (connection.getIdSeq() !== undefined) throw new Error(`Connection already has an ID-seq - is it in use? ${connection.getIdSeq()}`)
41
-
42
- const id = idSeq++
43
-
44
- connection.setIdSeq(id)
45
- this.connectionsInUse[id] = connection
46
-
47
- return connection
48
- }
49
-
50
- getConfiguration = () => digg(this, "configuration", "database", "default", "master")
51
-
52
- setCurrent() {
53
- global.velociousDatabasePool = this
54
- }
55
-
56
- async spawnConnection() {
57
- const defaultConfig = this.getConfiguration()
58
- const connection = await this.spawnConnectionWithConfiguration(defaultConfig)
59
-
60
- return connection
61
- }
62
-
63
- async spawnConnectionWithConfiguration(config) {
64
- const driverPath = `../drivers/${digg(config, "type")}/index.mjs`
65
- const DriverClassImport = await import(driverPath)
66
- const DriverClass = DriverClassImport.default
67
- const connection = new DriverClass(config)
68
-
69
- await connection.connect()
70
-
71
- return connection
72
- }
73
-
74
- async withConnection(callback) {
75
- const connection = await this.checkout()
76
- const id = connection.getIdSeq()
77
-
78
- await asyncLocalStorage.run(id, async () => {
79
- try {
80
- await callback()
81
- } finally {
82
- this.checkin(connection)
83
- }
84
- })
85
- }
86
-
87
- getCurrentConnection() {
88
- const id = asyncLocalStorage.getStore()
89
-
90
- if (id === undefined) {
91
- throw new Error("ID hasn't been set for this async context")
92
- }
93
-
94
- if (!(id in this.connectionsInUse)) {
95
- throw new Error(`Connection ${id} doesn't exist any more - has it been checked in again?`)
96
- }
97
-
98
- return this.connectionsInUse[id]
99
- }
100
- }
101
-
102
- const forwardMethods = ["createTableSql", "deleteSql", "insertSql", "query", "quote", "updateSql"]
103
-
104
- for (const forwardMethod of forwardMethods) {
105
- VelociousDatabasePool.prototype[forwardMethod] = function(...args) {
106
- const connection = this.getCurrentConnection()
107
-
108
- return connection[forwardMethod](...args)
109
- }
110
- }
111
-
112
- export default VelociousDatabasePool