velocious 1.0.97 → 1.0.98
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/eslint.config.js +1 -0
- package/package.json +2 -1
- package/spec/database/connection/drivers/mysql/query-parser-spec.js +4 -4
- package/spec/http-server/post-spec.js +2 -0
- package/src/application.js +27 -9
- package/src/configuration-resolver.js +29 -10
- package/src/configuration-types.js +44 -0
- package/src/configuration.js +63 -33
- package/src/database/drivers/base-column.js +6 -1
- package/src/database/drivers/base-columns-index.js +11 -1
- package/src/database/drivers/base-foreign-key.js +45 -0
- package/src/database/drivers/base-table.js +24 -2
- package/src/database/drivers/base.js +211 -39
- package/src/database/drivers/mssql/index.js +1 -3
- package/src/database/drivers/sqlite/sql/alter-table.js +4 -2
- package/src/database/handler.js +5 -0
- package/src/database/migration/index.js +77 -19
- package/src/database/migrator/files-finder.js +21 -22
- package/src/database/migrator/types.js +29 -0
- package/src/database/migrator.js +98 -59
- package/src/database/pool/async-tracked-multi-connection.js +42 -7
- package/src/database/pool/base-methods-forward.js +37 -0
- package/src/database/pool/base.js +79 -46
- package/src/database/pool/single-multi-use.js +18 -3
- package/src/database/query/alter-table-base.js +4 -4
- package/src/database/query/base.js +9 -2
- package/src/database/query/create-database-base.js +8 -0
- package/src/database/query/create-index-base.js +20 -5
- package/src/database/query/create-table-base.js +28 -9
- package/src/database/query/from-base.js +17 -0
- package/src/database/query/from-plain.js +8 -3
- package/src/database/query/from-table.js +8 -3
- package/src/database/query/index.js +43 -32
- package/src/database/query/join-base.js +28 -1
- package/src/database/query/join-object.js +67 -0
- package/src/database/query/join-plain.js +6 -1
- package/src/database/query/order-base.js +18 -0
- package/src/database/query/order-plain.js +8 -2
- package/src/database/query/select-base.js +15 -0
- package/src/database/query/select-plain.js +6 -1
- package/src/database/query/select-table-and-column.js +8 -2
- package/src/database/query/where-base.js +23 -1
- package/src/database/query/where-hash.js +15 -0
- package/src/database/query/where-plain.js +6 -0
- package/src/database/query-parser/base-query-parser.js +8 -2
- package/src/database/query-parser/from-parser.js +2 -0
- package/src/database/query-parser/joins-parser.js +10 -45
- package/src/database/query-parser/select-parser.js +2 -0
- package/src/database/record/index.js +1 -1
- package/src/database/table-data/index.js +39 -121
- package/src/database/table-data/table-column.js +54 -25
- package/src/database/table-data/table-foreign-key.js +5 -3
- package/src/database/table-data/table-index.js +12 -6
- package/src/database/table-data/table-reference.js +2 -0
- package/src/database/use-database.js +4 -2
- package/src/environment-handlers/base.js +41 -8
- package/src/environment-handlers/node/cli/commands/destroy/migration.js +3 -0
- package/src/environment-handlers/node/cli/commands/generate/migration.js +3 -0
- package/src/environment-handlers/node/cli/commands/generate/model.js +3 -0
- package/src/environment-handlers/node/cli/commands/init.js +3 -0
- package/src/environment-handlers/node.js +59 -28
- package/src/http-client/header.js +6 -0
- package/src/http-client/request.js +31 -5
- package/src/http-client/response.js +31 -7
- package/src/http-server/client/index.js +24 -4
- package/src/http-server/client/request-buffer/form-data-part.js +11 -0
- package/src/http-server/client/request-buffer/header.js +6 -0
- package/src/http-server/client/request-buffer/index.js +91 -13
- package/src/http-server/client/request-parser.js +26 -0
- package/src/http-server/client/request-runner.js +15 -3
- package/src/http-server/client/request.js +17 -0
- package/src/http-server/client/response.js +41 -1
- package/src/http-server/index.js +32 -4
- package/src/http-server/server-client.js +33 -2
- package/src/http-server/worker-handler/index.js +42 -9
- package/src/http-server/worker-handler/worker-script.js +2 -0
- package/src/http-server/worker-handler/worker-thread.js +34 -6
- package/src/logger.js +21 -15
- package/src/routes/app-routes.js +1 -1
- package/src/testing/test-files-finder.js +8 -4
- package/src/testing/test-runner.js +76 -24
- package/src/utils/backtrace-cleaner.js +6 -4
- package/src/utils/ensure-error.js +13 -0
- package/src/utils/file-exists.js +3 -1
- package/src/utils/rest-args-error.js +2 -0
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import restArgsError from "../../utils/rest-args-error.js"
|
|
2
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {object} TableIndexArgsType
|
|
7
|
+
* @property {string} name
|
|
8
|
+
* @property {boolean} unique
|
|
9
|
+
*/
|
|
10
|
+
|
|
3
11
|
export default class TableIndex {
|
|
4
12
|
/**
|
|
5
|
-
* @param {Array<string>} columns
|
|
6
|
-
* @param {
|
|
7
|
-
* @param {string} args.name
|
|
8
|
-
* @param {boolean} args.unique
|
|
13
|
+
* @param {Array<string | import("./table-column.js").default>} columns
|
|
14
|
+
* @param {TableIndexArgsType} [args]
|
|
9
15
|
*/
|
|
10
16
|
constructor(columns, args) {
|
|
11
17
|
if (args) {
|
|
@@ -19,12 +25,12 @@ export default class TableIndex {
|
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
/**
|
|
22
|
-
* @returns {Array<string>}
|
|
28
|
+
* @returns {Array<string | import("./table-column.js").default>}
|
|
23
29
|
*/
|
|
24
30
|
getColumns() { return this.columns }
|
|
25
31
|
|
|
26
32
|
/**
|
|
27
|
-
* @returns {string}
|
|
33
|
+
* @returns {string | undefined}
|
|
28
34
|
*/
|
|
29
35
|
getName() { return this.args?.name }
|
|
30
36
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import React from "react"
|
|
2
4
|
import useEnvSense from "env-sense/src/use-env-sense.js"
|
|
3
5
|
|
|
@@ -7,8 +9,8 @@ import restArgsError from "../utils/rest-args-error.js"
|
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* @param {object} args
|
|
10
|
-
* @param {
|
|
11
|
-
* @returns {
|
|
12
|
+
* @param {() => Promise<import("./migrator/types.js").RequireMigrationContextType>} args.migrationsRequireContextCallback
|
|
13
|
+
* @returns {{loaded: boolean}}
|
|
12
14
|
*/
|
|
13
15
|
export default function loadMigrations({migrationsRequireContextCallback, ...restArgs}) {
|
|
14
16
|
const instance = React.useMemo(() => ({running: false}), [])
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {object} CommandFileObjectType
|
|
5
|
+
* @property {string} name
|
|
6
|
+
* @property {string} file
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {object} MigrationObjectType
|
|
11
|
+
* @property {number} date
|
|
12
|
+
* @property {string} [fullPath]
|
|
13
|
+
* @property {string} migrationClassName
|
|
14
|
+
* @property {string} file
|
|
15
|
+
*/
|
|
16
|
+
|
|
1
17
|
export default class VelociousEnvironmentHandlerBase {
|
|
2
18
|
/**
|
|
3
19
|
* @param {import("../cli/base-command.js").default} _command
|
|
@@ -8,10 +24,10 @@ export default class VelociousEnvironmentHandlerBase {
|
|
|
8
24
|
}
|
|
9
25
|
|
|
10
26
|
/**
|
|
11
|
-
* @param {import("../cli/base-command.js").default}
|
|
27
|
+
* @param {import("../cli/base-command.js").default} command
|
|
12
28
|
* @returns {Promise<void>}
|
|
13
29
|
*/
|
|
14
|
-
async cliCommandsInit(
|
|
30
|
+
async cliCommandsInit(command) { // eslint-disable-line no-unused-vars
|
|
15
31
|
throw new Error("cliCommandsInit not implemented")
|
|
16
32
|
}
|
|
17
33
|
|
|
@@ -57,19 +73,20 @@ export default class VelociousEnvironmentHandlerBase {
|
|
|
57
73
|
|
|
58
74
|
/**
|
|
59
75
|
* @interface
|
|
76
|
+
* @returns {Promise<CommandFileObjectType[]>}
|
|
60
77
|
*/
|
|
61
78
|
async findCommands() { throw new Error("findCommands not implemented") }
|
|
62
79
|
|
|
63
80
|
/**
|
|
64
81
|
* @interface
|
|
82
|
+
* @returns {Promise<Array<MigrationObjectType>>}
|
|
65
83
|
*/
|
|
66
84
|
async findMigrations() { throw new Error("findMigrations not implemneted") }
|
|
67
85
|
|
|
68
86
|
/**
|
|
69
|
-
* @
|
|
70
|
-
* @param {
|
|
71
|
-
* @
|
|
72
|
-
* @returns {any}
|
|
87
|
+
* @param {import("../cli/base-command.js").default} command
|
|
88
|
+
* @param {typeof import("../cli/base-command.js").default} CommandClass
|
|
89
|
+
* @returns {Promise<any>}
|
|
73
90
|
*/
|
|
74
91
|
async forwardCommand(command, CommandClass) {
|
|
75
92
|
const newCommand = new CommandClass({
|
|
@@ -81,20 +98,36 @@ export default class VelociousEnvironmentHandlerBase {
|
|
|
81
98
|
|
|
82
99
|
/**
|
|
83
100
|
* @interface
|
|
101
|
+
* @returns {Promise<string>}
|
|
84
102
|
*/
|
|
85
|
-
|
|
103
|
+
getVelociousPath() { throw new Error("getVelociousPath not implemented") }
|
|
86
104
|
|
|
87
105
|
/**
|
|
88
106
|
* @interface
|
|
107
|
+
* @returns {Promise<import("../routes/index.js").default>}
|
|
89
108
|
*/
|
|
90
109
|
async importApplicationRoutes() { throw new Error("importApplicationRoutes not implemented") }
|
|
91
110
|
|
|
111
|
+
/**
|
|
112
|
+
* @interface
|
|
113
|
+
* @param {string[]} _testFiles
|
|
114
|
+
* @returns {Promise<void>}
|
|
115
|
+
*/
|
|
116
|
+
importTestFiles(_testFiles) { throw new Error("'importTestFiles' not implemented") } // eslint-disable-line no-unused-vars
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @interface
|
|
120
|
+
* @returns {Promise<void>}
|
|
121
|
+
*/
|
|
122
|
+
importTestingConfigPath() { throw new Error(`'importTestingConfigPath' not implemented`) }
|
|
123
|
+
|
|
92
124
|
/**
|
|
93
125
|
* @param {object} args
|
|
94
126
|
* @param {string[]} args.commandParts
|
|
127
|
+
* @returns {Promise<import ("../cli/base-command.js").default>}
|
|
95
128
|
* @interface
|
|
96
129
|
*/
|
|
97
|
-
async requireCommand({commandParts}) { throw new Error("requireCommand not implemented") } // eslint-disable-line no-unused-vars
|
|
130
|
+
async requireCommand({commandParts}) { throw new Error("'requireCommand' not implemented") } // eslint-disable-line no-unused-vars
|
|
98
131
|
|
|
99
132
|
/**
|
|
100
133
|
* @param {object} newArgs
|
|
@@ -2,6 +2,9 @@ import BaseCommand from "../../../../../cli/base-command.js"
|
|
|
2
2
|
import fs from "fs/promises"
|
|
3
3
|
|
|
4
4
|
export default class DbDestroyMigration extends BaseCommand {
|
|
5
|
+
/**
|
|
6
|
+
* @returns {Promise<void>}
|
|
7
|
+
*/
|
|
5
8
|
async execute() {
|
|
6
9
|
const migrationName = this.processArgs[1]
|
|
7
10
|
const migrationDir = `${this.getConfiguration().getDirectory()}/src/database/migrations`
|
|
@@ -5,6 +5,9 @@ import * as inflection from "inflection"
|
|
|
5
5
|
import strftime from "strftime"
|
|
6
6
|
|
|
7
7
|
export default class DbGenerateMigration extends BaseCommand {
|
|
8
|
+
/**
|
|
9
|
+
* @returns {Promise<void>}
|
|
10
|
+
*/
|
|
8
11
|
async execute() {
|
|
9
12
|
const migrationName = this.processArgs[1]
|
|
10
13
|
const migrationNameCamelized = inflection.camelize(migrationName.replaceAll("-", "_"))
|
|
@@ -4,6 +4,9 @@ import fs from "fs/promises"
|
|
|
4
4
|
import * as inflection from "inflection"
|
|
5
5
|
|
|
6
6
|
export default class DbGenerateModel extends BaseCommand {
|
|
7
|
+
/**
|
|
8
|
+
* @returns {Promise<void>}
|
|
9
|
+
*/
|
|
7
10
|
async execute() {
|
|
8
11
|
const modelName = this.processArgs[1]
|
|
9
12
|
const modelNameCamelized = inflection.camelize(modelName.replaceAll("-", "_"))
|
|
@@ -3,6 +3,9 @@ import fileExists from "../../../../utils/file-exists.js"
|
|
|
3
3
|
import fs from "fs/promises"
|
|
4
4
|
|
|
5
5
|
export default class VelociousCliCommandsInit extends BaseCommand {
|
|
6
|
+
/**
|
|
7
|
+
* @returns {Promise<void>}
|
|
8
|
+
*/
|
|
6
9
|
async execute() {
|
|
7
10
|
const velociousPath = await this.getEnvironmentHandler().getVelociousPath()
|
|
8
11
|
const projectPath = this.getConfiguration()?.getDirectory() || process.cwd()
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import Base from "./base.js"
|
|
2
4
|
import CliCommandsDestroyMigration from "./node/cli/commands/destroy/migration.js"
|
|
3
5
|
import CliCommandsInit from "./node/cli/commands/init.js"
|
|
@@ -13,11 +15,16 @@ import * as inflection from "inflection"
|
|
|
13
15
|
import path from "path"
|
|
14
16
|
|
|
15
17
|
export default class VelociousEnvironmentHandlerNode extends Base{
|
|
18
|
+
/** @type {import("./base.js").CommandFileObjectType[] | undefined} */
|
|
19
|
+
_findCommandsResult = undefined
|
|
20
|
+
|
|
16
21
|
/**
|
|
17
|
-
* @returns {Promise<Array<
|
|
22
|
+
* @returns {Promise<Array<import("./base.js").CommandFileObjectType>>}
|
|
18
23
|
*/
|
|
19
24
|
async findCommands() {
|
|
20
|
-
this._findCommandsResult ||= this._actualFindCommands()
|
|
25
|
+
this._findCommandsResult ||= await this._actualFindCommands()
|
|
26
|
+
|
|
27
|
+
if (!this._findCommandsResult) throw new Error("Could not get commands")
|
|
21
28
|
|
|
22
29
|
return this._findCommandsResult
|
|
23
30
|
}
|
|
@@ -49,38 +56,66 @@ export default class VelociousEnvironmentHandlerNode extends Base{
|
|
|
49
56
|
return commands
|
|
50
57
|
}
|
|
51
58
|
|
|
59
|
+
/**
|
|
60
|
+
* @param {import("../cli/base-command.js").default} command
|
|
61
|
+
* @returns {Promise<void>}
|
|
62
|
+
*/
|
|
52
63
|
async cliCommandsInit(command) {
|
|
53
64
|
return await this.forwardCommand(command, CliCommandsInit)
|
|
54
65
|
}
|
|
55
66
|
|
|
67
|
+
/**
|
|
68
|
+
* @param {import("../cli/base-command.js").default} command
|
|
69
|
+
* @returns {Promise<any>}
|
|
70
|
+
*/
|
|
56
71
|
async cliCommandsMigrationGenerate(command) {
|
|
57
72
|
return await this.forwardCommand(command, CliCommandsGenerateMigration)
|
|
58
73
|
}
|
|
59
74
|
|
|
75
|
+
/**
|
|
76
|
+
* @param {import("../cli/base-command.js").default} command
|
|
77
|
+
* @returns {Promise<any>}
|
|
78
|
+
*/
|
|
60
79
|
async cliCommandsMigrationDestroy(command) {
|
|
61
80
|
return await this.forwardCommand(command, CliCommandsDestroyMigration)
|
|
62
81
|
}
|
|
63
82
|
|
|
83
|
+
/**
|
|
84
|
+
* @param {import("../cli/base-command.js").default} command
|
|
85
|
+
* @returns {Promise<any>}
|
|
86
|
+
*/
|
|
64
87
|
async cliCommandsGenerateBaseModels(command) {
|
|
65
88
|
return await this.forwardCommand(command, CliCommandsGenerateBaseModels)
|
|
66
89
|
}
|
|
67
90
|
|
|
91
|
+
/**
|
|
92
|
+
* @param {import("../cli/base-command.js").default} command
|
|
93
|
+
* @returns {Promise<any>}
|
|
94
|
+
*/
|
|
68
95
|
async cliCommandsGenerateModel(command) {
|
|
69
96
|
return await this.forwardCommand(command, CliCommandsGenerateModel)
|
|
70
97
|
}
|
|
71
98
|
|
|
99
|
+
/**
|
|
100
|
+
* @param {import("../cli/base-command.js").default} command
|
|
101
|
+
* @returns {Promise<any>}
|
|
102
|
+
*/
|
|
72
103
|
async cliCommandsServer(command) {
|
|
73
104
|
return await this.forwardCommand(command, CliCommandsServer)
|
|
74
105
|
}
|
|
75
106
|
|
|
107
|
+
/**
|
|
108
|
+
* @param {import("../cli/base-command.js").default} command
|
|
109
|
+
* @returns {Promise<any>}
|
|
110
|
+
*/
|
|
76
111
|
async cliCommandsTest(command) {
|
|
77
112
|
return await this.forwardCommand(command, CliCommandsTest)
|
|
78
113
|
}
|
|
79
114
|
|
|
80
115
|
/**
|
|
81
|
-
* @param {
|
|
82
|
-
* @
|
|
83
|
-
* @returns {Promise<
|
|
116
|
+
* @param {object} args
|
|
117
|
+
* @param {string[]} args.commandParts
|
|
118
|
+
* @returns {Promise<import ("../cli/base-command.js").default>}
|
|
84
119
|
*/
|
|
85
120
|
async requireCommand({commandParts}) {
|
|
86
121
|
const commands = await this.findCommands()
|
|
@@ -89,7 +124,7 @@ export default class VelociousEnvironmentHandlerNode extends Base{
|
|
|
89
124
|
if (!command) {
|
|
90
125
|
const possibleCommands = commands.map(aCommand => aCommand.name)
|
|
91
126
|
|
|
92
|
-
throw new Error(`Unknown command: ${
|
|
127
|
+
throw new Error(`Unknown command: ${commandParts.join(":")} which should have been one of: ${possibleCommands.sort().join(", ")}`)
|
|
93
128
|
}
|
|
94
129
|
|
|
95
130
|
const commandClassImport = await import(command.file)
|
|
@@ -99,40 +134,35 @@ export default class VelociousEnvironmentHandlerNode extends Base{
|
|
|
99
134
|
}
|
|
100
135
|
|
|
101
136
|
/**
|
|
102
|
-
* @returns {Promise<Array<
|
|
137
|
+
* @returns {Promise<Array<import("./base.js").MigrationObjectType>>}
|
|
103
138
|
*/
|
|
104
139
|
async findMigrations() {
|
|
105
140
|
const migrationsPath = `${this.getConfiguration().getDirectory()}/src/database/migrations`
|
|
106
141
|
const glob = await fs.glob(`${migrationsPath}/**/*.js`)
|
|
107
|
-
|
|
142
|
+
let files = []
|
|
108
143
|
|
|
109
144
|
for await (const fullPath of glob) {
|
|
110
145
|
const file = await path.basename(fullPath)
|
|
111
146
|
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const migrationFiles = files
|
|
116
|
-
.map((file) => {
|
|
117
|
-
const match = file.match(/^(\d{14})-(.+)\.js$/)
|
|
147
|
+
const match = file.match(/^(\d{14})-(.+)\.js$/)
|
|
118
148
|
|
|
119
|
-
|
|
149
|
+
if (!match) continue
|
|
120
150
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
151
|
+
const date = parseInt(match[1])
|
|
152
|
+
const migrationName = match[2]
|
|
153
|
+
const migrationClassName = inflection.camelize(migrationName.replaceAll("-", "_"))
|
|
124
154
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
155
|
+
files.push({
|
|
156
|
+
file,
|
|
157
|
+
fullPath: `${migrationsPath}/${file}`,
|
|
158
|
+
date,
|
|
159
|
+
migrationClassName
|
|
131
160
|
})
|
|
132
|
-
|
|
133
|
-
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
files = files.sort((migration1, migration2) => migration1.date - migration2.date)
|
|
134
164
|
|
|
135
|
-
return
|
|
165
|
+
return files
|
|
136
166
|
}
|
|
137
167
|
|
|
138
168
|
/**
|
|
@@ -145,7 +175,7 @@ export default class VelociousEnvironmentHandlerNode extends Base{
|
|
|
145
175
|
}
|
|
146
176
|
|
|
147
177
|
/**
|
|
148
|
-
* @returns {string}
|
|
178
|
+
* @returns {Promise<string>}
|
|
149
179
|
*/
|
|
150
180
|
async getVelociousPath() {
|
|
151
181
|
if (!this._velociousPath) {
|
|
@@ -159,6 +189,7 @@ export default class VelociousEnvironmentHandlerNode extends Base{
|
|
|
159
189
|
}
|
|
160
190
|
|
|
161
191
|
/**
|
|
192
|
+
* @param {string[]} testFiles
|
|
162
193
|
* @returns {Promise<void>}
|
|
163
194
|
*/
|
|
164
195
|
async importTestFiles(testFiles) {
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import Header from "./header.js"
|
|
4
|
+
|
|
1
5
|
export default class Request {
|
|
6
|
+
/**
|
|
7
|
+
* @param {object} args
|
|
8
|
+
* @param {string} args.body
|
|
9
|
+
* @param {string} args.method
|
|
10
|
+
* @param {Header[]} args.headers
|
|
11
|
+
* @param {string} args.path
|
|
12
|
+
* @param {string} args.version
|
|
13
|
+
*/
|
|
2
14
|
constructor({body, method = "GET", headers = [], path, version = "1.1"}) {
|
|
3
15
|
this.body = body
|
|
4
16
|
this.headers = headers
|
|
@@ -17,26 +29,40 @@ export default class Request {
|
|
|
17
29
|
return requestString
|
|
18
30
|
}
|
|
19
31
|
|
|
20
|
-
|
|
21
|
-
|
|
32
|
+
/**
|
|
33
|
+
* @param {string} name
|
|
34
|
+
*/
|
|
35
|
+
getHeader(name) {
|
|
36
|
+
const compareName = name.toLowerCase().trim()
|
|
22
37
|
|
|
23
38
|
for (const header of this.headers) {
|
|
24
|
-
const headerCompareName = header.
|
|
39
|
+
const headerCompareName = header.getName().toLowerCase().trim()
|
|
25
40
|
|
|
26
41
|
if (compareName == headerCompareName) {
|
|
27
42
|
return header
|
|
28
43
|
}
|
|
29
44
|
}
|
|
30
45
|
|
|
31
|
-
throw new Error(`Header ${
|
|
46
|
+
throw new Error(`Header ${name} not found`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {string} name
|
|
51
|
+
* @param {string | number} value
|
|
52
|
+
*/
|
|
53
|
+
addHeader(name, value) {
|
|
54
|
+
this.headers.push(new Header(name, value))
|
|
32
55
|
}
|
|
33
56
|
|
|
34
57
|
prepare() {
|
|
35
58
|
if (this.body) {
|
|
36
|
-
this.addHeader("Content-Length", this.body.byteLength)
|
|
59
|
+
this.addHeader("Content-Length", Buffer.from(this.body).byteLength)
|
|
37
60
|
}
|
|
38
61
|
}
|
|
39
62
|
|
|
63
|
+
/**
|
|
64
|
+
* @param {function(string) : void} callback
|
|
65
|
+
*/
|
|
40
66
|
stream(callback) {
|
|
41
67
|
this.prepare()
|
|
42
68
|
|
|
@@ -1,23 +1,37 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import Header from "./header.js"
|
|
2
4
|
|
|
3
5
|
export default class Response {
|
|
6
|
+
/**
|
|
7
|
+
* @param {object} args
|
|
8
|
+
* @param {string} args.method
|
|
9
|
+
* @param {function() : void} args.onComplete
|
|
10
|
+
*/
|
|
4
11
|
constructor({method = "GET", onComplete}) {
|
|
5
12
|
if (!method) throw new Error(`Invalid method given: ${method}`)
|
|
6
13
|
|
|
14
|
+
/** @type {Header[]} */
|
|
7
15
|
this.headers = []
|
|
16
|
+
|
|
8
17
|
this.method = method.toUpperCase().trim()
|
|
9
18
|
this.onComplete = onComplete
|
|
10
19
|
this.state = "status-line"
|
|
11
20
|
|
|
12
|
-
|
|
13
|
-
this.response =
|
|
21
|
+
/** @type {Buffer} */
|
|
22
|
+
this.response = Buffer.alloc(0);
|
|
14
23
|
}
|
|
15
24
|
|
|
25
|
+
/** @param {Buffer} data */
|
|
16
26
|
feed(data) {
|
|
17
|
-
this.response
|
|
27
|
+
this.response = Buffer.concat([this.response, data])
|
|
18
28
|
this.tryToParse()
|
|
19
29
|
}
|
|
20
30
|
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} name
|
|
33
|
+
* @returns {Header}
|
|
34
|
+
*/
|
|
21
35
|
getHeader(name) {
|
|
22
36
|
const compareName = name.toLowerCase().trim()
|
|
23
37
|
|
|
@@ -33,9 +47,11 @@ export default class Response {
|
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
json() {
|
|
36
|
-
const contentTypeHeader = this.getHeader("Content-Type")?.getValue()
|
|
50
|
+
const contentTypeHeader = this.getHeader("Content-Type")?.getValue()
|
|
51
|
+
|
|
52
|
+
if (typeof contentTypeHeader != "string") throw new Error(`Content-Type wasn't a string: ${contentTypeHeader}`)
|
|
37
53
|
|
|
38
|
-
if (!contentTypeHeader.startsWith("application/json")) {
|
|
54
|
+
if (!contentTypeHeader.toLowerCase().trim().startsWith("application/json")) {
|
|
39
55
|
throw new Error(`Content-Type is not JSON: ${contentTypeHeader}`)
|
|
40
56
|
}
|
|
41
57
|
|
|
@@ -48,13 +64,21 @@ export default class Response {
|
|
|
48
64
|
tryToParse() {
|
|
49
65
|
while (true) {
|
|
50
66
|
if (this.state == "body") {
|
|
51
|
-
const contentLengthValue = this.getHeader("Content-Length")?.
|
|
67
|
+
const contentLengthValue = this.getHeader("Content-Length")?.getValue()
|
|
52
68
|
|
|
53
69
|
if (contentLengthValue === undefined) {
|
|
54
70
|
throw new Error("No content length given")
|
|
55
71
|
}
|
|
56
72
|
|
|
57
|
-
|
|
73
|
+
let contentLengthNumber
|
|
74
|
+
|
|
75
|
+
if (typeof contentLengthValue == "number") {
|
|
76
|
+
contentLengthNumber = contentLengthValue
|
|
77
|
+
} else if (contentLengthValue == "string") {
|
|
78
|
+
contentLengthNumber = parseInt(contentLengthValue)
|
|
79
|
+
} else {
|
|
80
|
+
throw new Error(`Content-Length is not a number: ${contentLengthValue}`)
|
|
81
|
+
}
|
|
58
82
|
|
|
59
83
|
if (this.response.byteLength >= contentLengthNumber) {
|
|
60
84
|
this.completeResponse()
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import {digg} from "diggerize"
|
|
2
4
|
import {EventEmitter} from "events"
|
|
3
5
|
import {Logger} from "../../logger.js"
|
|
@@ -8,26 +10,33 @@ export default class VeoliciousHttpServerClient {
|
|
|
8
10
|
events = new EventEmitter()
|
|
9
11
|
state = "initial"
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
/**
|
|
14
|
+
* @param {object} args
|
|
15
|
+
* @param {number} args.clientCount
|
|
16
|
+
* @param {import("../../configuration.js").default} args.configuration
|
|
17
|
+
*/
|
|
18
|
+
constructor({clientCount, configuration}) {
|
|
12
19
|
if (!configuration) throw new Error("No configuration given")
|
|
13
20
|
|
|
14
21
|
this.logger = new Logger(this)
|
|
15
22
|
this.clientCount = clientCount
|
|
16
23
|
this.configuration = configuration
|
|
17
|
-
|
|
24
|
+
|
|
25
|
+
/** @type {RequestRunner[]} */
|
|
18
26
|
this.requestRunners = []
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
executeCurrentRequest = () => {
|
|
22
30
|
this.logger.debug("executeCurrentRequest")
|
|
23
31
|
|
|
32
|
+
if (!this.currentRequest) throw new Error("No current request")
|
|
33
|
+
|
|
24
34
|
// We are done parsing the given request and can theoretically start parsing a new one, before the current request is done - so reset the state.
|
|
25
35
|
this.state = "initial"
|
|
26
36
|
|
|
27
37
|
const requestRunner = new RequestRunner({
|
|
28
38
|
configuration: this.configuration,
|
|
29
|
-
request: this.currentRequest
|
|
30
|
-
routes: this.routes
|
|
39
|
+
request: this.currentRequest
|
|
31
40
|
})
|
|
32
41
|
|
|
33
42
|
this.requestRunners.push(requestRunner)
|
|
@@ -36,6 +45,9 @@ export default class VeoliciousHttpServerClient {
|
|
|
36
45
|
requestRunner.run()
|
|
37
46
|
}
|
|
38
47
|
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} data
|
|
50
|
+
*/
|
|
39
51
|
onWrite(data) {
|
|
40
52
|
if (this.state == "initial") {
|
|
41
53
|
this.currentRequest = new Request({client: this, configuration: this.configuration})
|
|
@@ -43,6 +55,8 @@ export default class VeoliciousHttpServerClient {
|
|
|
43
55
|
this.currentRequest.feed(data)
|
|
44
56
|
this.state = "requestStarted"
|
|
45
57
|
} else if (this.state == "requestStarted") {
|
|
58
|
+
if (!this.currentRequest) throw new Error("No current request")
|
|
59
|
+
|
|
46
60
|
this.currentRequest.feed(data)
|
|
47
61
|
} else {
|
|
48
62
|
throw new Error(`Unknown state for client: ${this.state}`)
|
|
@@ -76,7 +90,13 @@ export default class VeoliciousHttpServerClient {
|
|
|
76
90
|
}
|
|
77
91
|
}
|
|
78
92
|
|
|
93
|
+
/**
|
|
94
|
+
* @param {RequestRunner} requestRunner
|
|
95
|
+
* @returns {void}
|
|
96
|
+
*/
|
|
79
97
|
sendResponse(requestRunner) {
|
|
98
|
+
if (!this.currentRequest) throw new Error("No current request")
|
|
99
|
+
|
|
80
100
|
const response = digg(requestRunner, "response")
|
|
81
101
|
const request = requestRunner.getRequest()
|
|
82
102
|
const body = response.getBody()
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
export default class FormDataPart {
|
|
4
|
+
/** @type {Record<string, import("./header.js").default>} */
|
|
2
5
|
headers = {}
|
|
6
|
+
|
|
7
|
+
/** @type {number[]} */
|
|
3
8
|
body = []
|
|
4
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @param {import("./header.js").default} header
|
|
12
|
+
*/
|
|
5
13
|
addHeader(header) {
|
|
6
14
|
const name = header.formattedName
|
|
7
15
|
|
|
@@ -36,6 +44,9 @@ export default class FormDataPart {
|
|
|
36
44
|
return this.value
|
|
37
45
|
}
|
|
38
46
|
|
|
47
|
+
/**
|
|
48
|
+
* @param {string} text
|
|
49
|
+
*/
|
|
39
50
|
removeFromBody(text) {
|
|
40
51
|
this.body = this.body.slice(0, this.body.length - text.length)
|
|
41
52
|
}
|