@platformatic/runtime 3.41.0 → 3.43.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
@@ -539,6 +539,7 @@ export type PlatformaticRuntimeConfig = {
539
539
  env?: {
540
540
  [k: string]: string;
541
541
  };
542
+ envfile?: string;
542
543
  sourceMaps?: boolean;
543
544
  nodeModulesSourceMaps?: string[];
544
545
  scheduler?: {
package/index.js CHANGED
@@ -67,6 +67,11 @@ export async function loadConfiguration (configOrRoot, sourceOrConfig, context)
67
67
  root,
68
68
  envFile: context?.envFile
69
69
  })
70
+
71
+ if (config.envfile) {
72
+ context.envFile = config.envfile
73
+ }
74
+
70
75
  const mod = extractModuleFromSchemaUrl(config)
71
76
  if (mod?.module !== '@platformatic/runtime') {
72
77
  return wrapInRuntimeConfig(config, context)
@@ -145,51 +150,24 @@ export async function create (configOrRoot, sourceOrConfig, context) {
145
150
  throw new NodeInspectorFlagsNotSupportedError()
146
151
  }
147
152
 
148
- let runtime = new Runtime(config, context)
153
+ const runtime = new Runtime(config, context)
149
154
  if (setupSignals) {
150
155
  handleSignal(runtime, config)
151
156
  }
152
157
 
153
- // Handle port handling
158
+ // Handle startup
154
159
  if (context?.start) {
155
- let port = config.server?.port
156
-
157
160
  try {
158
161
  await runtime.init()
162
+ if (context.reloaded) {
163
+ runtime.logger.info('The application has been successfully reloaded.')
164
+ }
165
+
166
+ await runtime.start()
159
167
  } catch (err) {
160
168
  await runtime.close()
161
169
  throw err
162
170
  }
163
-
164
- if (context.reloaded) {
165
- runtime.logger.info('The application has been successfully reloaded.')
166
- }
167
-
168
- while (true) {
169
- try {
170
- await runtime.start()
171
- break
172
- } catch (err) {
173
- if ((err.code !== 'EADDRINUSE' && err.code !== 'EACCES') || context?.skipPortInUseHandling) {
174
- throw err
175
- }
176
-
177
- await runtime.close()
178
-
179
- // Get the actual port from the error message if original port was 0
180
- if (!port) {
181
- const mo = err.message.match(/ address already in use (.+)/)
182
- const url = new URL(`http://${mo[1]}`)
183
- port = Number(url.port)
184
- }
185
-
186
- config.server.port = ++port
187
- runtime = new Runtime(config, context)
188
- if (setupSignals) {
189
- handleSignal(runtime, config)
190
- }
191
- }
192
- }
193
171
  }
194
172
 
195
173
  return runtime
@@ -214,6 +214,15 @@ export async function managementApiPlugin (app, opts) {
214
214
  reply.type('application/octet-stream').code(200).send(profileData)
215
215
  })
216
216
 
