@platformatic/runtime 1.22.0 → 1.24.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,27 @@
1
+ {
2
+ "$schema": "https://platformatic.dev/schemas/v0.20.0/runtime",
3
+ "entrypoint": "serviceApp",
4
+ "allowCycles": true,
5
+ "hotReload": true,
6
+ "autoload": {
7
+ "path": "../monorepo",
8
+ "exclude": [
9
+ "docs",
10
+ "composerApp"
11
+ ],
12
+ "mappings": {
13
+ "serviceAppWithLogger": {
14
+ "id": "with-logger",
15
+ "config": "platformatic.service.json"
16
+ },
17
+ "serviceAppWithMultiplePlugins": {
18
+ "id": "multi-plugin-service",
19
+ "config": "platformatic.service.json"
20
+ },
21
+ "dbApp": {
22
+ "id": "db-app",
23
+ "config": "platformatic.db.json"
24
+ }
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://platformatic.dev/schemas/v1.21.0/runtime",
3
+ "entrypoint": "serviceApp",
4
+ "allowCycles": true,
5
+ "hotReload": false,
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
+ "managementApi": {}
21
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "test-runtime-package",
3
+ "version": "1.0.42"
4
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "https://platformatic.dev/schemas/v1.22.0/runtime",
3
+ "entrypoint": "service-1",
4
+ "allowCycles": true,
5
+ "hotReload": false,
6
+ "autoload": {
7
+ "path": "./services"
8
+ },
9
+ "managementApi": true
10
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://platformatic.dev/schemas/v1.22.0/service",
3
+ "server": {
4
+ "hostname": "127.0.0.1",
5
+ "port": 0,
6
+ "logger": {
7
+ "name": "service-with-logger",
8
+ "level": "trace"
9
+ }
10
+ },
11
+ "service": {
12
+ "openapi": true
13
+ },
14
+ "plugins": {
15
+ "paths": ["plugin.js"]
16
+ },
17
+ "watch": true,
18
+ "metrics": {
19
+ "server": "parent"
20
+ }
21
+ }
@@ -0,0 +1,8 @@
1
+ 'use strict'
2
+
3
+ /** @param {import('fastify').FastifyInstance} app */
4
+ module.exports = async function (app) {
5
+ app.get('/hello', async () => {
6
+ return { service: 'service-2' }
7
+ })
8
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://platformatic.dev/schemas/v1.22.0/service",
3
+ "server": {
4
+ "hostname": "127.0.0.1",
5
+ "port": 0
6
+ },
7
+ "service": {
8
+ "openapi": true
9
+ },
10
+ "plugins": {
11
+ "paths": ["plugin.js"]
12
+ }
13
+ }
@@ -0,0 +1,8 @@
1
+ 'use strict'
2
+
3
+ /** @param {import('fastify').FastifyInstance} app */
4
+ module.exports = async function (app, options) {
5
+ app.get('/hello', async () => {
6
+ return { service: 'service-2' }
7
+ })
8
+ }
@@ -16,5 +16,8 @@
16
16
  "plugin.js"
17
17
  ]
18
18
  },
19
- "watch": true
19
+ "watch": true,
20
+ "metrics": {
21
+ "server": "parent"
22
+ }
20
23
  }
package/lib/api-client.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const { once, EventEmitter } = require('node:events')
4
4
  const { randomUUID } = require('node:crypto')
5
5
  const errors = require('./errors')
6
+ const { setTimeout: sleep } = require('node:timers/promises')
6
7
 
7
8
  const MAX_LISTENERS_COUNT = 100
8
9
 
