@platformatic/runtime 3.27.0 → 3.28.0-alpha.1
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/lib/management-api.js +38 -0
- package/lib/runtime.js +13 -0
- package/lib/worker/itc.js +51 -0
- package/package.json +15 -15
- package/schema.json +1 -1
package/lib/management-api.js
CHANGED
|
@@ -288,6 +288,44 @@ export async function managementApiPlugin (app, opts) {
|
|
|
288
288
|
app.get('/logs/live', { websocket: true }, async socket => {
|
|
289
289
|
runtime.addLoggerDestination(createWebSocketStream(socket))
|
|
290
290
|
})
|
|
291
|
+
|
|
292
|
+
app.get('/applications/:id/repl', { websocket: true }, async (socket, request) => {
|
|
293
|
+
const { id } = request.params
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
// Start REPL and get the communication port
|
|
297
|
+
const port = await runtime.startApplicationRepl(id)
|
|
298
|
+
|
|
299
|
+
// Forward messages between WebSocket and MessagePort
|
|
300
|
+
port.on('message', (message) => {
|
|
301
|
+
if (message.type === 'output') {
|
|
302
|
+
socket.send(message.data)
|
|
303
|
+
} else if (message.type === 'exit') {
|
|
304
|
+
socket.close()
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
socket.on('message', (data) => {
|
|
309
|
+
port.postMessage({ type: 'input', data: data.toString() })
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
socket.on('close', () => {
|
|
313
|
+
port.postMessage({ type: 'close' })
|
|
314
|
+
port.close()
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
socket.on('error', () => {
|
|
318
|
+
port.postMessage({ type: 'close' })
|
|
319
|
+
port.close()
|
|
320
|
+
})
|
|
321
|
+
} catch (error) {
|
|
322
|
+
socket.send(JSON.stringify({
|
|
323
|
+
error: error.message,
|
|
324
|
+
code: error.code
|
|
325
|
+
}))
|
|
326
|
+
socket.close()
|
|
327
|
+
}
|
|
328
|
+
})
|
|
291
329
|
}
|
|
292
330
|
|
|
293
331
|
export async function startManagementApi (runtime) {
|
package/lib/runtime.js
CHANGED
|
@@ -679,6 +679,19 @@ export class Runtime extends EventEmitter {
|
|
|
679
679
|
return sendViaITC(service, 'stopProfiling', options)
|
|
680
680
|
}
|
|
681
681
|
|
|
682
|
+
async startApplicationRepl (id, ensureStarted = true) {
|
|
683
|
+
const service = await this.#getApplicationById(id, ensureStarted)
|
|
684
|
+
|
|
685
|
+
// Create a MessageChannel for REPL communication
|
|
686
|
+
const { port1, port2 } = new MessageChannel()
|
|
687
|
+
|
|
688
|
+
// Send port1 to the worker to start the REPL
|
|
689
|
+
await sendViaITC(service, 'startRepl', port1, [port1])
|
|
690
|
+
|
|
691
|
+
// Return port2 for the caller to use
|
|
692
|
+
return port2
|
|
693
|
+
}
|
|
694
|
+
|
|
682
695
|
async updateUndiciInterceptors (undiciConfig) {
|
|
683
696
|
this.#config.undici = undiciConfig
|
|
684
697
|
|
package/lib/worker/itc.js
CHANGED
|
@@ -2,6 +2,8 @@ import { ensureLoggableError, executeInParallel, executeWithTimeout, kTimeout }
|
|
|
2
2
|
import { ITC } from '@platformatic/itc'
|
|
3
3
|
import { Unpromise } from '@watchable/unpromise'
|
|
4
4
|
import { once } from 'node:events'
|
|
5
|
+
import repl from 'node:repl'
|
|
6
|
+
import { Duplex } from 'node:stream'
|
|
5
7
|
import { parentPort, workerData } from 'node:worker_threads'
|
|
6
8
|
import {
|
|
7
9
|
ApplicationExitedError,
|
|
@@ -261,6 +263,55 @@ export function setupITC (controller, application, dispatcher, sharedContext) {
|
|
|
261
263
|
|
|
262
264
|
saveMessagingChannel (channel) {
|
|
263
265
|
messaging.addSource(channel)
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
startRepl (port) {
|
|
269
|
+
// Create a duplex stream that wraps the MessagePort
|
|
270
|
+
const replStream = new Duplex({
|
|
271
|
+
read () {},
|
|
272
|
+
write (chunk, encoding, callback) {
|
|
273
|
+
port.postMessage({ type: 'output', data: chunk.toString() })
|
|
274
|
+
callback()
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
port.on('message', (message) => {
|
|
279
|
+
if (message.type === 'input') {
|
|
280
|
+
replStream.push(message.data)
|
|
281
|
+
} else if (message.type === 'close') {
|
|
282
|
+
replStream.push(null)
|
|
283
|
+
}
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
port.on('close', () => {
|
|
287
|
+
replStream.push(null)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
// Start the REPL with the stream
|
|
291
|
+
const replServer = repl.start({
|
|
292
|
+
prompt: `${controller.applicationConfig.id}> `,
|
|
293
|
+
input: replStream,
|
|
294
|
+
output: replStream,
|
|
295
|
+
terminal: false,
|
|
296
|
+
useColors: true,
|
|
297
|
+
ignoreUndefined: true,
|
|
298
|
+
preview: false
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
// Expose useful context
|
|
302
|
+
// For service-based capabilities, expose the Fastify app
|
|
303
|
+
replServer.context.app = controller.capability?.getApplication?.()
|
|
304
|
+
replServer.context.capability = controller.capability
|
|
305
|
+
replServer.context.platformatic = globalThis.platformatic
|
|
306
|
+
replServer.context.config = controller.applicationConfig
|
|
307
|
+
replServer.context.logger = globalThis.platformatic?.logger
|
|
308
|
+
|
|
309
|
+
replServer.on('exit', () => {
|
|
310
|
+
port.postMessage({ type: 'exit' })
|
|
311
|
+
port.close()
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
return { started: true }
|
|
264
315
|
}
|
|
265
316
|
}
|
|
266
317
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.28.0-alpha.1",
|
|
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
|
-
"@platformatic/
|
|
40
|
-
"@platformatic/
|
|
41
|
-
"@platformatic/
|
|
42
|
-
"@platformatic/
|
|
43
|
-
"@platformatic/sql-graphql": "3.
|
|
44
|
-
"@platformatic/sql-mapper": "3.
|
|
45
|
-
"@platformatic/wattpm-pprof-capture": "3.
|
|
38
|
+
"@platformatic/composer": "3.28.0-alpha.1",
|
|
39
|
+
"@platformatic/db": "3.28.0-alpha.1",
|
|
40
|
+
"@platformatic/gateway": "3.28.0-alpha.1",
|
|
41
|
+
"@platformatic/node": "3.28.0-alpha.1",
|
|
42
|
+
"@platformatic/service": "3.28.0-alpha.1",
|
|
43
|
+
"@platformatic/sql-graphql": "3.28.0-alpha.1",
|
|
44
|
+
"@platformatic/sql-mapper": "3.28.0-alpha.1",
|
|
45
|
+
"@platformatic/wattpm-pprof-capture": "3.28.0-alpha.1"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@fastify/accepts": "^5.0.0",
|
|
@@ -71,12 +71,12 @@
|
|
|
71
71
|
"undici": "^7.0.0",
|
|
72
72
|
"undici-thread-interceptor": "^0.15.0",
|
|
73
73
|
"ws": "^8.16.0",
|
|
74
|
-
"@platformatic/basic": "3.
|
|
75
|
-
"@platformatic/foundation": "3.
|
|
76
|
-
"@platformatic/
|
|
77
|
-
"@platformatic/
|
|
78
|
-
"@platformatic/
|
|
79
|
-
"@platformatic/
|
|
74
|
+
"@platformatic/basic": "3.28.0-alpha.1",
|
|
75
|
+
"@platformatic/foundation": "3.28.0-alpha.1",
|
|
76
|
+
"@platformatic/itc": "3.28.0-alpha.1",
|
|
77
|
+
"@platformatic/generators": "3.28.0-alpha.1",
|
|
78
|
+
"@platformatic/telemetry": "3.28.0-alpha.1",
|
|
79
|
+
"@platformatic/metrics": "3.28.0-alpha.1"
|
|
80
80
|
},
|
|
81
81
|
"engines": {
|
|
82
82
|
"node": ">=22.19.0"
|
package/schema.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.
|
|
2
|
+
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.28.0-alpha.1.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"title": "Platformatic Runtime Config",
|
|
5
5
|
"type": "object",
|