@platformatic/service 0.23.2 → 0.24.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/index.d.ts +4 -7
- package/index.js +29 -141
- package/index.test-d.ts +4 -7
- package/lib/compile.js +4 -5
- package/lib/load-config.js +9 -24
- package/lib/plugins/sandbox-wrapper.js +8 -1
- package/lib/start.js +176 -0
- package/package.json +5 -5
- package/service.mjs +11 -4
- package/test/autoload.test.js +94 -0
- package/test/cli/compile.test.mjs +1 -1
- package/test/cli/helper.mjs +2 -2
- package/test/cli/start.test.mjs +40 -8
- package/test/cli/watch.test.mjs +8 -51
- package/test/config.test.js +0 -28
- package/test/fixtures/default-env-var-names.json +7 -0
- package/test/fixtures/nested-directories/modules/inventory/index.js +4 -0
- package/test/fixtures/nested-directories/plugins/decorator.js +3 -0
- package/test/fixtures/other-side.js +9 -0
- package/test/load-plugin.test.js +34 -0
- package/lib/start.mjs +0 -105
package/index.d.ts
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import { FastifyInstance
|
|
1
|
+
import { FastifyInstance } from "fastify"
|
|
2
2
|
|
|
3
3
|
export type pltServiceHandlerBuildServer = {
|
|
4
4
|
app: FastifyInstance
|
|
5
5
|
address: string
|
|
6
6
|
port: number
|
|
7
7
|
restart: () => Promise<void>
|
|
8
|
-
listen:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}>
|
|
12
|
-
stop: () => Promise<void>
|
|
13
|
-
inject: (opts: InjectOptions | string) => Promise<LightMyRequestResponse>
|
|
8
|
+
listen: FastifyInstance['listen']
|
|
9
|
+
close: FastifyInstance['close']
|
|
10
|
+
inject: FastifyInstance['inject']
|
|
14
11
|
}
|
|
15
12
|
|
|
16
13
|
declare module '@platformatic/service' {
|
package/index.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { readFile } = require('fs/promises')
|
|
4
|
-
|
|
5
|
-
const ConfigManager = require('@platformatic/config')
|
|
6
|
-
const { restartable } = require('@fastify/restartable')
|
|
7
3
|
const { isKeyEnabled } = require('@platformatic/utils')
|
|
8
4
|
|
|
9
5
|
const compiler = require('./lib/compile')
|
|
@@ -18,8 +14,9 @@ const setupHealthCheck = require('./lib/plugins/health-check')
|
|
|
18
14
|
const loadPlugins = require('./lib/plugins/plugins')
|
|
19
15
|
|
|
20
16
|
const { schema } = require('./lib/schema')
|
|
21
|
-
const { loadConfig
|
|
17
|
+
const { loadConfig } = require('./lib/load-config')
|
|
22
18
|
const { addLoggerToTheConfig } = require('./lib/utils')
|
|
19
|
+
const { start, buildServer } = require('./lib/start')
|
|
23
20
|
|
|
24
21
|
async function platformaticService (app, opts, toLoad = []) {
|
|
25
22
|
const configManager = app.platformatic.configManager
|
|
@@ -29,6 +26,17 @@ async function platformaticService (app, opts, toLoad = []) {
|
|
|
29
26
|
app.register(setupMetrics, config.metrics)
|
|
30
27
|
}
|
|
31
28
|
|
|
29
|
+
app.setErrorHandler(async (err, req, reply) => {
|
|
30
|
+
if (!(err instanceof Error)) {
|
|
31
|
+
req.log.debug({ err }, 'error encountered within the sandbox, wrapping it')
|
|
32
|
+
const newError = new Error(err.message)
|
|
33
|
+
newError.cause = err
|
|
34
|
+
newError.statusCode = err.statusCode || 500
|
|
35
|
+
err = newError
|
|
36
|
+
}
|
|
37
|
+
throw err
|
|
38
|
+
})
|
|
39
|
+
|
|
32
40
|
if (Array.isArray(toLoad)) {
|
|
33
41
|
for (const plugin of toLoad) {
|
|
34
42
|
await app.register(plugin)
|
|
@@ -73,123 +81,6 @@ async function platformaticService (app, opts, toLoad = []) {
|
|
|
73
81
|
}
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
platformaticService[Symbol.for('skip-override')] = true
|
|
77
|
-
platformaticService.schema = schema
|
|
78
|
-
platformaticService.envWhitelist = ['PORT', 'HOSTNAME']
|
|
79
|
-
|
|
80
|
-
async function adjustHttpsKeyAndCert (arg) {
|
|
81
|
-
if (typeof arg === 'string') {
|
|
82
|
-
return arg
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (!Array.isArray(arg)) {
|
|
86
|
-
// { path: pathToKeyOrCert }
|
|
87
|
-
return readFile(arg.path)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Array of strings or objects.
|
|
91
|
-
for (let i = 0; i < arg.length; ++i) {
|
|
92
|
-
arg[i] = await adjustHttpsKeyAndCert(arg[i])
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return arg
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function defaultConfig (app, source) {
|
|
99
|
-
const res = {
|
|
100
|
-
source,
|
|
101
|
-
...generateDefaultConfig(),
|
|
102
|
-
allowToWatch: ['.env', ...(app?.allowToWatch || [])],
|
|
103
|
-
envWhitelist: ['PORT', ...(app?.envWhitelist || [])]
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (app.schema) {
|
|
107
|
-
res.schema = app.schema
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return res
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async function buildServer (options, app) {
|
|
114
|
-
app = app || platformaticService
|
|
115
|
-
|
|
116
|
-
let configManager = options.configManager
|
|
117
|
-
if (!configManager) {
|
|
118
|
-
// instantiate a new config manager from current options
|
|
119
|
-
configManager = new ConfigManager(defaultConfig(app, options))
|
|
120
|
-
await configManager.parseAndValidate()
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// options is a path
|
|
124
|
-
if (typeof options === 'string') {
|
|
125
|
-
options = configManager.current
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
let url = null
|
|
129
|
-
|
|
130
|
-
async function createRestartable (fastify) {
|
|
131
|
-
const config = configManager.current
|
|
132
|
-
const root = fastify(config.server)
|
|
133
|
-
|
|
134
|
-
root.decorate('platformatic', { configManager, config })
|
|
135
|
-
root.register(app)
|
|
136
|
-
|
|
137
|
-
root.decorate('url', {
|
|
138
|
-
getter () {
|
|
139
|
-
return url
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
if (root.restarted) {
|
|
144
|
-
root.log.info('restarted')
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return root
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const { port, hostname, ...serverOptions } = options.server
|
|
151
|
-
|
|
152
|
-
if (serverOptions.https) {
|
|
153
|
-
serverOptions.https.key = await adjustHttpsKeyAndCert(serverOptions.https.key)
|
|
154
|
-
serverOptions.https.cert = await adjustHttpsKeyAndCert(serverOptions.https.cert)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const handler = await restartable(createRestartable)
|
|
158
|
-
|
|
159
|
-
configManager.on('update', async (newConfig) => {
|
|
160
|
-
handler.log.debug('config changed')
|
|
161
|
-
handler.log.trace({ newConfig }, 'new config')
|
|
162
|
-
|
|
163
|
-
if (newConfig.watch === false) {
|
|
164
|
-
/* c8 ignore next 4 */
|
|
165
|
-
if (handler.tsCompilerWatcher) {
|
|
166
|
-
handler.tsCompilerWatcher.kill('SIGTERM')
|
|
167
|
-
handler.log.debug('stop watching typescript files')
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (handler.fileWatcher) {
|
|
171
|
-
await handler.fileWatcher.stopWatching()
|
|
172
|
-
handler.log.debug('stop watching files')
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
await safeRestart(handler)
|
|
177
|
-
/* c8 ignore next 1 */
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
configManager.on('error', function (err) {
|
|
181
|
-
/* c8 ignore next 1 */
|
|
182
|
-
handler.log.error({ err }, 'error reloading the configuration')
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
handler.decorate('start', async () => {
|
|
186
|
-
url = await handler.listen({ host: hostname, port })
|
|
187
|
-
return url
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
return handler
|
|
191
|
-
}
|
|
192
|
-
|
|
193
84
|
async function onFilesUpdated (app) {
|
|
194
85
|
// Reload the config as well, otherwise we will have problems
|
|
195
86
|
// in case the files watcher triggers the config watcher too
|
|
@@ -209,32 +100,29 @@ async function onFilesUpdated (app) {
|
|
|
209
100
|
}
|
|
210
101
|
}
|
|
211
102
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
103
|
+
platformaticService[Symbol.for('skip-override')] = true
|
|
104
|
+
platformaticService.schema = schema
|
|
105
|
+
platformaticService.configType = 'service'
|
|
106
|
+
platformaticService.configManagerConfig = {
|
|
107
|
+
schema,
|
|
108
|
+
envWhitelist: ['PORT', 'HOSTNAME'],
|
|
109
|
+
allowToWatch: ['.env'],
|
|
110
|
+
schemaOptions: {
|
|
111
|
+
useDefaults: true,
|
|
112
|
+
coerceTypes: true,
|
|
113
|
+
allErrors: true,
|
|
114
|
+
strict: false
|
|
223
115
|
}
|
|
224
116
|
}
|
|
225
117
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
async function buildStart (loadConfig, buildServer, configManagerConfig) {
|
|
229
|
-
const { buildStart } = await import('./lib/start.mjs')
|
|
230
|
-
return buildStart(loadConfig, buildServer, configManagerConfig)
|
|
118
|
+
function _buildServer (options, app) {
|
|
119
|
+
return buildServer(options, app || platformaticService)
|
|
231
120
|
}
|
|
232
121
|
|
|
233
|
-
module.exports.buildServer =
|
|
122
|
+
module.exports.buildServer = _buildServer
|
|
234
123
|
module.exports.schema = require('./lib/schema')
|
|
235
124
|
module.exports.platformaticService = platformaticService
|
|
236
125
|
module.exports.loadConfig = loadConfig
|
|
237
126
|
module.exports.addLoggerToTheConfig = addLoggerToTheConfig
|
|
238
|
-
module.exports.generateConfigManagerConfig = generateDefaultConfig
|
|
239
127
|
module.exports.tsCompiler = compiler
|
|
240
|
-
module.exports.
|
|
128
|
+
module.exports.start = start
|
package/index.test-d.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { expectError, expectType } from 'tsd';
|
|
2
|
-
import {
|
|
3
|
-
FastifyInstance,
|
|
4
|
-
LightMyRequestResponse,
|
|
5
|
-
} from 'fastify';
|
|
2
|
+
import { FastifyInstance } from 'fastify';
|
|
6
3
|
import { pltServiceHandlerBuildServer } from '.';
|
|
7
4
|
|
|
8
5
|
const server: pltServiceHandlerBuildServer = {
|
|
@@ -10,9 +7,9 @@ const server: pltServiceHandlerBuildServer = {
|
|
|
10
7
|
address: 'localhost',
|
|
11
8
|
port: 3000,
|
|
12
9
|
restart: async () => {},
|
|
13
|
-
listen: async () =>
|
|
14
|
-
|
|
15
|
-
inject: async () =>
|
|
10
|
+
listen: async () => '',
|
|
11
|
+
close: (async () => undefined) as unknown as FastifyInstance['close'],
|
|
12
|
+
inject: (async () => undefined) as unknown as FastifyInstance['inject']
|
|
16
13
|
};
|
|
17
14
|
|
|
18
15
|
expectType<pltServiceHandlerBuildServer>(server);
|
package/lib/compile.js
CHANGED
|
@@ -102,11 +102,13 @@ async function compileWatch (cwd, config) {
|
|
|
102
102
|
return { child }
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
function buildCompileCmd (
|
|
105
|
+
function buildCompileCmd (app) {
|
|
106
106
|
return async function compileCmd (_args) {
|
|
107
107
|
let fullPath = null
|
|
108
108
|
try {
|
|
109
|
-
const { configManager } = await
|
|
109
|
+
const { configManager } = await loadConfig({}, _args, app, {
|
|
110
|
+
watch: false
|
|
111
|
+
})
|
|
110
112
|
await configManager.parseAndValidate()
|
|
111
113
|
fullPath = dirname(configManager.fullPath)
|
|
112
114
|
/* c8 ignore next 4 */
|
|
@@ -121,9 +123,6 @@ function buildCompileCmd (_loadConfig) {
|
|
|
121
123
|
}
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
const compileCmd = buildCompileCmd(loadConfig)
|
|
125
|
-
|
|
126
126
|
module.exports.compile = compile
|
|
127
127
|
module.exports.compileWatch = compileWatch
|
|
128
128
|
module.exports.buildCompileCmd = buildCompileCmd
|
|
129
|
-
module.exports.compileCmd = compileCmd
|
package/lib/load-config.js
CHANGED
|
@@ -4,32 +4,19 @@ const parseArgs = require('minimist')
|
|
|
4
4
|
const { access } = require('fs/promises')
|
|
5
5
|
const ConfigManager = require('@platformatic/config')
|
|
6
6
|
const deepmerge = require('@fastify/deepmerge')
|
|
7
|
-
const { schema } = require('./schema.js')
|
|
8
|
-
|
|
9
|
-
function generateDefaultConfig () {
|
|
10
|
-
return {
|
|
11
|
-
schema,
|
|
12
|
-
schemaOptions: {
|
|
13
|
-
useDefaults: true,
|
|
14
|
-
coerceTypes: true,
|
|
15
|
-
allErrors: true,
|
|
16
|
-
strict: false
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
7
|
|
|
21
8
|
// Unfortunately c8 does not see those on Windows
|
|
22
9
|
/* c8 ignore next 70 */
|
|
23
|
-
async function loadConfig (minimistConfig, _args,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
defaultConfig = { ...generateDefaultConfig(), ...defaultConfig }
|
|
10
|
+
async function loadConfig (minimistConfig, _args, app, overrides = {}) {
|
|
11
|
+
const configManagerConfig = {
|
|
12
|
+
...app.configManagerConfig,
|
|
13
|
+
...overrides
|
|
28
14
|
}
|
|
29
15
|
|
|
16
|
+
const configType = app.configType
|
|
17
|
+
|
|
30
18
|
const args = parseArgs(_args, deepmerge({ all: true })({
|
|
31
19
|
string: ['allow-env'],
|
|
32
|
-
boolean: ['hotReload'],
|
|
33
20
|
default: {
|
|
34
21
|
allowEnv: '', // The default is set in ConfigManager
|
|
35
22
|
hotReload: true
|
|
@@ -37,8 +24,7 @@ async function loadConfig (minimistConfig, _args, defaultConfig, configType = 's
|
|
|
37
24
|
alias: {
|
|
38
25
|
v: 'version',
|
|
39
26
|
c: 'config',
|
|
40
|
-
allowEnv: ['allow-env', 'E']
|
|
41
|
-
hotReload: ['hot-reload']
|
|
27
|
+
allowEnv: ['allow-env', 'E']
|
|
42
28
|
}
|
|
43
29
|
}, minimistConfig))
|
|
44
30
|
|
|
@@ -58,10 +44,10 @@ Error: ${err}
|
|
|
58
44
|
process.exit(1)
|
|
59
45
|
}
|
|
60
46
|
|
|
61
|
-
const envWhitelist = args.allowEnv ? args.allowEnv :
|
|
47
|
+
const envWhitelist = args.allowEnv ? args.allowEnv : configManagerConfig.envWhitelist
|
|
62
48
|
const configManager = new ConfigManager({
|
|
63
49
|
source: args.config,
|
|
64
|
-
...
|
|
50
|
+
...configManagerConfig,
|
|
65
51
|
envWhitelist
|
|
66
52
|
})
|
|
67
53
|
|
|
@@ -85,4 +71,3 @@ function printConfigValidationErrors (configManager) {
|
|
|
85
71
|
}
|
|
86
72
|
|
|
87
73
|
module.exports.loadConfig = loadConfig
|
|
88
|
-
module.exports.generateDefaultConfig = generateDefaultConfig
|
|
@@ -19,10 +19,17 @@ module.exports = fp(async function (app, opts) {
|
|
|
19
19
|
} else {
|
|
20
20
|
let loaded = await import(`file://${plugin.path}`)
|
|
21
21
|
/* c8 ignore next 3 */
|
|
22
|
-
if (loaded.__esModule === true) {
|
|
22
|
+
if (loaded.__esModule === true || typeof loaded.default === 'function') {
|
|
23
23
|
loaded = loaded.default
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
let skipOverride
|
|
27
|
+
if (plugin.encapsulate === false) {
|
|
28
|
+
skipOverride = loaded[Symbol.for('skip-override')]
|
|
29
|
+
loaded[Symbol.for('skip-override')] = true
|
|
30
|
+
}
|
|
25
31
|
await app.register(loaded, plugin.options)
|
|
32
|
+
loaded[Symbol.for('skip-override')] = skipOverride
|
|
26
33
|
}
|
|
27
34
|
}
|
|
28
35
|
})
|
package/lib/start.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { readFile } = require('fs/promises')
|
|
4
|
+
const close = require('close-with-grace')
|
|
5
|
+
const { loadConfig } = require('./load-config')
|
|
6
|
+
const { addLoggerToTheConfig } = require('./utils.js')
|
|
7
|
+
const ConfigManager = require('@platformatic/config')
|
|
8
|
+
const { restartable } = require('@fastify/restartable')
|
|
9
|
+
|
|
10
|
+
async function adjustHttpsKeyAndCert (arg) {
|
|
11
|
+
if (typeof arg === 'string') {
|
|
12
|
+
return arg
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!Array.isArray(arg)) {
|
|
16
|
+
// { path: pathToKeyOrCert }
|
|
17
|
+
return readFile(arg.path)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Array of strings or objects.
|
|
21
|
+
for (let i = 0; i < arg.length; ++i) {
|
|
22
|
+
arg[i] = await adjustHttpsKeyAndCert(arg[i])
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return arg
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function buildServer (options, app) {
|
|
29
|
+
let configManager = options.configManager
|
|
30
|
+
if (!configManager) {
|
|
31
|
+
// instantiate a new config manager from current options
|
|
32
|
+
configManager = new ConfigManager({ ...app.configManagerConfig, source: options })
|
|
33
|
+
await configManager.parseAndValidate()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// options is a path
|
|
37
|
+
if (typeof options === 'string') {
|
|
38
|
+
options = configManager.current
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let url = null
|
|
42
|
+
|
|
43
|
+
async function createRestartable (fastify) {
|
|
44
|
+
const config = configManager.current
|
|
45
|
+
const root = fastify(config.server)
|
|
46
|
+
|
|
47
|
+
root.decorate('platformatic', { configManager, config })
|
|
48
|
+
root.register(app)
|
|
49
|
+
|
|
50
|
+
root.decorate('url', {
|
|
51
|
+
getter () {
|
|
52
|
+
return url
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
if (root.restarted) {
|
|
57
|
+
root.log.info('restarted')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return root
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { port, hostname, ...serverOptions } = options.server
|
|
64
|
+
|
|
65
|
+
if (serverOptions.https) {
|
|
66
|
+
serverOptions.https.key = await adjustHttpsKeyAndCert(serverOptions.https.key)
|
|
67
|
+
serverOptions.https.cert = await adjustHttpsKeyAndCert(serverOptions.https.cert)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const handler = await restartable(createRestartable)
|
|
71
|
+
|
|
72
|
+
configManager.on('update', async (newConfig) => {
|
|
73
|
+
handler.log.debug('config changed')
|
|
74
|
+
handler.log.trace({ newConfig }, 'new config')
|
|
75
|
+
|
|
76
|
+
if (newConfig.watch === false) {
|
|
77
|
+
/* c8 ignore next 4 */
|
|
78
|
+
if (handler.tsCompilerWatcher) {
|
|
79
|
+
handler.tsCompilerWatcher.kill('SIGTERM')
|
|
80
|
+
handler.log.debug('stop watching typescript files')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (handler.fileWatcher) {
|
|
84
|
+
await handler.fileWatcher.stopWatching()
|
|
85
|
+
handler.log.debug('stop watching files')
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await safeRestart(handler)
|
|
90
|
+
/* c8 ignore next 1 */
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
configManager.on('error', function (err) {
|
|
94
|
+
/* c8 ignore next 1 */
|
|
95
|
+
handler.log.error({ err }, 'error reloading the configuration')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
handler.decorate('start', async () => {
|
|
99
|
+
url = await handler.listen({ host: hostname, port })
|
|
100
|
+
return url
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
return handler
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function safeRestart (app) {
|
|
107
|
+
try {
|
|
108
|
+
await app.restart()
|
|
109
|
+
/* c8 ignore next 8 */
|
|
110
|
+
} catch (err) {
|
|
111
|
+
app.log.error({
|
|
112
|
+
err: {
|
|
113
|
+
message: err.message,
|
|
114
|
+
stack: err.stack
|
|
115
|
+
}
|
|
116
|
+
}, 'failed to reload server')
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function start (appType, _args) {
|
|
121
|
+
const { configManager } = await loadConfig({}, _args, appType)
|
|
122
|
+
|
|
123
|
+
const config = configManager.current
|
|
124
|
+
|
|
125
|
+
addLoggerToTheConfig(config)
|
|
126
|
+
|
|
127
|
+
const _transformConfig = configManager._transformConfig.bind(configManager)
|
|
128
|
+
configManager._transformConfig = function () {
|
|
129
|
+
const config = configManager.current
|
|
130
|
+
addLoggerToTheConfig(config)
|
|
131
|
+
return _transformConfig(config)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let app = null
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
// Set the location of the config
|
|
138
|
+
app = await buildServer({ ...config, configManager }, appType)
|
|
139
|
+
|
|
140
|
+
await app.start()
|
|
141
|
+
// TODO: this log is used in the start command. Should be replaced
|
|
142
|
+
app.log.info({ url: app.url })
|
|
143
|
+
} catch (err) {
|
|
144
|
+
// TODO route this to a logger
|
|
145
|
+
console.error(err)
|
|
146
|
+
process.exit(1)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Ignore from CI because SIGUSR2 is not available
|
|
150
|
+
// on Windows
|
|
151
|
+
/* c8 ignore next 25 */
|
|
152
|
+
process.on('SIGUSR2', function () {
|
|
153
|
+
app.log.info('reloading configuration')
|
|
154
|
+
safeRestart(app)
|
|
155
|
+
return false
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
close(async ({ signal, err }) => {
|
|
159
|
+
// Windows does not support trapping signals
|
|
160
|
+
if (err) {
|
|
161
|
+
app.log.error({
|
|
162
|
+
err: {
|
|
163
|
+
message: err.message,
|
|
164
|
+
stack: err.stack
|
|
165
|
+
}
|
|
166
|
+
}, 'exiting')
|
|
167
|
+
} else if (signal) {
|
|
168
|
+
app.log.info({ signal }, 'received signal')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
await app.close()
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports.buildServer = buildServer
|
|
176
|
+
module.exports.start = start
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/service",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"fastify": "^4.17.0",
|
|
56
56
|
"fastify-metrics": "^10.3.0",
|
|
57
57
|
"fastify-plugin": "^4.5.0",
|
|
58
|
-
"fastify-sandbox": "^0.
|
|
58
|
+
"fastify-sandbox": "^0.12.0",
|
|
59
59
|
"graphql": "^16.6.0",
|
|
60
60
|
"help-me": "^4.2.0",
|
|
61
61
|
"mercurius": "^13.0.0",
|
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
"pino-pretty": "^10.0.0",
|
|
65
65
|
"rfdc": "^1.3.0",
|
|
66
66
|
"ua-parser-js": "^1.0.35",
|
|
67
|
-
"@platformatic/client": "0.
|
|
68
|
-
"@platformatic/config": "0.
|
|
69
|
-
"@platformatic/utils": "0.
|
|
67
|
+
"@platformatic/client": "0.24.0",
|
|
68
|
+
"@platformatic/config": "0.24.0",
|
|
69
|
+
"@platformatic/utils": "0.24.0"
|
|
70
70
|
},
|
|
71
71
|
"standard": {
|
|
72
72
|
"ignore": [
|
package/service.mjs
CHANGED
|
@@ -8,8 +8,9 @@ import { readFile } from 'fs/promises'
|
|
|
8
8
|
import { join } from 'desm'
|
|
9
9
|
import { generateJsonSchemaConfig } from './lib/gen-schema.js'
|
|
10
10
|
|
|
11
|
-
import
|
|
12
|
-
|
|
11
|
+
import { buildCompileCmd } from './lib/compile.js'
|
|
12
|
+
|
|
13
|
+
import { start, platformaticService } from './index.js'
|
|
13
14
|
|
|
14
15
|
const help = helpMe({
|
|
15
16
|
dir: join(import.meta.url, 'help'),
|
|
@@ -22,8 +23,14 @@ const program = commist({ maxDistance: 2 })
|
|
|
22
23
|
program.register('help', help.toStdout)
|
|
23
24
|
program.register('help start', help.toStdout.bind(null, ['start']))
|
|
24
25
|
|
|
25
|
-
program.register('start',
|
|
26
|
-
|
|
26
|
+
program.register('start', (argv) => {
|
|
27
|
+
start(platformaticService, argv).catch((err) => {
|
|
28
|
+
/* c8 ignore next 2 */
|
|
29
|
+
console.error(err)
|
|
30
|
+
process.exit(1)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
program.register('compile', buildCompileCmd(platformaticService))
|
|
27
34
|
program.register('schema config', generateJsonSchemaConfig)
|
|
28
35
|
program.register('schema', help.toStdout.bind(null, ['schema']))
|
|
29
36
|
|
package/test/autoload.test.js
CHANGED
|
@@ -430,3 +430,97 @@ test('nested directories', async ({ teardown, equal, same }) => {
|
|
|
430
430
|
equal(body, 'I\'m sorry, there was an error processing your request.')
|
|
431
431
|
}
|
|
432
432
|
})
|
|
433
|
+
|
|
434
|
+
test('disable encapsulation for a single file', async ({ teardown, equal, same }) => {
|
|
435
|
+
const config = {
|
|
436
|
+
server: {
|
|
437
|
+
hostname: '127.0.0.1',
|
|
438
|
+
port: 0,
|
|
439
|
+
// Windows CI is slow
|
|
440
|
+
pluginTimeout: 60 * 1000
|
|
441
|
+
},
|
|
442
|
+
service: {
|
|
443
|
+
openapi: true
|
|
444
|
+
},
|
|
445
|
+
plugins: {
|
|
446
|
+
paths: [{
|
|
447
|
+
path: join(__dirname, 'fixtures', 'nested-directories', 'plugins', 'decorator.js'),
|
|
448
|
+
encapsulate: false
|
|
449
|
+
}, {
|
|
450
|
+
path: join(__dirname, 'fixtures', 'nested-directories', 'plugins', 'handlers.js'),
|
|
451
|
+
encapsulate: false
|
|
452
|
+
}, {
|
|
453
|
+
path: join(__dirname, 'fixtures', 'nested-directories', 'modules'),
|
|
454
|
+
encapsulate: false,
|
|
455
|
+
maxDepth: 1
|
|
456
|
+
}]
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const app = await buildServer(config)
|
|
461
|
+
teardown(async () => {
|
|
462
|
+
await app.close()
|
|
463
|
+
})
|
|
464
|
+
await app.start()
|
|
465
|
+
|
|
466
|
+
{
|
|
467
|
+
const res = await request(`${app.url}/foo/baz`)
|
|
468
|
+
equal(res.statusCode, 404, 'status code')
|
|
469
|
+
const body = await res.body.text()
|
|
470
|
+
equal(body, 'I\'m sorry, I couldn\'t find what you were looking for.')
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
{
|
|
474
|
+
const res = await request(`${app.url}/foo`)
|
|
475
|
+
equal(res.statusCode, 200, 'status code')
|
|
476
|
+
const body = await res.body.text()
|
|
477
|
+
equal(body, 'bar')
|
|
478
|
+
}
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
test('disable encapsulation for a single file / different order', async ({ teardown, equal, same }) => {
|
|
482
|
+
const config = {
|
|
483
|
+
server: {
|
|
484
|
+
hostname: '127.0.0.1',
|
|
485
|
+
port: 0,
|
|
486
|
+
// Windows CI is slow
|
|
487
|
+
pluginTimeout: 60 * 1000
|
|
488
|
+
},
|
|
489
|
+
service: {
|
|
490
|
+
openapi: true
|
|
491
|
+
},
|
|
492
|
+
plugins: {
|
|
493
|
+
paths: [{
|
|
494
|
+
path: join(__dirname, 'fixtures', 'nested-directories', 'modules'),
|
|
495
|
+
encapsulate: false,
|
|
496
|
+
maxDepth: 1
|
|
497
|
+
}, {
|
|
498
|
+
path: join(__dirname, 'fixtures', 'nested-directories', 'plugins', 'decorator.js'),
|
|
499
|
+
encapsulate: false
|
|
500
|
+
}, {
|
|
501
|
+
path: join(__dirname, 'fixtures', 'nested-directories', 'plugins', 'handlers.js'),
|
|
502
|
+
encapsulate: false
|
|
503
|
+
}]
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const app = await buildServer(config)
|
|
508
|
+
teardown(async () => {
|
|
509
|
+
await app.close()
|
|
510
|
+
})
|
|
511
|
+
await app.start()
|
|
512
|
+
|
|
513
|
+
{
|
|
514
|
+
const res = await request(`${app.url}/foo/baz`)
|
|
515
|
+
equal(res.statusCode, 404, 'status code')
|
|
516
|
+
const body = await res.body.text()
|
|
517
|
+
equal(body, 'I\'m sorry, I couldn\'t find what you were looking for.')
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
{
|
|
521
|
+
const res = await request(`${app.url}/foo`)
|
|
522
|
+
equal(res.statusCode, 200, 'status code')
|
|
523
|
+
const body = await res.body.text()
|
|
524
|
+
equal(body, 'bar')
|
|
525
|
+
}
|
|
526
|
+
})
|
|
@@ -254,7 +254,7 @@ t.test('valid tsconfig file inside an inner folder', async (t) => {
|
|
|
254
254
|
await cp(testDir, cwd, { recursive: true })
|
|
255
255
|
|
|
256
256
|
try {
|
|
257
|
-
await execa('node', [cliPath, 'compile'], { cwd })
|
|
257
|
+
await execa('node', [cliPath, 'compile'], { cwd, stdio: 'inherit' })
|
|
258
258
|
} catch (err) {
|
|
259
259
|
t.fail('should not catch any error')
|
|
260
260
|
}
|
package/test/cli/helper.mjs
CHANGED
|
@@ -21,8 +21,8 @@ tap.teardown(() => {
|
|
|
21
21
|
|
|
22
22
|
export const cliPath = join(import.meta.url, '..', '..', 'service.mjs')
|
|
23
23
|
|
|
24
|
-
export async function start (
|
|
25
|
-
const child = execa('node', [cliPath, 'start', ...
|
|
24
|
+
export async function start (commandOpts, exacaOpts = {}) {
|
|
25
|
+
const child = execa('node', [cliPath, 'start', ...commandOpts], exacaOpts)
|
|
26
26
|
child.stderr.pipe(process.stdout)
|
|
27
27
|
|
|
28
28
|
const output = child.stdout.pipe(split(function (line) {
|
package/test/cli/start.test.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { request } from 'undici'
|
|
|
5
5
|
import { execa } from 'execa'
|
|
6
6
|
|
|
7
7
|
test('autostart', async ({ equal, same, match, teardown }) => {
|
|
8
|
-
const { child, url } = await start('-c', join(import.meta.url, '..', '..', 'fixtures', 'hello', 'platformatic.service.json'))
|
|
8
|
+
const { child, url } = await start(['-c', join(import.meta.url, '..', '..', 'fixtures', 'hello', 'platformatic.service.json')])
|
|
9
9
|
|
|
10
10
|
const res = await request(`${url}`)
|
|
11
11
|
equal(res.statusCode, 200)
|
|
@@ -18,7 +18,7 @@ test('autostart', async ({ equal, same, match, teardown }) => {
|
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
test('start command', async ({ equal, same, match, teardown }) => {
|
|
21
|
-
const { child, url } = await start('
|
|
21
|
+
const { child, url } = await start(['-c', join(import.meta.url, '..', '..', 'fixtures', 'hello', 'platformatic.service.json')])
|
|
22
22
|
|
|
23
23
|
const res = await request(`${url}`)
|
|
24
24
|
equal(res.statusCode, 200)
|
|
@@ -31,8 +31,17 @@ test('start command', async ({ equal, same, match, teardown }) => {
|
|
|
31
31
|
})
|
|
32
32
|
|
|
33
33
|
test('allow custom env properties', async ({ equal, same, match, teardown }) => {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
const { child, url } = await start(
|
|
35
|
+
[
|
|
36
|
+
'-c', join(import.meta.url, '..', 'fixtures', 'custom-port-placeholder.json'),
|
|
37
|
+
'--allow-env', 'A_CUSTOM_PORT'
|
|
38
|
+
],
|
|
39
|
+
{
|
|
40
|
+
env: {
|
|
41
|
+
A_CUSTOM_PORT: '11111'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
)
|
|
36
45
|
equal(url, 'http://127.0.0.1:11111', 'A_CUSTOM_PORT env variable has been used')
|
|
37
46
|
const res = await request(`${url}`)
|
|
38
47
|
equal(res.statusCode, 200)
|
|
@@ -43,14 +52,37 @@ test('allow custom env properties', async ({ equal, same, match, teardown }) =>
|
|
|
43
52
|
delete process.env.A_CUSTOM_PORT
|
|
44
53
|
})
|
|
45
54
|
|
|
55
|
+
test('use default env variables names', async ({ equal, match }) => {
|
|
56
|
+
const { child, url } = await start(
|
|
57
|
+
[
|
|
58
|
+
'-c', join(import.meta.url, '..', 'fixtures', 'default-env-var-names.json')
|
|
59
|
+
],
|
|
60
|
+
{
|
|
61
|
+
env: {
|
|
62
|
+
PORT: '11111',
|
|
63
|
+
HOSTNAME: '127.0.0.1'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
equal(url, 'http://127.0.0.1:11111', 'default env variable names has been used')
|
|
69
|
+
const res = await request(`${url}`)
|
|
70
|
+
equal(res.statusCode, 200)
|
|
71
|
+
const body = await res.body.json()
|
|
72
|
+
match(body, {}, 'response')
|
|
73
|
+
|
|
74
|
+
child.kill('SIGINT')
|
|
75
|
+
delete process.env.A_CUSTOM_PORT
|
|
76
|
+
})
|
|
77
|
+
|
|
46
78
|
test('default logger', async ({ equal, same, match, teardown }) => {
|
|
47
|
-
const { child, url } = await start('-c', join(import.meta.url, '..', '..', 'fixtures', 'hello', 'no-server-logger.json'))
|
|
79
|
+
const { child, url } = await start(['-c', join(import.meta.url, '..', '..', 'fixtures', 'hello', 'no-server-logger.json')])
|
|
48
80
|
match(url, /http:\/\/127.0.0.1:[0-9]+/)
|
|
49
81
|
child.kill('SIGINT')
|
|
50
82
|
})
|
|
51
83
|
|
|
52
84
|
test('plugin options', async ({ equal, same, match, teardown }) => {
|
|
53
|
-
const { child, url } = await start('-c', join(import.meta.url, '..', '..', 'fixtures', 'options', 'platformatic.service.yml'))
|
|
85
|
+
const { child, url } = await start(['-c', join(import.meta.url, '..', '..', 'fixtures', 'options', 'platformatic.service.yml')])
|
|
54
86
|
const res = await request(`${url}`)
|
|
55
87
|
equal(res.statusCode, 200)
|
|
56
88
|
const body = await res.body.json()
|
|
@@ -62,7 +94,7 @@ test('plugin options', async ({ equal, same, match, teardown }) => {
|
|
|
62
94
|
})
|
|
63
95
|
|
|
64
96
|
test('https embedded pem', async ({ equal, same, match, teardown }) => {
|
|
65
|
-
const { child, url } = await start('-c', join(import.meta.url, '..', '..', 'fixtures', 'https', 'embedded-pem.json'))
|
|
97
|
+
const { child, url } = await start(['-c', join(import.meta.url, '..', '..', 'fixtures', 'https', 'embedded-pem.json')])
|
|
66
98
|
|
|
67
99
|
match(url, /https:\/\//)
|
|
68
100
|
const res = await request(`${url}`)
|
|
@@ -76,7 +108,7 @@ test('https embedded pem', async ({ equal, same, match, teardown }) => {
|
|
|
76
108
|
})
|
|
77
109
|
|
|
78
110
|
test('https pem path', async ({ equal, same, match, teardown }) => {
|
|
79
|
-
const { child, url } = await start('-c', join(import.meta.url, '..', '..', 'fixtures', 'https', 'pem-path.json'))
|
|
111
|
+
const { child, url } = await start(['-c', join(import.meta.url, '..', '..', 'fixtures', 'https', 'pem-path.json')])
|
|
80
112
|
|
|
81
113
|
match(url, /https:\/\//)
|
|
82
114
|
const res = await request(`${url}`)
|
package/test/cli/watch.test.mjs
CHANGED
|
@@ -45,7 +45,7 @@ test('should watch js files by default', async ({ equal, teardown, comment }) =>
|
|
|
45
45
|
writeFile(pluginFilePath, createLoggingPlugin('v1'))
|
|
46
46
|
])
|
|
47
47
|
|
|
48
|
-
const { child, url } = await start('-c', configFilePath)
|
|
48
|
+
const { child, url } = await start(['-c', configFilePath])
|
|
49
49
|
teardown(() => child.kill('SIGINT'))
|
|
50
50
|
|
|
51
51
|
await writeFile(pluginFilePath, createLoggingPlugin('v2', true))
|
|
@@ -97,7 +97,7 @@ test('should watch allowed file', async ({ comment, teardown }) => {
|
|
|
97
97
|
writeFile(pluginFilePath, pluginCode)
|
|
98
98
|
])
|
|
99
99
|
|
|
100
|
-
const { child } = await start('-c', configFilePath)
|
|
100
|
+
const { child } = await start(['-c', configFilePath])
|
|
101
101
|
teardown(() => child.kill('SIGINT'))
|
|
102
102
|
|
|
103
103
|
writeFile(jsonFilePath, 'RESTARTED')
|
|
@@ -132,7 +132,7 @@ test('should not watch ignored file', async ({ teardown, equal }) => {
|
|
|
132
132
|
writeFile(pluginFilePath, createLoggingPlugin('v1'))
|
|
133
133
|
])
|
|
134
134
|
|
|
135
|
-
const { child, url } = await start('-c', configFilePath)
|
|
135
|
+
const { child, url } = await start(['-c', configFilePath])
|
|
136
136
|
teardown(() => child.kill('SIGINT'))
|
|
137
137
|
|
|
138
138
|
await writeFile(pluginFilePath, createLoggingPlugin('v2', true))
|
|
@@ -170,7 +170,7 @@ test('should not loop forever when doing ESM', async ({ comment, fail }) => {
|
|
|
170
170
|
writeFile(pluginFilePath, 'export default async (app) => {}')
|
|
171
171
|
])
|
|
172
172
|
|
|
173
|
-
const { child } = await start('-c', configFilePath)
|
|
173
|
+
const { child } = await start(['-c', configFilePath])
|
|
174
174
|
|
|
175
175
|
await sleep(1000)
|
|
176
176
|
|
|
@@ -239,7 +239,7 @@ test('should watch config file', async ({ comment, teardown }) => {
|
|
|
239
239
|
writeFile(pluginFilePath, pluginCode)
|
|
240
240
|
])
|
|
241
241
|
|
|
242
|
-
const { child } = await start('-c', configFilePath)
|
|
242
|
+
const { child } = await start(['-c', configFilePath])
|
|
243
243
|
teardown(() => child.kill('SIGINT'))
|
|
244
244
|
|
|
245
245
|
// We do not await
|
|
@@ -249,49 +249,6 @@ test('should watch config file', async ({ comment, teardown }) => {
|
|
|
249
249
|
}
|
|
250
250
|
})
|
|
251
251
|
|
|
252
|
-
test('should not hot reload files with `--hot-reload false`', async ({ teardown, equal }) => {
|
|
253
|
-
const tmpDir = await mkdtemp(join(os.tmpdir(), 'watch-'))
|
|
254
|
-
const pluginFilePath = join(tmpDir, 'plugin.js')
|
|
255
|
-
const configFilePath = join(tmpDir, 'platformatic.service.json')
|
|
256
|
-
|
|
257
|
-
const config = {
|
|
258
|
-
server: {
|
|
259
|
-
logger: {
|
|
260
|
-
level: 'info'
|
|
261
|
-
},
|
|
262
|
-
hostname: '127.0.0.1',
|
|
263
|
-
port: 0
|
|
264
|
-
},
|
|
265
|
-
plugins: {
|
|
266
|
-
paths: [pluginFilePath]
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
await Promise.all([
|
|
271
|
-
writeFile(configFilePath, JSON.stringify(config)),
|
|
272
|
-
writeFile(pluginFilePath, createLoggingPlugin('v1'))
|
|
273
|
-
])
|
|
274
|
-
|
|
275
|
-
const { child, url } = await start('-c', configFilePath, '--hot-reload', 'false')
|
|
276
|
-
teardown(() => child.kill('SIGINT'))
|
|
277
|
-
|
|
278
|
-
{
|
|
279
|
-
const res = await request(`${url}/version`)
|
|
280
|
-
const version = await res.body.text()
|
|
281
|
-
equal(version, 'v1')
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
await writeFile(pluginFilePath, createLoggingPlugin('v2', true))
|
|
285
|
-
|
|
286
|
-
await sleep(5000)
|
|
287
|
-
|
|
288
|
-
{
|
|
289
|
-
const res = await request(`${url}/version`)
|
|
290
|
-
const version = await res.body.text()
|
|
291
|
-
equal(version, 'v1')
|
|
292
|
-
}
|
|
293
|
-
})
|
|
294
|
-
|
|
295
252
|
test('should not fail when updating wrong config', async ({ equal, teardown, comment }) => {
|
|
296
253
|
const tmpDir = await mkdtemp(join(os.tmpdir(), 'watch-'))
|
|
297
254
|
comment(`using ${tmpDir}`)
|
|
@@ -309,7 +266,7 @@ test('should not fail when updating wrong config', async ({ equal, teardown, com
|
|
|
309
266
|
plugins: {
|
|
310
267
|
paths: [pluginFilePath]
|
|
311
268
|
},
|
|
312
|
-
watch:
|
|
269
|
+
watch: true
|
|
313
270
|
}
|
|
314
271
|
|
|
315
272
|
await Promise.all([
|
|
@@ -317,13 +274,13 @@ test('should not fail when updating wrong config', async ({ equal, teardown, com
|
|
|
317
274
|
writeFile(pluginFilePath, createLoggingPlugin('v1', true))
|
|
318
275
|
])
|
|
319
276
|
|
|
320
|
-
const { child, url } = await start('-c', configFilePath)
|
|
277
|
+
const { child, url } = await start(['-c', configFilePath])
|
|
321
278
|
teardown(() => child.kill('SIGINT'))
|
|
322
279
|
|
|
323
280
|
writeFile(configFilePath, 'this is not a valid config')
|
|
324
281
|
|
|
325
282
|
for await (const log of child.ndj) {
|
|
326
|
-
if (log.msg === '
|
|
283
|
+
if (log.msg === 'failed to reload server') break
|
|
327
284
|
}
|
|
328
285
|
|
|
329
286
|
const res = await request(`${url}/version`)
|
package/test/config.test.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
require('./helper')
|
|
4
4
|
const { test } = require('tap')
|
|
5
5
|
const { buildServer } = require('..')
|
|
6
|
-
const { loadConfig } = require('../lib/load-config')
|
|
7
6
|
const { request } = require('undici')
|
|
8
7
|
const { join } = require('path')
|
|
9
8
|
const os = require('os')
|
|
@@ -295,30 +294,3 @@ test('config reloads', async ({ teardown, equal, pass, same }) => {
|
|
|
295
294
|
same(await res.body.text(), 'ciao mondo', 'response')
|
|
296
295
|
}
|
|
297
296
|
})
|
|
298
|
-
|
|
299
|
-
test('can merge provided config with default config', async ({ plan, same }) => {
|
|
300
|
-
plan(6)
|
|
301
|
-
const configFile = join(__dirname, 'fixtures', 'custom-port-placeholder.json')
|
|
302
|
-
const defaultConfig = {
|
|
303
|
-
mergeDefaults: true,
|
|
304
|
-
onMissingEnv (key) {
|
|
305
|
-
same(key, 'A_CUSTOM_PORT')
|
|
306
|
-
return '42'
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
{
|
|
311
|
-
const config = await loadConfig({}, ['-c', configFile], defaultConfig)
|
|
312
|
-
same(config.configManager.current.server.port, 42)
|
|
313
|
-
// This comes from the default config.
|
|
314
|
-
same(config.configManager.schemaOptions.useDefaults, true)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
{
|
|
318
|
-
defaultConfig.mergeDefaults = false
|
|
319
|
-
const config = await loadConfig({}, ['-c', configFile], defaultConfig)
|
|
320
|
-
same(config.configManager.current.server.port, 42)
|
|
321
|
-
// This comes from the default config.
|
|
322
|
-
same(config.configManager.schemaOptions.useDefaults, undefined)
|
|
323
|
-
}
|
|
324
|
-
})
|
package/test/load-plugin.test.js
CHANGED
|
@@ -31,3 +31,37 @@ test('customize service', async ({ teardown, equal }) => {
|
|
|
31
31
|
equal(res.statusCode, 200)
|
|
32
32
|
equal(body, 'hello world')
|
|
33
33
|
})
|
|
34
|
+
|
|
35
|
+
test('catch errors from the other side', async ({ teardown, equal, same }) => {
|
|
36
|
+
async function myApp (app, opts) {
|
|
37
|
+
await platformaticService(app, opts, [async function (app) {
|
|
38
|
+
app.get('/', () => 'hello world')
|
|
39
|
+
}])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const app = await buildServer({
|
|
43
|
+
server: {
|
|
44
|
+
hostname: '127.0.0.1',
|
|
45
|
+
port: 0
|
|
46
|
+
},
|
|
47
|
+
plugins: {
|
|
48
|
+
paths: [{
|
|
49
|
+
path: require.resolve('./fixtures/other-side.js')
|
|
50
|
+
}]
|
|
51
|
+
}
|
|
52
|
+
}, myApp)
|
|
53
|
+
|
|
54
|
+
teardown(async () => {
|
|
55
|
+
await app.close()
|
|
56
|
+
})
|
|
57
|
+
await app.start()
|
|
58
|
+
|
|
59
|
+
const res = await (request(app.url))
|
|
60
|
+
const body = await res.body.json()
|
|
61
|
+
equal(res.statusCode, 500)
|
|
62
|
+
same(body, {
|
|
63
|
+
statusCode: 500,
|
|
64
|
+
error: 'Internal Server Error',
|
|
65
|
+
message: 'kaboom'
|
|
66
|
+
})
|
|
67
|
+
})
|
package/lib/start.mjs
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { buildServer } from '../index.js'
|
|
2
|
-
import close from 'close-with-grace'
|
|
3
|
-
import { loadConfig, generateDefaultConfig } from './load-config.js'
|
|
4
|
-
import { addLoggerToTheConfig } from './utils.js'
|
|
5
|
-
|
|
6
|
-
// TODO make sure coverage is reported for Windows
|
|
7
|
-
// Currently C8 is not reporting it
|
|
8
|
-
/* c8 ignore start */
|
|
9
|
-
|
|
10
|
-
function defaultConfig () {
|
|
11
|
-
const _defaultConfig = generateDefaultConfig()
|
|
12
|
-
return {
|
|
13
|
-
watch: true,
|
|
14
|
-
..._defaultConfig
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function buildStart (_loadConfig, _buildServer, _configManagerConfig) {
|
|
19
|
-
return async function start (_args) {
|
|
20
|
-
const _defaultConfig = _configManagerConfig ?? defaultConfig()
|
|
21
|
-
const { configManager, args } = await _loadConfig({}, _args, _defaultConfig)
|
|
22
|
-
|
|
23
|
-
const config = configManager.current
|
|
24
|
-
|
|
25
|
-
// Disable hot reload from the CLI
|
|
26
|
-
if (args.hotReload === false && configManager.current.plugins) {
|
|
27
|
-
configManager.current.plugins.hotReload = false
|
|
28
|
-
}
|
|
29
|
-
addLoggerToTheConfig(config)
|
|
30
|
-
|
|
31
|
-
const _transformConfig = configManager._transformConfig.bind(configManager)
|
|
32
|
-
configManager._transformConfig = function () {
|
|
33
|
-
const config = configManager.current
|
|
34
|
-
if (args.hotReload === false && config.plugins) {
|
|
35
|
-
config.plugins.hotReload = false
|
|
36
|
-
}
|
|
37
|
-
addLoggerToTheConfig(config)
|
|
38
|
-
return _transformConfig(config)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
let app = null
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
// Set the location of the config
|
|
45
|
-
app = await _buildServer({ ...config, configManager })
|
|
46
|
-
|
|
47
|
-
await app.start()
|
|
48
|
-
// TODO: this log is used in the start command. Should be replaced
|
|
49
|
-
app.log.info({ url: app.url })
|
|
50
|
-
} catch (err) {
|
|
51
|
-
// TODO route this to a logger
|
|
52
|
-
console.error(err)
|
|
53
|
-
process.exit(1)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Ignore from CI because SIGUSR2 is not available
|
|
57
|
-
// on Windows
|
|
58
|
-
process.on('SIGUSR2', function () {
|
|
59
|
-
app.log.info('reloading configuration')
|
|
60
|
-
safeRestart(app)
|
|
61
|
-
return false
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
close(async ({ signal, err }) => {
|
|
65
|
-
// Windows does not support trapping signals
|
|
66
|
-
if (err) {
|
|
67
|
-
app.log.error({
|
|
68
|
-
err: {
|
|
69
|
-
message: err.message,
|
|
70
|
-
stack: err.stack
|
|
71
|
-
}
|
|
72
|
-
}, 'exiting')
|
|
73
|
-
} else if (signal) {
|
|
74
|
-
app.log.info({ signal }, 'received signal')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
await app.close()
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const start = buildStart(loadConfig, buildServer)
|
|
83
|
-
|
|
84
|
-
async function safeRestart (app) {
|
|
85
|
-
try {
|
|
86
|
-
await app.restart()
|
|
87
|
-
} catch (err) {
|
|
88
|
-
app.log.error({
|
|
89
|
-
err: {
|
|
90
|
-
message: err.message,
|
|
91
|
-
stack: err.stack
|
|
92
|
-
}
|
|
93
|
-
}, 'failed to reload server')
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export default function (args) {
|
|
98
|
-
start(args).catch(exit)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function exit (err) {
|
|
102
|
-
console.error(err)
|
|
103
|
-
process.exit(1)
|
|
104
|
-
}
|
|
105
|
-
/* c8 ignore stop */
|