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.
Files changed (85) hide show
  1. package/eslint.config.js +1 -0
  2. package/package.json +2 -1
  3. package/spec/database/connection/drivers/mysql/query-parser-spec.js +4 -4
  4. package/spec/http-server/post-spec.js +2 -0
  5. package/src/application.js +27 -9
  6. package/src/configuration-resolver.js +29 -10
  7. package/src/configuration-types.js +44 -0
  8. package/src/configuration.js +63 -33
  9. package/src/database/drivers/base-column.js +6 -1
  10. package/src/database/drivers/base-columns-index.js +11 -1
  11. package/src/database/drivers/base-foreign-key.js +45 -0
  12. package/src/database/drivers/base-table.js +24 -2
  13. package/src/database/drivers/base.js +211 -39
  14. package/src/database/drivers/mssql/index.js +1 -3
  15. package/src/database/drivers/sqlite/sql/alter-table.js +4 -2
  16. package/src/database/handler.js +5 -0
  17. package/src/database/migration/index.js +77 -19
  18. package/src/database/migrator/files-finder.js +21 -22
  19. package/src/database/migrator/types.js +29 -0
  20. package/src/database/migrator.js +98 -59
  21. package/src/database/pool/async-tracked-multi-connection.js +42 -7
  22. package/src/database/pool/base-methods-forward.js +37 -0
  23. package/src/database/pool/base.js +79 -46
  24. package/src/database/pool/single-multi-use.js +18 -3
  25. package/src/database/query/alter-table-base.js +4 -4
  26. package/src/database/query/base.js +9 -2
  27. package/src/database/query/create-database-base.js +8 -0
  28. package/src/database/query/create-index-base.js +20 -5
  29. package/src/database/query/create-table-base.js +28 -9
  30. package/src/database/query/from-base.js +17 -0
  31. package/src/database/query/from-plain.js +8 -3
  32. package/src/database/query/from-table.js +8 -3
  33. package/src/database/query/index.js +43 -32
  34. package/src/database/query/join-base.js +28 -1
  35. package/src/database/query/join-object.js +67 -0
  36. package/src/database/query/join-plain.js +6 -1
  37. package/src/database/query/order-base.js +18 -0
  38. package/src/database/query/order-plain.js +8 -2
  39. package/src/database/query/select-base.js +15 -0
  40. package/src/database/query/select-plain.js +6 -1
  41. package/src/database/query/select-table-and-column.js +8 -2
  42. package/src/database/query/where-base.js +23 -1
  43. package/src/database/query/where-hash.js +15 -0
  44. package/src/database/query/where-plain.js +6 -0
  45. package/src/database/query-parser/base-query-parser.js +8 -2
  46. package/src/database/query-parser/from-parser.js +2 -0
  47. package/src/database/query-parser/joins-parser.js +10 -45
  48. package/src/database/query-parser/select-parser.js +2 -0
  49. package/src/database/record/index.js +1 -1
  50. package/src/database/table-data/index.js +39 -121
  51. package/src/database/table-data/table-column.js +54 -25
  52. package/src/database/table-data/table-foreign-key.js +5 -3
  53. package/src/database/table-data/table-index.js +12 -6
  54. package/src/database/table-data/table-reference.js +2 -0
  55. package/src/database/use-database.js +4 -2
  56. package/src/environment-handlers/base.js +41 -8
  57. package/src/environment-handlers/node/cli/commands/destroy/migration.js +3 -0
  58. package/src/environment-handlers/node/cli/commands/generate/migration.js +3 -0
  59. package/src/environment-handlers/node/cli/commands/generate/model.js +3 -0
  60. package/src/environment-handlers/node/cli/commands/init.js +3 -0
  61. package/src/environment-handlers/node.js +59 -28
  62. package/src/http-client/header.js +6 -0
  63. package/src/http-client/request.js +31 -5
  64. package/src/http-client/response.js +31 -7
  65. package/src/http-server/client/index.js +24 -4
  66. package/src/http-server/client/request-buffer/form-data-part.js +11 -0
  67. package/src/http-server/client/request-buffer/header.js +6 -0
  68. package/src/http-server/client/request-buffer/index.js +91 -13
  69. package/src/http-server/client/request-parser.js +26 -0
  70. package/src/http-server/client/request-runner.js +15 -3
  71. package/src/http-server/client/request.js +17 -0
  72. package/src/http-server/client/response.js +41 -1
  73. package/src/http-server/index.js +32 -4
  74. package/src/http-server/server-client.js +33 -2
  75. package/src/http-server/worker-handler/index.js +42 -9
  76. package/src/http-server/worker-handler/worker-script.js +2 -0
  77. package/src/http-server/worker-handler/worker-thread.js +34 -6
  78. package/src/logger.js +21 -15
  79. package/src/routes/app-routes.js +1 -1
  80. package/src/testing/test-files-finder.js +8 -4
  81. package/src/testing/test-runner.js +76 -24
  82. package/src/utils/backtrace-cleaner.js +6 -4
  83. package/src/utils/ensure-error.js +13 -0
  84. package/src/utils/file-exists.js +3 -1
  85. package/src/utils/rest-args-error.js +2 -0
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  import {EventEmitter} from "events"
2
4
  import FormDataPart from "./form-data-part.js"
