@platformatic/runtime 3.0.0-alpha.1 → 3.0.0-alpha.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/index.d.ts CHANGED
@@ -1,11 +1,32 @@
1
1
  import { FastifyError } from '@fastify/error'
2
+ import { Configuration, ConfigurationOptions, logFatalError, parseArgs } from '@platformatic/foundation'
2
3
  import { BaseGenerator } from '@platformatic/generators'
3
- import { Configuration, ConfigurationOptions } from '@platformatic/utils'
4
4
  import { JSONSchemaType } from 'ajv'
5
+ import * as colorette from 'colorette'
6
+ import { Logger } from 'pino'
5
7
  import { PlatformaticRuntimeConfig } from './config'
6
8
 
7
9
  export type RuntimeConfiguration = Promise<Configuration<PlatformaticRuntimeConfig>>
8
10
 
11
+ export type ServiceCommandContext = {
12
+ colorette: typeof colorette
13
+ parseArgs: typeof parseArgs
14
+ logFatalError: typeof logFatalError
15
+ }
16
+
17
+ export type ServiceCommand = (
18
+ logger: Logger,
19
+ configuration: Configuration<unknown>,
20
+ args: string[],
21
+ context: ServiceCommandContext
22
+ ) => Promise<void>
23
+
24
+ export interface ServicesCommands {
25
+ services: Record<string, Configuration<unknown>>
26
+ commands: Record<string, ServiceCommand>
27
+ help: Record<string, string | (() => string)>
28
+ }
29
+
9
30
  export module errors {
10
31
  export const RuntimeExitedError: () => FastifyError
11
32
  export const UnknownRuntimeAPICommandError: (command: string) => FastifyError
@@ -45,8 +66,6 @@ export class Generator extends BaseGenerator.BaseGenerator {}
45
66
 
46
67
  export class WrappedGenerator extends BaseGenerator.BaseGenerator {}
47
68
 
48
- export declare function getRuntimeLogsDir (runtimeDir: string, runtimePID: number): string
49
-
50
69
  export declare const schema: JSONSchemaType<PlatformaticRuntimeConfig>
51
70
 
52
71
  export declare class Runtime {}
@@ -71,3 +90,5 @@ export function create (
71
90
  ): Promise<Runtime>
72
91
 
73
92
  export declare function transform (config: RuntimeConfiguration): Promise<RuntimeConfiguration> | RuntimeConfiguration
93
+
94
+ export declare function loadServicesCommands (): Promise<ServicesCommands>
package/index.js CHANGED
@@ -1,13 +1,16 @@
1
1
  'use strict'
2
2
 
3
3
  const inspector = require('node:inspector')
4
- const { kMetadata } = require('@platformatic/utils')
5
- const { resolve, validationOptions } = require('@platformatic/basic')
6
4
  const {
5
+ kMetadata,
6
+ loadConfigurationModule,
7
+ abstractLogger,
8
+ findRuntimeConfigurationFile,
7
9
  loadConfiguration: utilsLoadConfiguration,
8
10
  extractModuleFromSchemaUrl,
9
11
  ensureLoggableError
10
- } = require('@platformatic/utils')
12
+ } = require('@platformatic/foundation')
13
+ const { resolve, validationOptions } = require('@platformatic/basic')
11
14
  const { NodeInspectorFlagsNotSupportedError } = require('./lib/errors')
12
15
  const { wrapInRuntimeConfig, transform } = require('./lib/config')
13
16
  const { RuntimeGenerator, WrappedGenerator } = require('./lib/generator')
@@ -15,7 +18,6 @@ const { Runtime } = require('./lib/runtime')
15
18
  const symbols = require('./lib/worker/symbols')
16
19
  const { schema } = require('./lib/schema')
17
20
  const { upgrade } = require('./lib/upgrade')
18
- const { getRuntimeLogsDir } = require('./lib/utils')
19
21
 
20
22
  async function restartRuntime (runtime) {
21
23
  runtime.logger.info('Received SIGUSR2, restarting all services ...')
@@ -56,6 +58,53 @@ async function loadConfiguration (configOrRoot, sourceOrConfig, context) {
56
58
  })
57
59
  }
58
60
 
