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.
- package/.github/dependabot.yml +1 -1
- package/README.md +7 -1
- package/bin/velocious.mjs +2 -2
- package/index.mjs +0 -2
- package/package.json +2 -1
- package/peak_flow.yml +1 -1
- package/spec/cli/commands/db/create-spec.mjs +1 -1
- package/spec/dummy/index.mjs +6 -4
- package/spec/dummy/src/config/configuration.example.mjs +5 -3
- package/spec/dummy/src/config/configuration.peakflow.mjs +5 -3
- package/spec/http-server/client-spec.mjs +5 -0
- package/src/application.mjs +5 -0
- package/src/cli/base-command.mjs +1 -1
- package/src/cli/commands/db/migrate.mjs +52 -5
- package/src/cli/commands/destroy/migration.mjs +1 -1
- package/src/cli/commands/generate/migration.mjs +1 -1
- package/src/cli/commands/generate/model.mjs +36 -0
- package/src/cli/commands/init.mjs +1 -1
- package/src/cli/commands/server.mjs +15 -0
- package/src/cli/index.mjs +1 -0
- package/src/configuration-resolver.mjs +2 -2
- package/src/configuration.mjs +35 -19
- package/src/controller.mjs +1 -1
- package/src/database/drivers/{sqlite-expo/index.mjs → sqlite/index.native.mjs} +1 -9
- package/src/database/drivers/sqlite/index.web.mjs +64 -0
- package/src/database/drivers/sqlite/query.web.mjs +9 -0
- package/src/database/migrate-from-require-context.mjs +53 -0
- package/src/database/migration/index.mjs +1 -1
- package/src/database/pool/async-tracked-multi-connection.mjs +81 -0
- package/src/database/pool/base.mjs +47 -0
- package/src/database/pool/single-multi-use.mjs +40 -0
- package/src/database/query/create-database-base.mjs +3 -1
- package/src/database/record/index.mjs +2 -2
- package/src/http-server/worker-handler/index.mjs +2 -1
- package/src/http-server/worker-handler/worker-thread.mjs +0 -1
- package/src/routes/app-routes.mjs +10 -0
- package/src/routes/resolver.mjs +1 -1
- package/src/templates/configuration.mjs +6 -4
- package/src/templates/generate-migration.mjs +2 -2
- package/src/templates/generate-model.mjs +4 -0
- package/src/templates/routes.mjs +1 -1
- package/src/database/drivers/sqlite/sql/create-database.mjs +0 -4
- package/src/database/pool/index.mjs +0 -112
- /package/src/database/drivers/{sqlite-expo/query.mjs → sqlite/query.native.mjs} +0 -0
package/.github/dependabot.yml
CHANGED
package/README.md
CHANGED
|
@@ -22,13 +22,19 @@ npx velocious init
|
|
|
22
22
|
# Migrations
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
npx velocious
|
|
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
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.
|
|
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)'
|
package/spec/dummy/index.mjs
CHANGED
|
@@ -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 (!
|
|
7
|
-
|
|
6
|
+
if (!this.velociousDummy) {
|
|
7
|
+
this.velociousDummy = new Dummy()
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
return
|
|
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())
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/src/application.mjs
CHANGED
|
@@ -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
|
}
|
package/src/cli/base-command.mjs
CHANGED
|
@@ -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 =
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
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.
|
|
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.
|
|
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?.
|
|
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 (
|
|
5
|
-
return
|
|
4
|
+
if (Configuration.current(false)) {
|
|
5
|
+
return Configuration.current()
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
const directory = args.directory || process.cwd()
|
package/src/configuration.mjs
CHANGED
|
@@ -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 (!
|
|
4
|
+
static current(throwError = true) {
|
|
5
|
+
if (!this.velociousConfiguration && throwError) throw new Error("A Velocious configuration hasn't been set")
|
|
7
6
|
|
|
8
|
-
return
|
|
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.
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async initialize() {
|
|
20
|
-
await this.initializeRoutes()
|
|
13
|
+
this._directory = directory
|
|
21
14
|
}
|
|
22
15
|
|
|
23
16
|
getDatabasePool() {
|
|
24
|
-
if (!this.isDatabasePoolInitialized())
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
//
|
|
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
|
-
|
|
59
|
+
this.constructor.velociousConfiguration = this
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
setRoutes(newRoutes) {
|
|
63
|
+
this.routes = newRoutes
|
|
48
64
|
}
|
|
49
65
|
}
|
package/src/controller.mjs
CHANGED
|
@@ -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"
|
|
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
|
|
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,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.
|
|
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
|
|
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 =
|
|
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
|
|
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
|
+
}
|
package/src/routes/resolver.mjs
CHANGED
|
@@ -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 = `${
|
|
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
|
|
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
|
-
|
|
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
|
|
1
|
+
import Migration from "velocious/src/database/migration/index.mjs"
|
|
2
2
|
|
|
3
|
-
export default class __MIGRATION_NAME__ extends
|
|
3
|
+
export default class __MIGRATION_NAME__ extends Migration {
|
|
4
4
|
async up() {
|
|
5
5
|
await this.connection().execute("...")
|
|
6
6
|
}
|
package/src/templates/routes.mjs
CHANGED
|
@@ -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
|
|
File without changes
|