velocious 1.0.4 → 1.0.6

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/index.mjs +1 -19
  2. package/package.json +6 -3
  3. package/spec/cli/commands/db/create-spec.mjs +1 -1
  4. package/spec/cli/commands/db/migrate-spec.mjs +2 -0
  5. package/spec/database/connection/drivers/mysql/query-parser-spec.mjs +6 -5
  6. package/spec/database/drivers/mysql/connection-spec.mjs +2 -3
  7. package/spec/database/record/create-spec.mjs +9 -0
  8. package/spec/database/record/find-spec.mjs +0 -1
  9. package/spec/database/record/query-spec.mjs +37 -0
  10. package/spec/dummy/index.mjs +14 -2
  11. package/spec/dummy/src/config/configuration.example.mjs +16 -1
  12. package/spec/dummy/src/config/configuration.peakflow.mjs +16 -1
  13. package/spec/dummy/src/database/migrations/20230728075328-create-projects.mjs +0 -1
  14. package/spec/dummy/src/database/migrations/20230728075329-create-tasks.mjs +0 -1
  15. package/spec/dummy/src/database/migrations/20250605133926-create-project-translations.mjs +16 -0
  16. package/spec/dummy/src/models/project.mjs +9 -0
  17. package/spec/dummy/src/models/task.mjs +6 -2
  18. package/src/big-brother.mjs +37 -0
  19. package/src/cli/commands/db/create.mjs +8 -6
  20. package/src/cli/commands/db/migrate.mjs +1 -2
  21. package/src/cli/commands/generate/migration.mjs +1 -1
  22. package/src/cli/commands/generate/model.mjs +1 -1
  23. package/src/configuration.mjs +39 -3
  24. package/src/database/drivers/base.mjs +60 -0
  25. package/src/database/drivers/mysql/column.mjs +8 -0
  26. package/src/database/drivers/mysql/index.mjs +34 -2
  27. package/src/database/drivers/mysql/options.mjs +1 -0
  28. package/src/database/drivers/mysql/query-parser.mjs +2 -23
  29. package/src/database/drivers/mysql/table.mjs +25 -0
  30. package/src/database/drivers/sqlite/base.mjs +108 -0
  31. package/src/database/drivers/sqlite/column.mjs +10 -0
  32. package/src/database/drivers/sqlite/index.native.mjs +22 -63
  33. package/src/database/drivers/sqlite/index.web.mjs +28 -37
  34. package/src/database/drivers/sqlite/options.mjs +2 -1
  35. package/src/database/drivers/sqlite/query-parser.mjs +2 -23
  36. package/src/database/drivers/sqlite/query.native.mjs +16 -1
  37. package/src/database/drivers/sqlite/query.web.mjs +27 -2
  38. package/src/database/drivers/sqlite/sql/create-index.mjs +4 -0
  39. package/src/database/drivers/sqlite/table.mjs +24 -0
  40. package/src/database/initializer-from-require-context.mjs +21 -0
  41. package/src/database/migrate-from-require-context.mjs +11 -2
  42. package/src/database/migration/index.mjs +34 -2
  43. package/src/database/migrator.mjs +75 -0
  44. package/src/database/pool/async-tracked-multi-connection.mjs +1 -1
  45. package/src/database/pool/base.mjs +19 -1
  46. package/src/database/pool/single-multi-use.mjs +1 -1
  47. package/src/database/query/base.mjs +2 -1
  48. package/src/database/query/create-index-base.mjs +50 -0
  49. package/src/database/query/create-table-base.mjs +40 -17
  50. package/src/database/query/index.mjs +83 -21
  51. package/src/database/query/insert-base.mjs +4 -0
  52. package/src/database/query/preloader/belongs-to.mjs +52 -0
  53. package/src/database/query/preloader/has-many.mjs +55 -0
  54. package/src/database/query/preloader.mjs +41 -0
  55. package/src/database/query/where-base.mjs +9 -0
  56. package/src/database/query/where-hash.mjs +35 -0
  57. package/src/database/query/where-plain.mjs +13 -0
  58. package/src/database/query-parser/base-query-parser.mjs +33 -0
  59. package/src/database/query-parser/group-parser.mjs +40 -0
  60. package/src/database/query-parser/joins-parser.mjs +48 -7
  61. package/src/database/query-parser/limit-parser.mjs +40 -0
  62. package/src/database/query-parser/options.mjs +4 -3
  63. package/src/database/query-parser/order-parser.mjs +39 -0
  64. package/src/database/query-parser/select-parser.mjs +5 -1
  65. package/src/database/query-parser/where-parser.mjs +39 -0
  66. package/src/database/record/index.mjs +464 -29
  67. package/src/database/record/instance-relationships/base.mjs +28 -0
  68. package/src/database/record/instance-relationships/belongs-to.mjs +20 -0
  69. package/src/database/record/instance-relationships/has-many.mjs +47 -0
  70. package/src/database/record/relationships/base.mjs +32 -0
  71. package/src/database/record/relationships/belongs-to.mjs +12 -0
  72. package/src/database/record/relationships/has-many.mjs +12 -0
  73. package/src/database/table-data/index.mjs +15 -25
  74. package/src/http-server/worker-handler/worker-thread.mjs +7 -4
  75. package/src/templates/generate-model.mjs +3 -1
  76. package/src/utils/rest-args-error.mjs +9 -0
  77. package/src/database/drivers/index.mjs +0 -5
  78. package/src/database/index.mjs +0 -15
  79. package/src/database/migrator/index.mjs +0 -15
