@platformatic/runtime 0.27.0 → 0.28.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/fixtures/configs/invalid-schema-type.config.json +1 -2
- package/fixtures/configs/monorepo-client-without-id.json +24 -0
- package/fixtures/configs/no-schema.config.json +1 -2
- package/fixtures/monorepo/serviceApp/platformatic.service-client-without-id.json +21 -0
- package/fixtures/monorepo/serviceApp/platformatic.service.json +1 -1
- package/fixtures/serviceAppThrowsOnStart/platformatic.service.json +0 -1
- package/fixtures/start-command-in-runtime.js +14 -0
- package/lib/api.js +257 -0
- package/lib/app.js +45 -57
- package/lib/config.js +36 -8
- package/lib/loader.mjs +21 -13
- package/lib/start.js +5 -17
- package/lib/unified-api.js +34 -7
- package/lib/worker.js +7 -58
- package/package.json +6 -6
- package/test/api.test.js +294 -0
- package/test/app.test.js +40 -0
- package/test/config.test.js +9 -0
- package/test/unified-api.test.js +33 -0
- package/test/worker.test.js +0 -21
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://platformatic.dev/schemas/v0.20.0/runtime",
|
|
3
|
+
"entrypoint": "serviceApp",
|
|
4
|
+
"allowCycles": true,
|
|
5
|
+
"hotReload": false,
|
|
6
|
+
"autoload": {
|
|
7
|
+
"path": "../monorepo",
|
|
8
|
+
"exclude": ["docs", "composerApp"],
|
|
9
|
+
"mappings": {
|
|
10
|
+
"serviceApp": {
|
|
11
|
+
"id": "serviceApp",
|
|
12
|
+
"config": "platformatic.service-client-without-id.json"
|
|
13
|
+
},
|
|
14
|
+
"serviceAppWithLogger": {
|
|
15
|
+
"id": "with-logger",
|
|
16
|
+
"config": "platformatic.service.json"
|
|
17
|
+
},
|
|
18
|
+
"serviceAppWithMultiplePlugins": {
|
|
19
|
+
"id": "multi-plugin-service",
|
|
20
|
+
"config": "platformatic.service.json"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://platformatic.dev/schemas/v0.20.0/service",
|
|
3
|
+
"server": {
|
|
4
|
+
"hostname": "127.0.0.1",
|
|
5
|
+
"port": 0
|
|
6
|
+
},
|
|
7
|
+
"service": {
|
|
8
|
+
"openapi": true
|
|
9
|
+
},
|
|
10
|
+
"plugins": {
|
|
11
|
+
"paths": [
|
|
12
|
+
"plugin.js"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"clients": [
|
|
16
|
+
{
|
|
17
|
+
"path": "./with-logger",
|
|
18
|
+
"url": "{PLT_WITH_LOGGER_URL}"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const assert = require('node:assert')
|
|
3
|
+
const { request } = require('undici')
|
|
4
|
+
const { startCommandInRuntime } = require('../lib/unified-api')
|
|
5
|
+
|
|
6
|
+
async function main () {
|
|
7
|
+
const entrypoint = await startCommandInRuntime(['-c', process.argv[2]])
|
|
8
|
+
const res = await request(entrypoint)
|
|
9
|
+
|
|
10
|
+
assert.strictEqual(res.statusCode, 200)
|
|
11
|
+
process.exit(42)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
main()
|
package/lib/api.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
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
|
+
#worker
|
|
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
|
+
}
|
|
59
|
+
|
|
60
|
+
async #sendCommand (command, params = {}) {
|
|
61
|
+
const operationId = randomUUID()
|
|
62
|
+
|
|
63
|
+
this.#worker.postMessage({ operationId, command, params })
|
|
64
|
+
const [message] = await once(this, operationId)
|
|
65
|
+
|
|
66
|
+
const { error, data } = message
|
|
67
|
+
if (error !== null) {
|
|
68
|
+
throw new Error(error)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return JSON.parse(data)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
class RuntimeApi {
|
|
76
|
+
#services
|
|
77
|
+
#dispatcher
|
|
78
|
+
|
|
79
|
+
constructor (services, dispatcher) {
|
|
80
|
+
this.#services = services
|
|
81
|
+
this.#dispatcher = dispatcher
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async startListening (parentPort) {
|
|
85
|
+
parentPort.on('message', async (message) => {
|
|
86
|
+
const command = message?.command
|
|
87
|
+
if (command) {
|
|
88
|
+
const res = await this.#executeCommand(message)
|
|
89
|
+
parentPort.postMessage(res)
|
|
90
|
+
|
|
91
|
+
if (command === 'plt:stop-services') {
|
|
92
|
+
process.exit() // Exit the worker thread.
|
|
93
|
+
}
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
await this.#handleProcessLevelEvent(message)
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async #handleProcessLevelEvent (message) {
|
|
101
|
+
for (const service of this.#services.values()) {
|
|
102
|
+
await service.handleProcessLevelEvent(message)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async #executeCommand (message) {
|
|
107
|
+
const { operationId, command, params } = message
|
|
108
|
+
try {
|
|
109
|
+
const res = await this.#runCommandHandler(command, params)
|
|
110
|
+
return { operationId, error: null, data: JSON.stringify(res || null) }
|
|
111
|
+
} catch (err) {
|
|
112
|
+
return { operationId, error: err.message }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async #runCommandHandler (command, params) {
|
|
117
|
+
switch (command) {
|
|
118
|
+
case 'plt:start-services':
|
|
119
|
+
return this.#startServices(params)
|
|
120
|
+
case 'plt:stop-services':
|
|
121
|
+
return this.#stopServices(params)
|
|
122
|
+
case 'plt:restart-services':
|
|
123
|
+
return this.#restartServices(params)
|
|
124
|
+
case 'plt:get-services':
|
|
125
|
+
return this.#getServices(params)
|
|
126
|
+
case 'plt:get-service-details':
|
|
127
|
+
return this.#getServiceDetails(params)
|
|
128
|
+
case 'plt:get-service-config':
|
|
129
|
+
return this.#getServiceConfig(params)
|
|
130
|
+
case 'plt:start-service':
|
|
131
|
+
return this.#startService(params)
|
|
132
|
+
case 'plt:stop-service':
|
|
133
|
+
return this.#stopService(params)
|
|
134
|
+
case 'plt:inject':
|
|
135
|
+
return this.#inject(params)
|
|
136
|
+
/* c8 ignore next 2 */
|
|
137
|
+
default:
|
|
138
|
+
throw new Error(`Unknown Runtime API command: '${command}'`)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async #startServices () {
|
|
143
|
+
let entrypointUrl = null
|
|
144
|
+
for (const service of this.#services.values()) {
|
|
145
|
+
await service.start()
|
|
146
|
+
|
|
147
|
+
if (service.appConfig.entrypoint) {
|
|
148
|
+
entrypointUrl = service.server.url
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const serviceUrl = new URL(service.appConfig.localUrl)
|
|
152
|
+
this.#dispatcher.route(serviceUrl.host, service.server)
|
|
153
|
+
}
|
|
154
|
+
return entrypointUrl
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async #stopServices () {
|
|
158
|
+
for (const service of this.#services.values()) {
|
|
159
|
+
const serviceStatus = service.getStatus()
|
|
160
|
+
if (serviceStatus === 'started') {
|
|
161
|
+
await service.stop()
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async #restartServices () {
|
|
167
|
+
let entrypointUrl = null
|
|
168
|
+
for (const service of this.#services.values()) {
|
|
169
|
+
if (service.server) {
|
|
170
|
+
await service.restart(true)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (service.appConfig.entrypoint) {
|
|
174
|
+
entrypointUrl = service.server.url
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const serviceUrl = new URL(service.appConfig.localUrl)
|
|
178
|
+
this.#dispatcher.route(serviceUrl.host, service.server)
|
|
179
|
+
}
|
|
180
|
+
return entrypointUrl
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#getServices () {
|
|
184
|
+
const services = { services: [] }
|
|
185
|
+
|
|
186
|
+
for (const service of this.#services.values()) {
|
|
187
|
+
const serviceId = service.appConfig.id
|
|
188
|
+
const serviceDetails = this.#getServiceDetails({ id: serviceId })
|
|
189
|
+
if (serviceDetails.entrypoint) {
|
|
190
|
+
services.entrypoint = serviceId
|
|
191
|
+
}
|
|
192
|
+
services.services.push(serviceDetails)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return services
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
#getServiceById (id) {
|
|
199
|
+
const service = this.#services.get(id)
|
|
200
|
+
|
|
201
|
+
if (!service) {
|
|
202
|
+
throw new Error(`Service with id '${id}' not found`)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return service
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
#getServiceDetails ({ id }) {
|
|
209
|
+
const service = this.#getServiceById(id)
|
|
210
|
+
const status = service.getStatus()
|
|
211
|
+
|
|
212
|
+
const { entrypoint, dependencies, localUrl } = service.appConfig
|
|
213
|
+
return { id, status, localUrl, entrypoint, dependencies }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#getServiceConfig ({ id }) {
|
|
217
|
+
const service = this.#getServiceById(id)
|
|
218
|
+
|
|
219
|
+
const { config } = service
|
|
220
|
+
if (!config) {
|
|
221
|
+
throw new Error(`Service with id '${id}' is not started`)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return config.configManager.current
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async #startService ({ id }) {
|
|
228
|
+
const service = this.#getServiceById(id)
|
|
229
|
+
await service.start()
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async #stopService ({ id }) {
|
|
233
|
+
const service = this.#getServiceById(id)
|
|
234
|
+
await service.stop()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async #inject ({ id, injectParams }) {
|
|
238
|
+
const service = this.#getServiceById(id)
|
|
239
|
+
|
|
240
|
+
const serviceStatus = service.getStatus()
|
|
241
|
+
if (serviceStatus !== 'started') {
|
|
242
|
+
throw new Error(`Service with id '${id}' is not started`)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const res = await service.server.inject(injectParams)
|
|
246
|
+
// Return only serializable properties.
|
|
247
|
+
return {
|
|
248
|
+
statusCode: res.statusCode,
|
|
249
|
+
statusMessage: res.statusMessage,
|
|
250
|
+
headers: res.headers,
|
|
251
|
+
body: res.body,
|
|
252
|
+
payload: res.payload
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
module.exports = { RuntimeApi, RuntimeApiClient }
|
package/lib/app.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const { once } = require('node:events')
|
|
3
|
-
const { dirname } = require('node:path')
|
|
3
|
+
const { dirname, basename } = require('node:path')
|
|
4
4
|
const { FileWatcher } = require('@platformatic/utils')
|
|
5
5
|
const {
|
|
6
6
|
buildServer,
|
|
@@ -27,6 +27,14 @@ class PlatformaticApp {
|
|
|
27
27
|
this.#logger = logger
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
getStatus () {
|
|
31
|
+
if (this.#started) {
|
|
32
|
+
return 'started'
|
|
33
|
+
} else {
|
|
34
|
+
return 'stopped'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
async restart (force) {
|
|
31
39
|
if (this.#restarting) {
|
|
32
40
|
return
|
|
@@ -54,7 +62,9 @@ class PlatformaticApp {
|
|
|
54
62
|
throw new Error('application is already started')
|
|
55
63
|
}
|
|
56
64
|
|
|
57
|
-
|
|
65
|
+
if (!this.#restarting) {
|
|
66
|
+
await this.#initializeConfig()
|
|
67
|
+
}
|
|
58
68
|
const { configManager } = this.config
|
|
59
69
|
const config = configManager.current
|
|
60
70
|
|
|
@@ -77,9 +87,6 @@ class PlatformaticApp {
|
|
|
77
87
|
this.#logAndExit(err)
|
|
78
88
|
}
|
|
79
89
|
|
|
80
|
-
this.server.platformatic.configManager = configManager
|
|
81
|
-
this.server.platformatic.config = config
|
|
82
|
-
|
|
83
90
|
if (config.plugins !== undefined && this.#originalWatch !== false) {
|
|
84
91
|
this.#startFileWatching()
|
|
85
92
|
}
|
|
@@ -110,26 +117,11 @@ class PlatformaticApp {
|
|
|
110
117
|
this.#started = false
|
|
111
118
|
}
|
|
112
119
|
|
|
113
|
-
async handleProcessLevelEvent ({
|
|
114
|
-
if (msg === 'plt:start') {
|
|
115
|
-
await this.start()
|
|
116
|
-
return
|
|
117
|
-
}
|
|
118
|
-
|
|
120
|
+
async handleProcessLevelEvent ({ signal, err }) {
|
|
119
121
|
if (!this.server) {
|
|
120
122
|
return false
|
|
121
123
|
}
|
|
122
124
|
|
|
123
|
-
if (msg === 'plt:restart') {
|
|
124
|
-
await this.restart(true)
|
|
125
|
-
return
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (msg === 'plt:stop') {
|
|
129
|
-
await this.stop()
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
|
|
133
125
|
if (signal === 'SIGUSR2') {
|
|
134
126
|
this.server.log.info('reloading configuration')
|
|
135
127
|
await this.restart()
|
|
@@ -161,49 +153,43 @@ class PlatformaticApp {
|
|
|
161
153
|
return appConfig.localServiceEnvVars.get(key)
|
|
162
154
|
}
|
|
163
155
|
})
|
|
164
|
-
const {
|
|
156
|
+
const { configManager } = this.config
|
|
165
157
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
158
|
+
function applyOverrides () {
|
|
159
|
+
if (appConfig._configOverrides instanceof Map) {
|
|
160
|
+
try {
|
|
161
|
+
appConfig._configOverrides.forEach((value, key) => {
|
|
162
|
+
if (typeof key !== 'string') {
|
|
163
|
+
throw new Error('config path must be a string.')
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const parts = key.split('.')
|
|
167
|
+
let next = configManager.current
|
|
168
|
+
let obj
|
|
169
|
+
|
|
170
|
+
for (let i = 0; next !== undefined && i < parts.length; ++i) {
|
|
171
|
+
obj = next
|
|
172
|
+
next = obj[parts[i]]
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (next !== undefined) {
|
|
176
|
+
obj[parts.at(-1)] = value
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
} catch (err) {
|
|
180
|
+
configManager.stopWatching()
|
|
181
|
+
throw err
|
|
182
|
+
}
|
|
189
183
|
}
|
|
190
184
|
}
|
|
191
185
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.#hotReload = args.hotReload && this.appConfig.hotReload
|
|
186
|
+
applyOverrides()
|
|
195
187
|
|
|
196
|
-
|
|
197
|
-
if (this.#hotReload) {
|
|
198
|
-
this.#hotReload = configManager.current.plugins.hotReload
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
configManager.current.plugins.hotReload = false
|
|
202
|
-
}
|
|
188
|
+
this.#hotReload = this.appConfig.hotReload
|
|
203
189
|
|
|
204
190
|
configManager.on('update', async (newConfig) => {
|
|
205
|
-
/* c8 ignore next 4 */
|
|
206
191
|
this.server.platformatic.config = newConfig
|
|
192
|
+
applyOverrides()
|
|
207
193
|
this.server.log.debug('config changed')
|
|
208
194
|
this.server.log.trace({ newConfig }, 'new config')
|
|
209
195
|
await this.restart()
|
|
@@ -228,11 +214,13 @@ class PlatformaticApp {
|
|
|
228
214
|
#startFileWatching () {
|
|
229
215
|
const server = this.server
|
|
230
216
|
const { configManager } = server.platformatic
|
|
217
|
+
// TODO FileWatcher and ConfigManager both watch the configuration file
|
|
218
|
+
// we should remove the watching from the ConfigManager
|
|
231
219
|
const fileWatcher = new FileWatcher({
|
|
232
220
|
path: dirname(configManager.fullPath),
|
|
233
221
|
/* c8 ignore next 2 */
|
|
234
222
|
allowToWatch: this.#originalWatch?.allow,
|
|
235
|
-
watchIgnore: this.#originalWatch?.ignore
|
|
223
|
+
watchIgnore: [...(this.#originalWatch?.ignore || []), basename(configManager.fullPath)]
|
|
236
224
|
})
|
|
237
225
|
|
|
238
226
|
fileWatcher.on('update', async () => {
|
package/lib/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const { readFile, readdir } = require('node:fs/promises')
|
|
3
|
-
const { join, resolve: pathResolve } = require('node:path')
|
|
3
|
+
const { basename, join, resolve: pathResolve } = require('node:path')
|
|
4
4
|
const Topo = require('@hapi/topo')
|
|
5
5
|
const ConfigManager = require('@platformatic/config')
|
|
6
6
|
const { schema } = require('./schema')
|
|
@@ -101,7 +101,7 @@ async function parseClientsAndComposer (configManager) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
if (dep.origin === `{${err.key}}`) {
|
|
104
|
-
service.localServiceEnvVars.set(err.key,
|
|
104
|
+
service.localServiceEnvVars.set(err.key, `http://${clientName}.plt.local`)
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
}
|
|
@@ -118,6 +118,7 @@ async function parseClientsAndComposer (configManager) {
|
|
|
118
118
|
const promises = parsed.clients.map((client) => {
|
|
119
119
|
// eslint-disable-next-line no-async-promise-executor
|
|
120
120
|
return new Promise(async (resolve, reject) => {
|
|
121
|
+
let clientName = client.serviceId ?? ''
|
|
121
122
|
let clientUrl
|
|
122
123
|
let missingKey
|
|
123
124
|
|
|
@@ -135,11 +136,15 @@ async function parseClientsAndComposer (configManager) {
|
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
const isLocal = missingKey && client.url === `{${missingKey}}`
|
|
138
|
-
|
|
139
|
-
const clientPackageJson = join(clientAbsolutePath, 'package.json')
|
|
140
|
-
const clientMetadata = JSON.parse(await readFile(clientPackageJson, 'utf8'))
|
|
139
|
+
|
|
141
140
|
/* c8 ignore next 20 - unclear why c8 is unhappy for nearly 20 lines here */
|
|
142
|
-
|
|
141
|
+
if (!clientName) {
|
|
142
|
+
const clientAbsolutePath = pathResolve(service.path, client.path)
|
|
143
|
+
const clientPackageJson = join(clientAbsolutePath, 'package.json')
|
|
144
|
+
const clientMetadata = JSON.parse(await readFile(clientPackageJson, 'utf8'))
|
|
145
|
+
|
|
146
|
+
clientName = clientMetadata.name ?? ''
|
|
147
|
+
}
|
|
143
148
|
|
|
144
149
|
if (clientUrl === undefined) {
|
|
145
150
|
// Combine the service name with the client name to avoid collisions
|
|
@@ -156,8 +161,8 @@ async function parseClientsAndComposer (configManager) {
|
|
|
156
161
|
|
|
157
162
|
const dependency = configManager.current.serviceMap.get(clientName)
|
|
158
163
|
|
|
164
|
+
/* c8 ignore next 4 */
|
|
159
165
|
if (dependency === undefined) {
|
|
160
|
-
/* c8 ignore next 3 */
|
|
161
166
|
reject(new Error(`service '${service.id}' has unknown dependency: '${clientName}'`))
|
|
162
167
|
return
|
|
163
168
|
}
|
|
@@ -206,4 +211,27 @@ platformaticRuntime.configManagerConfig = {
|
|
|
206
211
|
}
|
|
207
212
|
}
|
|
208
213
|
|
|
209
|
-
|
|
214
|
+
async function wrapConfigInRuntimeConfig ({ configManager, args }) {
|
|
215
|
+
/* c8 ignore next */
|
|
216
|
+
const id = basename(configManager.dirname) || 'main'
|
|
217
|
+
const wrapperConfig = {
|
|
218
|
+
$schema: schema.$id,
|
|
219
|
+
entrypoint: id,
|
|
220
|
+
allowCycles: false,
|
|
221
|
+
hotReload: true,
|
|
222
|
+
services: [
|
|
223
|
+
{
|
|
224
|
+
id,
|
|
225
|
+
path: configManager.dirname,
|
|
226
|
+
config: configManager.fullPath
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
const cm = new ConfigManager({ source: wrapperConfig, schema })
|
|
231
|
+
|
|
232
|
+
await _transformConfig(cm)
|
|
233
|
+
await cm.parseAndValidate()
|
|
234
|
+
return cm
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
module.exports = { platformaticRuntime, wrapConfigInRuntimeConfig }
|