@platformatic/runtime 0.28.1 → 0.30.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/fixtures/dbApp/platformatic.db.json +5 -0
- package/fixtures/dbApp/plugin.js +12 -0
- package/fixtures/monorepo/serviceApp/plugin.js +4 -0
- package/fixtures/start-command-in-runtime.js +2 -1
- package/fixtures/typescript/platformatic.runtime.json +12 -0
- package/fixtures/typescript/services/composer/platformatic.composer.json +28 -0
- package/fixtures/typescript/services/movies/global.d.ts +24 -0
- package/fixtures/typescript/services/movies/migrations/001.do.sql +6 -0
- package/fixtures/typescript/services/movies/migrations/001.undo.sql +3 -0
- package/fixtures/typescript/services/movies/platformatic.db.json +33 -0
- package/fixtures/typescript/services/movies/plugin.ts +5 -0
- package/fixtures/typescript/services/movies/tsconfig.json +21 -0
- package/fixtures/typescript/services/movies/types/Movie.d.ts +9 -0
- package/fixtures/typescript/services/movies/types/index.d.ts +7 -0
- package/fixtures/typescript/services/titles/client/client.d.ts +141 -0
- package/fixtures/typescript/services/titles/client/client.openapi.json +630 -0
- package/fixtures/typescript/services/titles/client/package.json +4 -0
- package/fixtures/typescript/services/titles/platformatic.service.json +31 -0
- package/fixtures/typescript/services/titles/plugins/example.ts +6 -0
- package/fixtures/typescript/services/titles/routes/root.ts +21 -0
- package/fixtures/typescript/services/titles/tsconfig.json +21 -0
- package/help/compile.txt +8 -0
- package/index.js +7 -7
- package/lib/api-client.js +91 -0
- package/lib/api.js +26 -77
- package/lib/app.js +6 -2
- package/lib/compile.js +43 -0
- package/lib/config.js +77 -14
- package/lib/message-port-writable.js +42 -0
- package/lib/start.js +38 -19
- package/lib/unified-api.js +2 -0
- package/lib/worker.js +25 -26
- package/package.json +12 -8
- package/runtime.mjs +4 -0
- package/test/api.test.js +12 -1
- package/test/app.test.js +1 -1
- package/test/cli/compile.test.mjs +65 -0
- package/test/cli/start.test.mjs +56 -0
- package/test/cli/validations.test.mjs +2 -1
- package/test/cli/watch.test.mjs +15 -12
- package/test/config.test.js +137 -1
- package/test/start.test.js +57 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"esModuleInterop": true,
|
|
5
|
+
"target": "es2019",
|
|
6
|
+
"sourceMap": true,
|
|
7
|
+
"pretty": true,
|
|
8
|
+
"noEmitOnError": true,
|
|
9
|
+
"outDir": "dist"
|
|
10
|
+
},
|
|
11
|
+
"watchOptions": {
|
|
12
|
+
"watchFile": "fixedPollingInterval",
|
|
13
|
+
"watchDirectory": "fixedPollingInterval",
|
|
14
|
+
"fallbackPolling": "dynamicPriority",
|
|
15
|
+
"synchronousWatchDirectory": true,
|
|
16
|
+
"excludeDirectories": [
|
|
17
|
+
"**/node_modules",
|
|
18
|
+
"dist"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
}
|
package/help/compile.txt
ADDED
package/index.js
CHANGED
|
@@ -3,11 +3,11 @@ const { buildServer } = require('./lib/build-server')
|
|
|
3
3
|
const { platformaticRuntime } = require('./lib/config')
|
|
4
4
|
const { start } = require('./lib/start')
|
|
5
5
|
const unifiedApi = require('./lib/unified-api')
|
|
6
|
+
const RuntimeApi = require('./lib/api')
|
|
6
7
|
|
|
7
|
-
module.exports =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
8
|
+
module.exports.buildServer = buildServer
|
|
9
|
+
module.exports.platformaticRuntime = platformaticRuntime
|
|
10
|
+
module.exports.schema = platformaticRuntime.schema
|
|
11
|
+
module.exports.RuntimeApi = RuntimeApi
|
|
12
|
+
module.exports.start = start
|
|
13
|
+
module.exports.unifiedApi = unifiedApi
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { once, EventEmitter } = require('node:events')
|
|
4
|
+
const { randomUUID } = require('node:crypto')
|
|
5
|
+
|
|
6
|
+
const MAX_LISTENERS_COUNT = 100
|
|
7
|
+
|
|
8
|
+
class RuntimeApiClient extends EventEmitter {
|
|
9
|
+
#exitCode
|
|
10
|
+
#exitPromise
|
|
11
|
+
|
|
12
|
+
constructor (worker) {
|
|
13
|
+
super()
|
|
14
|
+
this.setMaxListeners(MAX_LISTENERS_COUNT)
|
|
15
|
+
|
|
16
|
+
this.worker = worker
|
|
17
|
+
this.#exitPromise = this.#exitHandler()
|
|
18
|
+
this.worker.on('message', (message) => {
|
|
19
|
+
if (message.operationId) {
|
|
20
|
+
this.emit(message.operationId, message)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async start () {
|
|
26
|
+
return this.#sendCommand('plt:start-services')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async close () {
|
|
30
|
+
await this.#sendCommand('plt:stop-services')
|
|
31
|
+
await this.#exitPromise
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async restart () {
|
|
35
|
+
return this.#sendCommand('plt:restart-services')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async getServices () {
|
|
39
|
+
return this.#sendCommand('plt:get-services')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async getServiceDetails (id) {
|
|
43
|
+
return this.#sendCommand('plt:get-service-details', { id })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async getServiceConfig (id) {
|
|
47
|
+
return this.#sendCommand('plt:get-service-config', { id })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async startService (id) {
|
|
51
|
+
return this.#sendCommand('plt:start-service', { id })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async stopService (id) {
|
|
55
|
+
return this.#sendCommand('plt:stop-service', { id })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async inject (id, injectParams) {
|
|
59
|
+
return this.#sendCommand('plt:inject', { id, injectParams })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async #sendCommand (command, params = {}) {
|
|
63
|
+
const operationId = randomUUID()
|
|
64
|
+
|
|
65
|
+
this.worker.postMessage({ operationId, command, params })
|
|
66
|
+
const [message] = await Promise.race(
|
|
67
|
+
[once(this, operationId), this.#exitPromise]
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if (this.#exitCode !== undefined) {
|
|
71
|
+
throw new Error('The runtime exited before the operation completed')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const { error, data } = message
|
|
75
|
+
if (error !== null) {
|
|
76
|
+
throw new Error(error)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return JSON.parse(data)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async #exitHandler () {
|
|
83
|
+
this.#exitCode = undefined
|
|
84
|
+
return once(this.worker, 'exit').then((msg) => {
|
|
85
|
+
this.#exitCode = msg[0]
|
|
86
|
+
return msg
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = RuntimeApiClient
|
package/lib/api.js
CHANGED
|
@@ -1,84 +1,33 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
3
|
+
const FastifyUndiciDispatcher = require('fastify-undici-dispatcher')
|
|
4
|
+
const { Agent, setGlobalDispatcher } = require('undici')
|
|
5
|
+
const { PlatformaticApp } = require('./app')
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
constructor (worker) {
|
|
12
|
-
super()
|
|
13
|
-
this.setMaxListeners(MAX_LISTENERS_COUNT)
|
|
14
|
-
|
|
15
|
-
this.#worker = worker
|
|
16
|
-
this.#worker.on('message', (message) => {
|
|
17
|
-
if (message.operationId) {
|
|
18
|
-
this.emit(message.operationId, message)
|
|
19
|
-
}
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async start () {
|
|
24
|
-
return this.#sendCommand('plt:start-services')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async close () {
|
|
28
|
-
await this.#sendCommand('plt:stop-services')
|
|
29
|
-
await once(this.#worker, 'exit')
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async restart () {
|
|
33
|
-
return this.#sendCommand('plt:restart-services')
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async getServices () {
|
|
37
|
-
return this.#sendCommand('plt:get-services')
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async getServiceDetails (id) {
|
|
41
|
-
return this.#sendCommand('plt:get-service-details', { id })
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async getServiceConfig (id) {
|
|
45
|
-
return this.#sendCommand('plt:get-service-config', { id })
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async startService (id) {
|
|
49
|
-
return this.#sendCommand('plt:start-service', { id })
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async stopService (id) {
|
|
53
|
-
return this.#sendCommand('plt:stop-service', { id })
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async inject (id, injectParams) {
|
|
57
|
-
return this.#sendCommand('plt:inject', { id, injectParams })
|
|
58
|
-
}
|
|
7
|
+
class RuntimeApi {
|
|
8
|
+
#services
|
|
9
|
+
#dispatcher
|
|
10
|
+
#loaderPort
|
|
59
11
|
|
|
60
|
-
|
|
61
|
-
|
|
12
|
+
constructor (config, logger, loaderPort) {
|
|
13
|
+
this.#services = new Map()
|
|
62
14
|
|
|
63
|
-
|
|
64
|
-
|
|
15
|
+
for (let i = 0; i < config.services.length; ++i) {
|
|
16
|
+
const service = config.services[i]
|
|
17
|
+
const app = new PlatformaticApp(service, loaderPort, logger)
|
|
65
18
|
|
|
66
|
-
|
|
67
|
-
if (error !== null) {
|
|
68
|
-
throw new Error(error)
|
|
19
|
+
this.#services.set(service.id, app)
|
|
69
20
|
}
|
|
70
21
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
#dispatcher
|
|
22
|
+
const globalAgent = new Agent()
|
|
23
|
+
const globalDispatcher = new FastifyUndiciDispatcher({
|
|
24
|
+
dispatcher: globalAgent,
|
|
25
|
+
// setting the domain here allows for fail-fast scenarios
|
|
26
|
+
domain: '.plt.local'
|
|
27
|
+
})
|
|
78
28
|
|
|
79
|
-
|
|
80
|
-
this.#
|
|
81
|
-
this.#dispatcher = dispatcher
|
|
29
|
+
setGlobalDispatcher(globalDispatcher)
|
|
30
|
+
this.#dispatcher = globalDispatcher
|
|
82
31
|
}
|
|
83
32
|
|
|
84
33
|
async startListening (parentPort) {
|
|
@@ -116,9 +65,9 @@ class RuntimeApi {
|
|
|
116
65
|
async #runCommandHandler (command, params) {
|
|
117
66
|
switch (command) {
|
|
118
67
|
case 'plt:start-services':
|
|
119
|
-
return this
|
|
68
|
+
return this.startServices(params)
|
|
120
69
|
case 'plt:stop-services':
|
|
121
|
-
return this
|
|
70
|
+
return this.stopServices(params)
|
|
122
71
|
case 'plt:restart-services':
|
|
123
72
|
return this.#restartServices(params)
|
|
124
73
|
case 'plt:get-services':
|
|
@@ -139,7 +88,7 @@ class RuntimeApi {
|
|
|
139
88
|
}
|
|
140
89
|
}
|
|
141
90
|
|
|
142
|
-
async
|
|
91
|
+
async startServices () {
|
|
143
92
|
let entrypointUrl = null
|
|
144
93
|
for (const service of this.#services.values()) {
|
|
145
94
|
await service.start()
|
|
@@ -154,7 +103,7 @@ class RuntimeApi {
|
|
|
154
103
|
return entrypointUrl
|
|
155
104
|
}
|
|
156
105
|
|
|
157
|
-
async
|
|
106
|
+
async stopServices () {
|
|
158
107
|
for (const service of this.#services.values()) {
|
|
159
108
|
const serviceStatus = service.getStatus()
|
|
160
109
|
if (serviceStatus === 'started') {
|
|
@@ -254,4 +203,4 @@ class RuntimeApi {
|
|
|
254
203
|
}
|
|
255
204
|
}
|
|
256
205
|
|
|
257
|
-
module.exports =
|
|
206
|
+
module.exports = RuntimeApi
|
package/lib/app.js
CHANGED
|
@@ -118,6 +118,7 @@ class PlatformaticApp {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
async handleProcessLevelEvent ({ signal, err }) {
|
|
121
|
+
/* c8 ignore next 3 */
|
|
121
122
|
if (!this.server) {
|
|
122
123
|
return false
|
|
123
124
|
}
|
|
@@ -223,6 +224,7 @@ class PlatformaticApp {
|
|
|
223
224
|
watchIgnore: [...(this.#originalWatch?.ignore || []), basename(configManager.fullPath)]
|
|
224
225
|
})
|
|
225
226
|
|
|
227
|
+
/* c8 ignore next 4 */
|
|
226
228
|
fileWatcher.on('update', async () => {
|
|
227
229
|
this.server.log.debug('files changed')
|
|
228
230
|
this.restart()
|
|
@@ -236,12 +238,14 @@ class PlatformaticApp {
|
|
|
236
238
|
|
|
237
239
|
async #stopFileWatching () {
|
|
238
240
|
const watcher = this.server.platformatic.fileWatcher
|
|
241
|
+
// The configManager automatically watches for the config file changes
|
|
242
|
+
// therefore we need to stop it all the times.
|
|
243
|
+
await this.config.configManager.stopWatching()
|
|
239
244
|
|
|
240
245
|
if (watcher) {
|
|
241
|
-
await watcher.stopWatching()
|
|
242
246
|
this.server.log.debug('stop watching files')
|
|
247
|
+
await watcher.stopWatching()
|
|
243
248
|
this.server.platformatic.fileWatcher = undefined
|
|
244
|
-
this.server.platformatic.configManager.stopWatching()
|
|
245
249
|
}
|
|
246
250
|
}
|
|
247
251
|
|
package/lib/compile.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { loadConfig, tsCompiler } = require('@platformatic/service')
|
|
4
|
+
const { access } = require('node:fs/promises')
|
|
5
|
+
const { join } = require('path')
|
|
6
|
+
const pino = require('pino')
|
|
7
|
+
const pretty = require('pino-pretty')
|
|
8
|
+
const { isatty } = require('node:tty')
|
|
9
|
+
|
|
10
|
+
const { platformaticRuntime } = require('./config')
|
|
11
|
+
|
|
12
|
+
async function compile (argv) {
|
|
13
|
+
const { configManager } = await loadConfig({}, argv, platformaticRuntime, {
|
|
14
|
+
watch: false
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
let stream
|
|
18
|
+
|
|
19
|
+
/* c8 ignore next 6 */
|
|
20
|
+
if (isatty(process.stdout.fd)) {
|
|
21
|
+
stream = pretty({
|
|
22
|
+
translateTime: 'SYS:HH:MM:ss',
|
|
23
|
+
ignore: 'hostname,pid'
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const logger = pino(stream)
|
|
28
|
+
|
|
29
|
+
for (const service of configManager.current.services) {
|
|
30
|
+
const tsconfig = join(service.path, 'tsconfig.json')
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
await access(tsconfig)
|
|
34
|
+
} catch {
|
|
35
|
+
logger.trace(`No tsconfig.json found in ${service.path}, skipping...`)
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
await tsCompiler.compile(service.path, {}, logger.child({ name: service.id }))
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports.compile = compile
|
package/lib/config.js
CHANGED
|
@@ -37,6 +37,7 @@ async function _transformConfig (configManager) {
|
|
|
37
37
|
|
|
38
38
|
configManager.current.allowCycles = !!configManager.current.allowCycles
|
|
39
39
|
configManager.current.serviceMap = new Map()
|
|
40
|
+
configManager.current.inspectorOptions = null
|
|
40
41
|
|
|
41
42
|
let hasValidEntrypoint = false
|
|
42
43
|
|
|
@@ -121,22 +122,28 @@ async function parseClientsAndComposer (configManager) {
|
|
|
121
122
|
let clientName = client.serviceId ?? ''
|
|
122
123
|
let clientUrl
|
|
123
124
|
let missingKey
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
125
|
+
let isLocal = false
|
|
126
|
+
|
|
127
|
+
if (clientName === '' || client.url !== undefined) {
|
|
128
|
+
try {
|
|
129
|
+
clientUrl = await cm.replaceEnv(client.url)
|
|
130
|
+
/* c8 ignore next 2 - unclear why c8 is unhappy here */
|
|
131
|
+
} catch (err) {
|
|
132
|
+
if (err.name !== 'MissingValueError') {
|
|
133
|
+
/* c8 ignore next 3 */
|
|
134
|
+
reject(err)
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
missingKey = err.key
|
|
133
139
|
}
|
|
134
|
-
|
|
135
|
-
|
|
140
|
+
isLocal = missingKey && client.url === `{${missingKey}}`
|
|
141
|
+
/* c8 ignore next 3 */
|
|
142
|
+
} else {
|
|
143
|
+
/* c8 ignore next 2 */
|
|
144
|
+
isLocal = true
|
|
136
145
|
}
|
|
137
146
|
|
|
138
|
-
const isLocal = missingKey && client.url === `{${missingKey}}`
|
|
139
|
-
|
|
140
147
|
/* c8 ignore next 20 - unclear why c8 is unhappy for nearly 20 lines here */
|
|
141
148
|
if (!clientName) {
|
|
142
149
|
const clientAbsolutePath = pathResolve(service.path, client.path)
|
|
@@ -234,4 +241,60 @@ async function wrapConfigInRuntimeConfig ({ configManager, args }) {
|
|
|
234
241
|
return cm
|
|
235
242
|
}
|
|
236
243
|
|
|
237
|
-
|
|
244
|
+
function parseInspectorOptions (configManager) {
|
|
245
|
+
const { current, args } = configManager
|
|
246
|
+
const hasInspect = 'inspect' in args
|
|
247
|
+
const hasInspectBrk = 'inspect-brk' in args
|
|
248
|
+
let inspectFlag
|
|
249
|
+
|
|
250
|
+
if (hasInspect) {
|
|
251
|
+
inspectFlag = args.inspect
|
|
252
|
+
|
|
253
|
+
if (hasInspectBrk) {
|
|
254
|
+
throw new Error('--inspect and --inspect-brk cannot be used together')
|
|
255
|
+
}
|
|
256
|
+
} else if (hasInspectBrk) {
|
|
257
|
+
inspectFlag = args['inspect-brk']
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (inspectFlag !== undefined) {
|
|
261
|
+
let host = '127.0.0.1'
|
|
262
|
+
let port = 9229
|
|
263
|
+
|
|
264
|
+
if (typeof inspectFlag === 'string' && inspectFlag.length > 0) {
|
|
265
|
+
const splitAt = inspectFlag.lastIndexOf(':')
|
|
266
|
+
|
|
267
|
+
if (splitAt === -1) {
|
|
268
|
+
port = inspectFlag
|
|
269
|
+
} else {
|
|
270
|
+
host = inspectFlag.substring(0, splitAt)
|
|
271
|
+
port = inspectFlag.substring(splitAt + 1)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
port = Number.parseInt(port, 10)
|
|
275
|
+
|
|
276
|
+
if (!(port === 0 || (port >= 1024 && port <= 65535))) {
|
|
277
|
+
throw new Error('inspector port must be 0 or in range 1024 to 65535')
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (!host) {
|
|
281
|
+
throw new Error('inspector host cannot be empty')
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
current.inspectorOptions = {
|
|
286
|
+
host,
|
|
287
|
+
port,
|
|
288
|
+
breakFirstLine: hasInspectBrk,
|
|
289
|
+
hotReloadDisabled: !!current.hotReload
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
current.hotReload = false
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
module.exports = {
|
|
297
|
+
parseInspectorOptions,
|
|
298
|
+
platformaticRuntime,
|
|
299
|
+
wrapConfigInRuntimeConfig
|
|
300
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const assert = require('node:assert')
|
|
3
|
+
const { Writable } = require('node:stream')
|
|
4
|
+
|
|
5
|
+
class MessagePortWritable extends Writable {
|
|
6
|
+
constructor (options) {
|
|
7
|
+
const opts = { ...options, objectMode: true }
|
|
8
|
+
|
|
9
|
+
super(opts)
|
|
10
|
+
this.port = opts.port
|
|
11
|
+
this.metadata = opts.metadata
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
_writev (chunks, callback) {
|
|
15
|
+
// Process the logs here before trying to send them across the thread
|
|
16
|
+
// boundary. Sometimes the chunks have an undocumented method on them
|
|
17
|
+
// which will prevent sending the chunk itself across threads.
|
|
18
|
+
const logs = chunks.map((chunk) => {
|
|
19
|
+
// pino should always produce a string here.
|
|
20
|
+
assert(typeof chunk.chunk === 'string')
|
|
21
|
+
return chunk.chunk.trim()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
this.port.postMessage({
|
|
25
|
+
metadata: this.metadata,
|
|
26
|
+
logs
|
|
27
|
+
})
|
|
28
|
+
setImmediate(callback)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_final (callback) {
|
|
32
|
+
this.port.close()
|
|
33
|
+
callback()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_destroy (err, callback) {
|
|
37
|
+
this.port.close()
|
|
38
|
+
callback(err)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { MessagePortWritable }
|
package/lib/start.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const { once } = require('node:events')
|
|
3
|
+
const inspector = require('node:inspector')
|
|
3
4
|
const { join } = require('node:path')
|
|
4
5
|
const { pathToFileURL } = require('node:url')
|
|
5
6
|
const { Worker } = require('node:worker_threads')
|
|
6
7
|
const closeWithGrace = require('close-with-grace')
|
|
7
8
|
const { loadConfig } = require('@platformatic/service')
|
|
8
|
-
const { platformaticRuntime } = require('./config')
|
|
9
|
-
const
|
|
9
|
+
const { parseInspectorOptions, platformaticRuntime } = require('./config')
|
|
10
|
+
const RuntimeApiClient = require('./api-client.js')
|
|
10
11
|
const kLoaderFile = pathToFileURL(join(__dirname, 'loader.mjs')).href
|
|
11
12
|
const kWorkerFile = join(__dirname, 'worker.js')
|
|
12
13
|
const kWorkerExecArgv = [
|
|
@@ -16,21 +17,33 @@ const kWorkerExecArgv = [
|
|
|
16
17
|
]
|
|
17
18
|
|
|
18
19
|
async function start (argv) {
|
|
19
|
-
const
|
|
20
|
+
const config = await loadConfig({}, argv, platformaticRuntime, {
|
|
20
21
|
watch: true
|
|
21
22
|
})
|
|
22
|
-
const app = await startWithConfig(configManager)
|
|
23
23
|
|
|
24
|
+
config.configManager.args = config.args
|
|
25
|
+
const app = await startWithConfig(config.configManager)
|
|
24
26
|
await app.start()
|
|
25
27
|
return app
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
async function startWithConfig (configManager) {
|
|
30
|
+
async function startWithConfig (configManager, env = process.env) {
|
|
29
31
|
const config = configManager.current
|
|
32
|
+
|
|
33
|
+
if (inspector.url()) {
|
|
34
|
+
throw new Error('The Node.js inspector flags are not supported. Please use \'platformatic start --inspect\' instead.')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (configManager.args) {
|
|
38
|
+
parseInspectorOptions(configManager)
|
|
39
|
+
}
|
|
40
|
+
|
|
30
41
|
const worker = new Worker(kWorkerFile, {
|
|
31
42
|
/* c8 ignore next */
|
|
32
43
|
execArgv: config.hotReload ? kWorkerExecArgv : [],
|
|
33
|
-
|
|
44
|
+
transferList: config.loggingPort ? [config.loggingPort] : [],
|
|
45
|
+
workerData: { config },
|
|
46
|
+
env
|
|
34
47
|
})
|
|
35
48
|
|
|
36
49
|
worker.on('exit', () => {
|
|
@@ -38,23 +51,29 @@ async function startWithConfig (configManager) {
|
|
|
38
51
|
})
|
|
39
52
|
|
|
40
53
|
worker.on('error', () => {
|
|
41
|
-
// the error
|
|
42
|
-
|
|
54
|
+
// If this is the only 'error' handler, then exit the process as the default
|
|
55
|
+
// behavior. If anything else is listening for errors, then don't exit.
|
|
56
|
+
if (worker.listenerCount('error') === 1) {
|
|
57
|
+
// The error is logged in the worker.
|
|
58
|
+
process.exit(1)
|
|
59
|
+
}
|
|
43
60
|
})
|
|
44
61
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
if (config.hotReload) {
|
|
63
|
+
/* c8 ignore next 3 */
|
|
64
|
+
process.on('SIGUSR2', () => {
|
|
65
|
+
worker.postMessage({ signal: 'SIGUSR2' })
|
|
66
|
+
})
|
|
49
67
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
68
|
+
closeWithGrace((event) => {
|
|
69
|
+
worker.postMessage(event)
|
|
70
|
+
})
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
/* c8 ignore next 3 */
|
|
73
|
+
configManager.on('update', () => {
|
|
74
|
+
// TODO(cjihrig): Need to clean up and restart the worker.
|
|
75
|
+
})
|
|
76
|
+
}
|
|
58
77
|
|
|
59
78
|
await once(worker, 'message') // plt:init
|
|
60
79
|
|
package/lib/unified-api.js
CHANGED
|
@@ -163,9 +163,11 @@ async function startCommandInRuntime (args) {
|
|
|
163
163
|
let runtime
|
|
164
164
|
|
|
165
165
|
if (configType === 'runtime') {
|
|
166
|
+
config.configManager.args = config.args
|
|
166
167
|
runtime = await runtimeStartWithConfig(config.configManager)
|
|
167
168
|
} else {
|
|
168
169
|
const wrappedConfig = await wrapConfigInRuntimeConfig(config)
|
|
170
|
+
wrappedConfig.args = config.args
|
|
169
171
|
runtime = await runtimeStartWithConfig(wrappedConfig)
|
|
170
172
|
}
|
|
171
173
|
|