package/index.mjs CHANGED
@@ -1,19 +1 @@
1
- import Application from "./src/application.mjs"
2
- import Cli from "./src/cli/index.mjs"
3
- import Configuration from "./src/configuration.mjs"
4
- import Controller from "./src/controller.mjs"
5
- import Database from "./src/database/index.mjs"
6
- import HttpServer from "./src/http-server/index.mjs"
7
- import Routes from "./src/routes/index.mjs"
8
- import Spec from "./src/spec/index.mjs"
9
-
10
- export {
11
- Application,
12
- Cli,
13
- Configuration,
14
- Controller,
15
- Database,
16
- HttpServer,
17
- Routes,
18
- Spec
19
- }
1
+ export {}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "velocious": "bin/velocious.mjs"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.4",
6
+ "version": "1.0.6",
7
7
  "main": "index.mjs",
8
8
  "scripts": {
9
9
  "test": "jasmine",
@@ -14,7 +14,7 @@
14
14
  "type": "git",
15
15
  "url": "git+https://github.com/kaspernj/velocious.git"
16
16
  },
17
- "author": "k@spernj.org",
17
+ "author": "kasper@diestoeckels.de",
18
18
  "license": "ISC",
19
19
  "bugs": {
20
20
  "url": "https://github.com/kaspernj/velocious/issues"
@@ -24,14 +24,17 @@
24
24
  "devDependencies": {
25
25
  "jasmine": "^5.0.2",
26
26
  "mysql": "^2.18.1",
27
- "node-fetch": "^3.3.1"
27
+ "node-fetch": "^3.3.1",
28
+ "require-context": "^1.1.0"
28
29
  },
29
30
  "dependencies": {
31
+ "better-localstorage": "^1.0.7",
30
32
  "diggerize": "^1.0.5",
31
33
  "ejs": "^3.1.6",
32
34
  "escape-string-regexp": "^1.0.5",
33
35
  "incorporator": "^1.0.2",
34
36
  "inflection": "^3.0.0",
37
+ "sql-escape-string": "^1.1.0",
35
38
  "sql.js": "^1.12.0",
36
39
  "strftime": "^0.10.2"
37
40
  }
@@ -17,7 +17,7 @@ describe("Cli - Commands - db:create", () => {
17
17
  sql: 'CREATE DATABASE IF NOT EXISTS `velocious_test`'
18
18
  },
19
19
  {
20
- createSchemaMigrationsTableSql: 'CREATE TABLE IF NOT EXISTS schema_migrations (`version` varchar(255) PRIMARY KEY)'
20
+ createSchemaMigrationsTableSql: 'CREATE TABLE IF NOT EXISTS schema_migrations (`version` varchar(255) PRIMARY KEY NOT NULL)'
21
21
  }
22
22
  ]
23
23
  )
