@platformatic/runtime 2.74.3 → 3.0.0-alpha.2
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 +1 -1
- package/index.d.ts +70 -21
- package/index.js +151 -18
- package/lib/config.js +160 -219
- package/lib/errors.js +150 -108
- package/lib/{generator/runtime-generator.js → generator.js} +36 -53
- package/lib/logger.js +7 -30
- package/lib/management-api.js +6 -56
- package/lib/runtime.js +233 -264
- package/lib/schema.js +2 -1
- package/lib/shared-http-cache.js +1 -1
- package/lib/upgrade.js +6 -4
- package/lib/utils.js +1 -48
- package/lib/worker/app.js +52 -68
- package/lib/worker/itc.js +16 -4
- package/lib/worker/main.js +6 -3
- package/lib/worker/messaging.js +2 -2
- package/package.json +22 -30
- package/schema.json +2 -1
- package/help/compile.txt +0 -8
- package/help/help.txt +0 -5
- package/help/start.txt +0 -21
- package/index.test-d.ts +0 -41
- package/lib/build-server.js +0 -67
- package/lib/compile.js +0 -108
- package/lib/generator/README.md +0 -32
- package/lib/generator/errors.js +0 -10
- package/lib/generator/runtime-generator.d.ts +0 -37
- package/lib/start.js +0 -211
- package/lib/worker/default-stackable.js +0 -33
- package/runtime.mjs +0 -54
package/lib/config.js
CHANGED
|
@@ -1,35 +1,124 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { join, resolve: resolvePath, isAbsolute } = require('node:path')
|
|
3
4
|
const { readdir } = require('node:fs/promises')
|
|
4
5
|
const { createRequire } = require('node:module')
|
|
5
|
-
const {
|
|
6
|
+
const { importStackableAndConfig, validationOptions } = require('@platformatic/basic')
|
|
6
7
|
const {
|
|
7
|
-
|
|
8
|
+
kMetadata,
|
|
8
9
|
omitProperties,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
loadModule,
|
|
11
|
+
runtimeUnwrappablePropertiesList,
|
|
12
|
+
findConfigurationFile,
|
|
13
|
+
loadConfigurationModule,
|
|
14
|
+
loadConfiguration,
|
|
15
|
+
extractModuleFromSchemaUrl
|
|
16
|
+
} = require('@platformatic/foundation')
|
|
17
|
+
const {
|
|
18
|
+
InspectAndInspectBrkError,
|
|
19
|
+
InvalidEntrypointError,
|
|
20
|
+
InvalidServicesWithWebError,
|
|
21
|
+
MissingEntrypointError,
|
|
22
|
+
InspectorPortError,
|
|
23
|
+
InspectorHostError
|
|
24
|
+
} = require('./errors')
|
|
15
25
|
const { schema } = require('./schema')
|
|
16
|
-
const upgrade = require('./upgrade')
|
|
17
|
-
|
|
26
|
+
const { upgrade } = require('./upgrade')
|
|
27
|
+
|
|
28
|
+
async function wrapInRuntimeConfig (config, context) {
|
|
29
|
+
let serviceId = 'main'
|
|
30
|
+
try {
|
|
31
|
+
const packageJson = join(config[kMetadata].root, 'package.json')
|
|
32
|
+
serviceId = require(packageJson).name || 'main'
|
|
33
|
+
|
|
34
|
+
if (serviceId.startsWith('@')) {
|
|
35
|
+
serviceId = serviceId.split('/')[1]
|
|
36
|
+
}
|
|
37
|
+
} catch (err) {
|
|
38
|
+
// on purpose, the package.json might be missing
|
|
39
|
+
}
|
|
18
40
|
|
|
19
|
-
|
|
20
|
-
const
|
|
41
|
+
// If the service supports its (so far, only @platformatic/service and descendants)
|
|
42
|
+
const { hostname, port, http2, https } = config.server ?? {}
|
|
43
|
+
const server = { hostname, port, http2, https }
|
|
44
|
+
const production = context?.isProduction ?? context?.production
|
|
45
|
+
|
|
46
|
+
// Important: do not change the order of the properties in this object
|
|
47
|
+
/* c8 ignore next */
|
|
48
|
+
const wrapped = {
|
|
49
|
+
$schema: schema.$id,
|
|
50
|
+
server,
|
|
51
|
+
watch: !production,
|
|
52
|
+
...omitProperties(config.runtime ?? {}, runtimeUnwrappablePropertiesList),
|
|
53
|
+
entrypoint: serviceId,
|
|
54
|
+
services: [
|
|
55
|
+
{
|
|
56
|
+
id: serviceId,
|
|
57
|
+
path: config[kMetadata].root,
|
|
58
|
+
config: config[kMetadata].path
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
21
62
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
63
|
+
return loadConfiguration(wrapped, context?.schema ?? schema, {
|
|
64
|
+
validationOptions,
|
|
65
|
+
transform,
|
|
66
|
+
upgrade,
|
|
67
|
+
replaceEnv: true,
|
|
68
|
+
root: config[kMetadata].root,
|
|
69
|
+
...context
|
|
26
70
|
})
|
|
27
|
-
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function parseInspectorOptions (config, inspect, inspectBreak) {
|
|
74
|
+
const hasInspect = inspect != null
|
|
75
|
+
const hasInspectBrk = inspectBreak != null
|
|
76
|
+
|
|
77
|
+
if (hasInspect && hasInspectBrk) {
|
|
78
|
+
throw new InspectAndInspectBrkError()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const value = inspectBreak ?? inspect
|
|
82
|
+
|
|
83
|
+
if (!value) {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let host = '127.0.0.1'
|
|
88
|
+
let port = 9229
|
|
89
|
+
|
|
90
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
91
|
+
const splitAt = value.lastIndexOf(':')
|
|
92
|
+
|
|
93
|
+
if (splitAt === -1) {
|
|
94
|
+
port = value
|
|
95
|
+
} else {
|
|
96
|
+
host = value.substring(0, splitAt)
|
|
97
|
+
port = value.substring(splitAt + 1)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
port = Number.parseInt(port, 10)
|
|
101
|
+
|
|
102
|
+
if (!(port === 0 || (port >= 1024 && port <= 65535))) {
|
|
103
|
+
throw new InspectorPortError()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!host) {
|
|
107
|
+
throw new InspectorHostError()
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
config.inspectorOptions = { host, port, breakFirstLine: hasInspectBrk, watchDisabled: !!config.watch }
|
|
112
|
+
config.watch = false
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function transform (config, _, context) {
|
|
116
|
+
const production = context?.isProduction ?? context?.production
|
|
28
117
|
|
|
29
118
|
let services
|
|
30
119
|
if (config.web?.length) {
|
|
31
120
|
if (config.services?.length) {
|
|
32
|
-
throw new
|
|
121
|
+
throw new InvalidServicesWithWebError()
|
|
33
122
|
}
|
|
34
123
|
|
|
35
124
|
services = config.web
|
|
@@ -48,12 +137,7 @@ async function _transformConfig (configManager, args) {
|
|
|
48
137
|
const { exclude = [], mappings = {} } = config.autoload
|
|
49
138
|
let { path } = config.autoload
|
|
50
139
|
|
|
51
|
-
|
|
52
|
-
// while we are upgrading the config
|
|
53
|
-
if (configManager._fixPaths) {
|
|
54
|
-
path = pathResolve(configManager.dirname, path)
|
|
55
|
-
}
|
|
56
|
-
|
|
140
|
+
path = resolvePath(config[kMetadata].root, path)
|
|
57
141
|
const entries = await readdir(path, { withFileTypes: true })
|
|
58
142
|
|
|
59
143
|
for (let i = 0; i < entries.length; ++i) {
|
|
@@ -68,7 +152,7 @@ async function _transformConfig (configManager, args) {
|
|
|
68
152
|
const entryPath = join(path, entry.name)
|
|
69
153
|
|
|
70
154
|
let config
|
|
71
|
-
const configFilename = mapping.config ?? (await
|
|
155
|
+
const configFilename = mapping.config ?? (await findConfigurationFile(entryPath))
|
|
72
156
|
|
|
73
157
|
if (typeof configFilename === 'string') {
|
|
74
158
|
config = join(entryPath, configFilename)
|
|
@@ -85,8 +169,9 @@ async function _transformConfig (configManager, args) {
|
|
|
85
169
|
}
|
|
86
170
|
}
|
|
87
171
|
|
|
88
|
-
|
|
89
|
-
|
|
172
|
+
config.serviceMap = new Map()
|
|
173
|
+
config.inspectorOptions = undefined
|
|
174
|
+
parseInspectorOptions(config, context?.inspect, context?.inspectBreak)
|
|
90
175
|
|
|
91
176
|
let hasValidEntrypoint = false
|
|
92
177
|
|
|
@@ -96,64 +181,44 @@ async function _transformConfig (configManager, args) {
|
|
|
96
181
|
// We need to have absolute paths here, ot the `loadConfig` will fail
|
|
97
182
|
// Make sure we don't resolve if env var was not replaced
|
|
98
183
|
if (service.path && !isAbsolute(service.path) && !service.path.match(/^\{.*\}$/)) {
|
|
99
|
-
service.path =
|
|
184
|
+
service.path = resolvePath(config[kMetadata].root, service.path)
|
|
100
185
|
}
|
|
101
186
|
|
|
102
|
-
if (
|
|
103
|
-
service.config =
|
|
187
|
+
if (service.path && service.config) {
|
|
188
|
+
service.config = resolvePath(service.path, service.config)
|
|
104
189
|
}
|
|
105
190
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
})
|
|
121
|
-
} catch (err) {
|
|
122
|
-
// Fallback if for any reason a dependency is not found
|
|
123
|
-
try {
|
|
124
|
-
const manager = new ConfigManager({ source: pathResolve(service.path, service.config) })
|
|
125
|
-
await manager.parse()
|
|
126
|
-
const config = manager.current
|
|
127
|
-
const type = config.$schema ? ConfigManager.matchKnownSchema(config.$schema) : undefined
|
|
128
|
-
service.type = type
|
|
129
|
-
service.isPLTService = !!config.isPLTService
|
|
130
|
-
} catch (err) {
|
|
131
|
-
// This should not happen, it happens on running some unit tests if we prepare the runtime
|
|
132
|
-
// when not all the services configs are available. Given that we are running this only
|
|
133
|
-
// to ddetermine the type of the service, it's safe to ignore this error and default to unknown
|
|
134
|
-
service.type = 'unknown'
|
|
135
|
-
service.isPLTService = false
|
|
136
|
-
}
|
|
191
|
+
try {
|
|
192
|
+
let pkg
|
|
193
|
+
|
|
194
|
+
if (service.config) {
|
|
195
|
+
const config = await loadConfiguration(service.config)
|
|
196
|
+
pkg = await loadConfigurationModule(service.path, config)
|
|
197
|
+
|
|
198
|
+
service.type = extractModuleFromSchemaUrl(config, true).module
|
|
199
|
+
service.skipTelemetryHooks = pkg.skipTelemetryHooks
|
|
200
|
+
} else {
|
|
201
|
+
const { moduleName, stackable } = await importStackableAndConfig(service.path)
|
|
202
|
+
pkg = stackable
|
|
203
|
+
|
|
204
|
+
service.type = moduleName
|
|
137
205
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
// https://github.com/rollup/rollup/issues/5761
|
|
149
|
-
// TODO(mcollina): we should expose this inside every stackable configuration.
|
|
150
|
-
stackable.default.modulesToLoad?.forEach(m => {
|
|
151
|
-
const toLoad = _require.resolve(m)
|
|
152
|
-
loadModule(_require, toLoad).catch(() => {})
|
|
153
|
-
})
|
|
154
|
-
} catch {
|
|
155
|
-
// Nothing to do here
|
|
206
|
+
|
|
207
|
+
service.skipTelemetryHooks = pkg.skipTelemetryHooks
|
|
208
|
+
|
|
209
|
+
// This is needed to work around Rust bug on dylibs:
|
|
210
|
+
// https://github.com/rust-lang/rust/issues/91979
|
|
211
|
+
// https://github.com/rollup/rollup/issues/5761
|
|
212
|
+
const _require = createRequire(service.path)
|
|
213
|
+
for (const m of pkg.modulesToLoad ?? []) {
|
|
214
|
+
const toLoad = _require.resolve(m)
|
|
215
|
+
loadModule(_require, toLoad).catch(() => {})
|
|
156
216
|
}
|
|
217
|
+
} catch (err) {
|
|
218
|
+
// This should not happen, it happens on running some unit tests if we prepare the runtime
|
|
219
|
+
// when not all the services configs are available. Given that we are running this only
|
|
220
|
+
// to ddetermine the type of the service, it's safe to ignore this error and default to unknown
|
|
221
|
+
service.type = 'unknown'
|
|
157
222
|
}
|
|
158
223
|
|
|
159
224
|
service.entrypoint = service.id === config.entrypoint
|
|
@@ -169,7 +234,7 @@ async function _transformConfig (configManager, args) {
|
|
|
169
234
|
hasValidEntrypoint = true
|
|
170
235
|
}
|
|
171
236
|
|
|
172
|
-
|
|
237
|
+
config.serviceMap.set(service.id, service)
|
|
173
238
|
}
|
|
174
239
|
|
|
175
240
|
// If there is no entrypoint, autodetect one
|
|
@@ -188,7 +253,7 @@ async function _transformConfig (configManager, args) {
|
|
|
188
253
|
continue
|
|
189
254
|
}
|
|
190
255
|
|
|
191
|
-
if (service.type === 'composer') {
|
|
256
|
+
if (service.type === '@platformatic/composer') {
|
|
192
257
|
composers.push(service.id)
|
|
193
258
|
}
|
|
194
259
|
}
|
|
@@ -201,162 +266,38 @@ async function _transformConfig (configManager, args) {
|
|
|
201
266
|
}
|
|
202
267
|
}
|
|
203
268
|
|
|
204
|
-
if (!hasValidEntrypoint) {
|
|
269
|
+
if (!hasValidEntrypoint && !context.allowMissingEntrypoint) {
|
|
205
270
|
if (config.entrypoint) {
|
|
206
|
-
throw new
|
|
271
|
+
throw new InvalidEntrypointError(config.entrypoint)
|
|
207
272
|
} else if (services.length >= 1) {
|
|
208
|
-
throw new
|
|
273
|
+
throw new MissingEntrypointError()
|
|
209
274
|
}
|
|
210
275
|
// If there are no services, and no entrypoint it's an empty app.
|
|
211
276
|
// It won't start, but we should be able to parse and operate on it,
|
|
212
277
|
// like adding other services.
|
|
213
278
|
}
|
|
214
279
|
|
|
215
|
-
|
|
216
|
-
|
|
280
|
+
config.services = services
|
|
281
|
+
config.web = undefined
|
|
282
|
+
config.logger ??= {}
|
|
217
283
|
|
|
218
284
|
if (production) {
|
|
219
285
|
// Any value below 10 is considered as "immediate restart" and won't be processed via setTimeout or similar
|
|
220
286
|
// Important: do not use 2 otherwise ajv will convert to boolean `true`
|
|
221
|
-
|
|
287
|
+
config.restartOnError = 2
|
|
222
288
|
} else {
|
|
223
|
-
if (
|
|
224
|
-
|
|
225
|
-
} else if (
|
|
226
|
-
|
|
289
|
+
if (config.restartOnError === true) {
|
|
290
|
+
config.restartOnError = 5000
|
|
291
|
+
} else if (config.restartOnError < 0) {
|
|
292
|
+
config.restartOnError = 0
|
|
227
293
|
}
|
|
228
294
|
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
async function platformaticRuntime () {
|
|
232
|
-
// No-op. Here for consistency with other app types.
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
platformaticRuntime[Symbol.for('skip-override')] = true
|
|
236
|
-
platformaticRuntime.schema = schema
|
|
237
|
-
platformaticRuntime.configType = 'runtime'
|
|
238
|
-
platformaticRuntime.configManagerConfig = {
|
|
239
|
-
version: require('../package.json').version,
|
|
240
|
-
schema,
|
|
241
|
-
allowToWatch: ['.env'],
|
|
242
|
-
schemaOptions: {
|
|
243
|
-
useDefaults: true,
|
|
244
|
-
coerceTypes: true,
|
|
245
|
-
allErrors: true,
|
|
246
|
-
strict: false
|
|
247
|
-
},
|
|
248
|
-
async transformConfig (args) {
|
|
249
|
-
await _transformConfig(this, args)
|
|
250
|
-
},
|
|
251
|
-
upgrade
|
|
252
|
-
}
|
|
253
295
|
|
|
254
|
-
|
|
255
|
-
let serviceId = 'main'
|
|
256
|
-
try {
|
|
257
|
-
const packageJson = join(configManager.dirname, 'package.json')
|
|
258
|
-
serviceId = require(packageJson).name || 'main'
|
|
259
|
-
if (serviceId.startsWith('@')) {
|
|
260
|
-
serviceId = serviceId.split('/')[1]
|
|
261
|
-
}
|
|
262
|
-
} catch (err) {
|
|
263
|
-
// on purpose, the package.json might be missing
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// If the service supports its (so far, only @platformatic/service and descendants)
|
|
267
|
-
const { hostname, port, http2, https } = configManager.current.server ?? {}
|
|
268
|
-
const server = { hostname, port, http2, https }
|
|
269
|
-
|
|
270
|
-
// Important: do not change the order of the properties in this object
|
|
271
|
-
/* c8 ignore next */
|
|
272
|
-
const wrapperConfig = {
|
|
273
|
-
$schema: schema.$id,
|
|
274
|
-
server,
|
|
275
|
-
watch: !args?.production,
|
|
276
|
-
...omitProperties(configManager.current.runtime ?? {}, runtimeUnwrappablePropertiesList),
|
|
277
|
-
entrypoint: serviceId,
|
|
278
|
-
services: [
|
|
279
|
-
{
|
|
280
|
-
id: serviceId,
|
|
281
|
-
path: configManager.dirname,
|
|
282
|
-
config: configManager.fullPath
|
|
283
|
-
}
|
|
284
|
-
]
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const cm = new ConfigManager({
|
|
288
|
-
source: wrapperConfig,
|
|
289
|
-
schema,
|
|
290
|
-
schemaOptions: {
|
|
291
|
-
useDefaults: true,
|
|
292
|
-
coerceTypes: true,
|
|
293
|
-
allErrors: true,
|
|
294
|
-
strict: false
|
|
295
|
-
},
|
|
296
|
-
transformConfig (args) {
|
|
297
|
-
return _transformConfig(this, args)
|
|
298
|
-
}
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
await cm.parseAndValidate(true, [], opts)
|
|
302
|
-
|
|
303
|
-
return cm
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function parseInspectorOptions (configManager) {
|
|
307
|
-
const { current, args } = configManager
|
|
308
|
-
const hasInspect = 'inspect' in args
|
|
309
|
-
const hasInspectBrk = 'inspect-brk' in args
|
|
310
|
-
let inspectFlag
|
|
311
|
-
|
|
312
|
-
if (hasInspect) {
|
|
313
|
-
inspectFlag = args.inspect
|
|
314
|
-
|
|
315
|
-
if (hasInspectBrk) {
|
|
316
|
-
throw new errors.InspectAndInspectBrkError()
|
|
317
|
-
}
|
|
318
|
-
} else if (hasInspectBrk) {
|
|
319
|
-
inspectFlag = args['inspect-brk']
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (inspectFlag !== undefined) {
|
|
323
|
-
let host = '127.0.0.1'
|
|
324
|
-
let port = 9229
|
|
325
|
-
|
|
326
|
-
if (typeof inspectFlag === 'string' && inspectFlag.length > 0) {
|
|
327
|
-
const splitAt = inspectFlag.lastIndexOf(':')
|
|
328
|
-
|
|
329
|
-
if (splitAt === -1) {
|
|
330
|
-
port = inspectFlag
|
|
331
|
-
} else {
|
|
332
|
-
host = inspectFlag.substring(0, splitAt)
|
|
333
|
-
port = inspectFlag.substring(splitAt + 1)
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
port = Number.parseInt(port, 10)
|
|
337
|
-
|
|
338
|
-
if (!(port === 0 || (port >= 1024 && port <= 65535))) {
|
|
339
|
-
throw new errors.InspectorPortError()
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
if (!host) {
|
|
343
|
-
throw new errors.InspectorHostError()
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
current.inspectorOptions = {
|
|
348
|
-
host,
|
|
349
|
-
port,
|
|
350
|
-
breakFirstLine: hasInspectBrk,
|
|
351
|
-
watchDisabled: !!current.watch
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
current.watch = false
|
|
355
|
-
}
|
|
296
|
+
return config
|
|
356
297
|
}
|
|
357
298
|
|
|
358
299
|
module.exports = {
|
|
300
|
+
wrapInRuntimeConfig,
|
|
359
301
|
parseInspectorOptions,
|
|
360
|
-
|
|
361
|
-
wrapConfigInRuntimeConfig
|
|
302
|
+
transform
|
|
362
303
|
}
|