velocious 1.0.102 → 1.0.104

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 (41) hide show
  1. package/package.json +1 -1
  2. package/spec/database/drivers/create-sql/create-index-sql-spec.js +24 -0
  3. package/spec/database/record/create-spec.js +7 -0
  4. package/spec/dummy/src/model-bases/project.js +18 -0
  5. package/src/database/drivers/base-column.js +2 -2
  6. package/src/database/drivers/base-columns-index.js +6 -0
  7. package/src/database/drivers/base.js +34 -19
  8. package/src/database/drivers/mssql/index.js +70 -22
  9. package/src/database/drivers/mssql/sql/create-database.js +9 -2
  10. package/src/database/drivers/mssql/table.js +14 -4
  11. package/src/database/drivers/mysql/column.js +6 -0
  12. package/src/database/drivers/mysql/columns-index.js +3 -6
  13. package/src/database/drivers/mysql/foreign-key.js +2 -0
  14. package/src/database/drivers/mysql/index.js +43 -16
  15. package/src/database/drivers/mysql/query-parser.js +2 -0
  16. package/src/database/drivers/mysql/query.js +7 -2
  17. package/src/database/drivers/mysql/table.js +6 -0
  18. package/src/database/drivers/pgsql/index.js +78 -11
  19. package/src/database/drivers/sqlite/base.js +77 -25
  20. package/src/database/drivers/sqlite/column.js +8 -0
  21. package/src/database/drivers/sqlite/sql/alter-table.js +25 -20
  22. package/src/database/drivers/sqlite/sql/create-index.js +2 -0
  23. package/src/database/drivers/sqlite/sql/create-table.js +2 -0
  24. package/src/database/drivers/sqlite/sql/delete.js +4 -2
  25. package/src/database/drivers/sqlite/sql/drop-table.js +2 -0
  26. package/src/database/drivers/sqlite/sql/insert.js +2 -0
  27. package/src/database/drivers/sqlite/sql/update.js +2 -0
  28. package/src/database/drivers/sqlite/table.js +14 -0
  29. package/src/database/migration/index.js +6 -4
  30. package/src/database/pool/base-methods-forward.js +2 -2
  31. package/src/database/query/alter-table-base.js +1 -1
  32. package/src/database/query/base.js +2 -2
  33. package/src/database/query/create-database-base.js +8 -4
  34. package/src/database/query/create-index-base.js +12 -7
  35. package/src/database/query/create-table-base.js +4 -4
  36. package/src/database/query/drop-table-base.js +8 -8
  37. package/src/database/query/insert-base.js +18 -3
  38. package/src/database/query-parser/base-query-parser.js +2 -2
  39. package/src/database/record/index.js +27 -20
  40. package/src/database/table-data/table-column.js +3 -3
  41. package/src/environment-handlers/node/cli/commands/generate/base-models.js +19 -0
@@ -1,12 +1,17 @@
1
+ // @ts-check
2
+
1
3
  import AlterTableBase from "../../../query/alter-table-base.js"
2
4
  import CreateIndexBase from "../../../query/create-index-base.js"
3
- import {digs} from "diggerize"
4
- import * as inflection from "inflection"
5
5
  import {Logger} from "../../../../logger.js"
6
6
  import restArgsError from "../../../../utils/rest-args-error.js"
7
7
  import TableData from "../../../table-data/index.js"
8
8
 
