@platformatic/runtime 1.15.0 → 1.16.0

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.
@@ -23,5 +23,6 @@
23
23
  "paths": [
24
24
  "plugin.js"
25
25
  ]
26
- }
26
+ },
27
+ "watch": false
27
28
  }
@@ -1,14 +1,23 @@
1
1
  'use strict'
2
+
2
3
  const assert = require('node:assert')
3
- const { request } = require('undici')
4
+ const { request, setGlobalDispatcher, Agent } = require('undici')
4
5
  const { startCommand } = require('..')
5
6
 
7
+ setGlobalDispatcher(new Agent({
8
+ keepAliveTimeout: 1,
9
+ keepAliveMaxTimeout: 1
10
+ }))
11
+
6
12
  async function main () {
7
13
  const entrypoint = await startCommand(['-c', process.argv[2]])
8
14
  const endpoint = process.argv[3] ?? '/'
9
15
  const res = await request(entrypoint + endpoint)
10
16
 
11
17
  assert.strictEqual(res.statusCode, 200)
18
+
19
+ // Consume the body so the service can end
20
+ await res.body.text()
12
21
  process.exit(42)
13
22
  }
14
23
 
package/lib/api.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const FastifyUndiciDispatcher = require('fastify-undici-dispatcher')
4
- const { Agent, setGlobalDispatcher } = require('undici')
4
+ const { setGlobalDispatcher, getGlobalDispatcher } = require('undici')
5
5
  const { PlatformaticApp } = require('./app')
6
6
  const errors = require('./errors')
7
7
  const { printSchema } = require('graphql')
@@ -34,7 +34,7 @@ class RuntimeApi {
34
34
  this.#services.set(service.id, app)
35
35
  }
36
36
 
37
- const globalAgent = new Agent()
37
+ const globalAgent = getGlobalDispatcher()
38
38
  const globalDispatcher = new FastifyUndiciDispatcher({
39
39
  dispatcher: globalAgent,
40
40
  // setting the domain here allows for fail-fast scenarios
@@ -73,6 +73,10 @@ class RuntimeApi {
73
73
  }
74
74
  }
75
75
 
76
+ if (this.#dispatcher) {
77
+ await this.#dispatcher.close()
78
+ }
79
+
76
80
  process.exit() // Exit the worker thread if all services are stopped
77
81
  }
78
82
 
@@ -132,12 +136,14 @@ class RuntimeApi {
132
136
  }
133
137
 
134
138
  async stopServices () {
139
+ const stopServiceReqs = [this.#dispatcher.close()]
135
140
  for (const service of this.#services.values()) {
136
141
  const serviceStatus = service.getStatus()
137
142
  if (serviceStatus === 'started') {
138
- await service.stop()
143
+ stopServiceReqs.push(service.stop())
139
144
  }
140
145
  }
146
+ await Promise.all(stopServiceReqs)
141
147
  }
142
148
 
143
149
  async #restartServices () {
package/lib/config.js CHANGED
@@ -33,7 +33,14 @@ async function _transformConfig (configManager) {
33
33
 
34
34
  const config = join(entryPath, configFilename)
35
35
 
36
- services.push({ id, config, path: entryPath, useHttp: !!mapping.useHttp })
36
+ const service = { id, config, path: entryPath, useHttp: !!mapping.useHttp }
37
+ const existingServiceId = services.findIndex(service => service.id === id)
38
+
39
+ if (existingServiceId !== -1) {
40
+ services[existingServiceId] = service
41
+ } else {
42
+ services.push(service)
43
+ }
37
44
  }
38
45
  }
39
46
 
package/lib/start.js CHANGED
@@ -48,7 +48,11 @@ async function startWithConfig (configManager, env = process.env) {
48
48
  })
49
49
 
50
50
  let exited = null
