@platformatic/runtime 0.32.0 → 0.33.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.
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://platformatic.dev/schemas/v0.32.0/runtime",
3
+ "entrypoint": "serviceApp",
4
+ "allowCycles": true,
5
+ "hotReload": true,
6
+ "autoload": {
7
+ "path": "../monorepo",
8
+ "exclude": ["docs", "composerApp"],
9
+ "mappings": {
10
+ "serviceAppWithLogger": {
11
+ "id": "with-logger",
12
+ "config": "platformatic.service.json"
13
+ },
14
+ "serviceAppWithMultiplePlugins": {
15
+ "id": "multi-plugin-service",
16
+ "config": "platformatic.service.json"
17
+ }
18
+ }
19
+ }
20
+ }
@@ -15,5 +15,6 @@
15
15
  "paths": [
16
16
  "plugin.js"
17
17
  ]
18
- }
18
+ },
19
+ "watch": true
19
20
  }
@@ -1,10 +1,10 @@
1
1
  'use strict'
2
2
  const assert = require('node:assert')
3
3
  const { request } = require('undici')
4
- const { startCommandInRuntime } = require('../lib/unified-api')
4
+ const { startCommand } = require('../lib/unified-api')
5
5
 
6
6
  async function main () {
7
- const entrypoint = await startCommandInRuntime(['-c', process.argv[2]])
7
+ const entrypoint = await startCommand(['-c', process.argv[2]])
8
8
  const endpoint = process.argv[3] ?? '/'
9
9
  const res = await request(entrypoint + endpoint)
10
10
 
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://platformatic.dev/schemas/v0.31.0/runtime",
3
+ "entrypoint": "echo",
4
+ "allowCycles": false,
5
+ "hotReload": true,
6
+ "autoload": {
7
+ "path": "services",
8
+ "exclude": [
9
+ "docs"
10
+ ]
11
+ },
12
+ "telemetry": {
13
+ "serviceName": "test-runtime",
14
+ "version": "1.0.0",
15
+ "exporter": {
16
+ "type": "memory"
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://platformatic.dev/schemas/v0.28.1/service",
3
+ "server": {
4
+ "hostname": "127.0.0.1",
5
+ "port": "0",
6
+ "logger": {
7
+ "level": "info"
8
+ }
9
+ },
10
+ "service": {
11
+ "openapi": true
12
+ },
13
+ "plugins": {
14
+ "paths": [
15
+ "./routes"
16
+ ],
17
+ "typescript": false
18
+ }
19
+ }
@@ -0,0 +1,8 @@
1
+ 'use strict'
2
+ module.exports = async function (fastify, opts) {
3
+ // This returns the traceId set on the span by the service
4
+ fastify.get('/', async (request, reply) => {
5
+ const traceId = request.span.spanContext().traceId
6
+ return { traceId }
7
+ })
8
+ }
package/lib/api.js CHANGED
@@ -11,10 +11,12 @@ class RuntimeApi {
11
11
 
12
12
  constructor (config, logger, loaderPort) {
13
13
  this.#services = new Map()
14
+ const telemetryConfig = config.telemetry
14
15
 
15
16
  for (let i = 0; i < config.services.length; ++i) {
16
17
  const service = config.services[i]
17
- const app = new PlatformaticApp(service, loaderPort, logger)
18
+ const serviceTelemetryConfig = telemetryConfig ? { ...telemetryConfig, serviceName: `${telemetryConfig.serviceName}-${service.id}` } : null
19
+ const app = new PlatformaticApp(service, loaderPort, logger, serviceTelemetryConfig)
18
20
 
19
21
  this.#services.set(service.id, app)
20
22
  }
@@ -47,9 +49,9 @@ class RuntimeApi {
47
49
  }
48
50
 
49
51
  async #handleProcessLevelEvent (message) {
50
- for (const service of this.#services.values()) {
52
+ await Promise.allSettled(this.#services.values().map(async (service) => {
51
53
  await service.handleProcessLevelEvent(message)
52
- }
54
+ }))
53
55
  }
54
56
 
55
57
  async #executeCommand (message) {
package/lib/app.js CHANGED
@@ -1,7 +1,9 @@
1
1
  'use strict'
2
+
2
3
  const { once } = require('node:events')
3
4
  const { dirname, basename } = require('node:path')
4
5
  const { FileWatcher } = require('@platformatic/utils')
6
+ const debounce = require('debounce')
5
7
  const {
6
8
  buildServer,
7
9
  loadConfig
@@ -13,9 +15,11 @@ class PlatformaticApp {
13
15
  #restarting
14
16
  #started
15
17
  #originalWatch
18
+ #fileWatcher
16
19
  #logger
20
+ #telemetryConfig
17
21
 
18
- constructor (appConfig, loaderPort, logger) {
22
+ constructor (appConfig, loaderPort, logger, telemetryConfig) {
19
23
  this.appConfig = appConfig
20
24
  this.config = null
21
25
  this.#hotReload = false
@@ -24,9 +28,11 @@ class PlatformaticApp {
24
28
  this.server = null
25
29
  this.#started = false
26
30
  this.#originalWatch = null
31
+ this.#fileWatcher = null
27
32
  this.#logger = logger.child({
28
33
  name: this.appConfig.id
29
34
  })
35
+ this.#telemetryConfig = telemetryConfig
30
36
  }
31
37
 
32
38
  getStatus () {
@@ -38,16 +44,11 @@ class PlatformaticApp {
38
44
  }
39
45
 
40
46
  async restart (force) {
41
- if (this.#restarting) {
42
- return
43
- }
44
-
45
- if (!this.#hotReload && !force) {
47
+ if (this.#restarting || !this.#started || (!this.#hotReload && !force)) {
46
48
  return
47
49
  }
48
50
 
49
51
  this.#restarting = true
50
- await this.stop()
51
52
 
52
53
  /* c8 ignore next 4 - tests may not pass in a MessagePort. */
53
54
  if (this.#loaderPort) {
@@ -55,7 +56,13 @@ class PlatformaticApp {
55
56
  await once(this.#loaderPort, 'message')
56
57
  }
57
58
 
58
- await this.start()
59
+ this.#setuplogger(this.config.configManager)
60
+ try {
61
+ await this.server.restart()
62
+ } catch (err) {
63
+ this.#logAndExit(err)
64
+ }
65
+
59
66
  this.#restarting = false
60
67
  }
61
68
 
@@ -64,38 +71,37 @@ class PlatformaticApp {
64
71
  throw new Error('application is already started')
65
72
  }
66
73
 
67
- if (!this.#restarting) {
68
- await this.#initializeConfig()
69
- }
74
+ this.#started = true
75
+
76
+ await this.#initializeConfig()
77
+ this.#originalWatch = this.config.configManager.current.watch
78
+ this.config.configManager.current.watch = false
79
+
70
80
  const { configManager } = this.config
81
+ configManager.update({
82
+ ...configManager.current,
83
+ telemetry: this.#telemetryConfig
84
+ })
71
85
  const config = configManager.current
72
86
 
73
- this.#originalWatch = config.watch
74
- config.watch = false
75
87
  this.#setuplogger(configManager)
76
88
 
77
89
  try {
78
90
  // If this is a restart, have the fastify server restart itself. If this
79
91
  // is not a restart, then create a new server.
80
- if (this.#restarting) {
81
- await this.server.restart()
82
- } else {
83
- this.server = await buildServer({
84
- ...config,
85
- configManager
86
- })
87
- }
92
+ this.server = await buildServer({
93
+ ...config,
94
+ configManager
95
+ })
88
96
  } catch (err) {
89
97
  this.#logAndExit(err)
90
98
  }
91
99
 
92
- if (config.plugins !== undefined && this.#originalWatch !== false) {
100
+ if (config.plugins !== undefined) {
93
101
  this.#startFileWatching()
94
102
  }
95
103
 
96
- this.#started = true
97
-
98
- if (this.appConfig.entrypoint && !this.#restarting) {
104
+ if (this.appConfig.entrypoint) {
99
105
  try {
100
106
  await this.server.start()
101
107
  /* c8 ignore next 5 */
@@ -111,11 +117,9 @@ class PlatformaticApp {
111
117
  throw new Error('application has not been started')
112
118
  }
113
119
 
114
- if (!this.#restarting) {
115
- await this.server.close()
116
- }
117
-
118
120
  await this.#stopFileWatching()
121
+ await this.server.close()
122
+
119
123
  this.#started = false
120
124
  }
121
125
 
@@ -199,11 +203,13 @@ class PlatformaticApp {
199
203
  this.#hotReload = this.appConfig.hotReload
200
204
 
201
205
  configManager.on('update', async (newConfig) => {
202
- this.server.platformatic.config = newConfig
203
- applyOverrides()
204
- this.server.log.debug('config changed')
205
- this.server.log.trace({ newConfig }, 'new config')
206
- await this.restart()
206
+ if (this.server) { // when we setup telemetry on config, we don't have a server yet
207
+ this.server.platformatic.config = newConfig
208
+ applyOverrides()
209
+ this.server.log.debug('config changed')
210
+ this.server.log.trace({ newConfig }, 'new config')
211
+ await this.restart()
212
+ }
207
213
  })
208
214
 
209
215
  configManager.on('error', (err) => {
@@ -221,6 +227,9 @@ class PlatformaticApp {
221
227
  }
222
228
 
223
229
  #startFileWatching () {
230
+ if (this.#fileWatcher) {
231
+ return
232
+ }
224
233
  const server = this.server
225
234
  const { configManager } = server.platformatic
226
235
  // TODO FileWatcher and ConfigManager both watch the configuration file
@@ -233,27 +242,30 @@ class PlatformaticApp {
233
242
  })
234
243
 
235
244
  /* c8 ignore next 4 */
236
- fileWatcher.on('update', async () => {
237
- this.server.log.debug('files changed')
245
+ const restart = debounce(() => {
246
+ this.server.log.info('files changed')
238
247
  this.restart()
239
- })
248
+ }, 100) // debounce restart for 100ms
249
+ fileWatcher.on('update', restart)
240
250
 
241
251
  fileWatcher.startWatching()
242
252
  server.log.debug('start watching files')
243
253
  server.platformatic.fileWatcher = fileWatcher
244
254
  server.platformatic.configManager.startWatching()
255
+ this.#fileWatcher = fileWatcher
245
256
  }
246
257
 
247
258
  async #stopFileWatching () {
248
- const watcher = this.server.platformatic.fileWatcher
249
259
  // The configManager automatically watches for the config file changes
250
260
  // therefore we need to stop it all the times.
251
261
  await this.config.configManager.stopWatching()
252
262
 
263
+ const watcher = this.#fileWatcher
253
264
  if (watcher) {
254
265
  this.server.log.debug('stop watching files')
255
266
  await watcher.stopWatching()
256
267
  this.server.platformatic.fileWatcher = undefined
268
+ this.#fileWatcher = null
257
269
  }
258
270
  }
259
271
 
package/lib/schema.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #! /usr/bin/env node
2
2
  'use strict'
3
3
 
4
+ const telemetry = require('@platformatic/telemetry').schema
4
5
  const pkg = require('../package.json')
5
6
  const version = 'v' + pkg.version
6
7
  const platformaticRuntimeSchema = {
@@ -42,6 +43,7 @@ const platformaticRuntimeSchema = {
42
43
  }
43
44
  }
44
45
  },
46
+ telemetry,
45
47
  services: {
46
48
  type: 'array',
47
49
  default: [],
package/lib/start.js CHANGED
@@ -46,8 +46,12 @@ async function startWithConfig (configManager, env = process.env) {
46
46
  env
47
47
  })
48
48
 
49
+ let exited = null
49
50
  worker.on('exit', () => {
50
51
  configManager.fileWatcher?.stopWatching()
52
+ if (typeof exited === 'function') {
53
+ exited()
54
+ }
51
55
  })
52
56
 
53
57
  worker.on('error', () => {
@@ -65,8 +69,9 @@ async function startWithConfig (configManager, env = process.env) {
65
69
  worker.postMessage({ signal: 'SIGUSR2' })
66
70
  })
67
71
 
68
- closeWithGrace((event) => {
72
+ closeWithGrace((event, cb) => {
69
73
  worker.postMessage(event)
74
+ exited = cb
70
75
  })
71
76
 
72
77
  /* c8 ignore next 3 */
@@ -154,14 +154,6 @@ async function _start (args) {
154
154
  }
155
155
 
156
156
  async function startCommand (args) {
157
- try {
158
- await _start(args)
159
- } catch (err) {
160
- logErrorAndExit(err)
161
- }
162
- }
163
-
164
- async function startCommandInRuntime (args) {
165
157
  try {
166
158
  const configType = await getConfigType(args)
167
159
  const config = await _loadConfig({}, args, configType)
@@ -200,6 +192,5 @@ module.exports = {
200
192
  loadConfig: _loadConfig,
201
193
  start: _start,
202
194
  startCommand,
203
- startCommandInRuntime,
204
195
  getApp
205
196
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -24,13 +24,14 @@
24
24
  "standard": "^17.1.0",
25
25
  "tsd": "^0.28.1",
26
26
  "typescript": "^5.1.6",
27
- "@platformatic/sql-graphql": "0.32.0",
28
- "@platformatic/sql-mapper": "0.32.0"
27
+ "@platformatic/sql-graphql": "0.33.0",
28
+ "@platformatic/sql-mapper": "0.33.0"
29
29
  },
30
30
  "dependencies": {
31
31
  "@hapi/topo": "^6.0.2",
32
32
  "close-with-grace": "^1.2.0",
33
33
  "commist": "^3.2.0",
34
+ "debounce": "^1.2.1",
34
35
  "desm": "^1.3.0",
35
36
  "es-main": "^1.2.0",
36
37
  "fastest-levenshtein": "^1.0.16",
@@ -41,11 +42,12 @@
41
42
  "pino": "^8.14.1",
42
43
  "pino-pretty": "^10.0.0",
43
44
  "undici": "^5.22.1",
44
- "@platformatic/composer": "0.32.0",
45
- "@platformatic/config": "0.32.0",
46
- "@platformatic/db": "0.32.0",
47
- "@platformatic/service": "0.32.0",
48
- "@platformatic/utils": "0.32.0"
45
+ "@platformatic/composer": "0.33.0",
46
+ "@platformatic/config": "0.33.0",
47
+ "@platformatic/db": "0.33.0",
48
+ "@platformatic/service": "0.33.0",
49
+ "@platformatic/telemetry": "0.33.0",
50
+ "@platformatic/utils": "0.33.0"
49
51
  },
50
52
  "standard": {
51
53
  "ignore": [
package/runtime.mjs CHANGED
@@ -6,7 +6,7 @@ import { join } from 'desm'
6
6
  import isMain from 'es-main'
7
7
  import helpMe from 'help-me'
8
8
  import parseArgs from 'minimist'
9
- import { start } from './lib/start.js'
9
+ import { startCommand } from './lib/unified-api.js'
10
10
  import { compile as compileCmd } from './lib/compile.js'
11
11
 
12
12
  export const compile = compileCmd
@@ -22,7 +22,7 @@ const program = commist({ maxDistance: 2 })
22
22
  program.register('help', help.toStdout)
23
23
  program.register('help start', help.toStdout.bind(null, ['start']))
24
24
  program.register('help compile', help.toStdout.bind(null, ['compile']))
25
- program.register('start', start)
25
+ program.register('start', startCommand)
26
26
  program.register('compile', compile)
27
27
 
28
28
  export async function run (argv) {
package/test/app.test.js CHANGED
@@ -90,7 +90,7 @@ test('errors when stopping an already stopped application', async (t) => {
90
90
  })
91
91
 
92
92
  test('does not restart while restarting', async (t) => {
93
- const { logger } = getLoggerAndStream()
93
+ const { logger, stream } = getLoggerAndStream()
94
94
  const appPath = join(fixturesDir, 'monorepo', 'serviceApp')
95
95
  const configFile = join(appPath, 'platformatic.service.json')
96
96
  const config = {
@@ -105,17 +105,33 @@ test('does not restart while restarting', async (t) => {
105
105
  }
106
106
  const app = new PlatformaticApp(config, null, logger)
107
107
 
108
- t.after(app.stop.bind(app))
108
+ t.after(async () => {
109
+ try {
110
+ await app.stop()
111
+ } catch {}
112
+ })
109
113
  await app.start()
110
- t.mock.method(app, 'stop')
111
114
  await Promise.all([
112
115
  app.restart(),
113
116
  app.restart(),
114
117
  app.restart()
115
118
  ])
119
+ await app.stop()
120
+ stream.end()
121
+ const lines = []
122
+ for await (const line of stream) {
123
+ lines.push(line)
124
+ }
116
125
 
117
- // stop() should have only been called once despite three restart() calls.
118
- assert.strictEqual(app.stop.mock.calls.length, 1)
126
+ let count = 0
127
+ for (const line of lines) {
128
+ // every time we restart we log listening
129
+ if (line.msg.match(/listening/)) {
130
+ count++
131
+ }
132
+ }
133
+
134
+ assert.strictEqual(count, 2)
119
135
  })
120
136
 
121
137
  test('restarts on SIGUSR2', async (t) => {
@@ -35,12 +35,14 @@ export async function start (...args) {
35
35
 
36
36
  for await (const messages of on(output, 'data')) {
37
37
  for (const message of messages) {
38
- const url = message.url ??
39
- message.msg.match(/server listening at (.+)/i)?.[1]
38
+ if (message.msg) {
39
+ const url = message.url ??
40
+ message.msg.match(/server listening at (.+)/i)?.[1]
40
41
 
41
- if (url !== undefined) {
42
- clearTimeout(errorTimeout)
43
- return { child, url, output }
42
+ if (url !== undefined) {
43
+ clearTimeout(errorTimeout)
44
+ return { child, url, output }
45
+ }
44
46
  }
45
47
  }
46
48
  }
@@ -74,7 +74,7 @@ test('does not start if node inspector flags are provided', async (t) => {
74
74
  for (const message of messages) {
75
75
  stderr += message
76
76
 
77
- if (/Error: The Node.js inspector flags are not supported/.test(stderr)) {
77
+ if (/The Node.js inspector flags are not supported/.test(stderr)) {
78
78
  found = true
79
79
  break
80
80
  }
@@ -47,9 +47,13 @@ function createEsmLoggingPlugin (text, reloaded) {
47
47
  `
48
48
  }
49
49
 
50
+ function saferm (path) {
51
+ return rm(path, { recursive: true, force: true }).catch(() => {})
52
+ }
53
+
50
54
  test('watches CommonJS files', async (t) => {
51
55
  const tmpDir = await mkdtemp(join(base, 'watch-'))
52
- t.after(() => rm(tmpDir, { recursive: true, force: true }))
56
+ t.after(() => saferm(tmpDir))
53
57
  t.diagnostic(`using ${tmpDir}`)
54
58
  const configFileSrc = join(fixturesDir, 'configs', 'monorepo.json')
55
59
  const configFileDst = join(tmpDir, 'configs', 'monorepo.json')
@@ -65,8 +69,6 @@ test('watches CommonJS files', async (t) => {
65
69
  await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v1', false))
66
70
  const { child } = await start('-c', configFileDst)
67
71
  t.after(() => child.kill('SIGINT'))
68
- child.stdout.pipe(process.stderr)
69
- child.stderr.pipe(process.stderr)
70
72
 
71
73
  await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v2', true))
72
74
 
@@ -79,7 +81,7 @@ test('watches CommonJS files', async (t) => {
79
81
 
80
82
  test('watches ESM files', async (t) => {
81
83
  const tmpDir = await mkdtemp(join(base, 'watch-'))
82
- t.after(() => rm(tmpDir, { recursive: true, force: true }))
84
+ t.after(() => saferm(tmpDir))
83
85
  t.diagnostic(`using ${tmpDir}`)
84
86
  const configFileSrc = join(fixturesDir, 'configs', 'monorepo.json')
85
87
  const configFileDst = join(tmpDir, 'configs', 'monorepo.json')
@@ -95,8 +97,6 @@ test('watches ESM files', async (t) => {
95
97
  await writeFile(esmPluginFilePath, createEsmLoggingPlugin('v1', false))
96
98
  const { child } = await start('-c', configFileDst)
97
99
  t.after(() => child.kill('SIGINT'))
98
- child.stdout.pipe(process.stderr)
99
- child.stderr.pipe(process.stderr)
100
100
  await writeFile(esmPluginFilePath, createEsmLoggingPlugin('v2', true))
101
101
 
102
102
  for await (const log of child.ndj) {
@@ -108,7 +108,7 @@ test('watches ESM files', async (t) => {
108
108
 
109
109
  test('should not hot reload files with `--hot-reload false', async (t) => {
110
110
  const tmpDir = await mkdtemp(join(base, 'watch-'))
111
- t.after(() => rm(tmpDir, { recursive: true, force: true }))
111
+ t.after(() => saferm(tmpDir))
112
112
  t.diagnostic(`using ${tmpDir}`)
113
113
  const configFileSrc = join(fixturesDir, 'configs', 'monorepo.json')
114
114
  const configFileDst = join(tmpDir, 'configs', 'monorepo.json')
@@ -124,11 +124,84 @@ test('should not hot reload files with `--hot-reload false', async (t) => {
124
124
  await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v1', false))
125
125
  const { child, url } = await start('-c', configFileDst, '--hot-reload', 'false')
126
126
  t.after(() => child.kill('SIGINT'))
127
- child.stdout.pipe(process.stderr)
128
- child.stderr.pipe(process.stderr)
129
127
  await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v2', true))
130
128
  await sleep(5000)
131
129
  const res = await request(`${url}/version`)
132
130
  const version = await res.body.text()
133
131
  assert.strictEqual(version, 'v1')
134
132
  })
133
+
134
+ test('watches CommonJS files with hotreload', { timeout: 30000, skip: process.env.CI }, async (t) => {
135
+ const tmpDir = await mkdtemp(join(base, 'watch-'))
136
+ t.after(() => saferm(tmpDir))
137
+ t.diagnostic(`using ${tmpDir}`)
138
+ const configFileSrc = join(fixturesDir, 'configs', 'hotreload.json')
139
+ const configFileDst = join(tmpDir, 'configs', 'monorepo.json')
140
+ const appSrc = join(fixturesDir, 'monorepo')
141
+ const appDst = join(tmpDir, 'monorepo')
142
+ const cjsPluginFilePath = join(appDst, 'serviceAppWithLogger', 'plugin.js')
143
+
144
+ await Promise.all([
145
+ cp(configFileSrc, configFileDst),
146
+ cp(appSrc, appDst, { recursive: true })
147
+ ])
148
+
149
+ await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v1', false))
150
+ const { child } = await start('-c', configFileDst)
151
+ t.after(() => child.kill('SIGINT'))
152
+
153
+ await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v2', true))
154
+
155
+ let restartedSecondTime = false
156
+ let restartedThirdTime = false
157
+
158
+ for await (const log of child.ndj) {
159
+ if (log.msg === 'RELOADED v2') {
160
+ restartedSecondTime = true
161
+ } else if (log.msg === 'RELOADED v3') {
162
+ restartedThirdTime = true
163
+ break
164
+ } else if (log.msg?.match(/watching/)) {
165
+ await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v3', true))
166
+ }
167
+ }
168
+
169
+ assert.ok(restartedSecondTime)
170
+ assert.ok(restartedThirdTime)
171
+ })
172
+
173
+ test('watches CommonJS files with hotreload on a single service', { timeout: 30000, skip: process.env.CI }, async (t) => {
174
+ const tmpDir = await mkdtemp(join(base, 'watch-'))
175
+ t.after(() => saferm(tmpDir))
176
+ t.diagnostic(`using ${tmpDir}`)
177
+ const appSrc = join(fixturesDir, 'monorepo', 'serviceAppWithLogger')
178
+ const appDst = join(tmpDir)
179
+ const cjsPluginFilePath = join(appDst, 'plugin.js')
180
+
181
+ await Promise.all([
182
+ cp(appSrc, appDst, { recursive: true })
183
+ ])
184
+
185
+ await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v1', false))
186
+ const { child } = await start('-c', join(appDst, 'platformatic.service.json'))
187
+ t.after(() => child.kill('SIGINT'))
188
+
189
+ await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v2', true))
190
+
191
+ let restartedSecondTime = false
192
+ let restartedThirdTime = false
193
+
194
+ for await (const log of child.ndj) {
195
+ if (log.msg === 'RELOADED v2') {
196
+ restartedSecondTime = true
197
+ } else if (log.msg === 'RELOADED v3') {
198
+ assert.ok(restartedSecondTime)
199
+ restartedThirdTime = true
200
+ break
201
+ } else if (log.msg?.match(/listening/)) {
202
+ await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v3', true))
203
+ }
204
+ }
205
+
206
+ assert.ok(restartedThirdTime)
207
+ })
@@ -0,0 +1,37 @@
1
+ 'use strict'
2
+
3
+ const assert = require('node:assert')
4
+ const { request } = require('undici')
5
+ const { test } = require('node:test')
6
+ const { join } = require('node:path')
7
+ const { loadConfig } = require('@platformatic/service')
8
+ const { platformaticRuntime } = require('..')
9
+ const { startWithConfig } = require('../lib/start')
10
+ const fixturesDir = join(__dirname, '..', 'fixtures')
11
+
12
+ test('propagate the traceId correctly to runtime services', async (t) => {
13
+ const configFile = join(fixturesDir, 'telemetry', 'platformatic.runtime.json')
14
+ const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
15
+ const app = await startWithConfig(config.configManager)
16
+
17
+ t.after(async () => {
18
+ await app.close()
19
+ })
20
+
21
+ const entryUrl = await app.start()
22
+
23
+ const traceId = '5e994e8fb53b27c91dcd2fec22771d15'
24
+ const spanId = '166f3ab30f21800b'
25
+ const traceparent = `00-${traceId}-${spanId}-01`
26
+ const res = await request(entryUrl, {
27
+ method: 'GET',
28
+ path: '/',
29
+ headers: {
30
+ traceparent
31
+ }
32
+ })
33
+
34
+ assert.strictEqual(res.statusCode, 200)
35
+ const response = await res.body.json()
36
+ assert.strictEqual(response.traceId, traceId)
37
+ })
@@ -254,7 +254,6 @@ test('start()', async (t) => {
254
254
  const scriptFile = join(fixturesDir, 'starter.js')
255
255
  const configFile = join(fixturesDir, 'monorepo', 'serviceAppWithLogger', 'platformatic.service.json')
256
256
  const child = spawn(process.execPath, [scriptFile, configFile])
257
- child.stdout.pipe(process.stdout)
258
257
  child.stderr.pipe(process.stderr)
259
258
  const [exitCode] = await once(child, 'exit')
260
259
 
@@ -318,14 +317,11 @@ test('startCommand()', async (t) => {
318
317
 
319
318
  assert.strictEqual(exitCode, 42)
320
319
  })
321
- })
322
320
 
323
- test('startCommandInRuntime()', async (t) => {
324
321
  await t.test('can start a non-runtime application', async (t) => {
325
322
  const scriptFile = join(fixturesDir, 'start-command-in-runtime.js')
326
323
  const configFile = join(fixturesDir, 'monorepo', 'serviceAppWithLogger', 'platformatic.service.json')
327
324
  const child = spawn(process.execPath, [scriptFile, configFile])
328
- child.stdout.pipe(process.stdout)
329
325
  child.stderr.pipe(process.stderr)
330
326
  const [exitCode] = await once(child, 'exit')
331
327
 
@@ -336,7 +332,6 @@ test('startCommandInRuntime()', async (t) => {
336
332
  const scriptFile = join(fixturesDir, 'start-command-in-runtime.js')
337
333
  const configFile = join(fixturesDir, 'configs', 'monorepo.json')
338
334
  const child = spawn(process.execPath, [scriptFile, configFile])
339
- child.stdout.pipe(process.stdout)
340
335
  child.stderr.pipe(process.stderr)
341
336
  const [exitCode] = await once(child, 'exit')
342
337