@@ -29,13 +30,34 @@ class RuntimeApiClient extends EventEmitter {
29
30
 
30
31
  async close () {
31
32
  await this.#sendCommand('plt:stop-services')
32
- await this.#exitPromise
33
+
34
+ this.worker.postMessage({ command: 'plt:close' })
35
+ const res = await Promise.race([
36
+ this.#exitPromise,
37
+ // We must kill the worker if it doesn't exit in 10 seconds
38
+ // because it may be stuck in an infinite loop.
39
+ // This is a workaround for
40
+ // https://github.com/nodejs/node/issues/47748
41
+ // https://github.com/nodejs/node/issues/49344
42
+ // Remove once https://github.com/nodejs/node/pull/51290 is released
43
+ // on all lines.
44
+ // Likely to be removed when we drop support for Node.js 18.
45
+ sleep(10000, 'timeout', { ref: false })
46
+ ])
47
+
48
+ if (res === 'timeout') {
49
+ this.worker.unref()
50
+ }
33
51
  }
34
52
 
35
53
  async restart () {
36
54
  return this.#sendCommand('plt:restart-services')
37
55
  }
38
56
 
57
+ async getEntrypointDetails () {
58
+ return this.#sendCommand('plt:get-entrypoint-details')
59
+ }
60
+
39
61
  async getServices () {
40
62
  return this.#sendCommand('plt:get-services')
41
63
  }
package/lib/api.js CHANGED
@@ -9,9 +9,11 @@ const { printSchema } = require('graphql')
9
9
  class RuntimeApi {
10
10
  #services
11
11
  #dispatcher
12
+ #logger
12
13
 
13
14
  constructor (config, logger, loaderPort) {
14
15
  this.#services = new Map()
16
+ this.#logger = logger
15
17
  const telemetryConfig = config.telemetry
16
18
 
17
19
  for (let i = 0; i < config.services.length; ++i) {
@@ -47,12 +49,26 @@ class RuntimeApi {
47
49
  parentPort.on('message', async (message) => {
48
50
  const command = message?.command
49
51
  if (command) {
52
+ if (command === 'plt:close') {
53
+ // We close everything because they might be using
54
+ // a FinalizationRegistry and it may stuck us in an infinite loop.
55
+ // This is a workaround for
56
+ // https://github.com/nodejs/node/issues/47748
57
+ // https://github.com/nodejs/node/issues/49344
58
+ // Remove once https://github.com/nodejs/node/pull/51290 is released
59
+ // on all lines.
60
+ // Likely to be removed when we drop support for Node.js 18.
61
+ if (this.#dispatcher) {
62
+ await this.#dispatcher.close()
63
+ }
64
+ await this.stopServices()
65
+
66
+ setImmediate(process.exit) // Exit the worker thread.
67
+ return
68
+ }
69
+
50
70
  const res = await this.#executeCommand(message)
51
71
  parentPort.postMessage(res)
52
-
53
- if (command === 'plt:stop-services') {
54
- process.exit() // Exit the worker thread.
55
- }
56
72
  return
57
73
  }
58
74
  await this.#handleProcessLevelEvent(message)
@@ -96,6 +112,8 @@ class RuntimeApi {
96
112
  return this.stopServices(params)
97
113
  case 'plt:restart-services':
98
114
  return this.#restartServices(params)
115
+ case 'plt:get-entrypoint-details':
116
+ return this.#getEntrypointDetails(params)
99
117
  case 'plt:get-services':
100
118
  return this.#getServices(params)
101
119
  case 'plt:get-service-details':
@@ -134,7 +152,7 @@ class RuntimeApi {
134
152
  }
135
153
 
136
154
  async stopServices () {
137
- const stopServiceReqs = [this.#dispatcher.close()]
155
+ const stopServiceReqs = []
138
156
  for (const service of this.#services.values()) {
139
157
  const serviceStatus = service.getStatus()
140
158
  if (serviceStatus === 'started') {
@@ -161,6 +179,15 @@ class RuntimeApi {
161
179
  return entrypointUrl
162
180
  }
163
181
 
182
+ #getEntrypointDetails () {
183
+ for (const service of this.#services.values()) {
184
+ if (service.appConfig.entrypoint) {
185
+ return this.#getServiceDetails({ id: service.appConfig.id })
186
+ }
187
+ }
188
+ return null
189
+ }
190
+
164
191
  #getServices () {
165
192
  const services = { services: [] }
166
193
 
@@ -190,8 +217,15 @@ class RuntimeApi {
190
217
  const service = this.#getServiceById(id)
191
218
  const status = service.getStatus()
192
219
 
220
+ const type = service.config.configType
193
221
  const { entrypoint, dependencies, localUrl } = service.appConfig
194
- return { id, status, localUrl, entrypoint, dependencies }
222
+ const serviceDetails = { id, type, status, localUrl, entrypoint, dependencies }
223
+
224
+ if (entrypoint) {
225
+ serviceDetails.url = status === 'started' ? service.server.url : null
226
+ }
227
+
228
+ return serviceDetails
195
229
  }
196
230
 
197
231
  #getServiceConfig ({ id }) {
@@ -269,8 +303,7 @@ class RuntimeApi {
269
303
  statusCode: res.statusCode,
270
304
  statusMessage: res.statusMessage,
271
305
  headers: res.headers,
272
- body: res.body,
273
- payload: res.payload
306
+ body: res.body
274
307
  }
275
308
  }
276
309
  }
package/lib/app.js CHANGED
@@ -73,7 +73,10 @@ class PlatformaticApp {
73
73
  this.#setuplogger(this.config.configManager)
74
74
  await this.server.restart()
75
75
  } catch (err) {
76
- this.#logAndExit(err)
76
+ // The restart failed. Log the error and
77
+ // wait for another event.
78
+ // The old app is still available
79
+ this.#logger.error({ err })
77
80
  }
78
81
 
79
82
  this.#restarting = false
@@ -103,6 +106,16 @@ class PlatformaticApp {
103
106
  })
104
107
  }
105
108
 
109
+ if (!this.appConfig.entrypoint) {
110
+ configManager.update({
111
+ ...configManager.current,
112
+ server: {
113
+ ...(configManager.current.server || {}),
114
+ trustProxy: true
115
+ }
116
+ })
117
+ }
118
+
106
119
  const config = configManager.current
107
120
 
108
121
  this.#setuplogger(configManager)
package/lib/errors.js CHANGED
@@ -21,5 +21,6 @@ module.exports = {
21
21
  InspectorPortError: createError(`${ERROR_PREFIX}_INSPECTOR_PORT`, 'Inspector port must be 0 or in range 1024 to 65535'),
22
22
  InspectorHostError: createError(`${ERROR_PREFIX}_INSPECTOR_HOST`, 'Inspector host cannot be empty'),
23
23
  CannotMapSpecifierToAbsolutePathError: createError(`${ERROR_PREFIX}_CANNOT_MAP_SPECIFIER_TO_ABSOLUTE_PATH`, 'Cannot map "%s" to an absolute path'),
24
- NodeInspectorFlagsNotSupportedError: createError(`${ERROR_PREFIX}_NODE_INSPECTOR_FLAGS_NOT_SUPPORTED`, 'The Node.js inspector flags are not supported. Please use \'platformatic start --inspect\' instead.')
24
+ NodeInspectorFlagsNotSupportedError: createError(`${ERROR_PREFIX}_NODE_INSPECTOR_FLAGS_NOT_SUPPORTED`, 'The Node.js inspector flags are not supported. Please use \'platformatic start --inspect\' instead.'),
25
+ FailedToUnlinkManagementApiSocket: createError(`${ERROR_PREFIX}_FAILED_TO_UNLINK_MANAGEMENT_API_SOCKET`, 'Failed to unlink management API socket "%s"')
25
26
  }
@@ -84,7 +84,7 @@ class RuntimeGenerator extends BaseGenerator {
84
84
  PLT_SERVER_HOSTNAME: '0.0.0.0',
85
85
  PORT: this.config.port || 3042,
86
86
  PLT_SERVER_LOGGER_LEVEL: this.config.logLevel || 'info'
87
- })
87
+ }, { overwrite: false })
88
88
  }
89
89
 
90
90
  addServicesDependencies () {
@@ -0,0 +1,211 @@
1
+ 'use strict'
2
+
3
+ const { tmpdir, platform } = require('node:os')
4
+ const { join } = require('node:path')
5
+ const { readFile, mkdir, unlink } = require('node:fs/promises')
6
+ const fastify = require('fastify')
7
+ const { prettyFactory } = require('pino-pretty')
8
+ const errors = require('./errors')
9
+ const platformaticVersion = require('../package.json').version
10
+
11
+ const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'pids')
12
+
13
+ const pinoLogLevels = {
14
+ fatal: 60,
15
+ error: 50,
16
+ warn: 40,
17
+ info: 30,
18
+ debug: 20,
19
+ trace: 10
20
+ }
21
+
22
+ async function createManagementApi (configManager, runtimeApiClient, loggingPort) {
23
+ let apiConfig = configManager.current.managementApi
24
+ if (!apiConfig || apiConfig === true) {
25
+ apiConfig = {}
26
+ }
27
+
28
+ const app = fastify(apiConfig)
29
+ app.log.warn(
30
+ 'Runtime Management API is in the experimental stage. ' +
31
+ 'The feature is not subject to semantic versioning rules. ' +
32
+ 'Non-backward compatible changes or removal may occur in any future release. ' +
33
+ 'Use of the feature is not recommended in production environments.'
34
+ )
35
+
36
+ async function getRuntimePackageJson (cwd) {
37
+ const packageJsonPath = join(cwd, 'package.json')
38
+ const packageJsonFile = await readFile(packageJsonPath, 'utf8')
39
+ const packageJson = JSON.parse(packageJsonFile)
40
+ return packageJson
41
+ }
42
+
43
+ app.register(require('@fastify/websocket'))
44
+
45
+ app.register(async (app) => {
46
+ app.get('/metadata', async () => {
47
+ const packageJson = await getRuntimePackageJson(configManager.dirname).catch(() => ({}))
48
+ const entrypointDetails = await runtimeApiClient.getEntrypointDetails().catch(() => null)
49
+
50
+ return {
51
+ pid: process.pid,
52
+ cwd: process.cwd(),
53
+ argv: process.argv,
54
+ uptimeSeconds: Math.floor(process.uptime()),
55
+ execPath: process.execPath,
56
+ nodeVersion: process.version,
57
+ projectDir: configManager.dirname,
58
+ packageName: packageJson.name ?? null,
59
+ packageVersion: packageJson.version ?? null,
60
+ url: entrypointDetails?.url ?? null,
61
+ platformaticVersion
62
+ }
63
+ })
64
+
65
+ app.get('/config', async () => {
66
+ return configManager.current
67
+ })
68
+
69
+ app.get('/env', async () => {
70
+ return process.env
71
+ })
72
+
73
+ app.post('/stop', async () => {
74
+ app.log.debug('stop services')
75
+ await runtimeApiClient.close()
76
+ })
77
+
78
+ app.post('/reload', async () => {
79
+ app.log.debug('reload services')
80
+ await runtimeApiClient.restart()
81
+ })
82
+
83
+ app.get('/services', async () => {
84
+ return runtimeApiClient.getServices()
85
+ })
86
+
87
+ app.get('/services/:id', async (request) => {
88
+ const { id } = request.params
89
+ app.log.debug('get service details', { id })
90
+ return runtimeApiClient.getServiceDetails(id)
91
+ })
92
+
93
+ app.get('/services/:id/config', async (request) => {
94
+ const { id } = request.params
95
+ app.log.debug('get service config', { id })
96
+ return runtimeApiClient.getServiceConfig(id)
97
+ })
98
+
99
+ app.post('/services/:id/start', async (request) => {
100
+ const { id } = request.params
101
+ app.log.debug('start service', { id })
102
+ await runtimeApiClient.startService(id)
103
+ })
104
+
105
+ app.post('/services/:id/stop', async (request) => {
106
+ const { id } = request.params
107
+ app.log.debug('stop service', { id })
108
+ await runtimeApiClient.stopService(id)
109
+ })
110
+
111
+ app.all('/services/:id/proxy/*', async (request, reply) => {
112
+ const { id, '*': requestUrl } = request.params
113
+ app.log.debug('proxy request', { id, requestUrl })
114
+
115
+ const injectParams = {
116
+ method: request.method,
117
+ url: requestUrl || '/',
118
+ headers: request.headers,
119
+ query: request.query,
120
+ body: request.body
121
+ }
122
+
123
+ const res = await runtimeApiClient.inject(id, injectParams)
124
+
125
+ reply
126
+ .code(res.statusCode)
127
+ .headers(res.headers)
128
+ .send(res.body)
129
+ })
130
+
131
+ app.get('/logs', { websocket: true }, async (connection, req) => {
132
+ const logLevel = req.query.level || 'info'
133
+ const pretty = req.query.pretty !== 'false'
134
+ const serviceId = req.query.serviceId || null
135
+
136
+ const logLevelNumber = pinoLogLevels[logLevel]
137
+ const prettify = prettyFactory()
138
+
139
+ const handler = (message) => {
140
+ for (let log of message.logs) {
141
+ try {
142
+ const parsedLog = JSON.parse(log)
143
+ if (parsedLog.level < logLevelNumber) continue
144
+ if (serviceId && parsedLog.name !== serviceId) continue
145
+ if (pretty) {
146
+ log = prettify(parsedLog)
147
+ }
148
+ connection.socket.send(log)
149
+ } catch (err) {
150
+ console.error('Failed to parse log message: ', log, err)
151
+ }
152
+ }
153
+ }
154
+
155
+ loggingPort.on('message', handler)
156
+ connection.socket.on('close', () => {
157
+ loggingPort.off('message', handler)
158
+ })
159
+ connection.socket.on('error', () => {
160
+ loggingPort.off('message', handler)
161
+ })
162
+ connection.socket.on('end', () => {
163
+ loggingPort.off('message', handler)
164
+ })
165
+ })
166
+ }, { prefix: '/api' })
167
+
168
+ return app
169
+ }
170
+
171
+ async function startManagementApi (configManager, runtimeApiClient, loggingPort) {
172
+ const runtimePID = process.pid
173
+
174
+ let socketPath = null
175
+ if (platform() === 'win32') {
176
+ socketPath = '\\\\.\\pipe\\platformatic-' + runtimePID
177
+ } else {
178
+ await mkdir(PLATFORMATIC_TMP_DIR, { recursive: true })
179
+ socketPath = join(PLATFORMATIC_TMP_DIR, `${runtimePID}.sock`)
180
+ }
181
+
182
+ try {
183
+ await mkdir(PLATFORMATIC_TMP_DIR, { recursive: true })
184
+ await unlink(socketPath).catch((err) => {
185
+ if (err.code !== 'ENOENT') {
186
+ throw new errors.FailedToUnlinkManagementApiSocket(err.message)
187
+ }
188
+ })
189
+
190
+ const managementApi = await createManagementApi(
191
+ configManager,
192
+ runtimeApiClient,
193
+ loggingPort
194
+ )
195
+
196
+ if (platform() !== 'win32') {
197
+ managementApi.addHook('onClose', async () => {
198
+ await unlink(socketPath).catch(() => {})
199
+ })
200
+ }
201
+
202
+ await managementApi.listen({ path: socketPath })
203
+ return managementApi
204
+ /* c8 ignore next 4 */
205
+ } catch (err) {
206
+ console.error(err)
207
+ process.exit(1)
208
+ }
209
+ }
210
+
211
+ module.exports = { startManagementApi, createManagementApi }
package/lib/schema.js CHANGED
@@ -129,6 +129,15 @@ const platformaticRuntimeSchema = {
129
129
  },
130
130
  $schema: {
131
131
  type: 'string'
132
+ },
133
+ managementApi: {
134
+ anyOf: [
135
+ { type: 'boolean' },
136
+ {
137
+ type: 'object',
138
+ properties: {}
139
+ }
140
+ ]
132
141
  }
133
142
  },
134
143
  anyOf: [
package/lib/start.js CHANGED
@@ -1,16 +1,18 @@
1
1
  'use strict'
2
+
2
3
  const { once } = require('node:events')
3
4
  const inspector = require('node:inspector')
4
5
  const { join, resolve, dirname } = require('node:path')
5
- const fs = require('node:fs/promises')
6
+ const { writeFile } = require('node:fs/promises')
6
7
  const { pathToFileURL } = require('node:url')
7
8
  const { Worker } = require('node:worker_threads')
8
- const closeWithGrace = require('close-with-grace')
9
9
  const { start: serviceStart } = require('@platformatic/service')
10
+ const { printConfigValidationErrors } = require('@platformatic/config')
11
+ const closeWithGrace = require('close-with-grace')
10
12
  const { loadConfig } = require('./load-config')
13
+ const { startManagementApi } = require('./management-api')
11
14
  const { parseInspectorOptions, wrapConfigInRuntimeConfig } = require('./config')
12
15
  const RuntimeApiClient = require('./api-client.js')
13
- const { printConfigValidationErrors } = require('@platformatic/config')
14
16
  const errors = require('./errors')
15
17
  const pkg = require('../package.json')
16
18
 
@@ -42,14 +44,27 @@ async function startWithConfig (configManager, env = process.env) {
42
44
  // The configManager cannot be transferred to the worker, so remove it.
43
45
  delete config.configManager
44
46
 
47
+ let mainLoggingPort = null
48
+ let childLoggingPort = config.loggingPort
49
+
50
+ if (!childLoggingPort && config.managementApi) {
51
+ const { port1, port2 } = new MessageChannel()
52
+ mainLoggingPort = port1
53
+ childLoggingPort = port2
54
+
55
+ config.loggingPort = childLoggingPort
56
+ }
57
+
45
58
  const worker = new Worker(kWorkerFile, {
46
59
  /* c8 ignore next */
47
60
  execArgv: config.hotReload ? kWorkerExecArgv : [],
48
- transferList: config.loggingPort ? [config.loggingPort] : [],
61
+ transferList: childLoggingPort ? [childLoggingPort] : [],
49
62
  workerData: { config, dirname },
50
63
  env
51
64
  })
52
65
 
66
+ let managementApi = null
67
+
53
68
  let exited = null
54
69
  let isWorkerAlive = true
55
70
  worker.on('exit', (code) => {
@@ -57,6 +72,7 @@ async function startWithConfig (configManager, env = process.env) {
57
72
  process.exitCode = code
58
73
  isWorkerAlive = false
59
74
  configManager.fileWatcher?.stopWatching()
75
+ managementApi?.close()
60
76
  if (typeof exited === 'function') {
61
77
  exited()
62
78
  }
@@ -96,6 +112,16 @@ async function startWithConfig (configManager, env = process.env) {
96
112
  await once(worker, 'message') // plt:init
97
113
 
98
114
  const runtimeApiClient = new RuntimeApiClient(worker)
115
+
116
+ if (config.managementApi) {
117
+ managementApi = await startManagementApi(
118
+ configManager,
119
+ runtimeApiClient,
120
+ mainLoggingPort
121
+ )
122
+ runtimeApiClient.managementApi = managementApi
123
+ }
124
+
99
125
  return runtimeApiClient
100
126
  }
101
127
 
@@ -148,7 +174,7 @@ async function startCommand (args) {
148
174
  }
149
175
  const toWrite = join(dirname(resolve(args[0])), 'platformatic.service.json')
150
176
  console.log(`No config file found, creating ${join(dirname(args[0]), 'platformatic.service.json')}`)
151
- await fs.writeFile(toWrite, JSON.stringify(config, null, 2))
177
+ await writeFile(toWrite, JSON.stringify(config, null, 2))
152
178
  return startCommand(['--config', toWrite])
153
179
  }
154
180
  logErrorAndExit(err)
package/lib/worker.js CHANGED
@@ -12,6 +12,7 @@ const {
12
12
  } = require('node:worker_threads')
13
13
  const undici = require('undici')
14
14
  const pino = require('pino')
15
+ const pretty = require('pino-pretty')
15
16
  const { setGlobalDispatcher, Agent } = require('undici')
16
17
  const RuntimeApi = require('./api')
17
18
  const { MessagePortWritable } = require('./message-port-writable')
@@ -34,7 +35,6 @@ globalThis.fetch = undici.fetch
34
35
  const config = workerData.config
35
36
 
36
37
  let loggerConfig = config.server?.logger
37
- let destination
38
38
 
39
39
  if (loggerConfig) {
40
40
  loggerConfig = { ...loggerConfig }
@@ -42,21 +42,23 @@ if (loggerConfig) {
42
42
  loggerConfig = {}
43
43
  }
44
44
 
45
- /* c8 ignore next 10 */
45
+ const cliStream = isatty(1) ? pretty() : pino.destination(1)
46
+
47
+ let logger = null
46
48
  if (config.loggingPort) {
47
- destination = new MessagePortWritable({
49
+ const portStream = new MessagePortWritable({
48
50
  metadata: config.loggingMetadata,
49
51
  port: config.loggingPort
50
52
  })
51
- delete loggerConfig.transport
52
- } else if (!loggerConfig.transport && isatty(1)) {
53
- loggerConfig.transport = {
54
- target: 'pino-pretty'
55
- }
53
+ const multiStream = pino.multistream([
54
+ { stream: portStream, level: 'trace' },
55
+ { stream: cliStream, level: loggerConfig.level || 'info' }
56
+ ])
57
+ logger = pino({ level: 'trace' }, multiStream)
58
+ } else {
59
+ logger = pino(loggerConfig, cliStream)
56
60
  }
57
61
 
58
- const logger = pino(loggerConfig, destination)
59
-
60
62
  if (config.server) {
61
63
  config.server.logger = logger
62
64
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "1.22.0",
3
+ "version": "1.24.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -18,6 +18,7 @@
18
18
  "homepage": "https://github.com/platformatic/platformatic#readme",
19
19
  "devDependencies": {
20
20
  "@fastify/express": "^2.3.0",
21
+ "@matteo.collina/tspl": "^0.1.1",
21
22
  "borp": "^0.9.0",
22
23
  "c8": "^9.1.0",
23
24
  "execa": "^8.0.1",
@@ -30,11 +31,13 @@
30
31
  "tsd": "^0.30.4",
31
32
  "typescript": "^5.3.3",
32
33
  "undici-oauth-interceptor": "^0.4.2",
33
- "@platformatic/sql-graphql": "1.22.0",
34
- "@platformatic/sql-mapper": "1.22.0"
34
+ "ws": "^8.16.0",
35
+ "@platformatic/sql-graphql": "1.24.0",
36
+ "@platformatic/sql-mapper": "1.24.0"
35
37
  },
36
38
  "dependencies": {
37
39
  "@fastify/error": "^3.4.1",
40
+ "@fastify/websocket": "^9.0.0",
38
41
  "@hapi/topo": "^6.0.2",
39
42
  "boring-name-generator": "^1.0.3",
40
43
  "close-with-grace": "^1.2.0",
@@ -52,13 +55,13 @@
52
55
  "pino-pretty": "^10.3.1",
53
56
  "undici": "^6.6.0",
54
57
  "why-is-node-running": "^2.2.2",
55
- "@platformatic/composer": "1.22.0",
56
- "@platformatic/db": "1.22.0",
57
- "@platformatic/config": "1.22.0",
58
- "@platformatic/generators": "1.22.0",
59
- "@platformatic/service": "1.22.0",
60
- "@platformatic/telemetry": "1.22.0",
61
- "@platformatic/utils": "1.22.0"
58
+ "@platformatic/composer": "1.24.0",
59
+ "@platformatic/config": "1.24.0",
60
+ "@platformatic/db": "1.24.0",
61
+ "@platformatic/generators": "1.24.0",
62
+ "@platformatic/service": "1.24.0",
63
+ "@platformatic/telemetry": "1.24.0",
64
+ "@platformatic/utils": "1.24.0"
62
65
  },
63
66
  "standard": {
64
67
  "ignore": [
package/runtime.mjs CHANGED
@@ -45,5 +45,5 @@ export async function run (argv) {
45
45
  }
46
46
 
47
47
  if (isMain(import.meta)) {
48
- await run(process.argv.splice(2))
48
+ await run(process.argv.slice(2))
49
49
  }