@platformatic/runtime 3.39.0 → 3.40.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.
package/config.d.ts CHANGED
@@ -90,6 +90,12 @@ export type PlatformaticRuntimeConfig = {
90
90
  */
91
91
  directory?: string;
92
92
  };
93
+ management?:
94
+ | boolean
95
+ | {
96
+ enabled?: boolean;
97
+ operations?: string[];
98
+ };
93
99
  };
94
100
  };
95
101
  };
@@ -310,6 +316,12 @@ export type PlatformaticRuntimeConfig = {
310
316
  */
311
317
  socket?: string;
312
318
  };
319
+ management?:
320
+ | boolean
321
+ | {
322
+ enabled?: boolean;
323
+ operations?: string[];
324
+ };
313
325
  metrics?:
314
326
  | boolean
315
327
  | {
package/index.d.ts CHANGED
@@ -65,6 +65,77 @@ export module symbols {
65
65
  export declare const kWorkersBroadcast: unique symbol
66
66
  }
67
67
 
68
+ export interface InjectParams {
69
+ method?: string
70
+ url: string
71
+ headers?: Record<string, string>
72
+ query?: Record<string, string>
73
+ body?: unknown
74
+ }
75
+
76
+ export interface InjectResponse {
77
+ statusCode: number
78
+ statusMessage: string
79
+ headers: Record<string, string>
80
+ body: string
81
+ payload: string
82
+ rawPayload: ArrayBuffer
83
+ }
84
+
85
+ export interface ApplicationDetails {
86
+ id: string
87
+ type?: string
88
+ config?: string
89
+ path?: string
90
+ status?: string
91
+ dependencies?: string[]
92
+ version?: string
93
+ localUrl?: string
94
+ entrypoint?: boolean
95
+ sourceMaps?: boolean
96
+ workers?: number
97
+ url?: string | null
98
+ }
99
+
100
+ export interface RuntimeMetadata {
101
+ pid: number
102
+ cwd: string
103
+ argv: string[]
104
+ uptimeSeconds: number
105
+ execPath: string
106
+ nodeVersion: string
107
+ projectDir: string
108
+ packageName: string | null
109
+ packageVersion: string | null
110
+ url: string | null
111
+ platformaticVersion: string
112
+ }
113
+
114
+ export declare class ManagementClient {
115
+ constructor (allowedOperations?: string[])
116
+
117
+ getRuntimeStatus (): Promise<string>
118
+ getRuntimeMetadata (): Promise<RuntimeMetadata>
119
+ getRuntimeConfig (): Promise<Record<string, unknown>>
120
+ getRuntimeEnv (): Promise<Record<string, string>>
121
+ getApplicationsIds (): Promise<string[]>
122
+ getApplications (): Promise<{ entrypoint: string; production: boolean; applications: ApplicationDetails[] }>
123
+ getWorkers (): Promise<Record<string, unknown>>
124
+ getApplicationDetails (id: string): Promise<ApplicationDetails>
125
+ getApplicationConfig (id: string): Promise<Record<string, unknown>>
126
+ getApplicationEnv (id: string): Promise<Record<string, string>>
127
+ getApplicationOpenapiSchema (id: string): Promise<unknown>
128
+ getApplicationGraphqlSchema (id: string): Promise<unknown>
129
+ getMetrics (format?: string): Promise<{ metrics: unknown }>
130
+ startApplication (id: string): Promise<void>
131
+ stopApplication (id: string): Promise<void>
132
+ restartApplication (id: string): Promise<void>
133
+ restart (applications?: string[]): Promise<string>
134
+ addApplications (applications: unknown[], start?: boolean): Promise<ApplicationDetails[]>
135
+ removeApplications (ids: string[]): Promise<ApplicationDetails[]>
136
+ inject (id: string, injectParams: InjectParams): Promise<InjectResponse>
137
+ }
138
+
68
139
  export class Generator extends BaseGenerator {}
69
140
 
70
141
  export class WrappedGenerator extends BaseGenerator {}
package/index.js CHANGED
@@ -82,14 +82,22 @@ export async function loadConfiguration (configOrRoot, sourceOrConfig, context)
82
82
  })