51
- worker.on('exit', () => {
51
+ let isWorkerAlive = true
52
+ worker.on('exit', (code) => {
53
+ // TODO(mcollina): refactor to not set this here
54
+ process.exitCode = code
55
+ isWorkerAlive = false
52
56
  configManager.fileWatcher?.stopWatching()
53
57
  if (typeof exited === 'function') {
54
58
  exited()
@@ -70,9 +74,14 @@ async function startWithConfig (configManager, env = process.env) {
70
74
  worker.postMessage({ signal: 'SIGUSR2' })
71
75
  })
72
76
 
77
+ // TODO(mcollina): refactor to not alter globals here
73
78
  closeWithGrace((event, cb) => {
74
- worker.postMessage(event)
75
- exited = cb
79
+ if (isWorkerAlive) {
80
+ worker.postMessage(event)
81
+ exited = cb
82
+ } else {
83
+ setImmediate(cb)
84
+ }
76
85
  })
77
86
 
78
87
  /* c8 ignore next 3 */
package/lib/worker.js CHANGED
@@ -58,25 +58,6 @@ if (config.server) {
58
58
  config.server.logger = logger
59
59
  }
60
60
 
61
- /* c8 ignore next 4 */
62
- process.once('uncaughtException', (err) => {
63
- logger.error({ err }, 'runtime error')
64
- logger[pino.symbols.streamSym].flushSync?.()
65
- setImmediate(() => {
66
- process.exit(1)
67
- })
68
- })
69
-
70
- // Tested by test/cli/start.test.mjs by C8 does not see it.
71
- /* c8 ignore next 4 */
72
- process.once('unhandledRejection', (err) => {
73
- logger.error({ err }, 'runtime error')
74
- logger[pino.symbols.streamSym].flushSync?.()
75
- setImmediate(() => {
76
- process.exit(1)
77
- })
78
- })
79
-
80
61
  function main () {
81
62
  const { inspectorOptions } = workerData.config
82
63
 
@@ -93,6 +74,38 @@ function main () {
93
74
  runtime.startListening(parentPort)
94
75
 
95
76
  parentPort.postMessage('plt:init')
77
+
78
+ let stopping = false
79
+
80
+ async function stop () {
81
+ if (stopping) {
82
+ return
83
+ }
84
+
85
+ stopping = true
86
+ try {
87
+ await runtime.stopServices()
88
+ } catch (err) {
89
+ logger.error({ err }, 'error while stopping services')
90
+ }
91
+ }
92
+
93
+ /* c8 ignore next 4 */
94
+ process.on('uncaughtException', (err) => {
95
+ logger.error({ err }, 'runtime error')
96
+ stop().then(() => {
97
+ process.exit(1)
98
+ })
99
+ })
100
+
101
+ // Tested by test/cli/start.test.mjs by C8 does not see it.
102
+ /* c8 ignore next 4 */
103
+ process.on('unhandledRejection', (err) => {
104
+ logger.error({ err }, 'runtime error')
105
+ stop().then(() => {
106
+ process.exit(1)
107
+ })
108
+ })
96
109
  }
97
110
 
98
111
  main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -28,8 +28,8 @@
28
28
  "standard": "^17.1.0",
29
29
  "tsd": "^0.29.0",
30
30
  "typescript": "^5.2.2",
31
- "@platformatic/sql-mapper": "1.15.0",
32
- "@platformatic/sql-graphql": "1.15.0"
31
+ "@platformatic/sql-graphql": "1.16.0",
32
+ "@platformatic/sql-mapper": "1.16.0"
33
33
  },
34
34
  "dependencies": {
35
35
  "@fastify/error": "^3.4.0",
@@ -44,18 +44,19 @@
44
44
  "fastify": "^4.24.1",
45
45
  "fastify-undici-dispatcher": "^0.5.0",
46
46
  "graphql": "^16.8.1",
47
- "help-me": "^4.2.0",
47
+ "help-me": "^5.0.0",
48
48
  "minimist": "^1.2.8",
49
49
  "pino": "^8.16.0",
50
50
  "pino-pretty": "^10.2.3",
51
- "undici": "^6.0.0",
52
- "@platformatic/db": "1.15.0",
53
- "@platformatic/config": "1.15.0",
54
- "@platformatic/composer": "1.15.0",
55
- "@platformatic/generators": "1.15.0",
56
- "@platformatic/utils": "1.15.0",
57
- "@platformatic/telemetry": "1.15.0",
58
- "@platformatic/service": "1.15.0"
51
+ "undici": "^6.2.0",
52
+ "why-is-node-running": "^2.2.2",
53
+ "@platformatic/composer": "1.16.0",
54
+ "@platformatic/config": "1.16.0",
55
+ "@platformatic/generators": "1.16.0",
56
+ "@platformatic/service": "1.16.0",
57
+ "@platformatic/db": "1.16.0",
58
+ "@platformatic/telemetry": "1.16.0",
59
+ "@platformatic/utils": "1.16.0"
59
60
  },
60
61
  "standard": {
61
62
  "ignore": [
@@ -64,8 +65,8 @@
64
65
  ]
65
66
  },
66
67
  "scripts": {
67
- "test": "npm run lint && node test.js test/cli/*.test.mjs && tsd",
68
- "coverage": "npm run lint && c8 -x fixtures -x test node test.js && tsd",
68
+ "test": "npm run lint && node ./test/runner.js && tsd",
69
+ "coverage": "npm run lint && c8 -x fixtures -x test node ./test/runner.js && tsd",
69
70
  "lint": "standard | snazzy"
70
71
  }
71
72
  }
package/test.js DELETED
@@ -1,20 +0,0 @@
1
- 'use strict'
2
-
3
- const { tap, spec } = require('node:test/reporters')
4
- const { run } = require('node:test')
5
- const path = require('node:path')
6
- const glob = require('glob').globSync
7
-
8
- /* eslint-disable new-cap */
9
- const reporter = process.stdout.isTTY ? new spec() : tap
10
-
11
- const files = [
12
- ...glob(path.join(__dirname, 'test', '*.test.js')),
13
- ...glob(path.join(__dirname, 'test', 'cli', '*.test.mjs'))
14
- ]
15
-
16
- run({
17
- files,
18
- concurrency: 1,
19
- timeout: 30000
20
- }).compose(reporter).pipe(process.stdout)