@platformatic/runtime 0.26.1 → 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/composerApp/api1.json +65 -0
- package/fixtures/composerApp/platformatic.composer.json +23 -0
- package/fixtures/configs/invalid-schema-type.config.json +19 -0
- package/fixtures/configs/monorepo-client-without-id.json +24 -0
- package/fixtures/configs/no-schema.config.json +18 -0
- package/fixtures/dbApp/platformatic.db.json +22 -0
- 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/fixtures/start-command.js +9 -0
- package/fixtures/starter.js +9 -0
- package/index.js +5 -25
- package/lib/api.js +257 -0
- package/lib/app.js +62 -68
- package/lib/build-server.js +28 -0
- package/lib/config.js +36 -8
- package/lib/loader.mjs +21 -13
- package/lib/start.js +7 -19
- package/lib/unified-api.js +198 -0
- package/lib/worker.js +29 -53
- package/package.json +16 -13
- package/test/api.test.js +294 -0
- package/test/app.test.js +76 -13
- package/test/cli/start.test.mjs +4 -4
- package/test/config.test.js +9 -0
- package/test/unified-api.test.js +354 -0
- package/test/worker.test.js +0 -21
package/lib/app.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const { once } = require('node:events')
|
|
3
|
-
const { dirname } = require('node:path')
|
|
4
|
-
const {
|
|
5
|
-
addLoggerToTheConfig
|
|
6
|
-
} = require('@platformatic/service')
|
|
3
|
+
const { dirname, basename } = require('node:path')
|
|
4
|
+
const { FileWatcher } = require('@platformatic/utils')
|
|
7
5
|
const {
|
|
8
6
|
buildServer,
|
|
9
7
|
loadConfig
|
|
10
|
-
} = require('
|
|
11
|
-
const { FileWatcher } = require('@platformatic/utils')
|
|
8
|
+
} = require('./unified-api')
|
|
12
9
|
|
|
13
10
|
class PlatformaticApp {
|
|
14
11
|
#hotReload
|
|
@@ -16,8 +13,9 @@ class PlatformaticApp {
|
|
|
16
13
|
#restarting
|
|
17
14
|
#started
|
|
18
15
|
#originalWatch
|
|
16
|
+
#logger
|
|
19
17
|
|
|
20
|
-
constructor (appConfig, loaderPort) {
|
|
18
|
+
constructor (appConfig, loaderPort, logger) {
|
|
21
19
|
this.appConfig = appConfig
|
|
22
20
|
this.config = null
|
|
23
21
|
this.#hotReload = false
|
|
@@ -26,6 +24,15 @@ class PlatformaticApp {
|
|
|
26
24
|
this.server = null
|
|
27
25
|
this.#started = false
|
|
28
26
|
this.#originalWatch = null
|
|
27
|
+
this.#logger = logger
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getStatus () {
|
|
31
|
+
if (this.#started) {
|
|
32
|
+
return 'started'
|
|
33
|
+
} else {
|
|
34
|
+
return 'stopped'
|
|
35
|
+
}
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
async restart (force) {
|
|
@@ -55,12 +62,15 @@ class PlatformaticApp {
|
|
|
55
62
|
throw new Error('application is already started')
|
|
56
63
|
}
|
|
57
64
|
|
|
58
|
-
|
|
65
|
+
if (!this.#restarting) {
|
|
66
|
+
await this.#initializeConfig()
|
|
67
|
+
}
|
|
59
68
|
const { configManager } = this.config
|
|
60
69
|
const config = configManager.current
|
|
61
70
|
|
|
62
71
|
this.#originalWatch = config.watch
|
|
63
72
|
config.watch = false
|
|
73
|
+
this.#setuplogger(configManager)
|
|
64
74
|
|
|
65
75
|
try {
|
|
66
76
|
// If this is a restart, have the fastify server restart itself. If this
|
|
@@ -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,53 +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
|
-
if (configManager.current.server) {
|
|
194
|
-
addLoggerToTheConfig(configManager.current)
|
|
195
|
-
configManager.current.server.logger.name = this.appConfig.id
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
this.#hotReload = args.hotReload && this.appConfig.hotReload
|
|
186
|
+
applyOverrides()
|
|
199
187
|
|
|
200
|
-
|
|
201
|
-
if (this.#hotReload) {
|
|
202
|
-
this.#hotReload = configManager.current.plugins.hotReload
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
configManager.current.plugins.hotReload = false
|
|
206
|
-
}
|
|
188
|
+
this.#hotReload = this.appConfig.hotReload
|
|
207
189
|
|
|
208
190
|
configManager.on('update', async (newConfig) => {
|
|
209
|
-
/* c8 ignore next 4 */
|
|
210
191
|
this.server.platformatic.config = newConfig
|
|
192
|
+
applyOverrides()
|
|
211
193
|
this.server.log.debug('config changed')
|
|
212
194
|
this.server.log.trace({ newConfig }, 'new config')
|
|
213
195
|
await this.restart()
|
|
@@ -219,14 +201,26 @@ class PlatformaticApp {
|
|
|
219
201
|
})
|
|
220
202
|
}
|
|
221
203
|
|
|
204
|
+
#setuplogger (configManager) {
|
|
205
|
+
// Set the logger if not present (and the config supports it).
|
|
206
|
+
if (configManager.current.server) {
|
|
207
|
+
const childLogger = this.#logger.child({
|
|
208
|
+
name: this.appConfig.id
|
|
209
|
+
}, { level: configManager.current.server.logger?.level || 'info' })
|
|
210
|
+
configManager.current.server.logger = childLogger
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
222
214
|
#startFileWatching () {
|
|
223
215
|
const server = this.server
|
|
224
216
|
const { configManager } = server.platformatic
|
|
217
|
+
// TODO FileWatcher and ConfigManager both watch the configuration file
|
|
218
|
+
// we should remove the watching from the ConfigManager
|
|
225
219
|
const fileWatcher = new FileWatcher({
|
|
226
220
|
path: dirname(configManager.fullPath),
|
|
227
221
|
/* c8 ignore next 2 */
|
|
228
222
|
allowToWatch: this.#originalWatch?.allow,
|
|
229
|
-
watchIgnore: this.#originalWatch?.ignore
|
|
223
|
+
watchIgnore: [...(this.#originalWatch?.ignore || []), basename(configManager.fullPath)]
|
|
230
224
|
})
|
|
231
225
|
|
|
232
226
|
fileWatcher.on('update', async () => {
|
|
@@ -253,7 +247,7 @@ class PlatformaticApp {
|
|
|
253
247
|
|
|
254
248
|
#logAndExit (err) {
|
|
255
249
|
this.config?.configManager?.stopWatching()
|
|
256
|
-
|
|
250
|
+
this.#logger.error({ err })
|
|
257
251
|
process.exit(1)
|
|
258
252
|
}
|
|
259
253
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const ConfigManager = require('@platformatic/config')
|
|
3
|
+
const { platformaticRuntime } = require('./config')
|
|
4
|
+
const { startWithConfig } = require('./start')
|
|
5
|
+
|
|
6
|
+
async function buildServer (options = {}) {
|
|
7
|
+
if (!options.configManager) {
|
|
8
|
+
// Instantiate a new config manager from the current options.
|
|
9
|
+
const cm = new ConfigManager({
|
|
10
|
+
...platformaticRuntime.configManagerConfig,
|
|
11
|
+
source: options
|
|
12
|
+
})
|
|
13
|
+
await cm.parseAndValidate()
|
|
14
|
+
|
|
15
|
+
if (typeof options === 'string') {
|
|
16
|
+
options = { configManager: cm }
|
|
17
|
+
} else {
|
|
18
|
+
options.configManager = cm
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// The transformConfig() function can't be sent between threads.
|
|
23
|
+
delete options.configManager._transformConfig
|
|
24
|
+
|
|
25
|
+
return startWithConfig(options.configManager)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = { buildServer }
|
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 }
|
package/lib/loader.mjs
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { createRequire, isBuiltin } from 'node:module'
|
|
2
2
|
import { dirname, isAbsolute, resolve as pathResolve } from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
4
4
|
const require = createRequire(import.meta.url)
|
|
5
|
-
const thisFile = fileURLToPath(import.meta.url)
|
|
6
5
|
const isWindows = process.platform === 'win32'
|
|
7
6
|
let timestamp = process.hrtime.bigint()
|
|
8
7
|
let port
|
|
9
8
|
|
|
9
|
+
/* c8 ignore next 3 - c8 upgrade marked many existing things as uncovered */
|
|
10
10
|
function bustEsmCache () {
|
|
11
11
|
timestamp = process.hrtime.bigint()
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/* c8 ignore next 11 - c8 upgrade marked many existing things as uncovered */
|
|
14
15
|
function clearCjsCache () {
|
|
15
16
|
// This evicts all of the modules from the require() cache.
|
|
16
17
|
// Note: This does not clean up children references to the deleted module.
|
|
@@ -25,6 +26,7 @@ function clearCjsCache () {
|
|
|
25
26
|
|
|
26
27
|
function isRelativePath (p) {
|
|
27
28
|
// This function is extracted from Node core, so it should work.
|
|
29
|
+
/* c8 ignore next - c8 upgrade marked many existing things as uncovered */
|
|
28
30
|
return p.charAt(0) === '.' &&
|
|
29
31
|
/* c8 ignore next 9 */
|
|
30
32
|
(
|
|
@@ -38,15 +40,15 @@ function isRelativePath (p) {
|
|
|
38
40
|
)
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
function
|
|
42
|
-
// Convert the specifier into an absolute path if possible. If the
|
|
43
|
-
// cannot be converted to a path (for example for a core module),
|
|
44
|
-
// null.
|
|
43
|
+
function specifierToFileUrl (specifier, referencingModuleId) {
|
|
44
|
+
// Convert the specifier into an absolute path URL if possible. If the
|
|
45
|
+
// specifier cannot be converted to a path (for example for a core module),
|
|
46
|
+
// then return null.
|
|
45
47
|
try {
|
|
46
48
|
const url = new URL(specifier)
|
|
47
49
|
|
|
48
50
|
if (url.protocol === 'file:') {
|
|
49
|
-
|
|
51
|
+
return url.href
|
|
50
52
|
} else {
|
|
51
53
|
return null
|
|
52
54
|
}
|
|
@@ -54,12 +56,14 @@ function specifierToPath (specifier, referencingModuleId) {
|
|
|
54
56
|
// Ignore error.
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
/* c8 ignore next 3 - c8 upgrade marked many existing things as uncovered */
|
|
57
60
|
if (isBuiltin(specifier)) {
|
|
58
61
|
return null
|
|
59
62
|
}
|
|
60
63
|
|
|
64
|
+
/* c8 ignore next 3 */
|
|
61
65
|
if (isAbsolute(specifier)) {
|
|
62
|
-
return specifier
|
|
66
|
+
return pathToFileURL(specifier).href
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
/* c8 ignore next 3 */
|
|
@@ -67,31 +71,35 @@ function specifierToPath (specifier, referencingModuleId) {
|
|
|
67
71
|
throw new Error(`cannot map '${specifier}' to an absolute path`)
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
/* c8 ignore next 5 - c8 upgrade marked many existing things as uncovered */
|
|
70
75
|
if (isRelativePath(specifier)) {
|
|
71
|
-
return
|
|
76
|
+
return pathToFileURL(
|
|
77
|
+
pathResolve(dirname(fileURLToPath(referencingModuleId)), specifier)
|
|
78
|
+
).href
|
|
72
79
|
} else {
|
|
73
80
|
// The specifier is something in node_modules/.
|
|
74
81
|
const req = createRequire(referencingModuleId)
|
|
75
82
|
|
|
76
|
-
return req.resolve(specifier)
|
|
83
|
+
return pathToFileURL(req.resolve(specifier)).href
|
|
77
84
|
}
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
export async function resolve (specifier, context, nextResolve) {
|
|
81
|
-
const
|
|
88
|
+
const url = specifierToFileUrl(specifier, context.parentURL)
|
|
82
89
|
|
|
83
90
|
// If the specifier could not be mapped to a file, or the path is this file,
|
|
84
91
|
// then don't do anything.
|
|
85
|
-
if (typeof
|
|
92
|
+
if (typeof url !== 'string' || url === import.meta.url) {
|
|
86
93
|
return nextResolve(specifier, context)
|
|
87
94
|
}
|
|
88
95
|
|
|
89
|
-
return nextResolve(`${
|
|
96
|
+
return nextResolve(`${url}?ts=${timestamp}`, context)
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
export function globalPreload (context) {
|
|
93
100
|
port = context.port
|
|
94
101
|
port.on('message', () => {
|
|
102
|
+
/* c8 ignore next 3 - c8 upgrade marked many existing things as uncovered */
|
|
95
103
|
bustEsmCache()
|
|
96
104
|
clearCjsCache()
|
|
97
105
|
port.postMessage('plt:cache-cleared')
|
package/lib/start.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const { once } = require('node:events')
|
|
3
3
|
const { join } = require('node:path')
|
|
4
|
+
const { pathToFileURL } = require('node:url')
|
|
4
5
|
const { Worker } = require('node:worker_threads')
|
|
5
6
|
const closeWithGrace = require('close-with-grace')
|
|
6
7
|
const { loadConfig } = require('@platformatic/service')
|
|
7
8
|
const { platformaticRuntime } = require('./config')
|
|
8
|
-
const
|
|
9
|
+
const { RuntimeApiClient } = require('./api.js')
|
|
10
|
+
const kLoaderFile = pathToFileURL(join(__dirname, 'loader.mjs')).href
|
|
9
11
|
const kWorkerFile = join(__dirname, 'worker.js')
|
|
10
12
|
const kWorkerExecArgv = [
|
|
11
13
|
'--no-warnings',
|
|
@@ -35,8 +37,8 @@ async function startWithConfig (configManager) {
|
|
|
35
37
|
configManager.fileWatcher?.stopWatching()
|
|
36
38
|
})
|
|
37
39
|
|
|
38
|
-
worker.on('error', (
|
|
39
|
-
|
|
40
|
+
worker.on('error', () => {
|
|
41
|
+
// the error is logged in the worker
|
|
40
42
|
process.exit(1)
|
|
41
43
|
})
|
|
42
44
|
|
|
@@ -56,22 +58,8 @@ async function startWithConfig (configManager) {
|
|
|
56
58
|
|
|
57
59
|
await once(worker, 'message') // plt:init
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
worker.postMessage({ msg: 'plt:start' })
|
|
62
|
-
const [msg] = await once(worker, 'message') // plt:started
|
|
63
|
-
|
|
64
|
-
return msg.url
|
|
65
|
-
},
|
|
66
|
-
async close () {
|
|
67
|
-
worker.postMessage({ msg: 'plt:stop' })
|
|
68
|
-
await once(worker, 'exit')
|
|
69
|
-
},
|
|
70
|
-
async restart () {
|
|
71
|
-
worker.postMessage({ msg: 'plt:restart' })
|
|
72
|
-
await once(worker, 'message') // plt:restarted
|
|
73
|
-
}
|
|
74
|
-
}
|
|
61
|
+
const runtimeApiClient = new RuntimeApiClient(worker)
|
|
62
|
+
return runtimeApiClient
|
|
75
63
|
}
|
|
76
64
|
|
|
77
65
|
module.exports = { start, startWithConfig }
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const { resolve } = require('node:path')
|
|
3
|
+
const parseArgs = require('minimist')
|
|
4
|
+
const ConfigManager = require('@platformatic/config')
|
|
5
|
+
const {
|
|
6
|
+
platformaticService,
|
|
7
|
+
buildServer,
|
|
8
|
+
loadConfig,
|
|
9
|
+
start,
|
|
10
|
+
schema: serviceSchema
|
|
11
|
+
} = require('@platformatic/service')
|
|
12
|
+
const {
|
|
13
|
+
schema: dbSchema,
|
|
14
|
+
platformaticDB
|
|
15
|
+
} = require('@platformatic/db')
|
|
16
|
+
const {
|
|
17
|
+
schema: composerSchema,
|
|
18
|
+
platformaticComposer
|
|
19
|
+
} = require('@platformatic/composer')
|
|
20
|
+
const { buildServer: runtimeBuildServer } = require('./build-server')
|
|
21
|
+
const { platformaticRuntime, wrapConfigInRuntimeConfig } = require('./config')
|
|
22
|
+
const { schema: runtimeSchema } = require('./schema')
|
|
23
|
+
const {
|
|
24
|
+
start: runtimeStart,
|
|
25
|
+
startWithConfig: runtimeStartWithConfig
|
|
26
|
+
} = require('./start')
|
|
27
|
+
|
|
28
|
+
const kSupportedAppTypes = new Set(['service', 'db', 'composer', 'runtime'])
|
|
29
|
+
|
|
30
|
+
async function tryGetConfigTypeFromSchema (config) {
|
|
31
|
+
/* c8 ignore next 6 - c8 is not seeing this as covered for some reason. */
|
|
32
|
+
if (typeof config === 'string') {
|
|
33
|
+
// Handle config file paths.
|
|
34
|
+
const loadedConfig = await loadConfig({}, ['-c', config], platformaticService)
|
|
35
|
+
|
|
36
|
+
config = loadedConfig.configManager.current
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const schema = config?.$schema
|
|
40
|
+
|
|
41
|
+
if (typeof schema !== 'string') {
|
|
42
|
+
throw new Error('configuration is missing a schema')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const configType = schema.split('/').pop()
|
|
46
|
+
|
|
47
|
+
if (!kSupportedAppTypes.has(configType)) {
|
|
48
|
+
throw new Error(`unknown configuration type: '${configType}'`)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return configType
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function getConfigType (args = [], directory) {
|
|
55
|
+
try {
|
|
56
|
+
// The config type was not specified, so we need to figure it out.
|
|
57
|
+
// Try to get the config file from the provided arguments.
|
|
58
|
+
let { config } = parseArgs(args, { alias: { c: 'config' } })
|
|
59
|
+
|
|
60
|
+
if (!config) {
|
|
61
|
+
// Couldn't get the config file from the arguments, so look in the
|
|
62
|
+
// provided directory (or current directory) for any recognized
|
|
63
|
+
// config files.
|
|
64
|
+
const searchDir = directory ?? process.cwd()
|
|
65
|
+
const configFile = await ConfigManager.findConfigFile(searchDir)
|
|
66
|
+
|
|
67
|
+
config = resolve(searchDir, configFile)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// At this point, we have the config file. However, several different
|
|
71
|
+
// file formats are supported, so use the config manager to parse the
|
|
72
|
+
// file (without worrying about the validity of the file). We can then
|
|
73
|
+
// use the $schema field to determine the config type.
|
|
74
|
+
const configManager = new ConfigManager({ source: config })
|
|
75
|
+
const configString = await configManager.load()
|
|
76
|
+
const parsedConfig = configManager._parser(configString)
|
|
77
|
+
|
|
78
|
+
return await tryGetConfigTypeFromSchema(parsedConfig)
|
|
79
|
+
} catch (err) {
|
|
80
|
+
const configFiles = ConfigManager.listConfigFiles()
|
|
81
|
+
const msg = `
|
|
82
|
+
Missing config file!
|
|
83
|
+
Be sure to have a config file with one of the following names:
|
|
84
|
+
${configFiles.map((s) => ' * ' + s).join('\n')}
|
|
85
|
+
Alternatively run "npm create platformatic@latest" to generate a basic plt service config.
|
|
86
|
+
`
|
|
87
|
+
|
|
88
|
+
throw new Error(msg, { cause: err })
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function getCurrentSchema (configType) {
|
|
93
|
+
if (configType === 'service') {
|
|
94
|
+
return serviceSchema.schema
|
|
95
|
+
} else if (configType === 'db') {
|
|
96
|
+
return dbSchema
|
|
97
|
+
} else if (configType === 'composer') {
|
|
98
|
+
return composerSchema
|
|
99
|
+
} else if (configType === 'runtime') {
|
|
100
|
+
return runtimeSchema
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
throw new Error(`unknown configuration type: '${configType}'`)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* c8 ignore next 10 - for some reason c8 is not seeing this as covered. */
|
|
107
|
+
async function _buildServer (options) {
|
|
108
|
+
const configType = await tryGetConfigTypeFromSchema(options)
|
|
109
|
+
const app = getApp(configType)
|
|
110
|
+
|
|
111
|
+
if (app === platformaticRuntime) {
|
|
112
|
+
return runtimeBuildServer(options)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return buildServer(options, app)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function getApp (configType) {
|
|
119
|
+
if (configType === 'service') {
|
|
120
|
+
return platformaticService
|
|
121
|
+
} else if (configType === 'db') {
|
|
122
|
+
return platformaticDB
|
|
123
|
+
} else if (configType === 'composer') {
|
|
124
|
+
return platformaticComposer
|
|
125
|
+
} else if (configType === 'runtime') {
|
|
126
|
+
return platformaticRuntime
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
throw new Error('unknown kind: ' + configType)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function _loadConfig (minimistConfig, args, configType, overrides) {
|
|
133
|
+
// If the config type was specified, then use that. Otherwise, compute it.
|
|
134
|
+
if (typeof configType !== 'string') {
|
|
135
|
+
configType = await getConfigType(args)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return loadConfig(minimistConfig, args, getApp(configType), overrides)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function _start (args) {
|
|
142
|
+
const configType = await getConfigType(args)
|
|
143
|
+
|
|
144
|
+
if (configType === 'runtime') {
|
|
145
|
+
return runtimeStart(args)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return start(getApp(configType), args)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function startCommand (args) {
|
|
152
|
+
try {
|
|
153
|
+
await _start(args)
|
|
154
|
+
} catch (err) {
|
|
155
|
+
logErrorAndExit(err)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function startCommandInRuntime (args) {
|
|
160
|
+
try {
|
|
161
|
+
const configType = await getConfigType(args)
|
|
162
|
+
const config = await _loadConfig({}, args, configType)
|
|
163
|
+
let runtime
|
|
164
|
+
|
|
165
|
+
if (configType === 'runtime') {
|
|
166
|
+
runtime = await runtimeStartWithConfig(config.configManager)
|
|
167
|
+
} else {
|
|
168
|
+
const wrappedConfig = await wrapConfigInRuntimeConfig(config)
|
|
169
|
+
runtime = await runtimeStartWithConfig(wrappedConfig)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return await runtime.start()
|
|
173
|
+
} catch (err) {
|
|
174
|
+
logErrorAndExit(err)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function logErrorAndExit (err) {
|
|
179
|
+
delete err?.stack
|
|
180
|
+
console.error(err?.message)
|
|
181
|
+
|
|
182
|
+
if (err?.cause) {
|
|
183
|
+
console.error(`${err.cause}`)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
process.exit(1)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = {
|
|
190
|
+
buildServer: _buildServer,
|
|
191
|
+
getConfigType,
|
|
192
|
+
getCurrentSchema,
|
|
193
|
+
loadConfig: _loadConfig,
|
|
194
|
+
start: _start,
|
|
195
|
+
startCommand,
|
|
196
|
+
startCommandInRuntime,
|
|
197
|
+
getApp
|
|
198
|
+
}
|