@@ -17,6 +17,7 @@ describe("Cli - Commands - db:migrate", () => {
17
17
  await db.withConnection(async () => {
18
18
  await db.query("DROP TABLE IF EXISTS tasks")
19
19
  await db.query("DROP TABLE IF EXISTS projects")
20
+ await db.query("DROP TABLE IF EXISTS project_translations")
20
21
  })
21
22
 
22
23
  await cli.execute()
@@ -29,6 +30,7 @@ describe("Cli - Commands - db:migrate", () => {
29
30
 
30
31
  expect(tablesResult).toEqual(
31
32
  [
33
+ {Tables_in_velocious_test: "project_translations"},
32
34
  {Tables_in_velocious_test: "projects"},
33
35
  {Tables_in_velocious_test: "tasks"}
34
36
  ]
@@ -1,4 +1,5 @@
1
- import Database from "../../../../../src/database/index.mjs"
1
+ import DatabaseHandler from "../../../../../src/database/handler.mjs"
2
+ import DatabaseQuery from "../../../../../src/database/query/index.mjs"
2
3
  import MysqlQueryParser from "../../../../../src/database/drivers/mysql/query-parser.mjs"
3
4
 
4
5
  import FromTable from "../../../../../src/database/query/from-table.mjs"
@@ -10,8 +11,8 @@ const mysqlDriver = new MysqlDriverClass()
10
11
 
11
12
  describe("database - connection - drivers - mysql - query parser", () => {
12
13
  it("generates sql with selects, joins and orders", () => {
13
- const handler = new Database.Handler()
14
- const query = new Database.Query({driver: mysqlDriver, handler})
14
+ const handler = new DatabaseHandler()
15
+ const query = new DatabaseQuery({driver: mysqlDriver, handler})
15
16
  .select(["tasks.id", "tasks.name"])
16
17
  .from("tasks")
17
18
  .joins("LEFT JOIN projects ON projects.id = tasks.project_id")
@@ -22,8 +23,8 @@ describe("database - connection - drivers - mysql - query parser", () => {
22
23
  })
23
24
 
24
25
  it("generates sql with selects, joins and orders", () => {
25
- const handler = new Database.Handler()
26
- const query = new Database.Query({driver: mysqlDriver, handler})
26
+ const handler = new DatabaseHandler()
27
+ const query = new DatabaseQuery({driver: mysqlDriver, handler})
27
28
  .select([
28
29
  new SelectTableAndColumn({tableName: "tasks", columnName: "id"}),
29
30
  new SelectTableAndColumn({tableName: "tasks", columnName: "name"})
@@ -1,13 +1,12 @@
1
- import Database from "../../../../src/database/index.mjs"
1
+ import DatabaseDriversMysql from "../../../../src/database/drivers/mysql/index.mjs"
2
2
  import configuration from "../../../dummy/src/config/configuration.mjs"
3
3
  import {digg} from "diggerize"
4
4
 
5
5
  const mysqlConfig = digg(configuration, "database", "default", "master")
6
- const Mysql = Database.Drivers.Mysql
7
6
 
8
7
  describe("Database - Drivers - Mysql - Connection", () => {
9
8
  it("connects", async () => {
10
- const mysql = new Mysql(mysqlConfig)
9
+ const mysql = new DatabaseDriversMysql(mysqlConfig)
11
10
 
12
11
  await mysql.connect()
13
12
 
@@ -5,10 +5,19 @@ describe("Record - create", () => {
5
5
  it("creates a new simple record", async () => {
6
6
  await Dummy.run(async () => {
7
7
  const task = new Task({name: "Test task"})
8
+ const project = task.buildProject({nameEn: "Test project", nameDe: "Test projekt"})
8
9
 
9
10
  await task.save()
10
11
 
11
12
  expect(task.id()).not.toBeUndefined()
13
+ expect(task.name()).toEqual("Test task")
14
+ expect(task.project().id()).toEqual(project.id())
15
+ expect(task.project()).toEqual(project)
16
+
17
+ expect(project.id()).not.toBeUndefined()
18
+ expect(project.name()).toEqual("Test project")
19
+ expect(project.nameDe()).toEqual("Test projekt")
20
+ expect(project.nameEn()).toEqual("Test project")
12
21
  })
13
22
  })
14
23
  })
@@ -1,5 +1,4 @@
1
1
  import Dummy from "../../dummy/index.mjs"
2
- import RecordNotFoundError from "../../../src/database/record/record-not-found-error.mjs"
3
2
  import Task from "../../dummy/src/models/task.mjs"
4
3
 
5
4
  describe("Record - find", () => {
@@ -0,0 +1,37 @@
1
+ import Dummy from "../../dummy/index.mjs"
2
+ import Task from "../../dummy/src/models/task.mjs"
3
+
4
+ describe("Record - query", () => {
5
+ it("queries for records", async () => {
6
+ await Dummy.run(async () => {
7
+ const task = new Task({name: "Test task"})
8
+ const project = task.buildProject({nameEn: "Test project", nameDe: "Test projekt"})
9
+
10
+ await task.save()
11
+
12
+ expect(task.id()).not.toBeUndefined()
13
+ expect(task.name()).toEqual("Test task")
14
+ expect(task.project().id()).toEqual(project.id())
15
+ expect(task.project()).toEqual(project)
16
+
17
+ expect(project.id()).not.toBeUndefined()
18
+ expect(project.name()).toEqual("Test project")
19
+ expect(project.nameDe()).toEqual("Test projekt")
20
+ expect(project.nameEn()).toEqual("Test project")
21
+
22
+ const tasks = await Task.preload({project: {translations: true}}).toArray()
23
+ const newTask = tasks[0]
24
+ const newProject = newTask.project()
25
+
26
+ expect(newTask.id()).not.toBeUndefined()
27
+ expect(newTask.name()).toEqual("Test task")
28
+ expect(task.project().id()).toEqual(newProject.id())
29
+ expect(newTask.project()).toEqual(newProject)
30
+
31
+ expect(newProject.id()).not.toBeUndefined()
32
+ expect(newProject.name()).toEqual("Test project")
33
+ expect(newProject.nameDe()).toEqual("Test projekt")
34
+ expect(newProject.nameEn()).toEqual("Test project")
35
+ })
36
+ })
37
+ })
@@ -11,11 +11,23 @@ export default class Dummy {
11
11
  }
12
12
 
13
13
  static async prepare() {
14
+ dummyConfiguration.setCurrent()
15
+
14
16
  const db = dummyConfiguration.getDatabasePool()
15
17
 
16
18
  await db.withConnection(async () => {
17
19
  await db.query("DROP TABLE IF EXISTS tasks")
18
- await db.query("CREATE TABLE tasks (id MEDIUMINT NOT NULL AUTO_INCREMENT, name VARCHAR(255), description TEXT, PRIMARY KEY (id))")
20
+ await db.query("DROP TABLE IF EXISTS projects")
21
+ await db.query("DROP TABLE IF EXISTS project_translations")
22
+
23
+ await db.query("CREATE TABLE tasks (id MEDIUMINT NOT NULL AUTO_INCREMENT, project_id MEDIUMINT, name VARCHAR(255), description TEXT, PRIMARY KEY (id))")
24
+ await db.query("CREATE TABLE projects (id MEDIUMINT NOT NULL AUTO_INCREMENT, PRIMARY KEY (id))")
25
+ await db.query("CREATE TABLE project_translations (id MEDIUMINT NOT NULL AUTO_INCREMENT, project_id MEDIUMINT, locale VARCHAR(255), name VARCHAR(255), PRIMARY KEY (id))")
26
+ await db.query("CREATE UNIQUE INDEX unique_project_translation ON project_translations (project_id, locale)")
27
+
28
+ if (!dummyConfiguration.isInitialized()) {
29
+ await dummyConfiguration.initialize()
30
+ }
19
31
  })
20
32
  }
21
33
 
@@ -25,10 +37,10 @@ export default class Dummy {
25
37
 
26
38
  async run(callback) {
27
39
  await dummyConfiguration.getDatabasePool().withConnection(async () => {
40
+ await Dummy.prepare()
28
41
  await this.start()
29
42
 
30
43
  try {
31
- await Dummy.prepare()
32
44
  await callback()
33
45
  } finally {
34
46
  await this.stop()
@@ -1,7 +1,11 @@
1
1
  import AsyncTrackedMultiConnection from "../../../../src/database/pool/async-tracked-multi-connection.mjs"
2
2
  import Configuration from "../../../../src/configuration.mjs"
3
3
  import dummyDirectory from "../../dummy-directory.mjs"
4
+ import fs from "fs/promises"
5
+ import InitializerFromRequireContext from "../../../../src/database/initializer-from-require-context.mjs"
4
6
  import MysqlDriver from "../../../../src/database/drivers/mysql/index.mjs"
7
+ import path from "path"
8
+ import requireContext from "require-context"
5
9
 
6
10
  export default new Configuration({
7
11
  database: {
@@ -17,5 +21,16 @@ export default new Configuration({
17
21
  }
18
22
  }
19
23
  },
20
- directory: dummyDirectory()
24
+ directory: dummyDirectory(),
25
+ initializeModels: async ({configuration}) => {
26
+ const modelsPath = await fs.realpath(`${path.dirname(import.meta.dirname)}/../src/models`)
27
+ const requireContextModels = requireContext(modelsPath, true, /^(.+)\.mjs$/)
28
+ const initializerFromRequireContext = new InitializerFromRequireContext({requireContext: requireContextModels})
29
+
30
+ await configuration.getDatabasePool().withConnection(async () => {
31
+ await initializerFromRequireContext.initialize({configuration})
32
+ })
33
+ },
34
+ locale: () => "en",
35
+ locales: ["de", "en"]
21
36
  })
@@ -1,7 +1,11 @@
1
1
  import AsyncTrackedMultiConnection from "../../../../src/database/pool/async-tracked-multi-connection.mjs"
2
2
  import Configuration from "../../../../src/configuration.mjs"
3
3
  import dummyDirectory from "../../dummy-directory.mjs"
4
+ import fs from "fs/promises"
5
+ import InitializerFromRequireContext from "../../../../src/database/initializer-from-require-context.mjs"
4
6
  import MysqlDriver from "../../../../src/database/drivers/mysql/index.mjs"
7
+ import path from "path"
8
+ import requireContext from "require-context"
5
9
 
6
10
  export default new Configuration({
7
11
  database: {
@@ -18,5 +22,16 @@ export default new Configuration({
18
22
  }
19
23
  }
20
24
  },
21
- directory: dummyDirectory()
25
+ directory: dummyDirectory(),
26
+ initializeModels: async ({configuration}) => {
27
+ const modelsPath = await fs.realpath(`${path.dirname(import.meta.dirname)}/../src/models`)
28
+ const requireContextModels = requireContext(modelsPath, true, /^(.+)\.mjs$/)
29
+ const initializerFromRequireContext = new InitializerFromRequireContext({requireContext: requireContextModels})
30
+
31
+ await configuration.getDatabasePool().withConnection(async () => {
32
+ await initializerFromRequireContext.initialize({configuration})
33
+ })
34
+ },
35
+ locale: () => "en",
36
+ locales: ["de", "en"]
22
37
  })
@@ -3,7 +3,6 @@ import Migration from "../../../../../src/database/migration/index.mjs"
3
3
  export default class CreateProjects extends Migration {
4
4
  async change() {
5
5
  await this.createTable("projects", (table) => {
6
- table.bigint("id", {autoIncrement: true, primaryKey: true})
7
6
  table.string("name", {maxLength: 100, null: false})
8
7
  table.timestamps()
9
8
  })
@@ -3,7 +3,6 @@ import Migration from "../../../../../src/database/migration/index.mjs"
3
3
  export default class CreateTasks extends Migration {
4
4
  async change() {
5
5
  await this.createTable("tasks", (table) => {
6
- table.bigint("id", {autoIncrement: true, primaryKey: true})
7
6
  table.references("project")
8
7
  table.string("name")
9
8
  table.text("description")
@@ -0,0 +1,16 @@
1
+ import Migration from "../../../../../src/database/migration/index.mjs"
2
+
3
+ export default class CreateProjectTranslations extends Migration {
4
+ async up() {
5
+ await this.createTable("project_translations", (t) => {
6
+ t.references("project", {null: false})
7
+ t.string("locale", {null: false})
8
+ t.string("name")
9
+ t.timestamps()
10
+ })
11
+ }
12
+
13
+ async down() {
14
+ await this.dropTable("project_translations")
15
+ }
16
+ }
@@ -0,0 +1,9 @@
1
+ import DatabaseRecord from "../../../../src/database/record/index.mjs"
2
+
3
+ class Project extends DatabaseRecord {
4
+ }
5
+
6
+ Project.hasMany("tasks")
7
+ Project.translates("name")
8
+
9
+ export default Project
@@ -1,4 +1,8 @@
1
- import Database from "../../../../src/database/index.mjs"
1
+ import DatabaseRecord from "../../../../src/database/record/index.mjs"
2
2
 
3
- export default class Task extends Database.Record {
3
+ class Task extends DatabaseRecord {
4
4
  }
5
+
6
+ Task.belongsTo("project")
7
+
8
+ export default Task
@@ -0,0 +1,37 @@
1
+ export default class VelociousBigBrother {
2
+ constructor() {
3
+ this.enabledLoggers = {
4
+ databaseQuery: false
5
+ }
6
+ }
7
+
8
+ checkExists(name) {
9
+ if (!(name in this.enabledLoggers)) throw new Error(`Invalid logger name: ${name}`)
10
+ }
11
+
12
+ isEnabled(name) {
13
+ this.checkExists(name)
14
+
15
+ return this.enabledLoggers[name]
16
+ }
17
+
18
+ async run({after, before, name}, callback) {
19
+ this.checkExists(name)
20
+
21
+ if (!this.enabledLoggers[name]) {
22
+ return await callback()
23
+ }
24
+
25
+ if (before) {
26
+ before()
27
+ }
28
+
29
+ const startTime = new Date()
30
+ const result = await callback()
31
+ const endTime = new Date()
32
+
33
+ if (after) {
34
+ after({result})
35
+ }
36
+ }
37
+ }
@@ -39,12 +39,14 @@ export default class DbCreate extends BaseCommand{
39
39
 
40
40
  schemaMigrationsTable.string("version", {null: false, primaryKey: true})
41
41
 
42
- const createSchemaMigrationsTableSql = this.databaseConnection.createTableSql(schemaMigrationsTable)
43
-
44
- if (this.args.testing) {
45
- this.result.push({createSchemaMigrationsTableSql})
46
- } else {
47
- await this.databaseConnection.query(createSchemaMigrationsTableSql)
42
+ const createSchemaMigrationsTableSqls = this.databaseConnection.createTableSql(schemaMigrationsTable)
43
+
44
+ for (const createSchemaMigrationsTableSql of createSchemaMigrationsTableSqls) {
45
+ if (this.args.testing) {
46
+ this.result.push({createSchemaMigrationsTableSql})
47
+ } else {
48
+ await this.databaseConnection.query(createSchemaMigrationsTableSql)
49
+ }
48
50
  }
49
51
  }
50
52
  }
@@ -1,7 +1,6 @@
1
1
  import BaseCommand from "../../base-command.mjs"
2
- import {digg} from "diggerize"
3
2
  import fs from "node:fs/promises"
4
- import inflection from "inflection"
3
+ import * as inflection from "inflection"
5
4
 
6
5
  export default class DbMigrate extends BaseCommand {
7
6
  async execute() {
@@ -3,7 +3,7 @@ import {dirname} from "path"
3
3
  import {fileURLToPath} from "url"
4
4
  import fileExists from "../../../utils/file-exists.mjs"
5
5
  import fs from "node:fs/promises"
6
- import inflection from "inflection"
6
+ import * as inflection from "inflection"
7
7
  import strftime from "strftime"
8
8
 
9
9
  export default class DbGenerateMigration extends BaseCommand {
@@ -3,7 +3,7 @@ import {dirname} from "path"
3
3
  import {fileURLToPath} from "url"
4
4
  import fileExists from "../../../utils/file-exists.mjs"
5
5
  import fs from "node:fs/promises"
6
- import inflection from "inflection"
6
+ import * as inflection from "inflection"
7
7
 
8
8
  export default class DbGenerateModel extends BaseCommand {
9
9
  async execute() {
@@ -1,4 +1,5 @@
1
1
  import {digg} from "diggerize"
2
+ import restArgsError from "./utils/rest-args-error.mjs"
2
3
 
3
4
  export default class VelociousConfiguration {
4
5
  static current(throwError = true) {
@@ -7,10 +8,19 @@ export default class VelociousConfiguration {
7
8
  return this.velociousConfiguration
8
9
  }
9
10
 
10
- constructor({database, debug, directory}) {
11
+ constructor({database, debug, directory, initializeModels, locale, locales, ...restArgs}) {
12
+ restArgsError(restArgs)
13
+
14
+ if (!initializeModels) throw new Error("initializeModels wasn't given")
15
+
11
16
  this.database = database
12
17
  this.debug = debug
13
18
  this._directory = directory
19
+ this._initializeModels = initializeModels
20
+ this._isInitialized = false
21
+ this.locale = locale
22
+ this.locales = locales
23
+ this.modelClasses = {}
14
24
  }
15
25
 
16
26
  getDatabasePool() {
@@ -39,6 +49,26 @@ export default class VelociousConfiguration {
39
49
  return this._directory
40
50
  }
41
51
 
52
+ getLocale() {
53
+ if (typeof this.locale == "function") {
54
+ return this.locale()
55
+ } else if (this.locale) {
56
+ return this.locale
57
+ } else {
58
+ return this.getLocales()[0]
59
+ }
60
+ }
61
+
62
+ getLocales = () => digg(this, "locales")
63
+
64
+ getModelClass(name) {
65
+ const modelClass = this.modelClasses[name]
66
+
67
+ if (!modelClass) throw new Error(`No such model class ${name} in ${Object.keys(this.modelClasses).join(", ")}}`)
68
+
69
+ return modelClass
70
+ }
71
+
42
72
  initializeDatabasePool() {
43
73
  if (!this.database) throw new Error("No 'database' was given")
44
74
  if (this.databasePool) throw new Error("DatabasePool has already been initialized")
@@ -50,9 +80,15 @@ export default class VelociousConfiguration {
50
80
  }
51
81
 
52
82
  isDatabasePoolInitialized = () => Boolean(this.databasePool)
83
+ isInitialized = () => this._isInitialized
84
+
85
+ async initialize() {
86
+ await this._initializeModels({configuration: this})
87
+ this._isInitialized = true
88
+ }
53
89
 
54
- initialize() {
55
- // Doesn't currently do anything.
90
+ registerModelClass(modelClass) {
91
+ this.modelClasses[modelClass.name] = modelClass
56
92
  }
57
93
 
58
94
  setCurrent() {
@@ -1,8 +1,25 @@
1
+ import Query from "../query/index.mjs"
2
+ import Handler from "../handler.mjs"
3
+
1
4
  export default class VelociousDatabaseDriversBase {
2
5
  constructor(args) {
3
6
  this._args = args
4
7
  }
5
8
 
9
+ async createTable(...args) {
10
+ const sqls = this.createTableSql(...args)
11
+
12
+ for (const sql of sqls) {
13
+ await this.query(sql)
14
+ }
15
+ }
16
+
17
+ async delete(...args) {
18
+ const sql = this.deleteSql(...args)
19
+
20
+ await this.query(sql)
21
+ }
22
+
6
23
  getArgs() {
7
24
  return this._args
8
25
  }
@@ -11,7 +28,50 @@ export default class VelociousDatabaseDriversBase {
11
28
  return this.idSeq
12
29
  }
13
30
 
31
+ getTables() {
32
+ throw new Error(`${this.constructor.name}#getTables not implemented`)
33
+ }
34
+
35
+ async getTableByName(name) {
36
+ const tables = await this.getTables()
37
+ const table = tables.find((table) => table.getName() == name)
38
+
39
+ if (!table) throw new Error(`Couldn't find a table by that name: ${name}`)
40
+
41
+ return table
42
+ }
43
+
44
+ async insert(...args) {
45
+ const sql = this.insertSql(...args)
46
+
47
+ await this.query(sql)
48
+ }
49
+
50
+ lastInsertID() {
51
+ throw new Error(`${this.constructor.name}#lastInsertID not implemented`)
52
+ }
53
+
54
+ async select(tableName) {
55
+ const handler = new Handler()
56
+ const query = new Query({
57
+ driver: this,
58
+ handler
59
+ })
60
+
61
+ const sql = query
62
+ .from(tableName)
63
+ .toSql()
64
+
65
+ return await this.query(sql)
66
+ }
67
+
14
68
  setIdSeq(id) {
15
69
  this.idSeq = id
16
70
  }
71
+
72
+ async update(...args) {
73
+ const sql = this.updateSql(...args)
74
+
75
+ await this.query(sql)
76
+ }
17
77
  }
@@ -0,0 +1,8 @@
1
+ export default class VelociousDatabaseDriversMysqlColumn {
2
+ constructor(table, data) {
3
+ this.data = data
4
+ this.table = table
5
+ }
6
+
7
+ getName = () => this.data["Field"]
8
+ }
@@ -9,6 +9,7 @@ import Options from "./options.mjs"
9
9
  import mysql from "mysql"
10
10
  import query from "./query.mjs"
11
11
  import QueryParser from "./query-parser.mjs"
12
+ import Table from "./table.mjs"
12
13
  import Update from "./sql/update.mjs"
13
14
 
14
15
  export default class VelociousDatabaseDriversMysql extends Base{
@@ -64,13 +65,25 @@ export default class VelociousDatabaseDriversMysql extends Base{
64
65
  return new QueryParser({query}).toSql()
65
66
  }
66
67
 
67
- quote(string) {
68
+ escape(string) {
68
69
  if (!this.connection) throw new Error("Can't escape before connected")
69
70
 
70
71
  return this.connection.escape(string)
71
72
  }
72
73
 
73
- quoteColumn(string) {
74
+ quote(string) {
75
+ return `${this.escape(string)}`
76
+ }
77
+
78
+ quoteColumn = (string) => {
79
+ if (string.includes("`")) throw new Error(`Possible SQL injection in column name: ${string}`)
80
+
81
+ return `\`${string}\``
82
+ }
83
+
84
+ quoteTable = (string) => {
85
+ if (string.includes("`")) throw new Error(`Possible SQL injection in table name: ${string}`)
86
+
74
87
  return `\`${string}\``
75
88
  }
76
89
 
@@ -86,6 +99,25 @@ export default class VelociousDatabaseDriversMysql extends Base{
86
99
  return insert.toSql()
87
100
  }
88
101
 
102
+ async getTables() {
103
+ const result = await this.query("SHOW FULL TABLES")
104
+ const tables = []
105
+
106
+ for (const row of result) {
107
+ const table = new Table(this, row)
108
+
109
+ tables.push(table)
110
+ }
111
+
112
+ return tables
113
+ }
114
+
115
+ async lastInsertID() {
116
+ const result = await this.query("SELECT LAST_INSERT_ID() AS last_insert_id")
117
+
118
+ return digg(result, 0, "last_insert_id")
119
+ }
120
+
89
121
  options() {
90
122
  if (!this._options) {
91
123
  this._options = new Options({driver: this})