3
5
  import Header from "./header.js"
@@ -8,18 +10,38 @@ import querystring from "querystring"
8
10
 
9
11
  export default class RequestBuffer {
10
12
  bodyLength = 0
13
+
14
+ /** @type {number[]} */
11
15
  data = []
16
+
12
17
  events = new EventEmitter()
18
+
19
+ /** @type {Record<string, Header>} */
13
20
  headersByName = {}
21
+
22
+ multiPartyFormData = false
23
+
14
24
  params = {}
15
25
  readingBody = false
16
26
  state = "status"
17
27
 
28
+ /**
29
+ * @param {object} args
30
+ * @param {import("../../../configuration.js").default} args.configuration
31
+ */
18
32
  constructor({configuration}) {
19
33
  this.configuration = configuration
20
34
  this.logger = new Logger(this, {debug: false})
21
35
  }
22
36
 
37
+ destroy() {
38
+ // Do nothing for now...
39
+ }
40
+
41
+ /**
42
+ * @param {Buffer} data
43
+ * @returns {void}
44
+ */
23
45
  feed(data) {
24
46
  for (const char of data) {
25
47
  if (this.readingBody) this.bodyLength += 1
@@ -40,6 +62,10 @@ export default class RequestBuffer {
40
62
 
41
63
  break
42
64
  case "multi-part-form-data-body":
65
+ if (!this.formDataPart) throw new Error("FormData part not initialized")
66
+ if (!this.boundaryLineEnd) throw new Error("Boundary line end not initialized")
67
+ if (!this.boundaryLineNext) throw new Error("Boundary line next not initialized")
68
+
43
69
  const body = this.formDataPart.body // eslint-disable-line no-case-declarations
44
70
 
45
71
  body.push(char)
@@ -71,6 +97,8 @@ export default class RequestBuffer {
71
97
 
72
98
  break
73
99
  case "post-body":
100
+ if (!this.postBodyChars) throw new Error("postBodyChars not initialized")
101
+
74
102
  this.postBodyChars[this.bodyLength - 1] = char
75
103
 
76
104
  if (this.contentLength && this.bodyLength >= this.contentLength) {
@@ -84,6 +112,10 @@ export default class RequestBuffer {
84
112
  }
85
113
  }
86
114
 
115
+ /**
116
+ * @param {string} name
117
+ * @returns {Header}
118
+ */
87
119
  getHeader(name) {
88
120
  const result = this.headersByName[name.toLowerCase().trim()]
89
121
 
@@ -92,7 +124,11 @@ export default class RequestBuffer {
92
124
  return result
93
125
  }
94
126
 
127
+ /**
128
+ * @returns {Record<string, string>}
129
+ */
95
130
  getHeadersHash() {
131
+ /** @type {Record<string, string>} */
96
132
  const result = {}
97
133
 
98
134
  for (const headerFormattedName in this.headersByName) {
@@ -104,9 +140,14 @@ export default class RequestBuffer {
104
140
  return result
105
141
  }
106
142
 
143
+ /**
144
+ * @returns {void}
145
+ */
107
146
  formDataPartDone() {
108
147
  const formDataPart = this.formDataPart
109
148
 
149
+ if (!formDataPart) throw new Error("formDataPart wasnt set")
150
+
110
151
  this.formDataPart = undefined
111
152
  formDataPart.finish()
112
153
 
@@ -117,11 +158,18 @@ export default class RequestBuffer {
117
158
  return this.multiPartyFormData
118
159
  }
119
160
 
161
+ /**
162
+ * @returns {void}
163
+ */
120
164
  newFormDataPart() {
121
165
  this.formDataPart = new FormDataPart()
122
166
  this.setState("multi-part-form-data-header")
123
167
  }
124
168
 
169
+ /**
170
+ * @param {string} line
171
+ * @returns {void}
172
+ */
125
173
  parse(line) {
126
174
  if (this.state == "status") {
127
175
  this.parseStatusLine(line)
@@ -139,8 +187,10 @@ export default class RequestBuffer {
139
187
  const header = this.readHeaderFromLine(line)
140
188
 
141
189
  if (header) {
190
+ if (!this.formDataPart) throw new Error("formDataPart not set")
191
+
142
192
  this.formDataPart.addHeader(header)
143
- this.state == "multi-part-form-data"
193
+ //this.state == "multi-part-form-data"
144
194
  } else if (line == "\r\n") {
145
195
  this.setState("multi-part-form-data-body")
146
196
  }
@@ -149,6 +199,10 @@ export default class RequestBuffer {
149
199
  }
150
200
  }
151
201
 
202
+ /**
203
+ * @param {string} line
204
+ * @returns {Header | undefined}
205
+ */
152
206
  readHeaderFromLine(line) {
153
207
  const match = line.match(/^(.+): (.+)\r\n/)
154
208
 
@@ -159,6 +213,9 @@ export default class RequestBuffer {
159
213
  }
160
214
  }
161
215
 
216
+ /**
217
+ * @param {Header} header
218
+ */
162
219
  addHeader(header) {
163
220
  const formattedName = header.getFormattedName()
164
221
 
@@ -167,6 +224,10 @@ export default class RequestBuffer {
167
224
  if (formattedName == "content-length") this.contentLength = parseInt(header.getValue())
168
225
  }
169
226
 
227
+ /**
228
+ * @param {string} line
229
+ * @returns {void}
230
+ */
170
231
  parseHeader(line) {
171
232
  const header = this.readHeaderFromLine(line)
172
233
 
@@ -175,9 +236,9 @@ export default class RequestBuffer {
175
236
  this.addHeader(header)
176
237
  this.events.emit("header", header)
177
238
  } else if (line == "\r\n") {
178
- if (this.httpMethod.toUpperCase() == "GET" || this.httpMethod.toUpperCase() == "OPTIONS") {
239
+ if (this.httpMethod?.toUpperCase() == "GET" || this.httpMethod?.toUpperCase() == "OPTIONS") {
179
240
  this.completeRequest()
180
- } else if (this.httpMethod.toUpperCase() == "POST") {
241
+ } else if (this.httpMethod?.toUpperCase() == "POST") {
181
242
  this.readingBody = true
182
243
  this.bodyLength = 0
183
244
 
@@ -196,8 +257,11 @@ export default class RequestBuffer {
196
257
  } else if (!this.contentLength) {
197
258
  throw new Error("Content length hasn't been set")
198
259
  } else {
199
- this.postBodyBuffer = new ArrayBuffer(this.contentLength)
200
- this.postBodyChars = new Uint8Array(this.postBodyBuffer)
260
+ /** @type {number[]} */
261
+ this.postBodyChars = []
262
+
263
+ // this.postBodyBuffer = new ArrayBuffer(this.contentLength)
264
+ // this.postBodyChars = new Uint8Array(this.postBodyBuffer)
201
265
 
202
266
  this.setState("post-body")
203
267
  }
@@ -208,6 +272,10 @@ export default class RequestBuffer {
208
272
  }
209
273
  }
210
274
 
275
+ /**
276
+ * @param {string} line
277
+ * @returns {void}
278
+ */
211
279
  parseStatusLine(line) {
212
280
  const match = line.match(/^(GET|OPTIONS|POST) (.+?) HTTP\/(.+)\r\n/)
213
281
 
@@ -223,14 +291,20 @@ export default class RequestBuffer {
223
291
  }
224
292
 
225
293
  postRequestDone() {
226
- this.postBody = String.fromCharCode.apply(null, this.postBodyChars)
294
+ if (this.postBodyChars) {
295
+ this.postBody = String.fromCharCode.apply(null, this.postBodyChars)
296
+ }
227
297
 
228
298
  delete this.postBodyChars
229
- delete this.postBodyBuffer
299
+ // delete this.postBodyBuffer
230
300
 
231
301
  this.completeRequest()
232
302
  }
233
303
 
304
+ /**
305
+ * @param {string} newState
306
+ * @returns {void}
307
+ */
234
308
  setState(newState) {
235
309
  this.logger.debug(() => [`Changing state from ${this.state} to ${newState}`])
236
310
  this.state = newState
@@ -251,16 +325,20 @@ export default class RequestBuffer {
251
325
  }
252
326
 
253
327
  parseApplicationJsonParams() {
254
- const newParams = JSON.parse(this.postBody)
328
+ if (this.postBody) {
329
+ const newParams = JSON.parse(this.postBody)
255
330
 
256
- incorporate(this.params, newParams)
331
+ incorporate(this.params, newParams)
332
+ }
257
333
  }
258
334
 
259
335
  parseQueryStringPostParams() {
260
- const unparsedParams = querystring.parse(this.postBody)
261
- const paramsToObject = new ParamsToObject(unparsedParams)
262
- const newParams = paramsToObject.toObject()
336
+ if (this.postBody) {
337
+ const unparsedParams = querystring.parse(this.postBody)
338
+ const paramsToObject = new ParamsToObject(unparsedParams)
339
+ const newParams = paramsToObject.toObject()
263
340
 
264
- incorporate(this.params, newParams)
341
+ incorporate(this.params, newParams)
342
+ }
265
343
  }
266
344
  }
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  import {digg} from "diggerize"
2
4
  import {EventEmitter} from "events"
3
5
  import {incorporate} from "incorporator"
@@ -5,6 +7,10 @@ import ParamsToObject from "./params-to-object.js"
5
7
  import RequestBuffer from "./request-buffer/index.js"
6
8
 
7
9
  export default class VelociousHttpServerClientRequestParser {
10
+ /**
11
+ * @param {object} args
12
+ * @param {import("../../configuration.js").default} args.configuration
13
+ */
8
14
  constructor({configuration}) {
9
15
  if (!configuration) throw new Error("No configuration given")
10
16
 
@@ -26,7 +32,12 @@ export default class VelociousHttpServerClientRequestParser {
26
32
  this.requestBuffer.destroy()
27
33
  }
28
34
 
35
+ /**
36
+ * @param {import("./request-buffer/form-data-part.js").default} formDataPart
37
+ * @returns {void}
38
+ */
29
39
  onFormDataPart = (formDataPart) => {
40
+ /** @type {Record<string, string>} */
30
41
  const unorderedParams = {}
31
42
 
32
43
  unorderedParams[formDataPart.getName()] = formDataPart.getValue()
@@ -37,10 +48,25 @@ export default class VelociousHttpServerClientRequestParser {
37
48
  incorporate(this.params, newParams)
38
49
  }
39
50
 
51
+ /**
52
+ * @param {Buffer} data
53
+ */
40
54
  feed = (data) => this.requestBuffer.feed(data)
55
+
56
+ /**
57
+ * @param {string} name
58
+ */
41
59
  getHeader(name) { return this.requestBuffer.getHeader(name)?.value }
42
60
  getHeaders() { return this.requestBuffer.getHeadersHash() }
61
+
62
+ /**
63
+ * @returns {string}
64
+ */
43
65
  getHttpMethod() { return digg(this, "requestBuffer", "httpMethod") }
66
+
67
+ /**
68
+ * @returns {string}
69
+ */
44
70
  getHttpVersion() { return digg(this, "requestBuffer", "httpVersion") }
45
71
 
46
72
  _getHostMatch() {
@@ -1,4 +1,7 @@
1
+ // @ts-check
2
+
1
3
  import BacktraceCleaner from "../../utils/backtrace-cleaner.js"
4
+ import ensureError from "../../utils/ensure-error.js"
2
5
  import EventEmitter from "events"
3
6
  import {Logger} from "../../logger.js"
4
7
  import Response from "./response.js"
@@ -7,6 +10,11 @@ import RoutesResolver from "../../routes/resolver.js"
7
10
  export default class VelociousHttpServerClientRequestRunner {
8
11
  events = new EventEmitter()
9
12
 
13
+ /**
14
+ * @param {object} args
15
+ * @param {import("../../configuration.js").default} args.configuration
16
+ * @param {import("./request.js").default} args.request
17
+ */
10
18
  constructor({configuration, request}) {
11
19
  if (!configuration) throw new Error("No configuration given")
12
20
  if (!request) throw new Error("No request given")
@@ -30,8 +38,10 @@ export default class VelociousHttpServerClientRequestRunner {
30
38
  // Before we checked if the sec-fetch-mode was "cors", but it seems the sec-fetch-mode isn't always present
31
39
  await this.logger.debug(() => ["Run CORS", {httpMethod: request.httpMethod(), secFetchMode: request.header("sec-fetch-mode")}])
32
40
 
33
- if (configuration.cors) {
34
- await configuration.cors({request, response})
41
+ const cors = configuration.getCors()
42
+
43
+ if (cors) {
44
+ await cors({request, response})
35
45
  }
36
46
 
37
47
  if (request.httpMethod() == "OPTIONS" && request.header("sec-fetch-mode") == "cors") {
@@ -43,7 +53,9 @@ export default class VelociousHttpServerClientRequestRunner {
43
53
 
44
54
  await routesResolver.resolve()
45
55
  }
46
- } catch (error) {
56
+ } catch (e) {
57
+ const error = ensureError(e)
58
+
47
59
  await this.logger.error(() => [`Error while running request: ${BacktraceCleaner.getCleanedStack(error)}`])
48
60
 
49
61
  response.setStatus(500)
@@ -1,8 +1,15 @@
1
+ // @ts-check
2
+
1
3
  import {digg} from "diggerize"
2
4
  import RequestParser from "./request-parser.js"
3
5
  import restArgsError from "../../utils/rest-args-error.js"
4
6
 
5
7
  export default class VelociousHttpServerClientRequest {
8
+ /**
9
+ * @param {object} args
10
+ * @param {import("./index.js").default} args.client
11
+ * @param {import("../../configuration.js").default} args.configuration
12
+ */
6
13
  constructor({client, configuration, ...restArgs}) {
7
14
  restArgsError(restArgs)
8
15
 
@@ -12,7 +19,17 @@ export default class VelociousHttpServerClientRequest {
12
19
  }
13
20
 
14
21
  baseURL() { return `${this.protocol()}://${this.hostWithPort()}` }
22
+
23
+ /**
24
+ * @param {Buffer} data
25
+ * @returns {void}
26
+ */
15
27
  feed(data) { return this.requestParser.feed(data) }
28
+
29
+ /**
30
+ * @param {string} headerName
31
+ * @returns {string | null}
32
+ */
16
33
  header(headerName) { return this.getRequestBuffer().getHeader(headerName)?.getValue() }
17
34
  headers() { return this.getRequestBuffer().getHeadersHash() }
18
35
  httpMethod() { return this.requestParser.getHttpMethod() }
@@ -1,11 +1,25 @@
1
+ // @ts-check
2
+
1
3
  export default class VelociousHttpServerClientResponse {
2
- body = undefined
4
+ /** @type {string | null} */
5
+ body = null
6
+
7
+ /** @type {Record<string, string[]>} */
3
8
  headers = {}
4
9
 
10
+ /**
11
+ * @param {object} args
12
+ * @param {import("../../configuration.js").default} args.configuration
13
+ */
5
14
  constructor({configuration}) {
6
15
  this.configuration = configuration
7
16
  }
8
17
 
18
+ /**
19
+ * @param {string} key
20
+ * @param {string} value
21
+ * @returns {void}
22
+ */
9
23
  addHeader(key, value) {
10
24
  if (!(key in this.headers)) {
11
25
  this.headers[key] = []
@@ -14,10 +28,18 @@ export default class VelociousHttpServerClientResponse {
14
28
  this.headers[key].push(value)
15
29
  }
16
30
 
31
+ /**
32
+ * @param {string} key
33
+ * @param {string} value
34
+ * @returns {void}
35
+ */
17
36
  setHeader(key, value) {
18
37
  this.headers[key] = [value]
19
38
  }
20
39
 
40
+ /**
41
+ * @returns {string | null}
42
+ */
21
43
  getBody() {
22
44
  if (this.body !== undefined) {
23
45
  return this.body
@@ -26,23 +48,41 @@ export default class VelociousHttpServerClientResponse {
26
48
  throw new Error("No body has been set")
27
49
  }
28
50
 
51
+ /**
52
+ * @returns {number}
53
+ */
29
54
  getStatusCode() {
30
55
  return this.statusCode || 200
31
56
  }
32
57
 
58
+ /**
59
+ * @returns {string}
60
+ */
33
61
  getStatusMessage() {
34
62
  return this.statusMessage || "OK"
35
63
  }
36
64
 
65
+ /**
66
+ * @param {string} value
67
+ * @returns {void}
68
+ */
37
69
  setBody(value) {
38
70
  this.body = value
39
71
  }
40
72
 
73
+ /**
74
+ * @param {Error} error
75
+ * @returns {void}
76
+ */
41
77
  setErrorBody(error) {
42
78
  this.setHeader("Content-Type", "text/plain; charset=UTF-8")
43
79
  this.setBody(`${error.message}\n\n${error.stack}`)
44
80
  }
45
81
 
82
+ /**
83
+ * @param {number | string} status
84
+ * @returns {void}
85
+ */
46
86
  setStatus(status) {
47
87
  if (status == "success" || status == 200) {
48
88
  this.statusCode = 200
@@ -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"
@@ -7,11 +9,23 @@ import WorkerHandler from "./worker-handler/index.js"
7
9
 
8
10
  export default class VelociousHttpServer {
9
11
  clientCount = 0
12
+
13
+ /** @type {Record<string, ServerClient>} */
10
14
  clients = {}
15
+
11
16
  events = new EventEmitter()
12
17
  workerCount = 0
18
+
19
+ /** @type {WorkerHandler[]} */
13
20
  workerHandlers = []
14
21
 
22
+ /**
23
+ * @param {object} args
24
+ * @param {import("../configuration.js").default} args.configuration
25
+ * @param {string} [args.host]
26
+ * @param {number} [args.port]
27
+ * @param {number} [args.maxWorkers]
28
+ */
15
29
  constructor({configuration, host, maxWorkers, port}) {
16
30
  this.configuration = configuration
17
31
  this.logger = new Logger(this)
@@ -33,10 +47,12 @@ export default class VelociousHttpServer {
33
47
 
34
48
  _netServerListen() {
35
49
  return new Promise((resolve, reject) => {
50
+ if (!this.netServer) throw new Error("No netServer")
51
+
36
52
  try {
37
53
  this.netServer.listen(this.port, this.host, () => {
38
54
  this.logger.debug(`Velocious listening on ${this.host}:${this.port}`)
39
- resolve()
55
+ resolve(null)
40
56
  })
41
57
  } catch (error) {
42
58
  reject(error)
@@ -54,7 +70,11 @@ export default class VelociousHttpServer {
54
70
  * @returns {boolean}
55
71
  */
56
72
  isActive() {
57
- return this.netServer.listening
73
+ if (this.netServer) {
74
+ return this.netServer.listening
75
+ }
76
+
77
+ return false
58
78
  }
59
79
 
60
80
  async stopClients() {
@@ -71,11 +91,13 @@ export default class VelociousHttpServer {
71
91
 
72
92
  stopServer() {
73
93
  return new Promise((resolve, reject) => {
94
+ if (!this.netServer) throw new Error("No netServer to stop")
95
+
74
96
  this.netServer.close((error) => {
75
97
  if (error) {
76
98
  reject(error)
77
99
  } else {
78
- resolve()
100
+ resolve(null)
79
101
  }
80
102
  })
81
103
  })
@@ -90,6 +112,9 @@ export default class VelociousHttpServer {
90
112
  this.events.emit("close")
91
113
  }
92
114
 
115
+ /**
116
+ * @param {import("net").Socket} socket
117
+ */
93
118
  onConnection = (socket) => {
94
119
  const clientCount = this.clientCount
95
120
 
@@ -110,6 +135,9 @@ export default class VelociousHttpServer {
110
135
  this.clients[clientCount] = client
111
136
  }
112
137
 
138
+ /**
139
+ * @param {ServerClient} client
140
+ */
113
141
  onClientClose = (client) => {
114
142
  const clientCount = digg(client, "clientCount")
115
143
  const oldClientsLength = Object.keys(this.clients).length
@@ -140,7 +168,7 @@ export default class VelociousHttpServer {
140
168
  workerHandlerToUse() {
141
169
  this.logger.debug(`Worker handlers length: ${this.workerHandlers.length}`)
142
170
 
143
- const randomWorkerNumber = parseInt(Math.random() * this.workerHandlers.length)
171
+ const randomWorkerNumber = Math.floor(Math.random() * this.workerHandlers.length)
144
172
  const workerHandler = this.workerHandlers[randomWorkerNumber]
145
173
 
146
174
  if (!workerHandler) {
@@ -1,9 +1,17 @@
1
+ // @ts-check
2
+
1
3
  import EventEmitter from "events"
2
4
  import {Logger} from "../logger.js"
3
5
 
4
6
  export default class ServerClient {
5
7
  events = new EventEmitter()
6
8
 
9
+ /**
10
+ * @param {object} args
11
+ * @param {import("../configuration.js").default} args.configuration
12
+ * @param {import("net").Socket} args.socket
13
+ * @param {number} args.clientCount
14
+ */
7
15
  constructor({configuration, socket, clientCount}) {
8
16
  if (!configuration) throw new Error("No configuration given")
9
17
 
@@ -15,18 +23,26 @@ export default class ServerClient {
15
23
  socket.on("end", this.onSocketEnd)
16
24
  }
17
25
 
18
- listen = () => this.socket.on("data", this.onSocketData)
26
+ listen() {
27
+ this.socket.on("data", this.onSocketData)
28
+ }
19
29
 
20
30
  end() {
21
31
  return new Promise((resolve) => {
22
- this.socket.once("close", () => resolve())
32
+ this.socket.once("close", () => resolve(null))
23
33
  this.socket.end()
24
34
  })
25
35
  }
26
36
 
37
+ /**
38
+ * @param {Buffer} chunk
39
+ * @returns {void}
40
+ */
27
41
  onSocketData = (chunk) => {
28
42
  this.logger.debug(() => [`Socket ${this.clientCount}: ${chunk}`])
29
43
 
44
+ if (!this.worker) throw new Error("No worker")
45
+
30
46
  this.worker.postMessage({
31
47
  command: "clientWrite",
32
48
  chunk,
@@ -34,11 +50,18 @@ export default class ServerClient {
34
50
  })
35
51
  }
36
52
 
53
+ /**
54
+ * @returns {void}
55
+ */
37
56
  onSocketEnd = () => {
38
57
  this.logger.debug(`Socket ${this.clientCount} end`)
39
58
  this.events.emit("close", this)
40
59
  }
41
60
 
61
+ /**
62
+ * @param {string} data
63
+ * @returns {Promise<void>}
64
+ */
42
65
  async send(data) {
43
66
  return new Promise((resolve) => {
44
67
  this.logger.debug("Send", data)
@@ -47,4 +70,12 @@ export default class ServerClient {
47
70
  })
48
71
  })
49
72
  }
73
+
74
+ /**
75
+ * @param {import("worker_threads").Worker} newWorker
76
+ * @returns {void}
77
+ */
78
+ setWorker(newWorker) {
79
+ this.worker = newWorker
80
+ }
50
81
  }