83
83
  }
84
84
 
85
- export async function loadApplicationsCommands () {
85
+ export async function loadApplicationsCommands (executableName = '') {
86
86
  const applications = {}
87
87
  const commands = {}
88
88
  const help = {}
89
89
 
90
90
  let config
91
91
  try {
92
- const file = await findRuntimeConfigurationFile(abstractLogger, process.cwd(), null, false, false)
92
+ const file = await findRuntimeConfigurationFile(
93
+ abstractLogger,
94
+ process.cwd(),
95
+ null,
96
+ false,
97
+ false,
98
+ true,
99
+ executableName
100
+ )
93
101
 
94
102
  /* c8 ignore next 3 - Hard to test */
95
103
  if (!file) {
package/lib/config.js CHANGED
@@ -281,6 +281,10 @@ export async function prepareApplication (config, application, defaultWorkers) {
281
281
  application.watch = config.watch
282
282
  }
283
283
 
284
+ if (typeof application.management === 'undefined' && config.management) {
285
+ application.management = config.management
286
+ }
287
+
284
288
  return application
285
289
  }
286
290
 
package/lib/logger.js CHANGED
@@ -1,4 +1,4 @@
1
- import { buildPinoFormatters, buildPinoTimestamp, usePrettyPrint } from '@platformatic/foundation'
1
+ import { buildPinoFormatters, buildPinoTimestamp } from '@platformatic/foundation'
2
2
  import { isatty } from 'node:tty'
3
3
  import pino from 'pino'
4
4
  import pretty from 'pino-pretty'
@@ -158,7 +158,7 @@ export async function createLogger (config) {
158
158
 
159
159
  if (config.logger.transport) {
160
160
  cliStream = pino.transport(config.logger.transport)
161
- } else if ((process.env.FORCE_TTY || isatty(1)) && usePrettyPrint()) {
161
+ } else if ((process.env.FORCE_TTY || isatty(1)) && process.env.PLT_PRETTY_PRINT !== 'false') {
162
162
  cliStream = createPrettifier(context)
163
163
  } else {
164
164
  cliStream = pino.destination(1)
@@ -0,0 +1,139 @@
1
+ import {
2
+ applications as applicationSchema,
3
+ kMetadata,
4
+ validate
5
+ } from '@platformatic/foundation'
6
+ import { prepareApplication } from './config.js'
7
+
8
+ const allOperations = [
9
+ 'getRuntimeStatus',
10
+ 'getRuntimeMetadata',
11
+ 'getRuntimeConfig',
12
+ 'getRuntimeEnv',
13
+ 'getApplicationsIds',
14
+ 'getApplications',
15
+ 'getWorkers',
16
+ 'getApplicationDetails',
17
+ 'getApplicationConfig',
18
+ 'getApplicationEnv',
19
+ 'getApplicationOpenapiSchema',
20
+ 'getApplicationGraphqlSchema',
21
+ 'getMetrics',
22
+ 'startApplication',
23
+ 'stopApplication',
24
+ 'restartApplication',
25
+ 'restart',
26
+ 'addApplications',
27
+ 'removeApplications',
28
+ 'inject'
29
+ ]
30
+
31
+ export function createManagementHandlers (runtime, allowedOperations) {
32
+ const allowed = allowedOperations
33
+ ? new Set(allowedOperations)
34
+ : new Set(allOperations)
35
+
36
+ const handlers = new Map()
37
+
38
+ function register (name, fn) {
39
+ if (allowed.has(name)) {
40
+ handlers.set('management:' + name, fn)
41
+ }
42
+ }
43
+
44
+ // Read operations - no arguments
45
+ register('getRuntimeStatus', async () => {
46
+ return runtime.getRuntimeStatus()
47
+ })
48
+
49
+ register('getRuntimeMetadata', async () => {
50
+ return runtime.getRuntimeMetadata()
51
+ })
52
+
53
+ register('getRuntimeConfig', async () => {
54
+ return runtime.getRuntimeConfig()
55
+ })
56
+
57
+ register('getRuntimeEnv', async () => {
58
+ return runtime.getRuntimeEnv()
59
+ })
60
+
61
+ register('getApplicationsIds', async () => {
62
+ return runtime.getApplicationsIds()
63
+ })
64
+
65
+ register('getApplications', async () => {
66
+ return runtime.getApplications()
67
+ })
68
+
69
+ register('getWorkers', async () => {
70
+ return runtime.getWorkers()
71
+ })
72
+
73
+ // Read operations - with application id
74
+ register('getApplicationDetails', async ({ id }) => {
75
+ return runtime.getApplicationDetails(id)
76
+ })
77
+
78
+ register('getApplicationConfig', async ({ id }) => {
79
+ return runtime.getApplicationConfig(id)
80
+ })
81
+
82
+ register('getApplicationEnv', async ({ id }) => {
83
+ return runtime.getApplicationEnv(id)
84
+ })
85
+
86
+ register('getApplicationOpenapiSchema', async ({ id }) => {
87
+ return runtime.getApplicationOpenapiSchema(id)
88
+ })
89
+
90
+ register('getApplicationGraphqlSchema', async ({ id }) => {
91
+ return runtime.getApplicationGraphqlSchema(id)
92
+ })
93
+
94
+ // Metrics
95
+ register('getMetrics', async ({ format } = {}) => {
96
+ return runtime.getMetrics(format)
97
+ })
98
+
99
+ // Write operations
100
+ register('startApplication', async ({ id }) => {
101
+ await runtime.startApplication(id)
102
+ })
103
+
104
+ register('stopApplication', async ({ id }) => {
105
+ await runtime.stopApplication(id)
106
+ })
107
+
108
+ register('restartApplication', async ({ id }) => {
109
+ await runtime.restartApplication(id)
110
+ })
111
+
112
+ register('restart', async ({ applications } = {}) => {
113
+ return runtime.restart(applications)
114
+ })
115
+
116
+ register('addApplications', async ({ applications, start }) => {
117
+ const config = runtime.getRuntimeConfig(true)
118
+
119
+ validate(applicationSchema, applications, {}, true, config[kMetadata].root)
120
+
121
+ for (let i = 0; i < applications.length; i++) {
122
+ applications[i] = await prepareApplication(config, applications[i], config.workers)
123
+ }
124
+
125
+ return runtime.addApplications(applications, start)
126
+ })
127
+
128
+ register('removeApplications', async ({ ids }) => {
129
+ return runtime.removeApplications(ids)
130
+ })
131
+
132
+ register('inject', async ({ id, injectParams }) => {
133
+ return runtime.inject(id, injectParams)
134
+ })
135
+
136
+ return handlers
137
+ }
138
+
139
+ export { allOperations }
package/lib/runtime.js CHANGED
@@ -42,6 +42,7 @@ import {
42
42
  } from './errors.js'
43
43
  import { abstractLogger, createLogger } from './logger.js'
44
44
  import { startManagementApi } from './management-api.js'
45
+ import { createManagementHandlers } from './management-handlers.js'
45
46
  import { createChannelCreationHook } from './policies.js'
46
47
  import { startPrometheusServer } from './prom-server.js'
47
48
  import { startScheduler } from './scheduler.js'
@@ -1795,6 +1796,25 @@ export class Runtime extends EventEmitter {
1795
1796
  port: worker,
1796
1797
  handlers: this.#workerITCHandlers
1797
1798
  })
1799
+
1800
+ // Register management ITC handlers for privileged applications
1801
+ if (applicationConfig.management) {
1802
+ const mgmtEnabled = typeof applicationConfig.management === 'boolean'
1803
+ ? applicationConfig.management
1804
+ : applicationConfig.management.enabled !== false
1805
+
1806
+ if (mgmtEnabled) {
1807
+ const allowedOps = typeof applicationConfig.management === 'object'
1808
+ ? applicationConfig.management.operations
1809
+ : undefined
1810
+
1811
+ const handlers = createManagementHandlers(this, allowedOps)
1812
+ for (const [name, handler] of handlers) {
1813
+ worker[kITC].handle(name, handler)
1814
+ }
1815
+ }
1816
+ }
1817
+
1798
1818
  worker[kITC].listen()
1799
1819
 
1800
1820
  // Forward events from the worker
@@ -19,6 +19,7 @@ export async function setDispatcher (runtimeConfig) {
19
19
  }
20
20
 
21
21
  let userInterceptors = []
22
+
22
23
  if (Array.isArray(runtimeConfig.undici?.interceptors)) {
23
24
  const _require = createRequire(join(workerData.dirname, 'package.json'))
24
25
  userInterceptors = await loadInterceptors(_require, runtimeConfig.undici.interceptors)
@@ -104,7 +105,7 @@ async function loadInterceptor (_require, interceptorConfig, key) {
104
105
 
105
106
  const url = pathToFileURL(_require.resolve(module))
106
107
  const createInterceptor = (await import(url)).default
107
- const interceptor = createInterceptor(options)
108
+ const interceptor = await createInterceptor(options)
108
109
 
109
110
  const { updatableInterceptor, updateInterceptor } = createUpdatableInterceptor(interceptor)
110
111
 
@@ -275,6 +275,21 @@ async function main () {
275
275
  globalThis[kITC] = itc
276
276
  globalThis.platformatic.itc = itc
277
277
 
278
+ // Setup management client for privileged applications
279
+ if (applicationConfig.management) {
280
+ const mgmtEnabled = typeof applicationConfig.management === 'boolean'
281
+ ? applicationConfig.management
282
+ : applicationConfig.management.enabled !== false
283
+
284
+ if (mgmtEnabled) {
285
+ const { ManagementClient } = await import('./management.js')
286
+ const ops = typeof applicationConfig.management === 'object'
287
+ ? applicationConfig.management.operations
288
+ : undefined
289
+ globalThis.platformatic.management = new ManagementClient(ops)
290
+ }
291
+ }
292
+
278
293
  initHealthSignalsApi({
279
294
  workerId: workerData.worker.id,
280
295
  applicationId: applicationConfig.id
@@ -0,0 +1,99 @@
1
+ import { kITC } from './symbols.js'
2
+
3
+ export class ManagementClient {
4
+ #allowedOperations
5
+
6
+ constructor (allowedOperations) {
7
+ this.#allowedOperations = allowedOperations
8
+ ? new Set(allowedOperations)
9
+ : null
10
+ }
11
+
12
+ #send (operation, data) {
13
+ if (this.#allowedOperations && !this.#allowedOperations.has(operation)) {
14
+ throw new Error(`Operation "${operation}" is not allowed`)
15
+ }
16
+
17
+ return globalThis[kITC].send('management:' + operation, data)
18
+ }
19
+
20
+ getRuntimeStatus () {
21
+ return this.#send('getRuntimeStatus')
22
+ }
23
+
24
+ getRuntimeMetadata () {
25
+ return this.#send('getRuntimeMetadata')
26
+ }
27
+
28
+ getRuntimeConfig () {
29
+ return this.#send('getRuntimeConfig')
30
+ }
31
+
32
+ getRuntimeEnv () {
33
+ return this.#send('getRuntimeEnv')
34
+ }
35
+
36
+ getApplicationsIds () {
37
+ return this.#send('getApplicationsIds')
38
+ }
39
+
40
+ getApplications () {
41
+ return this.#send('getApplications')
42
+ }
43
+
44
+ getWorkers () {
45
+ return this.#send('getWorkers')
46
+ }
47
+
48
+ getApplicationDetails (id) {
49
+ return this.#send('getApplicationDetails', { id })
50
+ }
51
+
52
+ getApplicationConfig (id) {
53
+ return this.#send('getApplicationConfig', { id })
54
+ }
55
+
56
+ getApplicationEnv (id) {
57
+ return this.#send('getApplicationEnv', { id })
58
+ }
59
+
60
+ getApplicationOpenapiSchema (id) {
61
+ return this.#send('getApplicationOpenapiSchema', { id })
62
+ }
63
+
64
+ getApplicationGraphqlSchema (id) {
65
+ return this.#send('getApplicationGraphqlSchema', { id })
66
+ }
67
+
68
+ getMetrics (format) {
69
+ return this.#send('getMetrics', { format })
70
+ }
71
+
72
+ startApplication (id) {
73
+ return this.#send('startApplication', { id })
74
+ }
75
+
76
+ stopApplication (id) {
77
+ return this.#send('stopApplication', { id })
78
+ }
79
+
80
+ restartApplication (id) {
81
+ return this.#send('restartApplication', { id })
82
+ }
83
+
84
+ restart (applications) {
85
+ return this.#send('restart', { applications })
86
+ }
87
+
88
+ addApplications (applications, start) {
89
+ return this.#send('addApplications', { applications, start })
90
+ }
91
+
92
+ removeApplications (ids) {
93
+ return this.#send('removeApplications', { ids })
94
+ }
95
+
96
+ inject (id, injectParams) {
97
+ return this.#send('inject', { id, injectParams })
98
+ }
99
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "3.39.0",
3
+ "version": "3.40.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -35,14 +35,14 @@
35
35
  "typescript": "^5.5.4",
36
36
  "undici-oidc-interceptor": "^0.5.0",
37
37
  "why-is-node-running": "^2.2.2",
38
- "@platformatic/composer": "3.39.0",
39
- "@platformatic/db": "3.39.0",
40
- "@platformatic/service": "3.39.0",
41
- "@platformatic/node": "3.39.0",
42
- "@platformatic/sql-graphql": "3.39.0",
43
- "@platformatic/gateway": "3.39.0",
44
- "@platformatic/wattpm-pprof-capture": "3.39.0",
45
- "@platformatic/sql-mapper": "3.39.0"
38
+ "@platformatic/composer": "3.40.0",
39
+ "@platformatic/service": "3.40.0",
40
+ "@platformatic/gateway": "3.40.0",
41
+ "@platformatic/db": "3.40.0",
42
+ "@platformatic/sql-mapper": "3.40.0",
43
+ "@platformatic/wattpm-pprof-capture": "3.40.0",
44
+ "@platformatic/sql-graphql": "3.40.0",
45
+ "@platformatic/node": "3.40.0"
46
46
  },
47
47
  "dependencies": {
48
48
  "@fastify/accepts": "^5.0.0",
@@ -72,12 +72,12 @@
72
72
  "undici": "^7.0.0",
73
73
  "undici-thread-interceptor": "^1.3.1",
74
74
  "ws": "^8.16.0",
75
- "@platformatic/basic": "3.39.0",
76
- "@platformatic/generators": "3.39.0",
77
- "@platformatic/itc": "3.39.0",
78
- "@platformatic/telemetry": "3.39.0",
79
- "@platformatic/foundation": "3.39.0",
80
- "@platformatic/metrics": "3.39.0"
75
+ "@platformatic/foundation": "3.40.0",
76
+ "@platformatic/generators": "3.40.0",
77
+ "@platformatic/basic": "3.40.0",
78
+ "@platformatic/itc": "3.40.0",
79
+ "@platformatic/metrics": "3.40.0",
80
+ "@platformatic/telemetry": "3.40.0"
81
81
  },
82
82
  "engines": {
83
83
  "node": ">=22.19.0"
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.39.0.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.40.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Runtime Config",
5
5
  "type": "object",
@@ -354,6 +354,29 @@
354
354
  "additionalProperties": false
355
355
  }
356
356
  ]
357
+ },
358
+ "management": {
359
+ "anyOf": [
360
+ {
361
+ "type": "boolean"
362
+ },
363
+ {
364
+ "type": "object",
365
+ "properties": {
366
+ "enabled": {
367
+ "type": "boolean",
368
+ "default": true
369
+ },
370
+ "operations": {
371
+ "type": "array",
372
+ "items": {
373
+ "type": "string"
374
+ }
375
+ }
376
+ },
377
+ "additionalProperties": false
378
+ }
379
+ ]
357
380
  }
358
381
  }
359
382
  }
@@ -690,6 +713,29 @@
690
713
  "additionalProperties": false
691
714
  }
692
715
  ]
716
+ },
717
+ "management": {
718
+ "anyOf": [
719
+ {
720
+ "type": "boolean"
721
+ },
722
+ {
723
+ "type": "object",
724
+ "properties": {
725
+ "enabled": {
726
+ "type": "boolean",
727
+ "default": true
728
+ },
729
+ "operations": {
730
+ "type": "array",
731
+ "items": {
732
+ "type": "string"
733
+ }
734
+ }
735
+ },
736
+ "additionalProperties": false
737
+ }
738
+ ]
693
739
  }
694
740
  }
