velocious 1.0.85 → 1.0.87
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/bin/velocious.js +10 -1
- package/eslint.config.js +33 -0
- package/package.json +7 -2
- package/peak_flow.yml +6 -0
- package/spec/cli/commands/db/create-spec.js +4 -0
- package/spec/cli/commands/db/migrate-spec.js +4 -2
- package/spec/cli/commands/db/rollback-spec.js +179 -0
- package/spec/cli/commands/destroy/migration-spec.js +4 -0
- package/spec/cli/commands/generate/migration-spec.js +4 -0
- package/spec/cli/commands/init-spec.js +4 -0
- package/spec/dummy/index.js +7 -4
- package/spec/dummy/src/config/configuration.example.js +2 -0
- package/spec/dummy/src/config/configuration.peakflow.mariadb.js +2 -0
- package/spec/dummy/src/config/configuration.peakflow.mssql.js +2 -0
- package/spec/dummy/src/config/configuration.peakflow.pgsql.js +2 -0
- package/spec/dummy/src/config/configuration.peakflow.sqlite.js +2 -0
- package/spec/dummy/src/database/migrations/20250921121002-create-project-details.js +3 -1
- package/src/application.js +12 -0
- package/src/cli/base-command.js +9 -5
- package/src/cli/browser-cli.js +37 -0
- package/src/cli/commands/db/create.js +5 -5
- package/src/cli/commands/db/drop.js +4 -5
- package/src/cli/commands/db/migrate.js +6 -10
- package/src/cli/commands/db/reset.js +8 -12
- package/src/cli/commands/db/rollback.js +15 -0
- package/src/cli/commands/destroy/migration.js +2 -2
- package/src/cli/commands/generate/migration.js +3 -6
- package/src/cli/commands/generate/model.js +3 -6
- package/src/cli/commands/init.js +5 -8
- package/src/cli/commands/server.js +3 -3
- package/src/cli/commands/test.js +1 -1
- package/src/cli/index.js +15 -63
- package/src/cli/use-browser-cli.js +25 -0
- package/src/configuration-resolver.js +1 -1
- package/src/configuration.js +118 -9
- package/src/controller.js +29 -0
- package/src/database/drivers/base-column.js +14 -0
- package/src/database/drivers/base-columns-index.js +3 -0
- package/src/database/drivers/base-foreign-key.js +3 -0
- package/src/database/drivers/base-table.js +3 -0
- package/src/database/drivers/base.js +55 -1
- package/src/database/drivers/mssql/index.js +64 -1
- package/src/database/drivers/mysql/columns-index.js +0 -1
- package/src/database/drivers/sqlite/base.js +39 -0
- package/src/database/drivers/sqlite/connection-remote.js +1 -1
- package/src/database/drivers/sqlite/sql/alter-table.js +1 -1
- package/src/database/drivers/sqlite/sql/delete.js +15 -10
- package/src/database/migration/index.js +122 -1
- package/src/database/migrator/files-finder.js +13 -1
- package/src/database/migrator.js +125 -24
- package/src/database/pool/single-multi-use.js +1 -1
- package/src/database/query/alter-table-base.js +11 -0
- package/src/database/query/base.js +7 -0
- package/src/database/query/create-database-base.js +3 -0
- package/src/database/query/create-index-base.js +3 -1
- package/src/database/query/create-table-base.js +3 -0
- package/src/database/query/drop-table-base.js +4 -1
- package/src/database/query/from-base.js +7 -0
- package/src/database/query/index.js +96 -6
- package/src/database/query/insert-base.js +6 -0
- package/src/database/query/join-base.js +3 -0
- package/src/database/query/order-base.js +3 -0
- package/src/database/query/select-base.js +3 -0
- package/src/database/query/update-base.js +3 -0
- package/src/database/query/where-base.js +3 -0
- package/src/database/record/index.js +272 -19
- package/src/database/record/instance-relationships/base.js +66 -1
- package/src/database/record/relationships/base.js +41 -1
- package/src/database/record/validators/base.js +10 -0
- package/src/database/record/validators/presence.js +1 -1
- package/src/database/table-data/table-column.js +37 -3
- package/src/database/table-data/table-index.js +1 -1
- package/src/database/use-database.js +2 -2
- package/src/environment-handlers/base.js +53 -0
- package/src/environment-handlers/browser.js +171 -0
- package/src/environment-handlers/node.js +162 -0
- package/src/http-server/client/request-buffer/index.js +9 -9
- package/src/http-server/index.js +6 -0
- package/src/http-server/worker-handler/index.js +20 -19
- package/src/initializer.js +17 -1
- package/src/logger.js +3 -0
- package/src/routes/app-routes.js +6 -2
- package/src/routes/base-route.js +1 -1
- package/src/routes/get-route.js +1 -1
- package/src/routes/namespace-route.js +1 -1
- package/src/routes/post-route.js +1 -1
- package/src/routes/resolver.js +1 -1
- package/src/routes/resource-route.js +1 -1
- package/src/templates/configuration.js +4 -0
- package/src/testing/request-client.js +26 -3
- package/src/testing/test-files-finder.js +2 -2
- package/src/testing/test-runner.js +74 -28
- package/src/testing/test.js +62 -0
- package/src/utils/file-exists.js +7 -5
- package/src/utils/rest-args-error.js +5 -3
|
@@ -6,12 +6,37 @@ export default class VelociousDatabaseRecordBaseInstanceRelationship {
|
|
|
6
6
|
this.relationship = relationship
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @returns {boolean} Whether the relationship should be auto-saved before saving the parent model
|
|
11
|
+
*/
|
|
9
12
|
getAutoSave() { return this._autoSave }
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {boolean} newAutoSaveValue Whether the relationship should be auto-saved before saving the parent model
|
|
16
|
+
* @returns {void}
|
|
17
|
+
*/
|
|
10
18
|
setAutoSave(newAutoSaveValue) { this._autoSave = newAutoSaveValue }
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {boolean} newValue Whether the relationship is dirty (has been modified)
|
|
22
|
+
* @returns {void}
|
|
23
|
+
*/
|
|
11
24
|
setDirty(newValue) { this._dirty = newValue }
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @returns {boolean} Whether the relationship is dirty (has been modified)
|
|
28
|
+
*/
|
|
12
29
|
getDirty() { return this._dirty }
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @returns {boolean} Whether the relationship has been preloaded
|
|
33
|
+
*/
|
|
13
34
|
isLoaded() { return Boolean(this._loaded) }
|
|
14
35
|
|
|
36
|
+
/**
|
|
37
|
+
* @template T extends import("../index.js").default
|
|
38
|
+
* @returns {T|Array<T>} The loaded model or models (depending on relationship type)
|
|
39
|
+
*/
|
|
15
40
|
loaded() {
|
|
16
41
|
if (!this._preloaded && this.model.isPersisted()) {
|
|
17
42
|
throw new Error(`${this.model.constructor.name}#${this.relationship.getRelationshipName()} hasn't been preloaded`)
|
|
@@ -20,13 +45,53 @@ export default class VelociousDatabaseRecordBaseInstanceRelationship {
|
|
|
20
45
|
return this._loaded
|
|
21
46
|
}
|
|
22
47
|
|
|
48
|
+
/**
|
|
49
|
+
* @template T extends import("../index.js").default
|
|
50
|
+
* @param {T|Array<T>} model
|
|
51
|
+
*/
|
|
23
52
|
setLoaded(model) { this._loaded = model }
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @template T extends import("../index.js").default
|
|
56
|
+
* @returns {T|Array<T>} The loaded model or models (depending on relationship type)
|
|
57
|
+
*/
|
|
24
58
|
getPreloaded() { return this._preloaded }
|
|
25
|
-
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @template T extends import("../index.js").default
|
|
62
|
+
* @param {T|Array<T>} preloadedModelOrModels
|
|
63
|
+
*/
|
|
64
|
+
setPreloaded(preloadedModelOrModels) { this._preloaded = preloadedModelOrModels }
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @returns {string} The foreign key for this relationship
|
|
68
|
+
*/
|
|
26
69
|
getForeignKey() { return this.getRelationship().getForeignKey() }
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @template T extends import("../index.js").default
|
|
73
|
+
* @returns {T} model
|
|
74
|
+
*/
|
|
27
75
|
getModel() { return this.model }
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @returns {string} The primary key for this relationship's model
|
|
79
|
+
*/
|
|
28
80
|
getPrimaryKey() { return this.getRelationship().getPrimaryKey() }
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @template T extends import("../relationships/base.js").default
|
|
84
|
+
* @returns {T} The relationship object that this instance relationship is based on
|
|
85
|
+
*/
|
|
29
86
|
getRelationship() { return this.relationship }
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @returns {typeof import("../index.js").default} The model class that this instance relationship
|
|
90
|
+
*/
|
|
30
91
|
getTargetModelClass() { return this.getRelationship().getTargetModelClass() }
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @returns {string} The type of relationship (e.g. "has_many", "belongs_to", etc.)
|
|
95
|
+
*/
|
|
31
96
|
getType() { return this.getRelationship().getType() }
|
|
32
97
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import restArgsError from "../../../utils/rest-args-error.js"
|
|
2
2
|
|
|
3
3
|
export default class VelociousDatabaseRecordBaseRelationship {
|
|
4
|
-
constructor({className, configuration, dependent, foreignKey, inverseOf, klass, modelClass, primaryKey = "id", relationshipName, through, type, ...restArgs}) {
|
|
4
|
+
constructor({className, configuration, dependent, foreignKey, inverseOf, klass, modelClass, primaryKey = "id", relationshipName, through, type, ...restArgs}) { // eslint-disable-line no-unused-vars
|
|
5
5
|
restArgsError(restArgs)
|
|
6
6
|
|
|
7
7
|
if (!modelClass) throw new Error(`'modelClass' wasn't given for ${relationshipName}`)
|
|
@@ -20,12 +20,52 @@ export default class VelociousDatabaseRecordBaseRelationship {
|
|
|
20
20
|
this.type = type
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* @returns {string} What will be done when the parent record is destroyed. E.g. "destroy", "nullify", "restrict" etc.
|
|
25
|
+
*/
|
|
23
26
|
getDependent() { return this._dependent }
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @interface
|
|
30
|
+
* @returns {string} The name of the foreign key, e.g. "user_id", "post_id" etc.
|
|
31
|
+
*/
|
|
32
|
+
getForeignKey() {
|
|
33
|
+
throw new Error("getForeignKey not implemented")
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @interface
|
|
38
|
+
* @returns {string} The name of the inverse relationship, e.g. "posts", "comments" etc.
|
|
39
|
+
*/
|
|
40
|
+
getInverseOf() {
|
|
41
|
+
throw new Error("getInverseOf not implemented")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @template T extends import("../index.js").default
|
|
46
|
+
* @returns {typeof T}
|
|
47
|
+
*/
|
|
24
48
|
getModelClass() { return this.modelClass }
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @returns {string} The name of the relationship, e.g. "posts", "user", "comments" etc.
|
|
52
|
+
*/
|
|
25
53
|
getRelationshipName() { return this.relationshipName }
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @returns {string} The name of the foreign key, e.g. "id" etc.
|
|
57
|
+
*/
|
|
26
58
|
getPrimaryKey() { return this._primaryKey }
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @returns {string} The type of the relationship, e.g. "has_many", "belongs_to", "has_one", "has_and_belongs_to_many" etc.
|
|
62
|
+
*/
|
|
27
63
|
getType() { return this.type }
|
|
28
64
|
|
|
65
|
+
/**
|
|
66
|
+
* @template T extends import("../index.js").default
|
|
67
|
+
* @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.
|
|
68
|
+
*/
|
|
29
69
|
getTargetModelClass() {
|
|
30
70
|
if (this.className) {
|
|
31
71
|
return this.modelClass._getConfiguration().getModelClass(this.className)
|
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
export default class VelociousDatabaseRecordValidatorsBase {
|
|
2
|
+
/**
|
|
3
|
+
* @interface
|
|
4
|
+
* @template T extends import("../index.js").default
|
|
5
|
+
* @param {object} args
|
|
6
|
+
* @param {T} args.model
|
|
7
|
+
* @param {string} args.attributeName
|
|
8
|
+
*/
|
|
9
|
+
async validate({model, attributeName}) { // eslint-disable-line no-unused-vars
|
|
10
|
+
throw new Error("validate not implemented")
|
|
11
|
+
}
|
|
2
12
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Base from "./base.js"
|
|
2
2
|
|
|
3
3
|
export default class VelociousDatabaseRecordValidatorsPresence extends Base {
|
|
4
|
-
validate({model, attributeName}) {
|
|
4
|
+
async validate({model, attributeName}) {
|
|
5
5
|
const attributeValue = model.readAttribute(attributeName)?.trim()
|
|
6
6
|
|
|
7
7
|
if (!attributeValue) {
|
|
@@ -5,7 +5,7 @@ import TableForeignKey from "./table-foreign-key.js"
|
|
|
5
5
|
export default class TableColumn {
|
|
6
6
|
constructor(name, args) {
|
|
7
7
|
if (args) {
|
|
8
|
-
const {autoIncrement, default: columnDefault, foreignKey, index, isNewColumn, maxLength, name, null: argsNull, primaryKey, type, ...restArgs} = args
|
|
8
|
+
const {autoIncrement, default: columnDefault, dropColumn, foreignKey, index, isNewColumn, maxLength, name, null: argsNull, primaryKey, type, ...restArgs} = args // eslint-disable-line no-unused-vars
|
|
9
9
|
|
|
10
10
|
if (Object.keys(args).length == 0) {
|
|
11
11
|
throw new Error("Empty args given")
|
|
@@ -18,19 +18,50 @@ export default class TableColumn {
|
|
|
18
18
|
this.name = name
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* @returns {string} name
|
|
23
|
+
*/
|
|
21
24
|
getName() { return this.name }
|
|
22
25
|
|
|
26
|
+
/**
|
|
27
|
+
* @returns {string}
|
|
28
|
+
*/
|
|
23
29
|
getNewName() { return this._newName }
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} newName
|
|
33
|
+
* @returns {void}
|
|
34
|
+
*/
|
|
24
35
|
setNewName(newName) { this._newName = newName }
|
|
25
36
|
|
|
37
|
+
/**
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
26
40
|
getActualName() { return this.getNewName() || this.getName() }
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
/**
|
|
43
|
+
* @returns {boolean}
|
|
44
|
+
*/
|
|
45
|
+
getAutoIncrement() { return this.args?.autoIncrement || false }
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {boolean} newAutoIncrement
|
|
49
|
+
* @returns {void}
|
|
50
|
+
*/
|
|
29
51
|
setAutoIncrement(newAutoIncrement) { this.args.autoIncrement = newAutoIncrement }
|
|
30
52
|
|
|
31
53
|
getDefault() { return this.args?.default }
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
32
58
|
setDefault(newDefault) { this.args.default = newDefault }
|
|
33
59
|
|
|
60
|
+
/**
|
|
61
|
+
* @returns {boolean}
|
|
62
|
+
*/
|
|
63
|
+
getDropColumn() { return this.args?.dropColumn || false }
|
|
64
|
+
|
|
34
65
|
getForeignKey() { return this.args?.foreignKey }
|
|
35
66
|
setForeignKey(newForeignKey) { this.args.foreignKey = newForeignKey }
|
|
36
67
|
|
|
@@ -49,7 +80,10 @@ export default class TableColumn {
|
|
|
49
80
|
getType() { return this.args?.type }
|
|
50
81
|
setType(newType) { this.args.type = newType }
|
|
51
82
|
|
|
52
|
-
|
|
83
|
+
/**
|
|
84
|
+
* @returns {boolean}
|
|
85
|
+
*/
|
|
86
|
+
isNewColumn() { return this.args?.isNewColumn || false }
|
|
53
87
|
|
|
54
88
|
getSQL({forAlterTable, driver}) {
|
|
55
89
|
const databaseType = driver.getType()
|
|
@@ -3,7 +3,7 @@ import restArgsError from "../../utils/rest-args-error.js"
|
|
|
3
3
|
export default class TableIndex {
|
|
4
4
|
constructor(columns, args) {
|
|
5
5
|
if (args) {
|
|
6
|
-
const {name, unique, ...restArgs} = args
|
|
6
|
+
const {name, unique, ...restArgs} = args // eslint-disable-line no-unused-vars
|
|
7
7
|
|
|
8
8
|
restArgsError(restArgs)
|
|
9
9
|
}
|
|
@@ -5,7 +5,7 @@ import Configuration from "../configuration.js"
|
|
|
5
5
|
import Migrator from "./migrator.js"
|
|
6
6
|
import restArgsError from "../utils/rest-args-error.js"
|
|
7
7
|
|
|
8
|
-
const loadMigrations = function loadMigrations({
|
|
8
|
+
const loadMigrations = function loadMigrations({migrationsRequireContextCallback, ...restArgs}) {
|
|
9
9
|
const instance = React.useMemo(() => ({running: false}), [])
|
|
10
10
|
const {isServer} = useEnvSense()
|
|
11
11
|
const [loaded, setLoaded] = React.useState(false)
|
|
@@ -18,7 +18,7 @@ const loadMigrations = function loadMigrations({migrationsRequireContext, ...res
|
|
|
18
18
|
const migrator = new Migrator({configuration: Configuration.current()})
|
|
19
19
|
|
|
20
20
|
await migrator.prepare()
|
|
21
|
-
await migrator.migrateFilesFromRequireContext(
|
|
21
|
+
await migrator.migrateFilesFromRequireContext(await migrationsRequireContextCallback())
|
|
22
22
|
})
|
|
23
23
|
|
|
24
24
|
await Configuration.current().initialize()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export default class VelociousEnvironmentHandlerBase {
|
|
2
|
+
/**
|
|
3
|
+
* @interface
|
|
4
|
+
*/
|
|
5
|
+
async findCommands() { throw new Error("findCommands not implemented") }
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @interface
|
|
9
|
+
*/
|
|
10
|
+
async findMigrations() { throw new Error("findMigrations not implemneted") }
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @interface
|
|
14
|
+
*/
|
|
15
|
+
async getVelociousPath() { throw new Error("getVelociousPath not implemented") }
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @interface
|
|
19
|
+
*/
|
|
20
|
+
async importApplicationRoutes() { throw new Error("importApplicationRoutes not implemented") }
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @interface
|
|
24
|
+
*/
|
|
25
|
+
async requireCommand({commandParts}) { throw new Error("requireCommand not implemented") } // eslint-disable-line no-unused-vars
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {object} newArgs
|
|
29
|
+
* @returns {void}
|
|
30
|
+
*/
|
|
31
|
+
setArgs(newArgs) { this.args = newArgs }
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {import("../configuration.js").default} newConfiguration
|
|
35
|
+
* @returns {void}
|
|
36
|
+
*/
|
|
37
|
+
setConfiguration(newConfiguration) { this.configuration = newConfiguration }
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @returns {import("../configuration.js").default}
|
|
41
|
+
*/
|
|
42
|
+
getConfiguration() {
|
|
43
|
+
if (!this.configuration) throw new Error("Configuration hasn't been set")
|
|
44
|
+
|
|
45
|
+
return this.configuration
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string[]} newProcessArgs
|
|
50
|
+
* @returns {void}
|
|
51
|
+
*/
|
|
52
|
+
setProcessArgs(newProcessArgs) { this.processArgs = newProcessArgs }
|
|
53
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import Base from "./base.js"
|
|
2
|
+
import {digg} from "diggerize"
|
|
3
|
+
import * as inflection from "inflection"
|
|
4
|
+
import restArgsError from "../utils/rest-args-error.js"
|
|
5
|
+
|
|
6
|
+
export default class VelociousEnvironmentsHandlerBrowser extends Base {
|
|
7
|
+
/**
|
|
8
|
+
* @param {object} args
|
|
9
|
+
* @param {function() : void} args.migrationsRequireContextCallback
|
|
10
|
+
*/
|
|
11
|
+
constructor({migrationsRequireContextCallback, ...restArgs} = {}) {
|
|
12
|
+
super()
|
|
13
|
+
restArgsError(restArgs)
|
|
14
|
+
|
|
15
|
+
this.migrationsRequireContextCallback = migrationsRequireContextCallback
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @returns {object}
|
|
20
|
+
*/
|
|
21
|
+
migrationsRequireContext() {
|
|
22
|
+
const migrationsRequireContextCallback = digg(this, "migrationsRequireContextCallback")
|
|
23
|
+
|
|
24
|
+
if (!migrationsRequireContextCallback) throw new Error("migrationsRequireContextCallback is required")
|
|
25
|
+
|
|
26
|
+
this._migrationsRequireContextResult ||= migrationsRequireContextCallback()
|
|
27
|
+
|
|
28
|
+
return this._migrationsRequireContextResult
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @returns {Promise<Array<{name: string, file: string}>>}
|
|
33
|
+
*/
|
|
34
|
+
findCommands() {
|
|
35
|
+
this._findCommandsResult = this._actualFindCommands()
|
|
36
|
+
|
|
37
|
+
return this._findCommandsResult
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_findCommandsRequireContext() {
|
|
41
|
+
this.findCommandsRequireContextResult ||= require.context("../cli/commands", true, /\.js$/)
|
|
42
|
+
|
|
43
|
+
return this.findCommandsRequireContextResult
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
_actualFindCommands() {
|
|
47
|
+
const commandFiles = this._findCommandsRequireContext()
|
|
48
|
+
const commands = []
|
|
49
|
+
|
|
50
|
+
for (const aFilePath of commandFiles.keys()) {
|
|
51
|
+
const aFilePathParts = aFilePath.split("/")
|
|
52
|
+
const lastPart = aFilePathParts[aFilePathParts.length - 1]
|
|
53
|
+
let name
|
|
54
|
+
|
|
55
|
+
if (lastPart == "index.js") {
|
|
56
|
+
name = aFilePathParts[aFilePathParts.length - 2]
|
|
57
|
+
} else {
|
|
58
|
+
name = lastPart.replace(".js", "")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
commands.push({name, file: aFilePath})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return commands
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {Array<string>} commandParts
|
|
69
|
+
* @template T extends import("./base-command.js").default
|
|
70
|
+
* @returns {Promise<T>}
|
|
71
|
+
*/
|
|
72
|
+
async requireCommand({commandParts, ...restArgs}) {
|
|
73
|
+
restArgsError(restArgs)
|
|
74
|
+
|
|
75
|
+
let filePath = "."
|
|
76
|
+
|
|
77
|
+
for (let commandPart of commandParts) {
|
|
78
|
+
if (commandPart == "d") commandPart = "destroy"
|
|
79
|
+
if (commandPart == "g") commandPart = "generate"
|
|
80
|
+
if (commandPart == "s") commandPart = "server"
|
|
81
|
+
|
|
82
|
+
filePath += `/${commandPart}`
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const filePaths = []
|
|
86
|
+
|
|
87
|
+
filePaths.push(`${filePath}/index.js`)
|
|
88
|
+
filePath += ".js"
|
|
89
|
+
filePaths.push(filePath)
|
|
90
|
+
|
|
91
|
+
const commandsRequireContext = await this._findCommandsRequireContext()
|
|
92
|
+
let commandClassImport
|
|
93
|
+
|
|
94
|
+
for (const aFilePath of filePaths) {
|
|
95
|
+
commandClassImport = commandsRequireContext(aFilePath)
|
|
96
|
+
|
|
97
|
+
if (commandClassImport) {
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!commandClassImport) {
|
|
103
|
+
throw new Error(`Unknown command: ${commandParts.join(":")}. Possible commands: ${commandsRequireContext.keys()}`)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const CommandClass = commandClassImport.default
|
|
107
|
+
|
|
108
|
+
return CommandClass
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @returns {Promise<Array<{name: string, file: string}>>}
|
|
113
|
+
*/
|
|
114
|
+
async findMigrations() {
|
|
115
|
+
const migrationsRequireContext = await this.migrationsRequireContext()
|
|
116
|
+
const files = migrationsRequireContext
|
|
117
|
+
.keys()
|
|
118
|
+
.map((file) => {
|
|
119
|
+
// "13,14" because somes "require-context"-npm-module deletes first character!?
|
|
120
|
+
const match = file.match(/(\d{13,14})-(.+)\.js$/)
|
|
121
|
+
|
|
122
|
+
if (!match) return null
|
|
123
|
+
|
|
124
|
+
// Fix require-context-npm-module deletes first character
|
|
125
|
+
let fileName = file
|
|
126
|
+
let dateNumber = match[1]
|
|
127
|
+
|
|
128
|
+
if (dateNumber.length == 13) {
|
|
129
|
+
dateNumber = `2${dateNumber}`
|
|
130
|
+
fileName = `2${fileName}`
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Parse regex
|
|
134
|
+
const date = parseInt(dateNumber)
|
|
135
|
+
const migrationName = match[2]
|
|
136
|
+
const migrationClassName = inflection.camelize(migrationName.replaceAll("-", "_"))
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
file: fileName,
|
|
140
|
+
fullPath: file,
|
|
141
|
+
date,
|
|
142
|
+
migrationClassName
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
.filter((migration) => Boolean(migration))
|
|
146
|
+
.sort((migration1, migration2) => migration1.date - migration2.date)
|
|
147
|
+
|
|
148
|
+
return files
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @param {string} filePath
|
|
153
|
+
* @template T extends import("../migration/index.js").default
|
|
154
|
+
* @returns {Promise<T>}
|
|
155
|
+
*/
|
|
156
|
+
requireMigration = async (filePath) => {
|
|
157
|
+
if (!filePath) throw new Error("filePath is required")
|
|
158
|
+
|
|
159
|
+
const migrationsRequireContext = await this.migrationsRequireContext()
|
|
160
|
+
const migrationImport = migrationsRequireContext(filePath)
|
|
161
|
+
|
|
162
|
+
if (!migrationImport) throw new Error(`Migration file ${filePath} not found`)
|
|
163
|
+
|
|
164
|
+
const migrationImportDefault = migrationImport.default
|
|
165
|
+
|
|
166
|
+
if (!migrationImportDefault) throw new Error("Migration file must export a default migration class")
|
|
167
|
+
if (typeof migrationImportDefault !== "function") throw new Error("Migration default export isn't a function (should be a class which is a function in JS)")
|
|
168
|
+
|
|
169
|
+
return migrationImportDefault
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import Base from "./base.js"
|
|
2
|
+
import {dirname} from "path"
|
|
3
|
+
import {fileURLToPath} from "url"
|
|
4
|
+
import fs from "fs/promises"
|
|
5
|
+
import * as inflection from "inflection"
|
|
6
|
+
import path from "path"
|
|
7
|
+
|
|
8
|
+
export default class VelociousEnvironmentHandlerNode extends Base{
|
|
9
|
+
/**
|
|
10
|
+
* @returns {Promise<Array<{name: string, file: string}>>}
|
|
11
|
+
*/
|
|
12
|
+
async findCommands() {
|
|
13
|
+
this._findCommandsResult ||= this._actualFindCommands()
|
|
14
|
+
|
|
15
|
+
return this._findCommandsResult
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async _actualFindCommands() {
|
|
19
|
+
const basePath = await this.getBasePath()
|
|
20
|
+
const commandFiles = fs.glob(`${basePath}/src/cli/commands/**/*.js`)
|
|
21
|
+
const commands = []
|
|
22
|
+
|
|
23
|
+
for await (const aFilePath of commandFiles) {
|
|
24
|
+
const aFilePathParts = aFilePath.split("/")
|
|
25
|
+
const commandPathLocation = aFilePathParts.indexOf("commands") + 1
|
|
26
|
+
const lastPart = aFilePathParts[aFilePathParts.length - 1]
|
|
27
|
+
let name, paths
|
|
28
|
+
|
|
29
|
+
if (lastPart == "index.js") {
|
|
30
|
+
name = aFilePathParts[aFilePathParts.length - 2]
|
|
31
|
+
paths = aFilePathParts.slice(commandPathLocation, -2)
|
|
32
|
+
} else {
|
|
33
|
+
name = lastPart.replace(".js", "")
|
|
34
|
+
paths = aFilePathParts.slice(commandPathLocation, -1)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const commandName = `${paths.join(":")}${paths.length > 0 ? ":" : ""}${name}`
|
|
38
|
+
|
|
39
|
+
commands.push({name: commandName, file: aFilePath})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return commands
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {Array<string>} commandParts
|
|
47
|
+
* @template T extends import ("./base-command.js").default
|
|
48
|
+
* @returns {Promise<T>}
|
|
49
|
+
*/
|
|
50
|
+
async requireCommand({commandParts}) {
|
|
51
|
+
const commands = await this.findCommands()
|
|
52
|
+
const command = commands.find((aCommand) => aCommand.name === commandParts.join(":"))
|
|
53
|
+
|
|
54
|
+
if (!command) {
|
|
55
|
+
const possibleCommands = commands.map(aCommand => aCommand.name)
|
|
56
|
+
|
|
57
|
+
throw new Error(`Unknown command: ${this.args.processArgs[0]} which should have been one of: ${possibleCommands.sort().join(", ")}`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const commandClassImport = await import(command.file)
|
|
61
|
+
const CommandClass = commandClassImport.default
|
|
62
|
+
|
|
63
|
+
return CommandClass
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @returns {Promise<Array<{name: string, file: string}>>}
|
|
68
|
+
*/
|
|
69
|
+
async findMigrations() {
|
|
70
|
+
const migrationsPath = `${this.getConfiguration().getDirectory()}/src/database/migrations`
|
|
71
|
+
const glob = await fs.glob(`${migrationsPath}/**/*.js`)
|
|
72
|
+
const files = []
|
|
73
|
+
|
|
74
|
+
for await (const fullPath of glob) {
|
|
75
|
+
const file = await path.basename(fullPath)
|
|
76
|
+
|
|
77
|
+
files.push(file)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const migrationFiles = files
|
|
81
|
+
.map((file) => {
|
|
82
|
+
const match = file.match(/^(\d{14})-(.+)\.js$/)
|
|
83
|
+
|
|
84
|
+
if (!match) return null
|
|
85
|
+
|
|
86
|
+
const date = parseInt(match[1])
|
|
87
|
+
const migrationName = match[2]
|
|
88
|
+
const migrationClassName = inflection.camelize(migrationName.replaceAll("-", "_"))
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
file,
|
|
92
|
+
fullPath: `${migrationsPath}/${file}`,
|
|
93
|
+
date,
|
|
94
|
+
migrationClassName
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
.filter((migration) => Boolean(migration))
|
|
98
|
+
.sort((migration1, migration2) => migration1.date - migration2.date)
|
|
99
|
+
|
|
100
|
+
return migrationFiles
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @returns {Promise<import("../routes/index.js").default>}
|
|
105
|
+
*/
|
|
106
|
+
async importApplicationRoutes() {
|
|
107
|
+
const routesImport = await import(`${this.getConfiguration().getDirectory()}/src/config/routes.js`)
|
|
108
|
+
|
|
109
|
+
return routesImport.default
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @returns {string}
|
|
114
|
+
*/
|
|
115
|
+
async getVelociousPath() {
|
|
116
|
+
if (!this._velociousPath) {
|
|
117
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
118
|
+
const __dirname = dirname(__filename)
|
|
119
|
+
|
|
120
|
+
this._velociousPath = await fs.realpath(`${__dirname}/../..`)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return this._velociousPath
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @returns {Promise<void>}
|
|
128
|
+
*/
|
|
129
|
+
async importTestFiles(testFiles) {
|
|
130
|
+
for (const testFile of testFiles) {
|
|
131
|
+
await import(testFile)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async importTestingConfigPath() {
|
|
136
|
+
const testingConfigPath = this.getConfiguration().getTesting()
|
|
137
|
+
|
|
138
|
+
await import(testingConfigPath)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {string} filePath
|
|
143
|
+
* @template T extends import ("../migration/index.js").default
|
|
144
|
+
* @returns {Promise<T>}
|
|
145
|
+
*/
|
|
146
|
+
async requireMigration(filePath) {
|
|
147
|
+
const migrationImport = await import(filePath)
|
|
148
|
+
const migrationImportDefault = migrationImport.default
|
|
149
|
+
|
|
150
|
+
if (!migrationImportDefault) throw new Error("Migration file must export a default migration class")
|
|
151
|
+
if (typeof migrationImportDefault !== "function") throw new Error("Migration default export isn't a function (should be a class which is a function in JS)")
|
|
152
|
+
|
|
153
|
+
return migrationImportDefault
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async getBasePath() {
|
|
157
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
158
|
+
const basePath = await fs.realpath(`${dirname(__filename)}/../..`)
|
|
159
|
+
|
|
160
|
+
return basePath
|
|
161
|
+
}
|
|
162
|
+
}
|