@platformatic/runtime 2.74.3 → 2.75.0-alpha.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @platformatic/runtime
2
2
 
3
- Check out the full documentation for Platformatic Runtime on [our website](https://docs.platformatic.dev/docs/runtime/overview).
3
+ Check out the full documentation for Platformatic Runtime on [our website](https://docs.platformatic.dev/docs/reference/runtime/overview).
4
4
 
5
5
  ## Install
6
6
 
package/config.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * and run json-schema-to-typescript to regenerate this file.
6
6
  */
7
7
 
8
- export type HttpsSchemasPlatformaticDevPlatformaticRuntime2743Json = {
8
+ export type HttpsSchemasPlatformaticDevPlatformaticRuntime2750Alpha0Json = {
9
9
  [k: string]: unknown;
10
10
  } & {
11
11
  $schema?: string;
package/lib/config.js CHANGED
@@ -16,6 +16,33 @@ const { schema } = require('./schema')
16
16
  const upgrade = require('./upgrade')
17
17
  const { parseArgs } = require('node:util')
18
18
 
19
+ function autoDetectPprofCapture (config) {
20
+ // Check if package is installed
21
+ try {
22
+ let pprofCapturePath
23
+ try {
24
+ pprofCapturePath = require.resolve('@platformatic/watt-pprof-capture')
25
+ } catch (err) {
26
+ pprofCapturePath = require.resolve('../../watt-pprof-capture/index.js')
27
+ }
28
+
29
+ // Add to preload if not already present
30
+ if (!config.preload) {
31
+ config.preload = []
32
+ } else if (typeof config.preload === 'string') {
33
+ config.preload = [config.preload]
34
+ }
35
+
36
+ if (!config.preload.includes(pprofCapturePath)) {
37
+ config.preload.push(pprofCapturePath)
38
+ }
39
+ } catch (err) {
40
+ // Package not installed, skip silently
41
+ }
42
+
43
+ return config
44
+ }
45
+
19
46
  async function _transformConfig (configManager, args) {
20
47
  const config = configManager.current
21
48
 
@@ -90,6 +117,41 @@ async function _transformConfig (configManager, args) {
90
117
 
91
118
  let hasValidEntrypoint = false
92
119
 
120
+ // Validate and coerce workers values early to avoid runtime hangs when invalid
121
+ function coercePositiveInteger (value) {
122
+ if (typeof value === 'number') {
123
+ if (!Number.isInteger(value) || value < 1) return null
124
+ return value
125
+ }
126
+ if (typeof value === 'string') {
127
+ // Trim to handle accidental spaces
128
+ const trimmed = value.trim()
129
+ if (trimmed.length === 0) return null
130
+ const num = Number(trimmed)
131
+ if (!Number.isFinite(num) || !Number.isInteger(num) || num < 1) return null
132
+ return num
133
+ }
134
+ return null
135
+ }
136
+
137
+ function raiseInvalidWorkersError (location, received, hint) {
138
+ const extra = hint ? ` (${hint})` : ''
139
+ throw new errors.InvalidArgumentError(
140
+ `${location} workers must be a positive integer; received "${received}"${extra}`
141
+ )
142
+ }
143
+
144
+ // Root-level workers
145
+ if (typeof config.workers !== 'undefined') {
146
+ const coerced = coercePositiveInteger(config.workers)
147
+ if (coerced === null) {
148
+ const raw = configManager.currentRaw?.workers
149
+ const hint = typeof raw === 'string' && /\{.*\}/.test(raw) ? 'check your environment variable' : ''
150
+ raiseInvalidWorkersError('Runtime', config.workers, hint)
151
+ }
152
+ config.workers = coerced
153
+ }
154
+
93
155
  for (let i = 0; i < services.length; ++i) {
94
156
  const service = services[i]
95
157
 
@@ -156,6 +218,17 @@ async function _transformConfig (configManager, args) {
156
218
  }
157
219
  }
158
220
 
221
+ // Validate and coerce per-service workers
222
+ if (typeof service.workers !== 'undefined') {
223
+ const coerced = coercePositiveInteger(service.workers)
224
+ if (coerced === null) {
225
+ const raw = configManager.currentRaw?.services?.[i]?.workers
226
+ const hint = typeof raw === 'string' && /\{.*\}/.test(raw) ? 'check your environment variable' : ''
227
+ raiseInvalidWorkersError(`Service "${service.id}"`, service.workers, hint)
228
+ }
229
+ service.workers = coerced
230
+ }
231
+
159
232
  service.entrypoint = service.id === config.entrypoint
160
233
  service.dependencies = []
161
234
  service.localServiceEnvVars = new Map()
@@ -226,6 +299,9 @@ async function _transformConfig (configManager, args) {
226
299
  configManager.current.restartOnError = 0
227
300
  }
228
301
  }
302
+
303
+ // Auto-detect and add pprof capture if available
304
+ autoDetectPprofCapture(configManager.current)
229
305
  }
230
306
 
231
307
  async function platformaticRuntime () {
@@ -358,5 +434,6 @@ function parseInspectorOptions (configManager) {
358
434
  module.exports = {
359
435
  parseInspectorOptions,
360
436
  platformaticRuntime,
361
- wrapConfigInRuntimeConfig
437
+ wrapConfigInRuntimeConfig,
438
+ autoDetectPprofCapture
362
439
  }
@@ -1,11 +1,11 @@
1
1
  # Platformatic Runtime API
2
2
 
3
- This is a generated [Platformatic Runtime](https://docs.platformatic.dev/docs/runtime/overview) application.
3
+ This is a generated [Platformatic Runtime](https://docs.platformatic.dev/docs/reference/runtime/overview) application.
4
4
 
5
5
  ## Requirements
6
6
 
7
- Platformatic supports macOS, Linux and Windows ([WSL](https://docs.microsoft.com/windows/wsl/) recommended).
8
- You'll need to have [Node.js](https://nodejs.org/) >= v18.8.0 or >= v20.6.0
7
+ Platformatic supports macOS, Linux and Windows ([WSL](https://learn.microsoft.com/en-us/windows/wsl/) recommended).
8
+ You'll need to have [Node.js](https://nodejs.org/) (v20.16.0+ or v22.3.0+)
9
9
 
10
10
  ## Setup
11
11
 
@@ -27,7 +27,7 @@ const wrappableProperties = {
27
27
  }
28
28
 
29
29
  const engines = {
30
- node: '^18.8.0 || >=20.6.0'
30
+ node: '^22.14.0 || ^20.6.0'
31
31
  }
32
32
 
33
33
  function getRuntimeBaseEnvVars (config) {
@@ -90,6 +90,23 @@ async function managementApiPlugin (app, opts) {
90
90
  await runtime.stopService(id)
91
91
  })
92
92
 
93
+ app.post('/services/:id/pprof/start', async (request, reply) => {
94
+ const { id } = request.params
95
+ app.log.debug('start profiling', { id })
96
+
97
+ const options = request.body || {}
98
+ await runtime.startServiceProfiling(id, options)
99
+ reply.code(200).send({})
100
+ })
101
+
102
+ app.post('/services/:id/pprof/stop', async (request, reply) => {
103
+ const { id } = request.params
104
+ app.log.debug('stop profiling', { id })
105
+
106
+ const profileData = await runtime.stopServiceProfiling(id)
107
+ reply.type('application/octet-stream').code(200).send(profileData)
108
+ })
109
+
93
110
  app.all('/services/:id/proxy/*', async (request, reply) => {
94
111
  const { id, '*': requestUrl } = request.params
95
112
  app.log.debug('proxy request', { id, requestUrl })
package/lib/runtime.js CHANGED
@@ -795,6 +795,18 @@ class Runtime extends EventEmitter {
795
795
  return sendViaITC(service, 'getServiceEnv')
796
796
  }
797
797
 
798
+ async startServiceProfiling (id, options = {}, ensureStarted = true) {
799
+ const service = await this.#getServiceById(id, ensureStarted)
800
+
801
+ return sendViaITC(service, 'startProfiling', options)
802
+ }
803
+
804
+ async stopServiceProfiling (id, ensureStarted = true) {
805
+ const service = await this.#getServiceById(id, ensureStarted)
806
+
807
+ return sendViaITC(service, 'stopProfiling')
808
+ }
809
+
798
810
  async getServiceOpenapiSchema (id) {
799
811
  const service = await this.#getServiceById(id, true)
800
812
 
package/lib/worker/app.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const { getActiveResourcesInfo } = require('node:process')
3
4
  const { existsSync } = require('node:fs')
4
5
  const { EventEmitter } = require('node:events')
5
6
  const { resolve } = require('node:path')
@@ -219,6 +220,7 @@ class PlatformaticApp extends EventEmitter {
219
220
  globalThis.platformatic.onHttpStatsSize(url, size || 0)
220
221
  }
221
222
  }
223
+ globalThis.platformatic.onActiveResourcesEventLoop(getActiveResourcesInfo().length)
222
224
  return this.stackable.getMetrics({ format })
223
225
  }
224
226
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "2.74.3",
3
+ "version": "2.75.0-alpha.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -33,16 +33,16 @@
33
33
  "neostandard": "^0.12.0",
34
34
  "pino-abstract-transport": "^2.0.0",
35
35
  "split2": "^4.2.0",
36
- "tsd": "^0.32.0",
36
+ "tsd": "^0.33.0",
37
37
  "typescript": "^5.5.4",
38
38
  "undici-oidc-interceptor": "^0.5.0",
39
39
  "why-is-node-running": "^2.2.2",
40
- "@platformatic/composer": "2.74.3",
41
- "@platformatic/db": "2.74.3",
42
- "@platformatic/node": "2.74.3",
43
- "@platformatic/service": "2.74.3",
44
- "@platformatic/sql-graphql": "2.74.3",
45
- "@platformatic/sql-mapper": "2.74.3"
40
+ "@platformatic/composer": "2.75.0-alpha.0",
41
+ "@platformatic/db": "2.75.0-alpha.0",
42
+ "@platformatic/node": "2.75.0-alpha.0",
43
+ "@platformatic/service": "2.75.0-alpha.0",
44
+ "@platformatic/sql-graphql": "2.75.0-alpha.0",
45
+ "@platformatic/sql-mapper": "2.75.0-alpha.0"
46
46
  },
47
47
  "dependencies": {
48
48
  "@fastify/accepts": "^5.0.0",
@@ -76,14 +76,14 @@
76
76
  "undici": "^7.0.0",
77
77
  "undici-thread-interceptor": "^0.14.0",
78
78
  "ws": "^8.16.0",
79
- "@platformatic/basic": "2.74.3",
80
- "@platformatic/config": "2.74.3",
81
- "@platformatic/generators": "2.74.3",
82
- "@platformatic/metrics": "2.74.3",
83
- "@platformatic/itc": "2.74.3",
84
- "@platformatic/telemetry": "2.74.3",
85
- "@platformatic/ts-compiler": "2.74.3",
86
- "@platformatic/utils": "2.74.3"
79
+ "@platformatic/basic": "2.75.0-alpha.0",
80
+ "@platformatic/config": "2.75.0-alpha.0",
81
+ "@platformatic/generators": "2.75.0-alpha.0",
82
+ "@platformatic/metrics": "2.75.0-alpha.0",
83
+ "@platformatic/itc": "2.75.0-alpha.0",
84
+ "@platformatic/telemetry": "2.75.0-alpha.0",
85
+ "@platformatic/ts-compiler": "2.75.0-alpha.0",
86
+ "@platformatic/utils": "2.75.0-alpha.0"
87
87
  },
88
88
  "scripts": {
89
89
  "test": "pnpm run lint && borp --concurrency=1 --timeout=1200000 && tsd",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.74.3.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.75.0-alpha.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "type": "object",
5
5
  "properties": {