velocious 1.0.94 → 1.0.96

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "velocious": "bin/velocious.js"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.94",
6
+ "version": "1.0.96",
7
7
  "main": "index.js",
8
8
  "scripts": {
9
9
  "lint": "eslint",
@@ -15,37 +15,51 @@ import restArgsError from "../../utils/rest-args-error.js"
15
15
  * @typedef {Record<string, boolean|NestedPreloadRecord>} NestedPreloadRecord
16
16
  */
17
17
 
18
+ /**
19
+ * A generic query over some model type.
20
+ */
18
21
  export default class VelociousDatabaseQuery {
19
22
  /**
20
23
  * @param {object} args
21
- * @template {import("../drivers/base.js").default} Tdriver
22
- * @param {Tdriver} args.driver
23
- * @template {import("./from-base.js").default} TfromBase
24
- * @param {TfromBase[]} args.froms
25
- * @param {string[]} args.groups
26
- * @template {import("./join-base.js").default} TjoinBase
27
- * @param {TjoinBase[]} args.joins
24
+ * @param {import("../drivers/base.js").default} args.driver
25
+ * @param {Array<import("./from-base.js").default>} [args.froms]
26
+ * @param {string[]} [args.groups]
27
+ * @param {Array<import("./join-base.js").default>} [args.joins]
28
28
  * @param {import("../handler.js").default} args.handler
29
- * @param {number} args.limit
30
- * @template {import("../record/index.js").default} Trecord
31
- * @param {typeof Trecord} args.modelClass
32
- * @param {number} args.offset
33
- * @template {import("./order-basejs").default} TorderBase
34
- * @param {TorderBase[]} args.orders
35
- * @param {number} args.page
29
+ * @param {number | null} [args.limit]
30
+ * @param {typeof import("../record/index.js").default} args.modelClass
31
+ * @param {number | null} [args.offset]
32
+ * @param {Array<import("./order-base.js").default>} [args.orders]
33
+ * @param {number | null} [args.page]
36
34
  * @param {number} args.perPage
37
- * @param {NestedPreloadRecord} args.preload
38
- * @param {Record<string, Array<string>>} args.selects
39
- * @template {import("./where-base.js").default} TwhereBase
40
- * @param {TwhereBase[]} args.wheres
35
+ * @param {NestedPreloadRecord} [args.preload]
36
+ * @param {Array<import("./select-base.js").default>} [args.selects]
37
+ * @param {Array<import("./where-base.js").default>} [args.wheres]
41
38
  */
42
- constructor({driver, froms = [], groups = [], joins = [], handler, limit = null, modelClass, offset = null, orders = [], page = null, perPage, preload = {}, selects = [], wheres = [], ...restArgs}) {
39
+ constructor({
40
+ driver,
41
+ froms = [],
42
+ groups = [],
43
+ joins = [],
44
+ handler,
45
+ limit = null,
46
+ modelClass,
47
+ offset = null,
48
+ orders = [],
49
+ page = null,
50
+ perPage,
51
+ preload = {},
52
+ selects = [],
53
+ wheres = [],
54
+ ...restArgs
55
+ }) {
43
56
  if (!driver) throw new Error("No driver given to query")
44
57
  if (!handler) throw new Error("No handler given to query")
45
58
 
46
59
  restArgsError(restArgs)
47
60
 
48
61
  this.driver = driver
62
+
49
63
  this.handler = handler
50
64
  this.logger = new Logger(this)
51
65
  this.modelClass = modelClass
@@ -147,6 +161,11 @@ export default class VelociousDatabaseQuery {
147
161
  */
148
162
  getOptions() { return this.driver.options() }
149
163
 
164
+ /**
165
+ * @returns {Array<import("./select-base.js").default>}
166
+ */
167
+ getSelects() { return this._selects }
168
+
150
169
  /**
151
170
  * @returns {Promise<void>}
152
171
  */
@@ -160,7 +179,7 @@ export default class VelociousDatabaseQuery {
160
179
 
161
180
  /**
162
181
  * @param {number|string} recordId
163
- * @returns {Promise<InstanceType<this["modelClass"]>>}
182
+ * @returns {Promise<import("../record/index.js").default>}
164
183
  */
165
184
  async find(recordId) {
166
185
  const conditions = {}
@@ -179,7 +198,7 @@ export default class VelociousDatabaseQuery {
179
198
 
180
199
  /**
181
200
  * @param {object} conditions
182
- * @returns {Promise<InstanceType<this["modelClass"]>>}
201
+ * @returns {Promise<import("../record/index.js").default|null>}
183
202
  */
184
203
  async findBy(conditions) {
185
204
  const newConditions = {}
@@ -195,7 +214,7 @@ export default class VelociousDatabaseQuery {
195
214
 
196
215
  /**
197
216
  * @param {...Parameters<this["findOrInitializeBy"]>} args
198
- * @returns {Promise<InstanceType<this["modelClass"]>>}
217
+ * @returns {Promise<import("../record/index.js").default>}
199
218
  */
200
219
  async findOrCreateBy(...args) {
201
220
  const record = await this.findOrInitializeBy(...args)
@@ -209,7 +228,7 @@ export default class VelociousDatabaseQuery {
209
228
 
210
229
  /**
211
230
  * @param {object} conditions
212
- * @returns {Promise<InstanceType<this["modelClass"]>>}
231
+ * @returns {Promise<import("../record/index.js").default>}
213
232
  */
214
233
  async findByOrFail(conditions) {
215
234
  const newConditions = {}
@@ -232,7 +251,7 @@ export default class VelociousDatabaseQuery {
232
251
  /**
233
252
  * @param {object} conditions
234
253
  * @param {function() : void} callback
235
- * @returns {Promise<InstanceType<this["modelClass"]>>}
254
+ * @returns {Promise<import("../record/index.js").default>}
236
255
  */
237
256
  async findOrInitializeBy(conditions, callback) {
238
257
  const record = await this.findBy(conditions)
@@ -249,7 +268,7 @@ export default class VelociousDatabaseQuery {
249
268
  }
250
269
 
251
270
  /**
252
- * @returns {Promise<InstanceType<this["modelClass"]>>}
271
+ * @returns {Promise<import("../record/index.js").default>}
253
272
  */
254
273
  async first() {
255
274
  const newQuery = this.clone().limit(1).reorder(`${this.driver.quoteTable(this.modelClass.tableName())}.${this.driver.quoteColumn(this.modelClass.orderableColumn())}`)
@@ -298,7 +317,7 @@ export default class VelociousDatabaseQuery {
298
317
  }
299
318
 
300
319
  /**
301
- * @returns {Promise<InstanceType<this["modelClass"]>>}
320
+ * @returns {Promise<import("../record/index.js").default>}
302
321
  */
303
322
  async last() {
304
323
  const primaryKey = this.modelClass.primaryKey()
@@ -435,7 +454,7 @@ export default class VelociousDatabaseQuery {
435
454
 
436
455
  /**
437
456
  * Converts query results to array of model instances
438
- * @returns {Promise<Array<InstanceType<this["modelClass"]>>>}
457
+ * @returns {Promise<Array<import("../record/index.js").default>>}
439
458
  */
440
459
  async toArray() {
441
460
  const models = []
@@ -1,7 +1,15 @@
1
1
  import {digs} from "diggerize"
2
+ import restArgsError from "../../utils/rest-args-error.js"
2
3
 
3
4
  export default class VelociousDatabaseQueryParserSelectParser {
4
- constructor({pretty, query}) {
5
+ /**
6
+ * @param {object} args
7
+ * @param {boolean} args.pretty
8
+ * @param {import("../query/index.js").default} args.query
9
+ */
10
+ constructor({pretty, query, ...restArgs}) {
11
+ restArgsError(restArgs)
12
+
5
13
  this.pretty = pretty
6
14
  this.query = query
7
15
  }
@@ -34,7 +42,7 @@ export default class VelociousDatabaseQueryParserSelectParser {
34
42
  }
35
43
  }
36
44
 
37
- if (query._selects.length == 0) {
45
+ if (query.getSelects().length == 0) {
38
46
  if (query.modelClass) {
39
47
  sql += `${query.modelClass.connection().quoteTable(query.modelClass.tableName())}.*`
40
48
  } else {
@@ -44,8 +44,7 @@ class ValidationError extends Error {
44
44
 
45
45
  class VelociousDatabaseRecord {
46
46
  /**
47
- * @template T extends import("./validators/base.js").default
48
- * @returns {Record<string, T>}
47
+ * @returns {Record<string, import("./validators/base.js").default>}
49
48
  */
50
49
  static validatorTypes() {
51
50
  if (!this._validatorTypes) this._validatorTypes = {}
@@ -55,16 +54,14 @@ class VelociousDatabaseRecord {
55
54
 
56
55
  /**
57
56
  * @param {string} name
58
- * @template T extends import("./validators/base.js").default
59
- * @param {T} validatorClass
57
+ * @param {import("./validators/base.js").default} validatorClass
60
58
  */
61
59
  static registerValidatorType(name, validatorClass) {
62
60
  this.validatorTypes()[name] = validatorClass
63
61
  }
64
62
 
65
63
  /**
66
- * @template T extends import("./validators/base.js").default
67
- * @returns {T}
64
+ * @returns {import("./validators/base.js").default}
68
65
  */
69
66
  static getValidatorType(validatorName) {
70
67
  if (!(validatorName in this.validatorTypes())) throw new Error(`Validator type ${validatorName} not found`)
@@ -152,6 +149,10 @@ class VelociousDatabaseRecord {
152
149
  return this.getRelationshipByName(relationshipName)
153
150
  }
154
151
 
152
+ this.prototype[`${relationshipName}Loaded`] = function() {
153
+ return this.getRelationshipByName(relationshipName).loaded()
154
+ }
155
+
155
156
  this.prototype[`load${inflection.camelize(relationshipName)}`] = async function() {
156
157
  await this.getRelationshipByName(relationshipName).load()
157
158
  }
@@ -189,8 +190,7 @@ class VelociousDatabaseRecord {
189
190
  }
190
191
 
191
192
  /**
192
- * @template T extends import("./relationships/index.js").default
193
- * @returns {T}
193
+ * @returns {import("./relationships/base.js").default}
194
194
  */
195
195
  static getRelationshipByName(relationshipName) {
196
196
  if (!this._relationships) this._relationships = {}
@@ -203,7 +203,7 @@ class VelociousDatabaseRecord {
203
203
  }
204
204
 
205
205
  /**
206
- * @returns {Array}
206
+ * @returns {Array<import("./relationships/base.js").default>}
207
207
  */
208
208
  static getRelationships() {
209
209
  if (this._relationships) return Object.values(this._relationships)
@@ -269,7 +269,7 @@ class VelociousDatabaseRecord {
269
269
  }
270
270
 
271
271
  /**
272
- * @param {object} attributes
272
+ * @param {Record<string, any>} attributes
273
273
  * @returns {Promise<InstanceType<typeof this>>}
274
274
  */
275
275
  static async create(attributes) {
@@ -1050,7 +1050,7 @@ class VelociousDatabaseRecord {
1050
1050
 
1051
1051
  /**
1052
1052
  * Assigns the given attributes to the record.
1053
- * @param {object} attributesToAssign
1053
+ * @param {Record<string, any>} attributesToAssign
1054
1054
  * @returns {void}
1055
1055
  */
1056
1056
  assign(attributesToAssign) {
@@ -67,6 +67,13 @@ export default class VelociousDatabaseRecordBaseRelationship {
67
67
  */
68
68
  getRelationshipName() { return this.relationshipName }
69
69
 
70
+ /**
71
+ * @returns {boolean}
72
+ */
73
+ getPolymorphic() {
74
+ return this._polymorphic
75
+ }
76
+
70
77
  /**
71
78
  * @returns {string} The name of the foreign key, e.g. "id" etc.
72
79
  */
@@ -78,11 +85,12 @@ export default class VelociousDatabaseRecordBaseRelationship {
78
85
  getType() { return this.type }
79
86
 
80
87
  /**
81
- * @template T extends import("../index.js").default
82
- * @returns {typeof T} The target model class for this relationship, e.g. if the relationship is "posts" then the target model class is the Post class.
88
+ * @returns {typeof import("../index.js").default} The target model class for this relationship, e.g. if the relationship is "posts" then the target model class is the Post class.
83
89
  */
84
90
  getTargetModelClass() {
85
- if (this.className) {
91
+ if (this.getPolymorphic()) {
92
+ return null
93
+ } else if (this.className) {
86
94
  return this.modelClass._getConfiguration().getModelClass(this.className)
87
95
  } else if (this.klass) {
88
96
  return this.klass
@@ -25,7 +25,7 @@ export default class DbGenerateModel extends BaseCommand {
25
25
 
26
26
  let fileContent = `import Record from "velocious/src/database/record/index.js"\n\n`
27
27
 
28
- fileContent += `export default class ${modelNameCamelized} extends Record {\n`
28
+ fileContent += `export default class ${modelNameCamelized}Base extends Record {\n`
29
29
 
30
30
  const columns = await modelClass._getTable().getColumns()
31
31
  let methodsCount = 0
@@ -72,6 +72,99 @@ export default class DbGenerateModel extends BaseCommand {
72
72
  methodsCount++
73
73
  }
74
74
 
75
+ for (const relationship of modelClass.getRelationships()) {
76
+ let fileName, fullFilePath
77
+
78
+ if (relationship.getPolymorphic()) {
79
+ fileName = "velocious/src/database/record/index.js"
80
+ } else {
81
+ fileName = inflection.dasherize(inflection.underscore(relationship.getTargetModelClass().name))
82
+ fullFilePath = `src/models/${fileName}.js`
83
+ }
84
+
85
+ if (methodsCount > 0) {
86
+ fileContent += "\n"
87
+ }
88
+
89
+ if (relationship.getType() == "belongsTo" || relationship.getType() == "hasOne") {
90
+ let modelFilePath
91
+
92
+ if (fullFilePath && await fileExists(fullFilePath)) {
93
+ modelFilePath = `../models/${fileName}.js`
94
+ } else {
95
+ modelFilePath = "velocious/src/database/record/index.js"
96
+ }
97
+
98
+ fileContent += " /**\n"
99
+ fileContent += " * @interface\n"
100
+ fileContent += ` * @returns {import("${modelFilePath}").default}\n`
101
+ fileContent += " */\n"
102
+ fileContent += ` ${relationship.getRelationshipName()}() { return this.getRelationshipByName("${relationship.getRelationshipName()}").loaded() }\n`
103
+
104
+ fileContent += "\n"
105
+ fileContent += " /**\n"
106
+ fileContent += " * @interface\n"
107
+ fileContent += ` * @returns {import("${modelFilePath}").default}\n`
108
+ fileContent += " */\n"
109
+ fileContent += ` build${inflection.camelize(relationship.getRelationshipName())}() { throw new Error("Not implemented") }\n`
110
+
111
+ fileContent += "\n"
112
+ fileContent += " /**\n"
113
+ fileContent += " * @interface\n"
114
+ fileContent += " * @returns {Promise<void>}\n"
115
+ fileContent += " */\n"
116
+ fileContent += ` load${inflection.camelize(relationship.getRelationshipName())}() { throw new Error("Not implemented") }\n`
117
+
118
+ fileContent += "\n"
119
+ fileContent += " /**\n"
120
+ fileContent += " * @interface\n"
121
+ fileContent += ` * @param {import("${modelFilePath}").default} newModel\n`
122
+ fileContent += ` * @returns {void}\n`
123
+ fileContent += " */\n"
124
+ fileContent += ` set${inflection.camelize(relationship.getRelationshipName())}() { throw new Error("Not implemented") }\n`
125
+ } else if (relationship.getType() == "hasMany") {
126
+ let recordImport
127
+
128
+ if (fullFilePath && await fileExists(fullFilePath)) {
129
+ recordImport = `../models/${fileName}.js`
130
+ } else {
131
+ recordImport = "velocious/src/database/record/index.js"
132
+ }
133
+
134
+ fileContent += " /**\n"
135
+ fileContent += " * @interface\n"
136
+ fileContent += ` * @returns {import("velocious/src/database/query/index.js").default<import("${recordImport}").default>}\n`
137
+ fileContent += " */\n"
138
+ fileContent += ` ${relationship.getRelationshipName()}() { return this.getRelationshipByName("${relationship.getRelationshipName()}") }\n`
139
+
140
+ fileContent += "\n"
141
+ fileContent += " /**\n"
142
+ fileContent += " * @interface\n"
143
+ fileContent += ` * @returns {Array<import("${recordImport}").default>}\n`
144
+ fileContent += " */\n"
145
+ fileContent += ` ${relationship.getRelationshipName()}Loaded() { return this.getRelationshipByName("${relationship.getRelationshipName()}").loaded() }\n`
146
+
147
+ fileContent += "\n"
148
+ fileContent += " /**\n"
149
+ fileContent += " * @interface\n"
150
+ fileContent += " * @returns {Promise<void>}\n"
151
+ fileContent += " */\n"
152
+ fileContent += ` load${inflection.camelize(relationship.getRelationshipName())}() { throw new Error("Not implemented") }\n`
153
+
154
+ fileContent += "\n"
155
+ fileContent += " /**\n"
156
+ fileContent += " * @interface\n"
157
+ fileContent += ` * @param {Array<import("${recordImport}").default>} newModels\n`
158
+ fileContent += " * @returns {void>}\n"
159
+ fileContent += " */\n"
160
+ fileContent += ` set${inflection.camelize(relationship.getRelationshipName())}() { throw new Error("Not implemented") }\n`
161
+ } else {
162
+ throw new Error(`Unknown relationship type: ${relationship.getType()}`)
163
+ }
164
+
165
+ methodsCount++
166
+ }
167
+
75
168
  fileContent += "}\n"
76
169
 
77
170
  await fs.writeFile(modelPath, fileContent)