@ditojs/server 1.5.1 → 1.5.2

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/server",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "type": "module",
5
5
  "description": "Dito.js Server – Dito.js is a declarative and modern web framework, based on Objection.js, Koa.js and Vue.js",
6
6
  "repository": "https://github.com/ditojs/dito/tree/master/packages/server",
@@ -30,7 +30,7 @@
30
30
  "@originjs/vite-plugin-commonjs": "^1.0.3",
31
31
  "ajv": "^8.11.0",
32
32
  "ajv-formats": "^2.1.1",
33
- "aws-sdk": "^2.1106.0",
33
+ "aws-sdk": "^2.1108.0",
34
34
  "axios": "^0.26.1",
35
35
  "bcryptjs": "^2.4.3",
36
36
  "bytes": "^3.1.2",
@@ -76,10 +76,10 @@
76
76
  "objection": "^3.0.1"
77
77
  },
78
78
  "devDependencies": {
79
- "knex": "^1.0.4",
79
+ "knex": "^1.0.5",
80
80
  "objection": "^3.0.1",
81
81
  "pg": "^8.7.3",
82
82
  "sqlite3": "^5.0.2"
83
83
  },
84
- "gitHead": "40731960cf8b528f4f40f8b7e43e2e966138337c"
84
+ "gitHead": "b7861d7cea5426a8a487ce4c3b9d9f1ff3e908ce"
85
85
  }
@@ -22,8 +22,8 @@ import helmet from 'koa-helmet'
22
22
  import responseTime from 'koa-response-time'
23
23
  import Router from '@ditojs/router'
24
24
  import {
25
- isArray, isObject, isString, asArray, isPlainObject, hyphenate, clone, merge,
26
- parseDataPath, normalizeDataPath, isModule
25
+ isArray, isObject, isString, asArray, isPlainObject, isModule,
26
+ hyphenate, clone, merge, parseDataPath, normalizeDataPath, toPromiseCallback
27
27
  } from '@ditojs/utils'
28
28
  import SessionStore from './SessionStore.js'
29
29
  import { Validator } from './Validator.js'
@@ -74,6 +74,7 @@ export class Application extends Koa {
74
74
  log = {},
75
75
  ...rest
76
76
  } = config
77
+ this.server = null
77
78
  this.config = {
78
79
  app,
79
80
  log: log.silent || process.env.DITO_SILENT ? {} : log,
@@ -106,6 +107,10 @@ export class Application extends Koa {
106
107
  }
107
108
  }
108
109
 