9
9
  export default class VelociousDatabaseConnectionDriversSqliteSqlAlterTable extends AlterTableBase {
10
+ /**
11
+ * @param {object} args
12
+ * @param {import("../../base.js").default} args.driver
13
+ * @param {import("../../../table-data/index.js").default} args.tableData
14
+ */
10
15
  constructor({driver, tableData, ...restArgs}) {
11
16
  restArgsError(restArgs)
12
17
 
@@ -17,9 +22,15 @@ export default class VelociousDatabaseConnectionDriversSqliteSqlAlterTable exten
17
22
  this.tableData = tableData
18
23
  }
19
24
 
20
- async toSqls() {
21
- const {tableData} = digs(this, "tableData")
25
+ /**
26
+ * @returns {string[]}
27
+ */
28
+ async toSQLs() {
29
+ const {tableData} = this
22
30
  const table = await this.getDriver().getTableByName(tableData.getName())
31
+
32
+ if (!table) throw new Error(`Table ${tableData.getName()} does not exist`)
33
+
23
34
  const currentTableData = await table.getTableData()
24
35
  const options = this.getOptions()
25
36
  const tableName = tableData.getName()
@@ -43,19 +54,13 @@ export default class VelociousDatabaseConnectionDriversSqliteSqlAlterTable exten
43
54
  const newTableDataColumn = tableData.getColumns().find((newTableDataColumn) => newTableDataColumn.getName() == tableDataColumn.getName())
44
55
 
45
56
  if (newTableDataColumn) {
46
- const settingsToClone = ["autoIncrement", "default", "index", "foreignKey", "maxLength", "primaryKey", "type"]
47
-
48
- for (const settingToClone of settingsToClone) {
49
- const camelizedSettingToClone = inflection.camelize(settingToClone)
50
-
51
- if (!newTableDataColumn[`get${camelizedSettingToClone}`]) {
52
- throw new Error(`No such method on column: get${camelizedSettingToClone}`)
53
- }
54
-
55
- if (!newTableDataColumn[`get${camelizedSettingToClone}`]()) {
56
- newTableDataColumn[`set${camelizedSettingToClone}`](tableDataColumn[`get${camelizedSettingToClone}`]())
57
- }
58
- }
57
+ newTableDataColumn.setAutoIncrement(tableDataColumn.getAutoIncrement())
58
+ newTableDataColumn.setDefault(tableDataColumn.getDefault())
59
+ newTableDataColumn.setIndex(tableDataColumn.getIndex())
60
+ newTableDataColumn.setForeignKey(tableDataColumn.getForeignKey())
61
+ newTableDataColumn.setMaxLength(tableDataColumn.getMaxLength())
62
+ newTableDataColumn.setPrimaryKey(tableDataColumn.getPrimaryKey())
63
+ newTableDataColumn.setType(tableDataColumn.getType())
59
64
  }
60
65
 
61
66
  newTableData.addColumn(newTableDataColumn || tableDataColumn)
@@ -105,7 +110,7 @@ export default class VelociousDatabaseConnectionDriversSqliteSqlAlterTable exten
105
110
 
106
111
  const createNewTableSQL = this.getDriver().createTableSql(newTableData)
107
112
  const insertSQL = `INSERT INTO ${options.quoteTableName(tempTableName)} (${newColumnsSQL}) SELECT ${oldColumnsSQL} FROM ${options.quoteTableName(tableName)}`
108
- const dropTableSQL = `DROP TABLE ${options.quoteTableName(tableName)}`
113
+ const dropTableSQLs = `DROP TABLE ${options.quoteTableName(tableName)}`
109
114
  const renameTableSQL = `ALTER TABLE ${options.quoteTableName(tempTableName)} RENAME TO ${options.quoteTableName(tableName)}`
110
115
  const sqls = []
111
116
 
@@ -114,7 +119,7 @@ export default class VelociousDatabaseConnectionDriversSqliteSqlAlterTable exten
114
119
  }
115
120
 
116
121
  sqls.push(insertSQL)
117
- sqls.push(dropTableSQL)
122
+ sqls.push(dropTableSQLs)
118
123
  sqls.push(renameTableSQL)
119
124
 
120
125
  for (const tableDataIndex of currentTableData.getIndexes()) {
@@ -136,7 +141,7 @@ export default class VelociousDatabaseConnectionDriversSqliteSqlAlterTable exten
136
141
  tableName,
137
142
  unique: actualTableIndex.getUnique()
138
143
  }
139
- const createIndexSQLs = new CreateIndexBase(createIndexArgs).toSqls()
144
+ const createIndexSQLs = new CreateIndexBase(createIndexArgs).toSQLs()
140
145
 
141
146
  for (const createIndexSQL of createIndexSQLs) {
142
147
  sqls.push(createIndexSQL)
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  import CreateIndexBase from "../../../query/create-index-base.js"
2
4
 
3
5
  export default class VelociousDatabaseConnectionDriversSqliteSqlCreateIndex extends CreateIndexBase {
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  import CreateTableBase from "../../../query/create-table-base.js"
2
4
 
3
5
  export default class VelociousDatabaseConnectionDriversMysqlSqlCreateTable extends CreateTableBase {
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  import DeleteBase from "../../../query/delete-base.js"
2
4
 
3
5
  export default class VelociousDatabaseConnectionDriversMysqlSqlDelete extends DeleteBase {
@@ -17,8 +19,8 @@ export default class VelociousDatabaseConnectionDriversMysqlSqlDelete extends De
17
19
  sql += this.getOptions().quote(this.conditions[columnName])
18
20
  count++
19
21
  }
20
-
21
- return sql
22
22
  }
23
+
24
+ return sql
23
25
  }
24
26
  }
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  import DropTableBase from "../../../query/drop-table-base.js"
2
4
 
3
5
  export default class VelociousDatabaseConnectionDriversSqliteSqlDropTable extends DropTableBase {
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  import InsertBase from "../../../query/insert-base.js"
2
4
 
3
5
  export default class VelociousDatabaseConnectionDriversMysqlSqlInsert extends InsertBase {
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  import UpdateBase from "../../../query/update-base.js"
2
4
 
3
5
  export default class VelociousDatabaseConnectionDriversMysqlSqlUpdate extends UpdateBase {
@@ -1,15 +1,23 @@
1
+ // @ts-check
2
+
1
3
  import BaseTable from "../base-table.js"
2
4
  import Column from "./column.js"
3
5
  import ColumnsIndex from "./columns-index.js"
4
6
  import ForeignKey from "./foreign-key.js"
5
7
 
6
8
  export default class VelociousDatabaseDriversSqliteTable extends BaseTable {
9
+ /**
10
+ * @param {object} args
11
+ * @param {import("../base.js").default} args.driver
12
+ * @param {Record<string, any>} args.row
13
+ */
7
14
  constructor({driver, row}) {
8
15
  super()
9
16
  this.driver = driver
10
17
  this.row = row
11
18
  }
12
19
 
20
+ /** @returns {Promise<Array<import("../base-column.js").default>>} */
13
21
  async getColumns() {
14
22
  const result = await this.driver.query(`PRAGMA table_info('${this.getName()}')`)
15
23
  const columns = []
@@ -52,8 +60,14 @@ export default class VelociousDatabaseDriversSqliteTable extends BaseTable {
52
60
  return indexes
53
61
  }
54
62
 
63
+ /** @param {string} sql */
55
64
  _parseColumnsFromSQL(sql) {
56
65
  const columnsSQLMatch = sql.match(/\((.+?)\)/)
66
+
67
+ if (!columnsSQLMatch) {
68
+ throw new Error(`Could not match columns from SQL: ${sql}`)
69
+ }
70
+
57
71
  const columnsSQL = columnsSQLMatch[1].split(",")
58
72
  const columnNames = []
59
73
 
@@ -90,7 +90,7 @@ export default class VelociousDatabaseMigration {
90
90
 
91
91
  tableData.addColumn(columnName, tableColumnArgs)
92
92
 
93
- const sqls = await this.getDriver().alterTableSql(tableData)
93
+ const sqls = await this.getDriver().alterTableSQLs(tableData)
94
94
 
95
95
  for (const sql of sqls) {
96
96
  await this.getDriver().query(sql)
@@ -108,7 +108,7 @@ export default class VelociousDatabaseMigration {
108
108
 
109
109
  tableData.addColumn(columnName, tableColumnArgs)
110
110
 
111
- const sqls = await this.getDriver().alterTableSql(tableData)
111
+ const sqls = await this.getDriver().alterTableSQLs(tableData)
112
112
 
113
113
  for (const sql of sqls) {
114
114
  await this.getDriver().query(sql)
@@ -135,9 +135,11 @@ export default class VelociousDatabaseMigration {
135
135
  },
136
136
  args
137
137
  )
138
- const sql = this.getDriver().createIndexSql(createIndexArgs)
138
+ const sqls = this.getDriver().createIndexSQLs(createIndexArgs)
139
139
 
140
- await this.getDriver().query(sql)
140
+ for (const sql of sqls) {
141
+ await this.getDriver().query(sql)
142
+ }
141
143
  }
142
144
 
143
145
  /**
@@ -4,9 +4,9 @@
4
4
  export default function baseMethodsForward(PoolBase) {
5
5
  const forwardMethods = [
6
6
  "alterTable",
7
- "alterTableSql",
7
+ "alterTableSQLs",
8
8
  "createIndex",
9
- "createIndexSql",
9
+ "createIndexSQLs",
10
10
  "createTable",
11
11
  "createTableSql",
12
12
  "delete",
@@ -22,7 +22,7 @@ export default class VelociousDatabaseQueryAlterTableBase extends QueryBase {
22
22
  /**
23
23
  * @returns {string[]}
24
24
  */
25
- toSqls() {
25
+ toSQLs() {
26
26
  const databaseType = this.getDriver().getType()
27
27
  const sqls = []
28
28
  const {tableData} = this
@@ -40,7 +40,7 @@ export default class VelociousDatabaseQueryBase {
40
40
  * @abstract
41
41
  * @returns {string[]}
42
42
  */
43
- toSqls() {
44
- throw new Error("'toSqls' wasn't implemented")
43
+ toSQLs() {
44
+ throw new Error("'toSQLs' wasn't implemented")
45
45
  }
46
46
  }
@@ -1,13 +1,17 @@
1
1
  // @ts-check
2
2
 
3
+ /**
4
+ * @typedef {object} CreateDatabaseArgsType
5
+ * @property {import("../drivers/base.js").default} driver
6
+ * @property {string} databaseName
7
+ * @property {boolean} [ifNotExists]
8
+ */
9
+
3
10
  import QueryBase from "./base.js"
4
11
 
5
12
  export default class VelociousDatabaseQueryCreateDatabaseBase extends QueryBase {
6
13
  /**
7
- * @param {object} args
8
- * @param {import("../drivers/base.js").default} args.driver
9
- * @param {string} args.databaseName
10
- * @param {boolean} [args.ifNotExists]
14
+ * @param {CreateDatabaseArgsType} args
11
15
  */
12
16
  constructor({driver, databaseName, ifNotExists}) {
13
17
  super({driver})
@@ -28,13 +28,15 @@ export default class VelociousDatabaseQueryCreateIndexBase extends QueryBase {
28
28
  generateIndexName() {
29
29
  const databaseType = this.getDriver().getType()
30
30
  let indexName = `index_on_`
31
+ let columnCount = 0
31
32
 
32
33
  if (databaseType == "sqlite") indexName += `${this.tableName}_`
33
34
 
34
- for (const columnIndex in this.columns) {
35
- if (typeof columnIndex == "number" && columnIndex > 0) indexName += "_and_"
35
+ for (const column of this.columns) {
36
+ columnCount++
37
+
38
+ if (columnCount > 1) indexName += "_and_"
36
39
 
37
- const column = this.columns[columnIndex]
38
40
  let columnName
39
41
 
40
42
  if (typeof column == "string") {
@@ -52,7 +54,7 @@ export default class VelociousDatabaseQueryCreateIndexBase extends QueryBase {
52
54
  /**
53
55
  * @returns {string[]}
54
56
  */
55
- toSqls() {
57
+ toSQLs() {
56
58
  const databaseType = this.getDriver().getType()
57
59
  const indexName = this.name || this.generateIndexName()
58
60
  const options = this.getOptions()
@@ -83,10 +85,13 @@ export default class VelociousDatabaseQueryCreateIndexBase extends QueryBase {
83
85
  sql += ` ${options.quoteIndexName(indexName)}`
84
86
  sql += ` ON ${options.quoteTableName(tableName)} (`
85
87
 
86
- for (const columnIndex in this.columns) {
87
- if (typeof columnIndex == "number" && columnIndex > 0) sql += ", "
88
+ let columnCount = 0
89
+
90
+ for (const column of this.columns) {
91
+ columnCount++
92
+
93
+ if (columnCount > 1) sql += ", "
88
94
 
89
- const column = this.columns[columnIndex]
90
95
  let columnName
91
96
 
92
97
  if (typeof column == "string") {
@@ -10,8 +10,8 @@ export default class VelociousDatabaseQueryCreateTableBase extends QueryBase {
10
10
  /**
11
11
  * @param {object} args
12
12
  * @param {import("../drivers/base.js").default} args.driver
13
- * @param {boolean} args.ifNotExists
14
- * @param {boolean} args.indexInCreateTable
13
+ * @param {boolean} [args.ifNotExists]
14
+ * @param {boolean} [args.indexInCreateTable]
15
15
  * @param {TableData} args.tableData
16
16
  */
17
17
  constructor({driver, ifNotExists, indexInCreateTable = true, tableData}) {
@@ -128,7 +128,7 @@ export default class VelociousDatabaseQueryCreateTableBase extends QueryBase {
128
128
  tableName: tableData.getName(),
129
129
  unique: index.getUnique()
130
130
  }
131
- const createIndexSQLs = new CreateIndexBase(createIndexArgs).toSqls()
131
+ const createIndexSQLs = new CreateIndexBase(createIndexArgs).toSQLs()
132
132
 
133
133
  for (const createIndexSQL of createIndexSQLs) {
134
134
  sqls.push(createIndexSQL)
@@ -156,7 +156,7 @@ export default class VelociousDatabaseQueryCreateTableBase extends QueryBase {
156
156
  tableName: tableData.getName(),
157
157
  unique
158
158
  }
159
- const createIndexSQLs = new CreateIndexBase(createIndexArgs).toSqls()
159
+ const createIndexSQLs = new CreateIndexBase(createIndexArgs).toSQLs()
160
160
 
161
161
  for (const createIndexSQL of createIndexSQLs) {
162
162
  sqls.push(createIndexSQL)
@@ -1,18 +1,18 @@
1
- import {digs} from "diggerize"
1
+ // @ts-check
2
+
2
3
  import QueryBase from "./base.js"
3
4
  import restArgsError from "../../utils/rest-args-error.js"
4
5
 
5
6
  export default class VelociousDatabaseQueryDropTableBase extends QueryBase {
6
7
  /**
7
8
  * @param {object} args
8
- * @param {boolean} args.cascade
9
+ * @param {boolean} [args.cascade]
9
10
  * @param {import("./../drivers/base.js").default} args.driver
10
- * @param {boolean} args.ifExists
11
- * @param {object} args.options
11
+ * @param {boolean} [args.ifExists]
12
12
  * @param {string} args.tableName
13
13
  */
14
- constructor({cascade, driver, ifExists, options, tableName, ...restArgs}) {
15
- super({driver, options})
14
+ constructor({cascade, driver, ifExists, tableName, ...restArgs}) {
15
+ super({driver})
16
16
 
17
17
  restArgsError(restArgs)
18
18
 
@@ -24,10 +24,10 @@ export default class VelociousDatabaseQueryDropTableBase extends QueryBase {
24
24
  /**
25
25
  * @returns {string[]}
26
26
  */
27
- toSql() {
27
+ toSQLs() {
28
28
  const databaseType = this.getDatabaseType()
29
29
  const options = this.getOptions()
30
- const {cascade, ifExists, tableName} = digs(this, "cascade", "ifExists", "tableName")
30
+ const {cascade, ifExists, tableName} = this
31
31
  const sqls = []
32
32
  let sql = ""
33
33
 
@@ -1,6 +1,18 @@
1
+ // @ts-check
2
+
1
3
  import restArgsError from "../../utils/rest-args-error.js"
2
4
 
3
5
  export default class VelociousDatabaseQueryInsertBase {
6
+ /**
7
+ * @param {object} args
8
+ * @param {Record<string, any>} [args.data]
9
+ * @param {import("../drivers/base.js").default} args.driver
10
+ * @param {string} args.tableName
11
+ * @param {Array<string>} [args.columns]
12
+ * @param {boolean} [args.multiple]
13
+ * @param {string[]} [args.returnLastInsertedColumnNames]
14
+ * @param {Array<Array<string>>} [args.rows]
15
+ */
4
16
  constructor({columns, data, driver, multiple, tableName, returnLastInsertedColumnNames, rows, ...restArgs}) {
5
17
  if (!driver) throw new Error("No driver given to insert base")
6
18
  if (!tableName) throw new Error(`Invalid table name given to insert base: ${tableName}`)
@@ -47,7 +59,7 @@ export default class VelociousDatabaseQueryInsertBase {
47
59
  lastInsertedSQL += ` INSERTED.${driver.quoteColumn(columnName)}`
48
60
  }
49
61
 
50
- if (Object.keys(this.data).length <= 0) {
62
+ if (this.data && Object.keys(this.data).length <= 0) {
51
63
  sql += lastInsertedSQL
52
64
  }
53
65
  } else if (driver.getType() == "mysql" || driver.getType() == "pgsql" || (driver.getType() == "sqlite" && driver.supportsInsertIntoReturning())) {
@@ -86,7 +98,7 @@ export default class VelociousDatabaseQueryInsertBase {
86
98
  sql += ")"
87
99
  }
88
100
 
89
- if (this.returnLastInsertedColumnNames && driver.getType() == "mssql" && Object.keys(this.data).length > 0) {
101
+ if (this.returnLastInsertedColumnNames && driver.getType() == "mssql" && this.data && Object.keys(this.data).length > 0) {
90
102
  sql += lastInsertedSQL
91
103
  }
92
104
 
@@ -104,7 +116,7 @@ export default class VelociousDatabaseQueryInsertBase {
104
116
  sql += this._valuesSql(row)
105
117
  }
106
118
  } else {
107
- if (Object.keys(this.data).length > 0) {
119
+ if (this.data && Object.keys(this.data).length > 0) {
108
120
  sql += " VALUES "
109
121
  sql += this._valuesSql(Object.values(this.data))
110
122
  } else if (driver.getType() == "sqlite" || driver.getType() == "mssql") {
@@ -123,6 +135,9 @@ export default class VelociousDatabaseQueryInsertBase {
123
135
  return sql
124
136
  }
125
137
 
138
+ /**
139
+ * @param {any[]} data
140
+ */
126
141
  _valuesSql(data) {
127
142
  let count = 0
128
143
  let sql = "("
@@ -11,10 +11,10 @@ import WhereParser from "./where-parser.js"
11
11
  export default class VelociousDatabaseBaseQueryParser {
12
12
  /**
13
13
  * @param {object} args
14
- * @param {boolean} args.pretty
14
+ * @param {boolean} [args.pretty]
15
15
  * @param {import("../query/index.js").default} args.query
16
16
  */
17
- constructor({pretty, query}) {
17
+ constructor({pretty = false, query}) {
18
18
  if (!query) throw new Error("No query given")
19
19
 
20
20
  this.pretty = pretty
@@ -468,9 +468,6 @@ class VelociousDatabaseRecord {
468
468
  const columnNameToAttributeName = this.getColumnNameToAttributeNameMap()
469
469
  const attributeNameToColumnName = this.getAttributeNameToColumnNameMap()
470
470
 
471
- /** @type {Record<string, (this: VelociousDatabaseRecord) => unknown>} */
472
- const proto = /** @type {any} */ (this.prototype);
473
-
474
471
  for (const column of this._columns) {
475
472
  this._columnsAsHash[column.getName()] = column
476
473
 
@@ -480,17 +477,18 @@ class VelociousDatabaseRecord {
480
477
  attributeNameToColumnName[camelizedColumnName] = column.getName()
481
478
  columnNameToAttributeName[column.getName()] = camelizedColumnName
482
479
 
483
- proto[camelizedColumnName] = function() {
480
+ // @ts-expect-error
481
+ this.prototype[camelizedColumnName] = function() {
484
482
  return this.readAttribute(camelizedColumnName)
485
483
  }
486
484
 
487
485
  // @ts-expect-error
488
- proto[`set${camelizedColumnNameBigFirst}`] = function(newValue) {
489
- // @ts-expect-error
486
+ this.prototype[`set${camelizedColumnNameBigFirst}`] = function(newValue) {
490
487
  return this._setColumnAttribute(camelizedColumnName, newValue)
491
488
  }
492
489
 
493
- proto[`has${camelizedColumnNameBigFirst}`] = function() {
490
+ // @ts-expect-error
491
+ this.prototype[`has${camelizedColumnNameBigFirst}`] = function() {
494
492
  // @ts-expect-error
495
493
  let value = this[camelizedColumnName]()
496
494
 
@@ -539,24 +537,20 @@ class VelociousDatabaseRecord {
539
537
  const nameCamelized = inflection.camelize(name)
540
538
  const setterMethodName = `set${nameCamelized}`
541
539
 
542
- /** @type {Record<string, unknown>} */
543
540
  // @ts-expect-error
544
- const self = this
545
-
546
- /** @type {Record<string, (this: VelociousDatabaseRecord) => unknown>} */
547
- const proto = /** @type {any} */ (this.prototype);
548
-
549
- proto[name] = function getTranslatedAttribute() {
541
+ this.prototype[name] = function getTranslatedAttribute() {
550
542
  const locale = this._getConfiguration().getLocale()
551
543
 
552
544
  return this._getTranslatedAttributeWithFallback(name, locale)
553
545
  }
554
546
 
555
- proto[`has${nameCamelized}`] = function hasTranslatedAttribute() {
556
- const candidate = self[name]
547
+ // @ts-expect-error
548
+ this.prototype[`has${nameCamelized}`] = function hasTranslatedAttribute() {
549
+ // @ts-expect-error
550
+ const candidate = this[name]
557
551
 
558
552
  if (typeof candidate == "function") {
559
- const value = candidate()
553
+ const value = candidate.bind(this)()
560
554
 
561
555
  return this._hasAttribute(value)
562
556
  } else {
@@ -565,11 +559,9 @@ class VelociousDatabaseRecord {
565
559
  }
566
560
 
567
561
  // @ts-expect-error
568
- proto[setterMethodName] = function setTranslatedAttribute(newValue) {
569
- // @ts-expect-error
562
+ this.prototype[setterMethodName] = function setTranslatedAttribute(newValue) {
570
563
  const locale = this._getConfiguration().getLocale()
571
564
 
572
- // @ts-expect-error
573
565
  return this._setTranslatedAttribute(name, locale, newValue)
574
566
  }
575
567
 
@@ -577,6 +569,7 @@ class VelociousDatabaseRecord {
577
569
  const localeCamelized = inflection.camelize(locale)
578
570
  const getterMethodNameLocalized = `${name}${localeCamelized}`
579
571
  const setterMethodNameLocalized = `${setterMethodName}${localeCamelized}`
572
+ const hasMethodNameLocalized = `has${inflection.camelize(name)}${localeCamelized}`
580
573
 
581
574
  // @ts-expect-error
582
575
  this.prototype[getterMethodNameLocalized] = function getTranslatedAttributeWithLocale() {
@@ -587,6 +580,20 @@ class VelociousDatabaseRecord {
587
580
  this.prototype[setterMethodNameLocalized] = function setTranslatedAttributeWithLocale(newValue) {
588
581
  return this._setTranslatedAttribute(name, locale, newValue)
589
582
  }
583
+
584
+ // @ts-expect-error
585
+ this.prototype[hasMethodNameLocalized] = function hasTranslatedAttribute() {
586
+ // @ts-expect-error
587
+ const candidate = this[getterMethodNameLocalized]
588
+
589
+ if (typeof candidate == "function") {
590
+ const value = candidate.bind(this)()
591
+
592
+ return this._hasAttribute(value)
593
+ } else {
594
+ throw new Error(`Expected candidate to be a function but it was: ${typeof candidate}`)
595
+ }
596
+ }
590
597
  }
591
598
  }
592
599
  }
@@ -97,7 +97,7 @@ export default class TableColumn {
97
97
  getForeignKey() { return this.args?.foreignKey }
98
98
 
99
99
  /**
100
- * @param {boolean | object} newForeignKey
100
+ * @param {boolean | object | undefined} newForeignKey
101
101
  * @returns {void}
102
102
  */
103
103
  setForeignKey(newForeignKey) { this.args.foreignKey = newForeignKey }
@@ -138,7 +138,7 @@ export default class TableColumn {
138
138
  getMaxLength() { return this.args?.maxLength }
139
139
 
140
140
  /**
141
- * @param {number} newMaxLength
141
+ * @param {number | undefined} newMaxLength
142
142
  * @returns {void}
143
143
  */
144
144
  setMaxLength(newMaxLength) { this.args.maxLength = newMaxLength }
@@ -171,7 +171,7 @@ export default class TableColumn {
171
171
  getType() { return this.args?.type }
172
172
 
173
173
  /**
174
- * @param {string} newType
174
+ * @param {string | undefined} newType
175
175
  * @returns {void}
176
176
  */
177
177
  setType(newType) { this.args.type = newType }
@@ -98,6 +98,16 @@ export default class DbGenerateModel extends BaseCommand {
98
98
  fileContent += ` ${name}() { return this._getTranslatedAttributeWithFallback("${name}", this._getConfiguration().getLocale()) }\n`
99
99
  methodsCount++
100
100
 
101
+ const hasName = `has${inflection.camelize(name)}`
102
+
103
+ fileContent += `\n`
104
+ fileContent += " /**\n"
105
+ fileContent += ` * @abstract\n`
106
+ fileContent += ` * @returns {boolean}\n`
107
+ fileContent += " */\n"
108
+ fileContent += ` ${hasName}() { throw new Error("${hasName} not implemented") }\n`
109
+ methodsCount++
110
+
101
111
  for (const locale of this.getConfiguration().getLocales()) {
102
112
  const localeMethodName = `${name}${inflection.camelize(locale)}`
103
113
 
@@ -109,7 +119,16 @@ export default class DbGenerateModel extends BaseCommand {
109
119
  }
110
120
 
111
121
  fileContent += ` ${localeMethodName}() { return this._getTranslatedAttributeWithFallback("${name}", "${locale}") }\n`
122
+ methodsCount++
123
+
124
+ const localeHasName = `has${inflection.camelize(localeMethodName)}`
112
125
 
126
+ fileContent += `\n`
127
+ fileContent += " /**\n"
128
+ fileContent += ` * @abstract\n`
129
+ fileContent += ` * @returns {boolean}\n`
130
+ fileContent += " */\n"
131
+ fileContent += ` ${localeHasName}() { throw new Error("${localeHasName} not implemented") }\n`
113
132
  methodsCount++
114
133
  }
115
134
  }