695
741
  }
@@ -1024,6 +1070,29 @@
1024
1070
  "additionalProperties": false
1025
1071
  }
1026
1072
  ]
1073
+ },
1074
+ "management": {
1075
+ "anyOf": [
1076
+ {
1077
+ "type": "boolean"
1078
+ },
1079
+ {
1080
+ "type": "object",
1081
+ "properties": {
1082
+ "enabled": {
1083
+ "type": "boolean",
1084
+ "default": true
1085
+ },
1086
+ "operations": {
1087
+ "type": "array",
1088
+ "items": {
1089
+ "type": "string"
1090
+ }
1091
+ }
1092
+ },
1093
+ "additionalProperties": false
1094
+ }
1095
+ ]
1027
1096
  }
1028
1097
  }
1029
1098
  }
@@ -1358,6 +1427,29 @@
1358
1427
  "additionalProperties": false
1359
1428
  }
1360
1429
  ]
1430
+ },
1431
+ "management": {
1432
+ "anyOf": [
1433
+ {
1434
+ "type": "boolean"
1435
+ },
1436
+ {
1437
+ "type": "object",
1438
+ "properties": {
1439
+ "enabled": {
1440
+ "type": "boolean",
1441
+ "default": true
1442
+ },
1443
+ "operations": {
1444
+ "type": "array",
1445
+ "items": {
1446
+ "type": "string"
1447
+ }
1448
+ }
1449
+ },
1450
+ "additionalProperties": false
1451
+ }
1452
+ ]
1361
1453
  }
1362
1454
  }
1363
1455
  }
@@ -2101,6 +2193,29 @@
2101
2193
  ],
2102
2194
  "default": true
2103
2195
  },
2196
+ "management": {
2197
+ "anyOf": [
2198
+ {
2199
+ "type": "boolean"
2200
+ },
2201
+ {
2202
+ "type": "object",
2203
+ "properties": {
2204
+ "enabled": {
2205
+ "type": "boolean",
2206
+ "default": true
2207
+ },
2208
+ "operations": {
2209
+ "type": "array",
2210
+ "items": {
2211
+ "type": "string"
2212
+ }
2213
+ }
2214
+ },
2215
+ "additionalProperties": false
2216
+ }
2217
+ ]
2218
+ },
2104
2219
  "metrics": {
2105
2220
  "anyOf": [
2106
2221
  {