61
+ async function loadServicesCommands () {
62
+ const services = {}
63
+ const commands = {}
64
+ const help = {}
65
+
66
+ let config
67
+ try {
68
+ const file = await findRuntimeConfigurationFile(abstractLogger, process.cwd(), null, false, false)
69
+
70
+ /* c8 ignore next 3 - Hard to test */
71
+ if (!file) {
72
+ throw new Error('No runtime configuration file found.')
73
+ }
74
+
75
+ config = await loadConfiguration(file)
76
+
77
+ /* c8 ignore next 3 - Hard to test */
78
+ if (!config) {
79
+ throw new Error('No runtime configuration file found.')
80
+ }
81
+ } catch {
82
+ return { services, commands, help }
83
+ }
84
+
85
+ for (const service of config.services) {
86
+ try {
87
+ const serviceConfig = await utilsLoadConfiguration(service.config)
88
+ const pkg = await loadConfigurationModule(service.path, serviceConfig)
89
+
90
+ if (pkg.createCommands) {
91
+ const definition = await pkg.createCommands(service.id)
92
+ for (const command of Object.keys(definition.commands)) {
93
+ services[command] = service
94
+ }
95
+
96
+ Object.assign(commands, definition.commands)
97
+ Object.assign(help, definition.help)
98
+ }
99
+ /* c8 ignore next 3 - Hard to test */
100
+ } catch {
101
+ // No-op, ignore the service
102
+ }
103
+ }
104
+
105
+ return { services, commands, help }
106
+ }
107
+
59
108
  async function create (configOrRoot, sourceOrConfig, context) {
60
109
  const config = await loadConfiguration(configOrRoot, sourceOrConfig, context)
61
110
 
@@ -75,10 +124,12 @@ async function create (configOrRoot, sourceOrConfig, context) {
75
124
  await runtime.start()
76
125
  break
77
126
  } catch (err) {
78
- if (err.code !== 'EADDRINUSE' || context?.skipPortInUseHandling) {
127
+ if ((err.code !== 'EADDRINUSE' && err.code !== 'EACCES') || context?.skipPortInUseHandling) {
79
128
  throw err
80
129
  }
81
130
 
131
+ await runtime.close()
132
+
82
133
  // Get the actual port from the error message if original port was 0
83
134
  if (!port) {
84
135
  const mo = err.message.match(/ address already in use (.+)/)
@@ -101,7 +152,6 @@ const platformaticVersion = require('./package.json').version
101
152
  module.exports.errors = require('./lib/errors')
102
153
  module.exports.Generator = RuntimeGenerator
103
154
  module.exports.WrappedGenerator = WrappedGenerator
104
- module.exports.getRuntimeLogsDir = getRuntimeLogsDir
105
155
  module.exports.schema = schema
106
156
  module.exports.symbols = symbols
107
157
  module.exports.Runtime = Runtime
@@ -110,3 +160,4 @@ module.exports.version = platformaticVersion
110
160
  module.exports.loadConfiguration = loadConfiguration
111
161
  module.exports.create = create
112
162
  module.exports.transform = transform
163
+ module.exports.loadServicesCommands = loadServicesCommands
package/lib/config.js CHANGED
@@ -13,7 +13,7 @@ const {
13
13
  loadConfigurationModule,
14
14
  loadConfiguration,
15
15
  extractModuleFromSchemaUrl
16
- } = require('@platformatic/utils')
16
+ } = require('@platformatic/foundation')
17
17
  const {
18
18
  InspectAndInspectBrkError,
19
19
  InvalidEntrypointError,
package/lib/generator.js CHANGED
@@ -18,8 +18,9 @@ const {
18
18
  findConfigurationFile,
19
19
  loadConfiguration,
20
20
  loadConfigurationFile,
21
- kMetadata
22
- } = require('@platformatic/utils')
21
+ kMetadata,
22
+ defaultPackageManager
23
+ } = require('@platformatic/foundation')
23
24
  const { createRequire } = require('node:module')
24
25
 
25
26
  const wrappableProperties = {
@@ -34,7 +35,7 @@ const wrappableProperties = {
34
35
  }
35
36
 
36
37
  const engines = {
37
- node: '>=22.16.0'
38
+ node: '>=22.18.0'
38
39
  }
39
40
 
40
41
  const ERROR_PREFIX = 'PLT_RUNTIME_GEN'
@@ -65,6 +66,7 @@ class RuntimeGenerator extends BaseGenerator {
65
66
  this.services = []
66
67
  this.existingServices = []
67
68
  this.entryPoint = null
69
+ this.packageManager = opts.packageManager ?? defaultPackageManager
68
70
  }
69
71
 
70
72
  async addService (service, name) {
@@ -84,9 +86,7 @@ class RuntimeGenerator extends BaseGenerator {
84
86
  service
85
87
  })
86
88
 
87
- if (typeof service.setRuntime === 'function') {
88
- service.setRuntime(this)
89
- }
89
+ service.setRuntime(this)
90
90
  }
91
91
 
92
92
  setEntryPoint (entryPoint) {
@@ -100,7 +100,6 @@ class RuntimeGenerator extends BaseGenerator {
100
100
  async generatePackageJson () {
101
101
  const template = {
102
102
  name: `${this.runtimeName}`,
103
- workspaces: [this.servicesFolder + '/*'],
104
103
  scripts: {
105
104
  dev: this.config.devCommand,
106
105
  build: this.config.buildCommand,
@@ -118,13 +117,11 @@ class RuntimeGenerator extends BaseGenerator {
118
117
  },
119
118
  engines
120
119
  }
121
- if (this.config.typescript) {
122
- const typescriptVersion = JSON.parse(await readFile(join(__dirname, '..', 'package.json'), 'utf-8'))
123
- .devDependencies.typescript
124
- template.scripts.clean = 'rm -fr ./dist'
125
- template.scripts.build = 'platformatic compile'
126
- template.devDependencies.typescript = typescriptVersion
120
+
121
+ if (this.packageManager === 'npm' || this.packageManager === 'yarn') {
122
+ template.workspaces = [this.servicesFolder + '/*']
127
123
  }
124
+
128
125
  return template
129
126
  }
130
127
 
@@ -191,7 +188,6 @@ class RuntimeGenerator extends BaseGenerator {
191
188
  // set default config
192
189
  service.setConfig()
193
190
  }
194
- service.config.typescript = this.config.typescript
195
191
  })
196
192
  }
197
193
 
@@ -226,7 +222,7 @@ class RuntimeGenerator extends BaseGenerator {
226
222
  this.addFile({
227
223
  path: '',
228
224
  file: '.env.sample',
229
- contents: envObjectToString(this.config.env)
225
+ contents: envObjectToString(this.config.defaultEnv)
230
226
  })
231
227
 
232
228
  return {
@@ -256,18 +252,6 @@ class RuntimeGenerator extends BaseGenerator {
256
252
  async prepareQuestions () {
257
253
  await this.populateFromExistingConfig()
258
254
 
259
- // typescript
260
- this.questions.push({
261
- type: 'list',
262
- name: 'typescript',
263
- message: 'Do you want to use TypeScript?',
264
- default: false,
265
- choices: [
266
- { name: 'yes', value: true },
267
- { name: 'no', value: false }
268
- ]
269
- })
270
-
271
255
  if (this.existingConfig) {
272
256
  return
273
257
  }
@@ -311,11 +295,6 @@ class RuntimeGenerator extends BaseGenerator {
311
295
  async prepareServiceFiles () {
312
296
  let servicesEnv = {}
313
297
  for (const svc of this.services) {
314
- // Propagate TypeScript
315
- svc.service.setConfig({
316
- ...svc.service.config,
317
- typescript: this.config.typescript
318
- })
319
298
  const svcEnv = await svc.service.prepare()
320
299
  servicesEnv = {
321
300
  ...servicesEnv,
@@ -590,7 +569,7 @@ class WrappedGenerator extends BaseGenerator {
590
569
  this.addFile({
591
570
  path: '',
592
571
  file: '.env.sample',
593
- contents: (await this.#readExistingFile('.env.sample', '', '\n')) + envObjectToString(this.config.env)
572
+ contents: (await this.#readExistingFile('.env.sample', '', '\n')) + envObjectToString(this.config.defaultEnv)
594
573
  })
595
574
  }
596
575
 
package/lib/logger.js CHANGED
@@ -1,11 +1,9 @@
1
1
  'use strict'
2
2
 
3
- const { once } = require('node:events')
4
- const { join } = require('node:path')
5
3
  const { isatty } = require('node:tty')
6
4
  const pino = require('pino')
7
5
  const pretty = require('pino-pretty')
8
- const { abstractLogger, buildPinoFormatters, buildPinoTimestamp } = require('@platformatic/utils')
6
+ const { abstractLogger, buildPinoFormatters, buildPinoTimestamp } = require('@platformatic/foundation')
9
7
 
10
8
  const customPrettifiers = {
11
9
  name (name, _, obj) {
@@ -19,26 +17,24 @@ const customPrettifiers = {
19
17
  }
20
18
 
21
19
  // Create the runtime logger
22
- async function createLogger (config, runtimeLogsDir) {
20
+ async function createLogger (config) {
23
21
  const loggerConfig = { ...config.logger, transport: undefined }
24
22
  if (config.logger.base === null) {
25
23
  loggerConfig.base = undefined
26
24
  }
27
25
 
28
- // PLT_RUNTIME_LOGGER_STDOUT is used in test to reduce verbosity
29
- let cliStream = process.env.PLT_RUNTIME_LOGGER_STDOUT
30
- ? pino.destination(process.env.PLT_RUNTIME_LOGGER_STDOUT)
31
- : isatty(1)
32
- ? pretty({ customPrettifiers })
33
- : pino.destination(1)
26
+ let cliStream
34
27
 
35
28
  if (config.logger.transport) {
36
29
  cliStream = pino.transport(config.logger.transport)
30
+ } else {
31
+ cliStream = isatty(1) ? pretty({ customPrettifiers }) : pino.destination(1)
37
32
  }
38
33
 
39
34
  if (loggerConfig.formatters) {
40
35
  loggerConfig.formatters = buildPinoFormatters(loggerConfig.formatters)
41
36
  }
37
+
42
38
  if (loggerConfig.timestamp) {
43
39
  loggerConfig.timestamp = buildPinoTimestamp(loggerConfig.timestamp)
44
40
  }
@@ -57,25 +53,6 @@ async function createLogger (config, runtimeLogsDir) {
57
53
  logsLimitCount = 1
58
54
  }
59
55
 
60
- const pinoRoll = pino.transport({
61
- target: 'pino-roll',
62
- options: {
63
- file: join(runtimeLogsDir, 'logs'),
64
- mode: 0o600,
65
- size: logsFileMb + 'm',
66
- mkdir: true,
67
- fsync: true,
68
- limit: {
69
- count: logsLimitCount
70
- }
71
- }
72
- })
73
-
74
- multiStream.add({ level: loggerConfig.level, stream: pinoRoll })
75
-
76
- // Make sure there is a file before continuing otherwise the management API log endpoint might bail out
77
- await once(pinoRoll, 'ready')
78
-
79
56
  return [pino(loggerConfig, multiStream), multiStream]
80
57
  }
81
58
 
@@ -2,14 +2,11 @@
2
2
 
3
3
  const { platform, tmpdir } = require('node:os')
4
4
  const { join } = require('node:path')
5
- const { createDirectory, safeRemove } = require('@platformatic/utils')
5
+ const { createDirectory, safeRemove } = require('@platformatic/foundation')
6
6
 
7
7
  const fastify = require('fastify')
8
8
  const ws = require('ws')
9
9
 
10
- const errors = require('./errors')
11
- const { getRuntimeLogsDir } = require('./utils')
12
-
13
10
  const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'runtimes')
14
11
 
15
12
  async function managementApiPlugin (app, opts) {
@@ -152,54 +149,7 @@ async function managementApiPlugin (app, opts) {
152
149
  })
153
150
 
154
151
  app.get('/logs/live', { websocket: true }, async (socket, req) => {
155
- const startLogId = req.query.start ? parseInt(req.query.start) : null
156
-
157
- if (startLogId) {
158
- const logIds = await runtime.getLogIds()
159
- if (!logIds.includes(startLogId)) {
160
- throw new errors.LogFileNotFound(startLogId)
161
- }
162
- }
163
-
164
- const stream = ws.createWebSocketStream(socket)
165
- runtime.pipeLogsStream(stream, req.log, startLogId)
166
- })
167
-
168
- app.get('/logs/indexes', async req => {
169
- const returnAllIds = req.query.all === 'true'
170
-
171
- if (returnAllIds) {
172
- const runtimesLogsIds = await runtime.getAllLogIds()
173
- return runtimesLogsIds
174
- }
175
-
176
- const runtimeLogsIds = await runtime.getLogIds()
177
- return { indexes: runtimeLogsIds }
178
- })
179
-
180
- app.get('/logs/all', async (req, reply) => {
181
- const runtimePID = parseInt(req.query.pid) || process.pid
182
-
183
- const logsIds = await runtime.getLogIds(runtimePID)
184
- const startLogId = logsIds.at(0)
185
- const endLogId = logsIds.at(-1)
186
-
187
- reply.hijack()
188
-
189
- runtime.pipeLogsStream(reply.raw, req.log, startLogId, endLogId, runtimePID)
190
- })
191
-
192
- app.get('/logs/:id', async req => {
193
- const logId = parseInt(req.params.id)
194
- const runtimePID = parseInt(req.query.pid) || process.pid
195
-
196
- const logIds = await runtime.getLogIds(runtimePID)
197
- if (!logIds || !logIds.includes(logId)) {
198
- throw new errors.LogFileNotFound(logId)
199
- }
200
-
201
- const logFileStream = await runtime.getLogFileStream(logId, runtimePID)
202
- return logFileStream
152
+ runtime.addLoggerDestination(ws.createWebSocketStream(socket))
203
153
  })
204
154
  }
205
155
 
@@ -212,9 +162,6 @@ async function startManagementApi (runtime, root) {
212
162
  await createDirectory(runtimePIDDir, true)
213
163
  }
214
164
 
215
- const runtimeLogsDir = getRuntimeLogsDir(root, process.pid)
216
- await createDirectory(runtimeLogsDir, true)
217
-
218
165
  let socketPath = null
219
166
  if (platform() === 'win32') {
220
167
  socketPath = '\\\\.\\pipe\\platformatic-' + runtimePID.toString()
@@ -232,6 +179,9 @@ async function startManagementApi (runtime, root) {
232
179
  }
233
180
  })
234
181
 
182
+ // When the runtime closes, close the management API as well
183
+ runtime.on('closed', managementApi.close.bind(managementApi))
184
+
235
185
  await managementApi.listen({ path: socketPath })
236
186
  return managementApi
237
187
  /* c8 ignore next 4 */
@@ -77,6 +77,7 @@ async function startPrometheusServer (runtime, opts) {
77
77
  const auth = opts.auth ?? null
78
78
 
79
79
  const promServer = fastify({ name: 'Prometheus server' })
80
+ promServer.register(require('@fastify/accepts'))
80
81
 
81
82
  let onRequestHook
82
83
  if (auth) {
@@ -122,9 +123,12 @@ async function startPrometheusServer (runtime, opts) {
122
123
  logLevel: 'warn',
123
124
  onRequest: onRequestHook,
124
125
  handler: async (req, reply) => {
125
- reply.type('text/plain')
126
- const { metrics } = await runtime.getMetrics('text')
127
- return metrics
126
+ const accepts = req.accepts()
127
+ const reqType = !accepts.type('text/plain') && accepts.type('application/json') ? 'json' : 'text'
128
+ if (reqType === 'text') {
129
+ reply.type('text/plain')
130
+ }
131
+ return (await runtime.getMetrics(reqType)).metrics
128
132
  },
129
133
  })
130
134
 
package/lib/runtime.js CHANGED
@@ -4,21 +4,21 @@ const { ITC } = require('@platformatic/itc')
4
4
  const {
5
5
  features,
6
6
  ensureLoggableError,
7
+ ensureError,
7
8
  executeWithTimeout,
8
9
  deepmerge,
9
10
  parseMemorySize,
10
11
  kTimeout,
11
12
  kMetadata
12
- } = require('@platformatic/utils')
13
+ } = require('@platformatic/foundation')
13
14
  const { once, EventEmitter } = require('node:events')
14
- const { createReadStream, watch, existsSync } = require('node:fs')
15
- const { readdir, readFile, stat, access } = require('node:fs/promises')
15
+ const { existsSync } = require('node:fs')
16
+ const { readFile } = require('node:fs/promises')
16
17
  const { STATUS_CODES } = require('node:http')
17
18
  const { join } = require('node:path')
18
19
  const { pathToFileURL } = require('node:url')
19
20
  const { setTimeout: sleep, setImmediate: immediate } = require('node:timers/promises')
20
21
  const { Worker } = require('node:worker_threads')
21
- const ts = require('tail-file-stream')
22
22
  const { Agent, interceptors: undiciInterceptors, request } = require('undici')
23
23
  const { createThreadInterceptor } = require('undici-thread-interceptor')
24
24
  const SonicBoom = require('sonic-boom')
@@ -29,7 +29,7 @@ const { startManagementApi } = require('./management-api')
29
29
  const { startPrometheusServer } = require('./prom-server')
30
30
  const { startScheduler } = require('./scheduler')
31
31
  const { createSharedStore } = require('./shared-http-cache')
32
- const { getRuntimeTmpDir, getRuntimeLogsDir } = require('./utils')
32
+ const { getRuntimeTmpDir } = require('./utils')
33
33
  const { sendViaITC, waitEventFromITC } = require('./worker/itc')
34
34
  const { RoundRobinMap } = require('./worker/round-robin-map.js')
35
35
  const {
@@ -71,7 +71,6 @@ class Runtime extends EventEmitter {
71
71
  #context
72
72
  #isProduction
73
73
  #runtimeTmpDir
74
- #runtimeLogsDir
75
74
  #servicesIds
76
75
  #entrypointId
77
76
  #url
@@ -92,6 +91,7 @@ class Runtime extends EventEmitter {
92
91
  servicesConfigsPatches
93
92
  #scheduler
94
93
  #stdio
94
+ #sharedContext
95
95
 
96
96
  constructor (config, context) {
97
97
  super()
@@ -103,7 +103,6 @@ class Runtime extends EventEmitter {
103
103
  this.#context = context ?? {}
104
104
  this.#isProduction = this.#context.isProduction ?? this.#context.production ?? false
105
105
  this.#runtimeTmpDir = getRuntimeTmpDir(this.#root)
106
- this.#runtimeLogsDir = getRuntimeLogsDir(this.#root, process.pid)
107
106
  this.#workers = new RoundRobinMap()
108
107
  this.#servicesIds = []
109
108
  this.#url = undefined
@@ -133,8 +132,11 @@ class Runtime extends EventEmitter {
133
132
  getHttpCacheValue: this.#getHttpCacheValue.bind(this),
134
133
  setHttpCacheValue: this.#setHttpCacheValue.bind(this),
135
134
  deleteHttpCacheValue: this.#deleteHttpCacheValue.bind(this),
136
- invalidateHttpCache: this.invalidateHttpCache.bind(this)
135
+ invalidateHttpCache: this.invalidateHttpCache.bind(this),
136
+ updateSharedContext: this.updateSharedContext.bind(this),
137
+ getSharedContext: this.getSharedContext.bind(this)
137
138
  }
139
+ this.#sharedContext = {}
138
140
  }
139
141
 
140
142
  async init () {
@@ -157,7 +159,7 @@ class Runtime extends EventEmitter {
157
159
  }
158
160
 
159
161
  // Create the logger
160
- const [logger, destination] = await createLogger(config, this.#runtimeLogsDir)
162
+ const [logger, destination] = await createLogger(config)
161
163
  this.logger = logger
162
164
  this.#loggerDestination = destination
163
165
 
@@ -379,12 +381,8 @@ class Runtime extends EventEmitter {
379
381
 
380
382
  await this.stop(silent)
381
383
 
382
- if (this.#managementApi) {
383
- // This allow a close request coming from the management API to correctly be handled
384
- setImmediate(() => {
385
- this.#managementApi.close()
386
- })
387
- }
384
+ // The management API autocloses by itself via event in management-api.js.
385
+ // This is needed to let management API stop endpoint to reply.
388
386
 
389
387
  if (this.#prometheusServer) {
390
388
  await this.#prometheusServer.close()
@@ -549,8 +547,7 @@ class Runtime extends EventEmitter {
549
547
  metrics = await this.getFormattedMetrics()
550
548
  } catch (error) {
551
549
  if (!(error instanceof errors.RuntimeExitedError)) {
552
- // TODO(mcollina): use the logger
553
- console.error('Error collecting metrics', error)
550
+ this.logger.error({ err: ensureLoggableError(error) }, 'Error collecting metrics')
554
551
  }
555
552
  return
556
553
  }
@@ -563,87 +560,18 @@ class Runtime extends EventEmitter {
563
560
  }, COLLECT_METRICS_TIMEOUT).unref()
564
561
  }
565
562
 
566
- async pipeLogsStream (writableStream, logger, startLogId, endLogId, runtimePID) {
567
- endLogId = endLogId || Infinity
568
- runtimePID = runtimePID ?? process.pid
569
-
570
- const runtimeLogFiles = await this.#getRuntimeLogFiles(runtimePID)
571
-
572
- if (runtimeLogFiles.length === 0) {
573
- writableStream.end()
574
- return
575
- }
576
-
577
- let latestFileId = parseInt(runtimeLogFiles.at(-1).slice('logs.'.length))
578
-
579
- let fileStream = null
580
- let fileId = startLogId ?? latestFileId
581
- let isClosed = false
563
+ async addLoggerDestination (writableStream) {
564
+ // Add the stream - We output everything we get
565
+ this.#loggerDestination.add({ stream: writableStream, level: 1 })
582
566
 
583
- const runtimeLogsDir = this.#getRuntimeLogsDir(runtimePID)
584
-
585
- const watcher = watch(runtimeLogsDir, async (event, filename) => {
586
- if (event === 'rename' && filename.startsWith('logs')) {
587
- const logFileId = parseInt(filename.slice('logs.'.length))
588
- if (logFileId > latestFileId) {
589
- latestFileId = logFileId
590
- fileStream.unwatch()
591
- }
592
- }
593
- }).unref()
594
-
595
- const streamLogFile = () => {
596
- if (fileId > endLogId) {
597
- writableStream.end()
598
- return
599
- }
600
-
601
- const fileName = 'logs.' + fileId
602
- const filePath = join(runtimeLogsDir, fileName)
603
-
604
- const prevFileStream = fileStream
605
-
606
- fileStream = ts.createReadStream(filePath)
607
- fileStream.pipe(writableStream, { end: false, persistent: false })
608
-
609
- if (prevFileStream) {
610
- prevFileStream.unpipe(writableStream)
611
- prevFileStream.destroy()
612
- }
613
-
614
- fileStream.on('close', () => {
615
- if (latestFileId > fileId && !isClosed) {
616
- streamLogFile(++fileId)
617
- }
618
- })
619
-
620
- fileStream.on('error', err => {
621
- isClosed = true
622
- logger.error(err, 'Error streaming log file')
623
- fileStream.destroy()
624
- watcher.close()
625
- writableStream.end()
626
- })
627
-
628
- fileStream.on('eof', () => {
629
- if (fileId >= endLogId) {
630
- writableStream.end()
631
- return
632
- }
633
- if (latestFileId > fileId) {
634
- fileStream.unwatch()
635
- }
636
- })
637
-
638
- return fileStream
639
- }
640
-
641
- streamLogFile(fileId)
567
+ // Immediately get the counter of the lastId so we can use it to later remove it
568
+ const id = this.#loggerDestination.lastId
642
569
 
643
570
  const onClose = () => {
644
- isClosed = true
645
- watcher.close()
646
- fileStream.destroy()
571
+ writableStream.removeListener('close', onClose)
572
+ writableStream.removeListener('error', onClose)
573
+ this.removeListener('closed', onClose)
574
+ this.#loggerDestination.remove(id)
647
575
  }
648
576
 
649
577
  writableStream.on('close', onClose)
@@ -1004,44 +932,6 @@ class Runtime extends EventEmitter {
1004
932
  this.servicesConfigsPatches.delete(id)
1005
933
  }
1006
934
 
1007
- async getLogIds (runtimePID) {
1008
- runtimePID = runtimePID ?? process.pid
1009
-
1010
- const runtimeLogFiles = await this.#getRuntimeLogFiles(runtimePID)
1011
- const runtimeLogIds = []
1012
-
1013
- for (const logFile of runtimeLogFiles) {
1014
- const logId = parseInt(logFile.slice('logs.'.length))
1015
- runtimeLogIds.push(logId)
1016
- }
1017
- return runtimeLogIds
1018
- }
1019
-
1020
- async getAllLogIds () {
1021
- const runtimesLogFiles = await this.#getAllLogsFiles()
1022
- const runtimesLogsIds = []
1023
-
1024
- for (const runtime of runtimesLogFiles) {
1025
- const runtimeLogIds = []
1026
- for (const logFile of runtime.runtimeLogFiles) {
1027
- const logId = parseInt(logFile.slice('logs.'.length))
1028
- runtimeLogIds.push(logId)
1029
- }
1030
- runtimesLogsIds.push({
1031
- pid: runtime.runtimePID,
1032
- indexes: runtimeLogIds
1033
- })
1034
- }
1035
-
1036
- return runtimesLogsIds
1037
- }
1038
-
1039
- async getLogFileStream (logFileId, runtimePID) {
1040
- const runtimeLogsDir = this.#getRuntimeLogsDir(runtimePID)
1041
- const filePath = join(runtimeLogsDir, `logs.${logFileId}`)
1042
- return createReadStream(filePath)
1043
- }
1044
-
1045
935
  #getHttpCacheValue ({ request }) {
1046
936
  if (!this.#sharedHttpCache) {
1047
937
  return
@@ -1111,6 +1001,33 @@ class Runtime extends EventEmitter {
1111
1001
  return super.emit(event, payload)
1112
1002
  }
1113
1003
 
1004
+ async updateSharedContext (options = {}) {
1005
+ const { context, overwrite = false } = options
1006
+
1007
+ const sharedContext = overwrite ? {} : this.#sharedContext
1008
+ Object.assign(sharedContext, context)
1009
+
1010
+ this.#sharedContext = sharedContext
1011
+
1012
+ const promises = []
1013
+ for (const worker of this.#workers.values()) {
1014
+ promises.push(sendViaITC(worker, 'setSharedContext', sharedContext))
1015
+ }
1016
+
1017
+ const results = await Promise.allSettled(promises)
1018
+ for (const result of results) {
1019
+ if (result.status === 'rejected') {
1020
+ this.logger.error({ err: result.reason }, 'Cannot update shared context')
1021
+ }
1022
+ }
1023
+
1024
+ return sharedContext
1025
+ }
1026
+
1027
+ getSharedContext () {
1028
+ return this.#sharedContext
1029
+ }
1030
+
1114
1031
  async #setDispatcher (undiciConfig) {
1115
1032
  const config = this.#config
1116
1033
 
@@ -1226,8 +1143,7 @@ class Runtime extends EventEmitter {
1226
1143
  count: workersCount
1227
1144
  },
1228
1145
  inspectorOptions,
1229
- dirname: this.#root,
1230
- runtimeLogsDir: this.#runtimeLogsDir
1146
+ dirname: this.#root
1231
1147
  },
1232
1148
  argv: serviceConfig.arguments,
1233
1149
  execArgv,
@@ -1549,16 +1465,24 @@ class Runtime extends EventEmitter {
1549
1465
  gracePeriod > 0 ? gracePeriod : 1
1550
1466
  )
1551
1467
  }
1552
- } catch (error) {
1468
+ } catch (err) {
1469
+ const error = ensureError(err)
1470
+
1553
1471
  // TODO: handle port allocation error here
1554
- if (error.code === 'EADDRINUSE') throw error
1472
+ if (error.code === 'EADDRINUSE' || error.code === 'EACCES') throw error
1555
1473
 
1556
1474
  this.#cleanupWorker(worker)
1557
1475
 
1558
1476
  if (worker[kWorkerStatus] !== 'exited') {
1559
1477
  // This prevent the exit handler to restart service
1560
1478
  worker[kWorkerStatus] = 'exited'
1561
- await worker.terminate()
1479
+
1480
+ // Wait for the worker to exit gracefully, otherwise we terminate it
1481
+ const waitTimeout = await executeWithTimeout(once(worker, 'exit'), config.gracefulShutdown.service)
1482
+
1483
+ if (waitTimeout === kTimeout) {
1484
+ await worker.terminate()
1485
+ }
1562
1486
  }
1563
1487
 
1564
1488
  this.emit('service:worker:start:error', { ...eventPayload, error })
@@ -1836,7 +1760,11 @@ class Runtime extends EventEmitter {
1836
1760
  })
1837
1761
  }
1838
1762
 
1839
- this.#workersBroadcastChannel.postMessage(workers)
1763
+ try {
1764
+ this.#workersBroadcastChannel.postMessage(workers)
1765
+ } catch (err) {
1766
+ this.logger?.error({ err }, 'Error when broadcasting workers')
1767
+ }
1840
1768
  }
1841
1769
 
1842
1770
  async #getWorkerMessagingChannel ({ service, worker }, context) {
@@ -1867,49 +1795,6 @@ class Runtime extends EventEmitter {
1867
1795
  return packageJson
1868
1796
  }
1869
1797
 
1870
- #getRuntimeLogsDir (runtimePID) {
1871
- return join(this.#runtimeTmpDir, runtimePID.toString(), 'logs')
1872
- }
1873
-
1874
- async #getRuntimeLogFiles (runtimePID) {
1875
- const runtimeLogsDir = this.#getRuntimeLogsDir(runtimePID)
1876
- const runtimeLogsFiles = await readdir(runtimeLogsDir)
1877
- return runtimeLogsFiles
1878
- .filter(file => file.startsWith('logs'))
1879
- .sort((log1, log2) => {
1880
- const index1 = parseInt(log1.slice('logs.'.length))
1881
- const index2 = parseInt(log2.slice('logs.'.length))
1882
- return index1 - index2
1883
- })
1884
- }
1885
-
1886
- async #getAllLogsFiles () {
1887
- try {
1888
- await access(this.#runtimeTmpDir)
1889
- } catch (err) {
1890
- this.logger.error({ err: ensureLoggableError(err) }, 'Cannot access temporary folder.')
1891
- return []
1892
- }
1893
-
1894
- const runtimePIDs = await readdir(this.#runtimeTmpDir)
1895
- const runtimesLogFiles = []
1896
-
1897
- for (const runtimePID of runtimePIDs) {
1898
- const runtimeLogsDir = this.#getRuntimeLogsDir(runtimePID)
1899
- const runtimeLogsDirStat = await stat(runtimeLogsDir)
1900
- const runtimeLogFiles = await this.#getRuntimeLogFiles(runtimePID)
1901
- const lastModified = runtimeLogsDirStat.mtime
1902
-
1903
- runtimesLogFiles.push({
1904
- runtimePID: parseInt(runtimePID),
1905
- runtimeLogFiles,
1906
- lastModified
1907
- })
1908
- }
1909
-
1910
- return runtimesLogFiles.sort((runtime1, runtime2) => runtime1.lastModified - runtime2.lastModified)
1911
- }
1912
-
1913
1798
  #handleWorkerStandardStreams (worker, serviceId, workerId) {
1914
1799
  const binding = { name: serviceId }
1915
1800
 
package/lib/schema.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #! /usr/bin/env node
2
2
  'use strict'
3
3
 
4
- const { schemaComponents } = require('@platformatic/utils')
4
+ const { schemaComponents } = require('@platformatic/foundation')
5
5
 
6
6
  const pkg = require('../package.json')
7
7
 
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { join } = require('node:path')
4
- const { loadModule } = require('@platformatic/utils')
4
+ const { loadModule } = require('@platformatic/foundation')
5
5
  const MemoryCacheStore = require('@platformatic/undici-cache-memory')
6
6
  const { createRequire } = require('node:module')
7
7
 
package/lib/upgrade.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { abstractLogger } = require('@platformatic/utils')
3
+ const { abstractLogger } = require('@platformatic/foundation')
4
4
  const { join } = require('node:path')
5
5
  const { semgrator } = require('semgrator')
6
6
 
package/lib/utils.js CHANGED
@@ -20,14 +20,8 @@ function getRuntimeTmpDir (runtimeDir) {
20
20
  return join(platformaticTmpDir, runtimeDirHash)
21
21
  }
22
22
 
23
- function getRuntimeLogsDir (runtimeDir, runtimePID) {
24
- const runtimeTmpDir = getRuntimeTmpDir(runtimeDir)
25
- return join(runtimeTmpDir, runtimePID.toString(), 'logs')
26
- }
27
-
28
23
  module.exports = {
29
24
  getArrayDifference,
30
- getRuntimeLogsDir,
31
25
  getRuntimeTmpDir,
32
26
  getServiceUrl
33
27
  }
package/lib/worker/app.js CHANGED
@@ -11,11 +11,11 @@ const {
11
11
  FileWatcher,
12
12
  listRecognizedConfigurationFiles,
13
13
  loadConfigurationModule,
14
- loadConfiguration
15
- } = require('@platformatic/utils')
14
+ loadConfiguration,
15
+ ensureLoggableError
16
+ } = require('@platformatic/foundation')
16
17
  const { getGlobalDispatcher, setGlobalDispatcher } = require('undici')
17
18
  const debounce = require('debounce')
18
-
19
19
  const errors = require('../errors')
20
20
  const { getServiceUrl } = require('../utils')
21
21
 
@@ -129,10 +129,14 @@ class PlatformaticApp extends EventEmitter {
129
129
  this.#updateDispatcher()
130
130
  } catch (err) {
131
131
  if (err.validationErrors) {
132
- console.error('Validation errors:', err.validationErrors)
132
+ globalThis.platformatic.logger.error(
133
+ { err: ensureLoggableError(err.validationErrors) },
134
+ 'The application threw a validation error.'
135
+ )
136
+
133
137
  process.exit(1)
134
138
  } else {
135
- this.#logAndExit(err)
139
+ this.#logAndThrow(err)
136
140
  }
137
141
  }
138
142
  }
@@ -147,7 +151,7 @@ class PlatformaticApp extends EventEmitter {
147
151
  try {
148
152
  await this.stackable.init?.()
149
153
  } catch (err) {
150
- this.#logAndExit(err)
154
+ this.#logAndThrow(err)
151
155
  }
152
156
 
153
157
  if (this.#watch) {
@@ -181,8 +185,8 @@ class PlatformaticApp extends EventEmitter {
181
185
  this.emit('start')
182
186
  }
183
187
 
184
- async stop () {
185
- if (!this.#started || this.#starting) {
188
+ async stop (force = false) {
189
+ if (!force && (!this.#started || this.#starting)) {
186
190
  throw new errors.ApplicationNotStartedError()
187
191
  }
188
192
 
@@ -205,6 +209,18 @@ class PlatformaticApp extends EventEmitter {
205
209
  }
206
210
 
207
211
  async getMetrics ({ format }) {
212
+ const dispatcher = getGlobalDispatcher()
213
+ if (globalThis.platformatic?.onHttpStatsFree && dispatcher?.stats) {
214
+ for (const url in dispatcher.stats) {
215
+ const { free, connected, pending, queued, running, size } = dispatcher.stats[url]
216
+ globalThis.platformatic.onHttpStatsFree(url, free || 0)
217
+ globalThis.platformatic.onHttpStatsConnected(url, connected || 0)
218
+ globalThis.platformatic.onHttpStatsPending(url, pending || 0)
219
+ globalThis.platformatic.onHttpStatsQueued(url, queued || 0)
220
+ globalThis.platformatic.onHttpStatsRunning(url, running || 0)
221
+ globalThis.platformatic.onHttpStatsSize(url, size || 0)
222
+ }
223
+ }
208
224
  return this.stackable.getMetrics({ format })
209
225
  }
210
226
 
@@ -251,9 +267,9 @@ class PlatformaticApp extends EventEmitter {
251
267
  }
252
268
  }
253
269
 
254
- #logAndExit (err) {
255
- console.error(err)
256
- process.exit(1)
270
+ #logAndThrow (err) {
271
+ globalThis.platformatic.logger.error({ err: ensureLoggableError(err) }, 'The application threw an error.')
272
+ throw err
257
273
  }
258
274
 
259
275
  #updateDispatcher () {
package/lib/worker/itc.js CHANGED
@@ -4,6 +4,7 @@ const { once } = require('node:events')
4
4
  const { parentPort, workerData } = require('node:worker_threads')
5
5
 
6
6
  const { ITC } = require('@platformatic/itc')
7
+ const { ensureLoggableError } = require('@platformatic/foundation')
7
8
  const { Unpromise } = require('@watchable/unpromise')
8
9
 
9
10
  const errors = require('../errors')
@@ -56,7 +57,13 @@ async function waitEventFromITC (worker, event) {
56
57
  return safeHandleInITC(worker, () => once(worker[kITC], event))
57
58
  }
58
59
 
59
- function setupITC (app, service, dispatcher) {
60
+ async function closeITC (dispatcher, itc, messaging) {
61
+ await dispatcher.interceptor.close()
62
+ itc.close()
63
+ messaging.close()
64
+ }
65
+
66
+ function setupITC (app, service, dispatcher, sharedContext) {
60
67
  const messaging = new MessagingITC(app.appConfig.id, workerData.config)
61
68
 
62
69
  Object.assign(globalThis.platformatic ?? {}, {
@@ -79,7 +86,14 @@ function setupITC (app, service, dispatcher) {
79
86
  // This gives a chance to a stackable to perform custom logic
80
87
  globalThis.platformatic.events.emit('start')
81
88
 
82
- await app.start()
89
+ try {
90
+ await app.start()
91
+ } catch (e) {
92
+ await app.stop(true)
93
+ await closeITC(dispatcher, itc, messaging)
94
+
95
+ throw ensureLoggableError(e)
96
+ }
83
97
  }
84
98
 
85
99
  if (service.entrypoint) {
@@ -104,9 +118,7 @@ function setupITC (app, service, dispatcher) {
104
118
  await app.stop()
105
119
  }
106
120
 
107
- await dispatcher.interceptor.close()
108
- itc.close()
109
- messaging.close()
121
+ await closeITC(dispatcher, itc, messaging)
110
122
  },
111
123
 
112
124
  async build () {
@@ -210,6 +222,10 @@ function setupITC (app, service, dispatcher) {
210
222
  }
211
223
  },
212
224
 
225
+ setSharedContext (context) {
226
+ sharedContext._set(context)
227
+ },
228
+
213
229
  saveMessagingChannel (channel) {
214
230
  messaging.addSource(channel)
215
231
  }
@@ -16,12 +16,13 @@ const {
16
16
  getPrivateSymbol,
17
17
  buildPinoFormatters,
18
18
  buildPinoTimestamp
19
- } = require('@platformatic/utils')
19
+ } = require('@platformatic/foundation')
20
20
  const dotenv = require('dotenv')
21
21
  const pino = require('pino')
22
22
  const { fetch } = require('undici')
23
23
 
24
24
  const { PlatformaticApp } = require('./app')
25
+ const { SharedContext } = require('./shared-context')
25
26
  const { setupITC } = require('./itc')
26
27
  const { setDispatcher } = require('./interceptors')
27
28
  const { kId, kITC, kStderrMarker } = require('./symbols')
@@ -64,6 +65,11 @@ function patchLogging () {
64
65
  }
65
66
 
66
67
  function createLogger () {
68
+ // Do not propagate runtime transports to the worker
69
+ if (workerData.config.logger) {
70
+ delete workerData.config.logger.transport
71
+ }
72
+
67
73
  const pinoOptions = {
68
74
  level: 'trace',
69
75
  name: workerData.serviceConfig.id,
@@ -187,8 +193,15 @@ async function main () {
187
193
  }
188
194
  }
189
195
 
196
+ const sharedContext = new SharedContext()
197
+ // Limit the amount of methods a user can call
198
+ globalThis.platformatic.sharedContext = {
199
+ get: () => sharedContext.get(),
200
+ update: (...args) => sharedContext.update(...args)
201
+ }
202
+
190
203
  // Setup interaction with parent port
191
- const itc = setupITC(app, service, threadDispatcher)
204
+ const itc = setupITC(app, service, threadDispatcher, sharedContext)
192
205
  globalThis[kITC] = itc
193
206
 
194
207
  // Get the dependencies
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { executeWithTimeout, kTimeout } = require('@platformatic/utils')
3
+ const { executeWithTimeout, kTimeout } = require('@platformatic/foundation')
4
4
  const { ITC, generateResponse, sanitize } = require('@platformatic/itc')
5
5
  const errors = require('../errors')
6
6
  const { RoundRobinMap } = require('./round-robin-map')
@@ -0,0 +1,26 @@
1
+ 'use strict'
2
+
3
+ const { kITC } = require('./symbols')
4
+
5
+ class SharedContext {
6
+ constructor () {
7
+ this.sharedContext = null
8
+ }
9
+
10
+ update (context, options = {}) {
11
+ return globalThis[kITC].send('updateSharedContext', { ...options, context })
12
+ }
13
+
14
+ get () {
15
+ if (this.sharedContext === null) {
16
+ this.sharedContext = globalThis[kITC].send('getSharedContext')
17
+ }
18
+ return this.sharedContext
19
+ }
20
+
21
+ _set (context) {
22
+ this.sharedContext = context
23
+ }
24
+ }
25
+
26
+ module.exports = { SharedContext }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "3.0.0-alpha.1",
3
+ "version": "3.0.0-alpha.2",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "author": "Platformatic Inc. <oss@platformatic.dev> (https://platformatic.dev)",
@@ -33,58 +33,57 @@
33
33
  "typescript": "^5.5.4",
34
34
  "undici-oidc-interceptor": "^0.5.0",
35
35
  "why-is-node-running": "^2.2.2",
36
- "@platformatic/composer": "3.0.0-alpha.1",
37
- "@platformatic/db": "3.0.0-alpha.1",
38
- "@platformatic/node": "3.0.0-alpha.1",
39
- "@platformatic/service": "3.0.0-alpha.1",
40
- "@platformatic/sql-graphql": "3.0.0-alpha.1",
41
- "@platformatic/sql-mapper": "3.0.0-alpha.1"
36
+ "@platformatic/composer": "3.0.0-alpha.2",
37
+ "@platformatic/db": "3.0.0-alpha.2",
38
+ "@platformatic/node": "3.0.0-alpha.2",
39
+ "@platformatic/service": "3.0.0-alpha.2",
40
+ "@platformatic/sql-mapper": "3.0.0-alpha.2",
41
+ "@platformatic/sql-graphql": "3.0.0-alpha.2"
42
42
  },
43
43
  "dependencies": {
44
44
  "@fastify/accepts": "^5.0.0",
45
45
  "@fastify/error": "^4.0.0",
46
46
  "@fastify/websocket": "^11.0.0",
47
47
  "@hapi/topo": "^6.0.2",
48
- "@opentelemetry/api": "^1.8.0",
48
+ "@opentelemetry/api": "^1.9.0",
49
49
  "@platformatic/undici-cache-memory": "^0.8.1",
50
50
  "@watchable/unpromise": "^1.0.2",
51
51
  "change-case-all": "^2.1.0",
52
52
  "close-with-grace": "^2.0.0",
53
- "commist": "^3.2.0",
53
+ "colorette": "^2.0.20",
54
54
  "cron": "^4.1.0",
55
55
  "debounce": "^2.0.0",
56
56
  "dotenv": "^16.4.5",
57
57
  "dotenv-tool": "^0.1.1",
58
- "es-main": "^1.3.0",
59
58
  "fastest-levenshtein": "^1.0.16",
60
59
  "fastify": "^5.0.0",
61
60
  "graphql": "^16.8.1",
62
61
  "help-me": "^5.0.0",
63
62
  "minimist": "^1.2.8",
64
- "pino": "^9.0.0",
63
+ "pino": "^9.9.0",
65
64
  "pino-pretty": "^13.0.0",
66
- "pino-roll": "^2.0.0",
67
65
  "prom-client": "^15.1.2",
68
66
  "semgrator": "^0.3.0",
69
67
  "sonic-boom": "^4.2.0",
70
- "tail-file-stream": "^0.2.0",
71
68
  "undici": "^7.0.0",
72
69
  "undici-thread-interceptor": "^0.14.0",
73
70
  "ws": "^8.16.0",
74
- "@platformatic/metrics": "3.0.0-alpha.1",
75
- "@platformatic/basic": "3.0.0-alpha.1",
76
- "@platformatic/generators": "3.0.0-alpha.1",
77
- "@platformatic/itc": "3.0.0-alpha.1",
78
- "@platformatic/telemetry": "3.0.0-alpha.1",
79
- "@platformatic/ts-compiler": "3.0.0-alpha.1",
80
- "@platformatic/utils": "3.0.0-alpha.1"
71
+ "@platformatic/basic": "3.0.0-alpha.2",
72
+ "@platformatic/generators": "3.0.0-alpha.2",
73
+ "@platformatic/foundation": "3.0.0-alpha.2",
74
+ "@platformatic/itc": "3.0.0-alpha.2",
75
+ "@platformatic/metrics": "3.0.0-alpha.2",
76
+ "@platformatic/telemetry": "3.0.0-alpha.2"
77
+ },
78
+ "engines": {
79
+ "node": ">=22.18.0"
81
80
  },
82
81
  "scripts": {
83
82
  "test": "pnpm run lint && borp --concurrency=1 --timeout=1200000",
84
83
  "test:main": "borp --concurrency=1 --timeout=1200000 test/*.test.js test/*.test.mjs test/versions/*.test.js test/versions/*.test.mjs",
85
84
  "test:api": "borp --concurrency=1 --timeout=1200000 test/api/*.test.js test/api/*.test.mjs test/management-api/*.test.js test/management-api/*.test.mjs",
86
85
  "test:cli": "borp --concurrency=1 --timeout=1200000 test/cli/*.test.js test/cli/*.test.mjs test/cli/**/*.test.js test/cli/**/*.test.mjs",
87
- "test:start": "borp --concurrency=1 --timeout=1200000 test/start/*.test.js test/start/*.test.mjs",
86
+ "test:start": "borp --concurrency=1 --reporter=tap --timeout=1200000 test/start/*.test.js test/start/*.test.mjs",
88
87
  "test:multiple-workers": "borp --concurrency=1 --timeout=1200000 test/multiple-workers/*.test.js test/multiple-workers/*.test.mjs",
89
88
  "coverage": "pnpm run lint && borp -X fixtures -X test -C --concurrency=1 --timeout=1200000 ",
90
89
  "gen-schema": "node lib/schema.js > schema.json",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.0.0-alpha.1.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.0.0-alpha.2.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Runtime Config",
5
5
  "type": "object",