110
+ get isRunning() {
111
+ return !!this.server
112
+ }
113
+
109
114
  addRoute(
110
115
  method, path, transacted, middlewares, controller = null, action = null
111
116
  ) {
@@ -283,10 +288,6 @@ export class Application extends Koa {
283
288
  return Object.values(this.services).find(callback)
284
289
  }
285
290
 
286
- forEachService(callback) {
287
- return Promise.all(Object.values(this.services).map(callback))
288
- }
289
-
290
291
  addControllers(controllers, namespace) {
291
292
  for (const [key, value] of Object.entries(controllers)) {
292
293
  if (isModule(value) || isPlainObject(value)) {
@@ -718,46 +719,54 @@ export class Application extends Koa {
718
719
  this.on('error', this.logError)
719
720
  }
720
721
  await this.emit('before:start')
721
- await this.forEachService(service => service.start())
722
- const { server: { host, port } } = this.config
723
- this.server = await new Promise((resolve, reject) => {
724
- const server = this.listen(port, host, () => {
725
- const { port } = server.address()
722
+ this.server = await new Promise(resolve => {
723
+ const server = this.listen(this.config.server, () => {
724
+ const { address, port } = server.address()
726
725
  console.info(
727
- `Dito server started at http://${host}:${port}`
726
+ `Dito.js server started at http://${address}:${port}`
728
727
  )
729
728
  resolve(server)
730
729
  })
731
- if (!server) {
732
- reject(new Error(`Unable to start server at http://${host}:${port}`))
733
- }
734
730
  })
731
+ if (!this.server) {
732
+ throw new Error('Unable to start Dito.js server')
733
+ }
735
734
  await this.emit('after:start')
736
735
  }
737
736
 
738
- async stop() {
739
- await this.emit('before:stop')
740
- this.server = await new Promise((resolve, reject) => {
741
- const { server } = this
742
- if (server) {
743
- server.close(err => {
744
- if (err) {
745
- reject(err)
746
- } else {
747
- resolve(null)
748
- }
749
- })
750
- // Hack to make sure that we close the server,
751
- // even if sockets are still open.
752
- // Taken from https://stackoverflow.com/a/36830072.
753
- // A proper solution would be to use a library, ex: https://github.com/godaddy/terminus
754
- setImmediate(() => server.emit('close'))
755
- } else {
756
- reject(new Error('Server is not running'))
757
- }
758
- })
759
- await this.forEachService(service => service.stop())
760
- await this.emit('after:stop')
737
+ async stop(timeout = 0) {
738
+ if (!this.server) {
739
+ throw new Error('Dito.js server is not running')
740
+ }
741
+
742
+ const promise = (async () => {
743
+ await this.emit('before:stop')
744
+ await new Promise((resolve, reject) => {
745
+ this.server.close(toPromiseCallback(resolve, reject))
746
+ })
747
+ // Hack to make sure that the server is closed, even if sockets are still
748
+ // open after `server.close()`, see: https://stackoverflow.com/a/36830072
749
+ this.server.emit('close')
750
+ this.server = null
751
+ await this.emit('after:stop')
752
+ })()
753
+
754
+ if (timeout > 0) {
755
+ await Promise.race([
756
+ promise,
757
+ new Promise((resolve, reject) =>
758
+ setTimeout(reject,
759
+ timeout,
760
+ new Error(
761
+ `Timeout reached while stopping Dito.js server (${timeout}ms)`
762
+ )
763
+ )
764
+ )
765
+ ])
766
+ } else {
767
+ await promise
768
+ }
769
+
761
770
  if (this.config.log.errors !== false) {
762
771
  this.off('error', this.logError)
763
772
  }
@@ -1,4 +1,5 @@
1
1
  import path from 'path'
2
+ import { exit } from 'process'
2
3
  import Koa from 'koa'
3
4
  import serve from 'koa-static'
4
5
  import { defineConfig, createServer } from 'vite'
@@ -90,6 +91,7 @@ export class AdminController extends Controller {
90
91
  }
91
92
  }
92
93
 
94
+ // @override
93
95
  compose() {
94
96
  this.koa = new Koa()
95
97
  this.koa.use(this.middleware())
@@ -128,6 +130,32 @@ export class AdminController extends Controller {
128
130
  }
129
131
  }
130
132
  })
133
+
134
+ let closed = false
135
+
136
+ // Monkey-patch `process.exit()` to filter out the calls caused by vite's
137
+ // handling of SIGTERM, see: https://github.com/vitejs/vite/issues/7627
138
+ process.exit = code => {
139
+ // Filter out calls from inside vite by looking at the stack trace.
140
+ if (new Error().stack.includes('/vite/dist/')) {
141
+ // vite's own `exitProcess()` just called `process.exit(), and this
142
+ // means it has already called `server.close()` internally.
143
+ closed = true
144
+ process.exit = exit
145
+ } else {
146
+ exit(code)
147
+ }
148
+ }
149
+
150
+ this.app.once('before:stop', () => {
151
+ // For good timing it seems crucial to not add more ticks with async
152
+ // signature, so we directly return the `server.close()` promise instead.
153
+ process.exit = exit
154
+ if (!closed) {
155
+ closed = true
156
+ return server.close()
157
+ }
158
+ })
131
159
  this.koa.use(handleConnectMiddleware(server.middlewares, {
132
160
  expandMountPath: true
133
161
  }))
@@ -26,6 +26,12 @@ export class Controller {
26
26
  initialize() {
27
27
  }
28
28
 
29
+ // @return {Application|Function} [app or function]
30
+ compose() {
31
+ // To be overridden in sub-classes, if the controller needs to install
32
+ // middleware. For normal routes, use `this.app.addRoute()` instead.
33
+ }
34
+
29
35
  setup(isRoot = true, setupActionsObject = true) {
30
36
  this._setupEmitter(this.hooks, {
31
37
  // Support wildcard hooks only on controllers:
@@ -246,12 +252,6 @@ export class Controller {
246
252
  ])
247
253
  }
248
254
 
249
- // @return {Application|Function} [app or function]
250
- compose() {
251
- // To be overridden in sub-classes, if the controller needs to install
252
- // middleware. For normal routes, use `this.app.addRoute()` instead.
253
- }
254
-
255
255
  getPath(type, path) {
256
256
  // To be overridden by sub-classes.
257
257
  return path
@@ -11,6 +11,8 @@ export class Service {
11
11
 
12
12
  setup(config) {
13
13
  this.config = config
14
+ this.app.on('before:start', () => this.start())
15
+ this.app.on('after:stop', () => this.stop())
14
16
  }
15
17
 
16
18
  // @overridable