217
+ app.post('/applications/:id/heap-snapshot', async (request, reply) => {
218
+ const { id } = request.params
219
+ app.log.debug('take heap snapshot', { id })
220
+
221
+ const stream = await runtime.takeApplicationHeapSnapshot(id)
222
+ reply.type('application/octet-stream').send(stream)
223
+ return reply
224
+ })
225
+
217
226
  app.get('/metrics', { logLevel: 'debug' }, async (req, reply) => {
218
227
  const config = await runtime.getRuntimeConfig()
219
228
 
package/lib/runtime.js CHANGED
@@ -19,6 +19,7 @@ import { STATUS_CODES } from 'node:http'
19
19
  import { createRequire } from 'node:module'
20
20
  import { availableParallelism } from 'node:os'
21
21
  import { dirname, isAbsolute, join } from 'node:path'
22
+ import { Readable } from 'node:stream'
22
23
  import { setImmediate as immediate, setTimeout as sleep } from 'node:timers/promises'
23
24
  import { pathToFileURL } from 'node:url'
24
25
  import { Worker } from 'node:worker_threads'
@@ -736,6 +737,30 @@ export class Runtime extends EventEmitter {
736
737
  return sendViaITC(service, 'stopProfiling', options)
737
738
  }
738
739
 
740
+ async takeApplicationHeapSnapshot (id, ensureStarted = true) {
741
+ const service = await this.#getApplicationById(id, ensureStarted)
742
+
743
+ const { port1, port2 } = new MessageChannel()
744
+
745
+ const readable = new Readable({ read () {} })
746
+
747
+ port2.on('message', (message) => {
748
+ if (message.type === 'chunk') {
749
+ readable.push(message.chunk)
750
+ } else if (message.type === 'error') {
751
+ readable.destroy(new Error(message.message))
752
+ port2.close()
753
+ } else if (message.type === 'end') {
754
+ readable.push(null)
755
+ port2.close()
756
+ }
757
+ })
758
+
759
+ await sendViaITC(service, 'takeHeapSnapshot', port1, [port1])
760
+
761
+ return readable
762
+ }
763
+
739
764
  async startApplicationRepl (id, ensureStarted = true) {
740
765
  const service = await this.#getApplicationById(id, ensureStarted)
741
766
 
package/lib/worker/itc.js CHANGED
@@ -342,6 +342,28 @@ export function setupITC (controller, application, dispatcher, sharedContext) {
342
342
  messaging.addSource(channel)
343
343
  },
344
344
 
345
+ takeHeapSnapshot (port) {
346
+ const { Session } = createRequire(import.meta.url)('node:inspector')
347
+ const session = new Session()
348
+ session.connect()
349
+
350
+ session.on('HeapProfiler.addHeapSnapshotChunk', (m) => {
351
+ port.postMessage({ type: 'chunk', chunk: m.params.chunk })
352
+ })
353
+
354
+ session.post('HeapProfiler.takeHeapSnapshot', null, (err) => {
355
+ session.disconnect()
356
+ if (err) {
357
+ port.postMessage({ type: 'error', message: err.message })
358
+ } else {
359
+ port.postMessage({ type: 'end' })
360
+ }
361
+ port.close()
362
+ })
363
+
364
+ return { started: true }
365
+ },
366
+
345
367
  startRepl (port) {
346
368
  // Check if running in subprocess mode - forward through ChildManager
347
369
  const childManager = controller.capability?.getChildManager?.()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "3.41.0",
3
+ "version": "3.43.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.41.0",
39
- "@platformatic/db": "3.41.0",
40
- "@platformatic/gateway": "3.41.0",
41
- "@platformatic/node": "3.41.0",
42
- "@platformatic/service": "3.41.0",
43
- "@platformatic/sql-graphql": "3.41.0",
44
- "@platformatic/sql-mapper": "3.41.0",
45
- "@platformatic/wattpm-pprof-capture": "3.41.0"
38
+ "@platformatic/db": "3.43.0",
39
+ "@platformatic/composer": "3.43.0",
40
+ "@platformatic/node": "3.43.0",
41
+ "@platformatic/gateway": "3.43.0",
42
+ "@platformatic/service": "3.43.0",
43
+ "@platformatic/sql-graphql": "3.43.0",
44
+ "@platformatic/sql-mapper": "3.43.0",
45
+ "@platformatic/wattpm-pprof-capture": "3.43.0"
46
46
  },
47
47
  "dependencies": {
48
48
  "@fastify/accepts": "^5.0.0",
@@ -63,7 +63,7 @@
63
63
  "graphql": "^16.8.1",
64
64
  "help-me": "^5.0.0",
65
65
  "minimist": "^1.2.8",
66
- "pino": "^9.9.0",
66
+ "pino": "^10.1.0",
67
67
  "pino-opentelemetry-transport": "^2.0.0",
68
68
  "pino-pretty": "^13.0.0",
69
69
  "semgrator": "^0.3.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.41.0",
76
- "@platformatic/generators": "3.41.0",
77
- "@platformatic/itc": "3.41.0",
78
- "@platformatic/telemetry": "3.41.0",
79
- "@platformatic/foundation": "3.41.0",
80
- "@platformatic/metrics": "3.41.0"
75
+ "@platformatic/basic": "3.43.0",
76
+ "@platformatic/generators": "3.43.0",
77
+ "@platformatic/metrics": "3.43.0",
78
+ "@platformatic/foundation": "3.43.0",
79
+ "@platformatic/telemetry": "3.43.0",
80
+ "@platformatic/itc": "3.43.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.41.0.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.43.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Runtime Config",
5
5
  "type": "object",
@@ -2751,6 +2751,9 @@
2751
2751
  "type": "string"
2752
2752
  }
2753
2753
  },
2754
+ "envfile": {
2755
+ "type": "string"
2756
+ },
2754
2757
  "sourceMaps": {
2755
2758
  "type": "boolean",
2756
2759
  "default": false