@platformatic/runtime 1.29.0 → 1.30.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.
@@ -3,6 +3,7 @@
3
3
  "entrypoint": "serviceApp",
4
4
  "allowCycles": true,
5
5
  "hotReload": false,
6
+ "managementApi": false,
6
7
  "autoload": {
7
8
  "path": "../monorepo",
8
9
  "exclude": [
@@ -7,8 +7,8 @@ module.exports = async function (app) {
7
7
  })
8
8
 
9
9
  app.get('/large-logs', async (req) => {
10
- const largeLog = 'a'.repeat(5 * 1024 * 1024)
11
- for (let i = 0; i < 10; i++) {
10
+ const largeLog = 'a'.repeat(100)
11
+ for (let i = 0; i < 500000; i++) {
12
12
  app.log.trace(largeLog)
13
13
  }
14
14
  })
@@ -14,6 +14,7 @@ const ComposerGenerator = require('@platformatic/composer/lib/generator/composer
14
14
  const { CannotFindGeneratorForTemplateError, CannotRemoveServiceOnUpdateError } = require('../errors')
15
15
  const { getServiceTemplateFromSchemaUrl } = require('@platformatic/generators/lib/utils')
16
16
  const { DotEnvTool } = require('dotenv-tool')
17
+ const { getArrayDifference } = require('../utils')
17
18
 
18
19
  class RuntimeGenerator extends BaseGenerator {
19
20
  constructor (opts) {
@@ -91,7 +92,8 @@ class RuntimeGenerator extends BaseGenerator {
91
92
  this.addEnvVars({
92
93
  PLT_SERVER_HOSTNAME: '0.0.0.0',
93
94
  PORT: this.config.port || 3042,
94
- PLT_SERVER_LOGGER_LEVEL: this.config.logLevel || 'info'
95
+ PLT_SERVER_LOGGER_LEVEL: this.config.logLevel || 'info',
96
+ PLT_MANAGEMENT_API: true
95
97
  }, { overwrite: false })
96
98
  }
97
99
 
@@ -165,7 +167,8 @@ class RuntimeGenerator extends BaseGenerator {
165
167
  logger: {
166
168
  level: '{PLT_SERVER_LOGGER_LEVEL}'
167
169
  }
168
- }
170
+ },
171
+ managementApi: '{PLT_MANAGEMENT_API}'
169
172
  }
170
173
 
171
174
  return config
@@ -337,13 +340,11 @@ class RuntimeGenerator extends BaseGenerator {
337
340
 
338
341
  async update (newConfig) {
339
342
  let allServicesDependencies = {}
340
- function getDifference (a, b) {
341
- return a.filter(element => {
342
- return !b.includes(element)
343
- })
344
- }
345
- this.config.isUpdating = true
343
+ const runtimeAddedEnvKeys = []
346
344
 
345
+ this.config.isUpdating = true
346
+ const currrentPackageJson = JSON.parse(await readFile(join(this.targetDirectory, 'package.json'), 'utf-8'))
347
+ const currentRuntimeDependencies = currrentPackageJson.dependencies
347
348
  // check all services are present with the same template
348
349
  const allCurrentServicesNames = this.services.map((s) => s.name)
349
350
  const allNewServicesNames = newConfig.services.map((s) => s.name)
@@ -354,7 +355,7 @@ class RuntimeGenerator extends BaseGenerator {
354
355
 
355
356
  await envTool.load()
356
357
 
357
- const removedServices = getDifference(allCurrentServicesNames, allNewServicesNames)
358
+ const removedServices = getArrayDifference(allCurrentServicesNames, allNewServicesNames)
358
359
  if (removedServices.length > 0) {
359
360
  throw new CannotRemoveServiceOnUpdateError(removedServices.join(', '))
360
361
  }
@@ -373,12 +374,22 @@ class RuntimeGenerator extends BaseGenerator {
373
374
  // update existing services env values
374
375
  // otherwise, is a new service
375
376
  baseConfig.isUpdating = true
377
+
378
+ // handle service's plugin differences
379
+ const oldServiceMetadata = await serviceInstance.loadFromDir(newService.name, this.targetDirectory)
380
+ const oldServicePackages = oldServiceMetadata.plugins.map((meta) => meta.name)
381
+ const newServicePackages = newService.plugins.map((meta) => meta.name)
382
+ const pluginsToRemove = getArrayDifference(oldServicePackages, newServicePackages)
383
+ pluginsToRemove.forEach((p) => delete currentRuntimeDependencies[p])
376
384
  }
377
385
  serviceInstance.setConfig(baseConfig)
386
+
387
+ const serviceEnvPrefix = `PLT_${serviceInstance.config.envPrefix}`
378
388
  for (const plug of newService.plugins) {
379
389
  await serviceInstance.addPackage(plug)
380
390
  for (const opt of plug.options) {
381
- const key = `PLT_${serviceInstance.config.envPrefix}_${opt.name}`
391
+ const key = `${serviceEnvPrefix}_${opt.name}`
392
+ runtimeAddedEnvKeys.push(key)
382
393
  const value = opt.value
383
394
  if (envTool.hasKey(key)) {
384
395
  envTool.updateKey(key, value)
@@ -390,11 +401,16 @@ class RuntimeGenerator extends BaseGenerator {
390
401
  allServicesDependencies = { ...allServicesDependencies, ...serviceInstance.config.dependencies }
391
402
  await serviceInstance.prepare()
392
403
  await serviceInstance.writeFiles()
404
+ // cleanup runtime env removing keys not present anymore in service plugins
405
+ const allKeys = envTool.getKeys()
406
+ allKeys.forEach((k) => {
407
+ if (k.startsWith('PLT_') && !runtimeAddedEnvKeys.includes(k)) {
408
+ envTool.deleteKey(k)
409
+ }
410
+ })
393
411
  }
394
412
 
395
413
  // update runtime package.json dependencies
396
- // read current package.json file
397
- const currrentPackageJson = JSON.parse(await readFile(join(this.targetDirectory, 'package.json'), 'utf-8'))
398
414
  currrentPackageJson.dependencies = {
399
415
  ...currrentPackageJson.dependencies,
400
416
  ...allServicesDependencies
@@ -402,7 +418,7 @@ class RuntimeGenerator extends BaseGenerator {
402
418
  this.addFile({
403
419
  path: '',
404
420
  file: 'package.json',
405
- contents: JSON.stringify(currrentPackageJson)
421
+ contents: JSON.stringify(currrentPackageJson, null, 2)
406
422
  })
407
423
 
408
424
  await this.writeFiles()
package/lib/logs.js CHANGED
@@ -21,7 +21,9 @@ async function getLogFiles () {
21
21
  return runtimeLogFiles
22
22
  }
23
23
 
24
- async function pipeLiveLogs (writableStream, logger, startLogIndex) {
24
+ async function pipeLogsStream (writableStream, logger, startLogIndex, endLogIndex) {
25
+ endLogIndex = endLogIndex || Infinity
26
+
25
27
  const runtimeLogFiles = await getLogFiles()
26
28
  if (runtimeLogFiles.length === 0) {
27
29
  writableStream.end()
@@ -47,6 +49,11 @@ async function pipeLiveLogs (writableStream, logger, startLogIndex) {
47
49
  }).unref()
48
50
 
49
51
  const streamLogFile = () => {
52
+ if (fileIndex > endLogIndex) {
53
+ writableStream.end()
54
+ return
55
+ }
56
+
50
57
  const fileName = 'logs.' + fileIndex
51
58
  const filePath = join(runtimeTmpDir, fileName)
52
59
 
@@ -61,7 +68,7 @@ async function pipeLiveLogs (writableStream, logger, startLogIndex) {
61
68
  }
62
69
 
63
70
  fileStream.on('error', (err) => {
64
- logger.log.error(err, 'Error streaming log file')
71
+ logger.error(err, 'Error streaming log file')
65
72
  fileStream.destroy()
66
73
  watcher.close()
67
74
  writableStream.end()
@@ -72,6 +79,10 @@ async function pipeLiveLogs (writableStream, logger, startLogIndex) {
72
79
  })
73
80
 
74
81
  fileStream.on('eof', () => {
82
+ if (fileIndex >= endLogIndex) {
83
+ writableStream.end()
84
+ return
85
+ }
75
86
  if (latestFileIndex > fileIndex) {
76
87
  streamLogFile(++fileIndex)
77
88
  } else {
@@ -106,7 +117,7 @@ async function getLogFileStream (logFileIndex) {
106
117
  }
107
118
 
108
119
  module.exports = {
109
- pipeLiveLogs,
120
+ pipeLogsStream,
110
121
  getLogFileStream,
111
122
  getLogIndexes
112
123
  }
@@ -6,7 +6,7 @@ const { readFile, mkdir, unlink } = require('node:fs/promises')
6
6
  const fastify = require('fastify')
7
7
  const ws = require('ws')
8
8
  const errors = require('./errors')
9
- const { pipeLiveLogs, getLogFileStream, getLogIndexes } = require('./logs')
9
+ const { pipeLogsStream, getLogFileStream, getLogIndexes } = require('./logs')
10
10
  const platformaticVersion = require('../package.json').version
11
11
 
12
12
  const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'runtimes')
@@ -118,14 +118,16 @@ async function createManagementApi (configManager, runtimeApiClient) {
118
118
 
119
119
  app.get('/metrics/live', { websocket: true }, async (socket) => {
120
120
  const cachedMetrics = runtimeApiClient.getCachedMetrics()
121
- const serializedMetrics = cachedMetrics
122
- .map((metric) => JSON.stringify(metric))
123
- .join('\n')
124
- socket.send(serializedMetrics)
121
+ if (cachedMetrics.length > 0) {
122
+ const serializedMetrics = cachedMetrics
123
+ .map((metric) => JSON.stringify(metric))
124
+ .join('\n')
125
+ socket.send(serializedMetrics + '\n')
126
+ }
125
127
 
126
128
  const eventHandler = (metrics) => {
127
129
  const serializedMetrics = JSON.stringify(metrics)
128
- socket.send(serializedMetrics)
130
+ socket.send(serializedMetrics + '\n')
129
131
  }
130
132
 
131
133
  runtimeApiClient.on('metrics', eventHandler)
@@ -150,8 +152,7 @@ async function createManagementApi (configManager, runtimeApiClient) {
150
152
  }
151
153
 
152
154
  const stream = ws.createWebSocketStream(socket)
153
-
154
- pipeLiveLogs(stream, req.log, startLogIndex)
155
+ pipeLogsStream(stream, req.log, startLogIndex)
155
156
  })
156
157
 
157
158
  app.get('/logs/indexes', async () => {
@@ -159,6 +160,15 @@ async function createManagementApi (configManager, runtimeApiClient) {
159
160
  return { indexes: logIndexes }
160
161
  })
161
162
 
163
+ app.get('/logs/all', async (req, reply) => {
164
+ const logIndexes = await getLogIndexes()
165
+ const startLogIndex = logIndexes.at(0)
166
+ const endLogIndex = logIndexes.at(-1)
167
+
168
+ reply.hijack()
169
+ pipeLogsStream(reply.raw, req.log, startLogIndex, endLogIndex)
170
+ })
171
+
162
172
  app.get('/logs/:id', async (req) => {
163
173
  const { id } = req.params
164
174
 
package/lib/schema.js CHANGED
@@ -150,6 +150,7 @@ const platformaticRuntimeSchema = {
150
150
  managementApi: {
151
151
  anyOf: [
152
152
  { type: 'boolean' },
153
+ { type: 'string' },
153
154
  {
154
155
  type: 'object',
155
156
  properties: {
@@ -163,7 +164,8 @@ const platformaticRuntimeSchema = {
163
164
  },
164
165
  additionalProperties: false
165
166
  }
166
- ]
167
+ ],
168
+ default: true
167
169
  },
168
170
  metrics: {
169
171
  anyOf: [
package/lib/utils.js ADDED
@@ -0,0 +1,10 @@
1
+ 'use strict'
2
+ function getArrayDifference (a, b) {
3
+ return a.filter(element => {
4
+ return !b.includes(element)
5
+ })
6
+ }
7
+
8
+ module.exports = {
9
+ getArrayDifference
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "1.29.0",
3
+ "version": "1.30.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -33,8 +33,8 @@
33
33
  "typescript": "^5.4.2",
34
34
  "undici-oidc-interceptor": "^0.5.0",
35
35
  "why-is-node-running": "^2.2.2",
36
- "@platformatic/sql-graphql": "1.29.0",
37
- "@platformatic/sql-mapper": "1.29.0"
36
+ "@platformatic/sql-mapper": "1.30.0",
37
+ "@platformatic/sql-graphql": "1.30.0"
38
38
  },
39
39
  "dependencies": {
40
40
  "ws": "^8.16.0",
@@ -47,7 +47,7 @@
47
47
  "commist": "^3.2.0",
48
48
  "debounce": "^2.0.0",
49
49
  "desm": "^1.3.1",
50
- "dotenv-tool": "^0.0.2",
50
+ "dotenv-tool": "^0.1.1",
51
51
  "es-main": "^1.3.0",
52
52
  "fastest-levenshtein": "^1.0.16",
53
53
  "fastify": "^4.26.2",
@@ -62,13 +62,13 @@
62
62
  "tail-file-stream": "^0.1.0",
63
63
  "undici": "^6.9.0",
64
64
  "why-is-node-running": "^2.2.2",
65
- "@platformatic/composer": "1.29.0",
66
- "@platformatic/config": "1.29.0",
67
- "@platformatic/db": "1.29.0",
68
- "@platformatic/generators": "1.29.0",
69
- "@platformatic/telemetry": "1.29.0",
70
- "@platformatic/service": "1.29.0",
71
- "@platformatic/utils": "1.29.0"
65
+ "@platformatic/composer": "1.30.0",
66
+ "@platformatic/db": "1.30.0",
67
+ "@platformatic/config": "1.30.0",
68
+ "@platformatic/generators": "1.30.0",
69
+ "@platformatic/service": "1.30.0",
70
+ "@platformatic/telemetry": "1.30.0",
71
+ "@platformatic/utils": "1.30.0"
72
72
  },
73
73
  "standard": {
74
74
  "ignore": [