@platformatic/service 0.21.1 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fixtures/hello/warn-log.service.json +19 -0
- package/fixtures/hello-client/platformatic.service.json +1 -1
- package/fixtures/hello-client-ts/platformatic.service.json +3 -2
- package/index.js +139 -229
- package/lib/compile.js +9 -5
- package/lib/load-config.js +13 -15
- package/lib/plugins/clients.js +12 -0
- package/lib/plugins/cors.js +29 -0
- package/lib/plugins/file-watcher.js +44 -0
- package/lib/plugins/health-check.js +17 -0
- package/lib/plugins/plugins.js +56 -0
- package/lib/plugins/typescript.js +46 -0
- package/lib/root-endpoint/index.js +1 -0
- package/lib/schema.js +8 -1
- package/lib/start.mjs +27 -135
- package/lib/utils.js +2 -11
- package/package.json +24 -23
- package/test/autoload.test.js +77 -59
- package/test/cli/compile.test.mjs +45 -36
- package/test/cli/start.test.mjs +13 -1
- package/test/cli/watch.test.mjs +40 -2
- package/test/clients.test.js +25 -18
- package/test/config.test.js +67 -27
- package/test/cors.test.js +48 -30
- package/test/fixtures/bad-typescript-plugin/platformatic.service.json +3 -1
- package/test/fixtures/custom-port-placeholder.json +10 -0
- package/test/fixtures/typescript-autoload/platformatic.service.json +3 -1
- package/test/fixtures/typescript-plugin/platformatic.service.json +3 -1
- package/test/fixtures/typescript-plugin-nocompile/platformatic.service.json +2 -1
- package/test/graphql.test.js +18 -14
- package/test/healthcheck.test.js +22 -13
- package/test/https.test.js +13 -11
- package/test/lambda.test.js +103 -0
- package/test/load-and-reload-files.test.js +98 -109
- package/test/load-plugin.test.js +8 -4
- package/test/metrics.test.js +59 -39
- package/test/routes.test.js +43 -25
- package/test/utils.test.js +6 -15
- package/test/watch.test.js +182 -0
- /package/lib/{graphql.js → plugins/graphql.js} +0 -0
- /package/lib/{metrics-plugin.js → plugins/metrics.js} +0 -0
- /package/lib/{openapi.js → plugins/openapi.js} +0 -0
- /package/lib/{sandbox-wrapper.js → plugins/sandbox-wrapper.js} +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"server": {
|
|
3
|
+
"hostname": "127.0.0.1",
|
|
4
|
+
"port": 0,
|
|
5
|
+
"logger": {
|
|
6
|
+
"level": "warn",
|
|
7
|
+
"name": "hello server"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"plugins": {
|
|
11
|
+
"paths": ["./plugin.js"],
|
|
12
|
+
"hotReload": false
|
|
13
|
+
},
|
|
14
|
+
"service": {
|
|
15
|
+
"openapi": true
|
|
16
|
+
},
|
|
17
|
+
"metrics": false,
|
|
18
|
+
"watch": false
|
|
19
|
+
}
|
package/index.js
CHANGED
|
@@ -1,170 +1,75 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { start } = require('@fastify/restartable')
|
|
4
|
-
const sandbox = require('fastify-sandbox')
|
|
5
|
-
const underPressure = require('@fastify/under-pressure')
|
|
6
|
-
const { schema } = require('./lib/schema')
|
|
7
|
-
const ConfigManager = require('@platformatic/config')
|
|
8
|
-
const { loadConfig, generateDefaultConfig } = require('./lib/load-config')
|
|
9
|
-
const { addLoggerToTheConfig, getJSPluginPath, isFileAccessible } = require('./lib/utils')
|
|
10
|
-
const { isKeyEnabled, deepmerge } = require('@platformatic/utils')
|
|
11
|
-
const compiler = require('./lib/compile')
|
|
12
|
-
const { join, dirname, resolve } = require('path')
|
|
13
3
|
const { readFile } = require('fs/promises')
|
|
14
|
-
const wrapperPath = join(__dirname, 'lib', 'sandbox-wrapper.js')
|
|
15
|
-
const setupOpenAPI = require('./lib/openapi.js')
|
|
16
|
-
const setupGraphQL = require('./lib/graphql.js')
|
|
17
|
-
|
|
18
|
-
function createServerConfig (config) {
|
|
19
|
-
// convert the config file to a new structure
|
|
20
|
-
// to make @fastify/restartable happy
|
|
21
|
-
const serverConfig = Object.assign({ ...config.server }, config)
|
|
22
|
-
delete serverConfig.server
|
|
23
|
-
return serverConfig
|
|
24
|
-
}
|
|
25
4
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
origin = new RegExp(origin.regexp)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
5
|
+
const ConfigManager = require('@platformatic/config')
|
|
6
|
+
const { restartable } = require('@fastify/restartable')
|
|
7
|
+
const { isKeyEnabled } = require('@platformatic/utils')
|
|
32
8
|
|
|
33
|
-
|
|
34
|
-
|
|
9
|
+
const compiler = require('./lib/compile')
|
|
10
|
+
const setupCors = require('./lib/plugins/cors')
|
|
11
|
+
const setupOpenAPI = require('./lib/plugins/openapi.js')
|
|
12
|
+
const setupGraphQL = require('./lib/plugins/graphql.js')
|
|
13
|
+
const setupClients = require('./lib/plugins/clients')
|
|
14
|
+
const setupMetrics = require('./lib/plugins/metrics')
|
|
15
|
+
const setupTsCompiler = require('./lib/plugins/typescript')
|
|
16
|
+
const setupFileWatcher = require('./lib/plugins/file-watcher')
|
|
17
|
+
const setupHealthCheck = require('./lib/plugins/health-check')
|
|
18
|
+
const loadPlugins = require('./lib/plugins/plugins')
|
|
19
|
+
|
|
20
|
+
const { schema } = require('./lib/schema')
|
|
21
|
+
const { loadConfig, generateDefaultConfig } = require('./lib/load-config')
|
|
22
|
+
const { addLoggerToTheConfig } = require('./lib/utils')
|
|
35
23
|
|
|
36
24
|
async function platformaticService (app, opts, toLoad = []) {
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
const configManager = app.platformatic.configManager
|
|
26
|
+
const config = configManager.current
|
|
27
|
+
|
|
28
|
+
if (isKeyEnabled('metrics', config)) {
|
|
29
|
+
app.register(setupMetrics, config.metrics)
|
|
39
30
|
}
|
|
40
31
|
|
|
41
32
|
if (Array.isArray(toLoad)) {
|
|
42
33
|
for (const plugin of toLoad) {
|
|
43
|
-
await app.register(plugin
|
|
34
|
+
await app.register(plugin)
|
|
44
35
|
}
|
|
45
36
|
}
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
app.decorate('platformatic', {})
|
|
49
|
-
}
|
|
38
|
+
const serviceConfig = config.service || {}
|
|
50
39
|
|
|
51
|
-
{
|
|
52
|
-
|
|
53
|
-
const configManager = opts.configManager
|
|
54
|
-
/* c8 ignore next 3 */
|
|
55
|
-
if (fileWatcher !== undefined) {
|
|
56
|
-
app.platformatic.fileWatcher = fileWatcher
|
|
57
|
-
}
|
|
58
|
-
if (configManager !== undefined) {
|
|
59
|
-
app.platformatic.configManager = configManager
|
|
60
|
-
app.platformatic.config = configManager.current
|
|
61
|
-
/* c8 ignore next 3 */
|
|
62
|
-
} else {
|
|
63
|
-
throw new Error('configManager is required')
|
|
64
|
-
}
|
|
40
|
+
if (isKeyEnabled('openapi', serviceConfig)) {
|
|
41
|
+
await app.register(setupOpenAPI, serviceConfig.openapi)
|
|
65
42
|
}
|
|
66
43
|
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// for some unknown reason, c8 is not detecting any of this
|
|
71
|
-
// despite being covered by test/routes.test.js
|
|
72
|
-
/* c8 ignore next 3 */
|
|
73
|
-
if (serviceConfig?.openapi) {
|
|
74
|
-
await setupOpenAPI(app, app.platformatic.config?.service?.openapi)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/* c8 ignore next 3 */
|
|
78
|
-
if (serviceConfig?.graphql) {
|
|
79
|
-
await setupGraphQL(app, app.platformatic.config?.service?.graphql)
|
|
80
|
-
}
|
|
44
|
+
if (isKeyEnabled('graphql', serviceConfig)) {
|
|
45
|
+
await app.register(setupGraphQL, serviceConfig.graphql)
|
|
81
46
|
}
|
|
82
47
|
|
|
83
|
-
|
|
84
|
-
app.register(
|
|
85
|
-
url: plugin.url
|
|
86
|
-
})
|
|
48
|
+
if (isKeyEnabled('clients', config)) {
|
|
49
|
+
app.register(setupClients, config.clients)
|
|
87
50
|
}
|
|
88
51
|
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const tsConfigPath = join(dirname(configPath), 'tsconfig.json')
|
|
93
|
-
/* c8 ignore next 21 */
|
|
94
|
-
if (await isFileAccessible(tsConfigPath)) {
|
|
95
|
-
const tsConfig = JSON.parse(await readFile(tsConfigPath, 'utf8'))
|
|
96
|
-
const outDir = resolve(dirname(tsConfigPath), tsConfig.compilerOptions.outDir)
|
|
97
|
-
opts.plugins.paths = opts.plugins.paths.map((plugin) => {
|
|
98
|
-
if (typeof plugin === 'string') {
|
|
99
|
-
return getJSPluginPath(configPath, plugin, outDir)
|
|
100
|
-
} else {
|
|
101
|
-
return {
|
|
102
|
-
path: getJSPluginPath(configPath, plugin.path, outDir),
|
|
103
|
-
options: plugin.options
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
})
|
|
107
|
-
} else {
|
|
108
|
-
for (const plugin of opts.plugins.paths) {
|
|
109
|
-
const path = typeof plugin === 'string' ? plugin : plugin.path
|
|
110
|
-
if (path.endsWith('.ts')) {
|
|
111
|
-
throw new Error(`Cannot load plugin ${path}, tsconfig.json not found`)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// if not defined, we default to true (which can happen only if config is set programmatically,
|
|
117
|
-
// that's why we ignore the coverage of the `undefined` case, which cannot be covered in cli tests)
|
|
118
|
-
// all individual plugin hot reload settings will be overloaded by global hot reload
|
|
119
|
-
/* c8 ignore next 1 */
|
|
120
|
-
const hotReload = opts.plugins.hotReload !== false
|
|
121
|
-
const isWatchEnabled = app.platformatic.config.watch !== false
|
|
122
|
-
app.log.debug({ plugins: opts.plugins.path }, 'loading plugin')
|
|
123
|
-
|
|
124
|
-
if (isWatchEnabled && hotReload) {
|
|
125
|
-
await app.register(sandbox, {
|
|
126
|
-
path: wrapperPath,
|
|
127
|
-
options: { paths: opts.plugins.paths },
|
|
128
|
-
customizeGlobalThis (_globalThis) {
|
|
129
|
-
// Taken from https://github.com/nodejs/undici/blob/fa9fd9066569b6357acacffb806aa804b688c9d8/lib/global.js#L5
|
|
130
|
-
const globalDispatcher = Symbol.for('undici.globalDispatcher.1')
|
|
131
|
-
const dispatcher = globalThis[globalDispatcher]
|
|
132
|
-
/* istanbul ignore else */
|
|
133
|
-
if (dispatcher) {
|
|
134
|
-
_globalThis[globalDispatcher] = dispatcher
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
})
|
|
138
|
-
} else {
|
|
139
|
-
await app.register(require(wrapperPath), { paths: opts.plugins.paths })
|
|
52
|
+
if (config.plugins) {
|
|
53
|
+
if (config.plugins.typescript) {
|
|
54
|
+
await app.register(setupTsCompiler)
|
|
140
55
|
}
|
|
56
|
+
await app.register(loadPlugins)
|
|
141
57
|
}
|
|
142
58
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (Array.isArray(origin)) {
|
|
147
|
-
origin = origin.map(originToRegexp)
|
|
148
|
-
} else {
|
|
149
|
-
origin = originToRegexp(origin)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
opts.cors.origin = origin
|
|
59
|
+
if (isKeyEnabled('watch', config)) {
|
|
60
|
+
await app.register(setupFileWatcher, { onFilesUpdated })
|
|
61
|
+
}
|
|
153
62
|
|
|
154
|
-
|
|
63
|
+
if (config.server.cors) {
|
|
64
|
+
app.register(setupCors, config.server.cors)
|
|
155
65
|
}
|
|
156
66
|
|
|
157
|
-
if (isKeyEnabled('healthCheck',
|
|
158
|
-
app.register(
|
|
159
|
-
exposeStatusRoute: '/status',
|
|
160
|
-
healthCheckInterval: opts.healthCheck.interval !== undefined ? opts.healthCheck.interval : 5000,
|
|
161
|
-
...opts.healthCheck,
|
|
162
|
-
healthCheck: opts.healthCheck.fn
|
|
163
|
-
})
|
|
67
|
+
if (isKeyEnabled('healthCheck', config.server)) {
|
|
68
|
+
app.register(setupHealthCheck, config.server.healthCheck)
|
|
164
69
|
}
|
|
165
70
|
|
|
166
71
|
if (!app.hasRoute({ url: '/', method: 'GET' }) && !Array.isArray(toLoad)) {
|
|
167
|
-
await app.register(require('./lib/root-endpoint')
|
|
72
|
+
await app.register(require('./lib/root-endpoint'))
|
|
168
73
|
}
|
|
169
74
|
}
|
|
170
75
|
|
|
@@ -172,37 +77,6 @@ platformaticService[Symbol.for('skip-override')] = true
|
|
|
172
77
|
platformaticService.schema = schema
|
|
173
78
|
platformaticService.envWhitelist = ['PORT', 'HOSTNAME']
|
|
174
79
|
|
|
175
|
-
function adjustConfigBeforeMerge (cm) {
|
|
176
|
-
// This function and adjustConfigAfterMerge() are needed because there are
|
|
177
|
-
// edge cases that deepmerge() does not handle properly. This code does not
|
|
178
|
-
// live in the generic config manager because that object is not aware of
|
|
179
|
-
// these schema dependent details.
|
|
180
|
-
const stash = new Map()
|
|
181
|
-
|
|
182
|
-
// If a pino instance is passed as the logger, it will contain a child()
|
|
183
|
-
// function that is not enumerable. Non-enumerables are not copied by
|
|
184
|
-
// deepmerge(), so stash the logger here.
|
|
185
|
-
/* c8 ignore next 5 */
|
|
186
|
-
if (typeof cm.server?.logger?.child === 'function' &&
|
|
187
|
-
!Object.prototype.propertyIsEnumerable.call(cm.server.logger, 'child')) {
|
|
188
|
-
stash.set('server.logger', cm.server.logger)
|
|
189
|
-
cm.server.logger = null
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return stash
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function adjustConfigAfterMerge (options, stash) {
|
|
196
|
-
// Restore any config that needed to be stashed prior to merging.
|
|
197
|
-
const pinoLogger = stash.get('server.logger')
|
|
198
|
-
|
|
199
|
-
/* c8 ignore next 4 */
|
|
200
|
-
if (pinoLogger) {
|
|
201
|
-
options.server.logger = pinoLogger
|
|
202
|
-
options.configManager.current.server.logger = pinoLogger
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
80
|
async function adjustHttpsKeyAndCert (arg) {
|
|
207
81
|
if (typeof arg === 'string') {
|
|
208
82
|
return arg
|
|
@@ -239,92 +113,128 @@ function defaultConfig (app, source) {
|
|
|
239
113
|
async function buildServer (options, app) {
|
|
240
114
|
app = app || platformaticService
|
|
241
115
|
|
|
242
|
-
|
|
116
|
+
let configManager = options.configManager
|
|
117
|
+
if (!configManager) {
|
|
243
118
|
// instantiate a new config manager from current options
|
|
244
|
-
|
|
245
|
-
await
|
|
246
|
-
const stash = adjustConfigBeforeMerge(cm.current)
|
|
247
|
-
options = deepmerge({}, options, cm.current)
|
|
248
|
-
options.configManager = cm
|
|
249
|
-
adjustConfigAfterMerge(options, stash)
|
|
119
|
+
configManager = new ConfigManager(defaultConfig(app, options))
|
|
120
|
+
await configManager.parseAndValidate()
|
|
250
121
|
}
|
|
251
122
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
options
|
|
255
|
-
options.server = { ...options.server, ...options.server.https }
|
|
256
|
-
delete options.server.https
|
|
257
|
-
options.server.protocol = 'https'
|
|
258
|
-
} else {
|
|
259
|
-
options.server.protocol = 'http'
|
|
123
|
+
// options is a path
|
|
124
|
+
if (typeof options === 'string') {
|
|
125
|
+
options = configManager.current
|
|
260
126
|
}
|
|
261
|
-
const serverConfig = createServerConfig(options)
|
|
262
|
-
|
|
263
|
-
serverConfig.originalConfig = options
|
|
264
|
-
serverConfig.app = app
|
|
265
|
-
const handler = await start(serverConfig)
|
|
266
|
-
|
|
267
|
-
Object.defineProperty(handler, 'url', {
|
|
268
|
-
get () {
|
|
269
|
-
const protocol = serverConfig.protocol
|
|
270
|
-
const address = handler.address
|
|
271
|
-
const port = handler.port
|
|
272
|
-
const url = `${protocol}://${address}:${port}`
|
|
273
|
-
return url
|
|
274
|
-
}
|
|
275
|
-
})
|
|
276
127
|
|
|
277
|
-
|
|
128
|
+
let url = null
|
|
278
129
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (debounce) {
|
|
283
|
-
return debounce
|
|
284
|
-
}
|
|
130
|
+
async function createRestartable (fastify) {
|
|
131
|
+
const config = configManager.current
|
|
132
|
+
const root = fastify(config.server)
|
|
285
133
|
|
|
286
|
-
|
|
287
|
-
|
|
134
|
+
root.decorate('platformatic', { configManager, config })
|
|
135
|
+
root.register(app)
|
|
288
136
|
|
|
289
|
-
|
|
290
|
-
|
|
137
|
+
root.decorate('url', {
|
|
138
|
+
getter () {
|
|
139
|
+
return url
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
if (root.restarted) {
|
|
144
|
+
root.log.info('restarted')
|
|
291
145
|
}
|
|
292
146
|
|
|
293
|
-
|
|
294
|
-
// TODO: remove the ignore, we shoduld be testing
|
|
295
|
-
// this on Windows
|
|
296
|
-
const fileWatcher = handler.app.platformatic.fileWatcher
|
|
297
|
-
opts.fileWatcher = fileWatcher
|
|
298
|
-
opts.configManager = configManager
|
|
299
|
-
opts = createServerConfig(opts)
|
|
300
|
-
opts.app = app
|
|
301
|
-
|
|
302
|
-
debounce = _restart(opts).then(() => {
|
|
303
|
-
handler.app.log.info('restarted')
|
|
304
|
-
}).finally(() => {
|
|
305
|
-
debounce = null
|
|
306
|
-
})
|
|
307
|
-
return debounce
|
|
147
|
+
return root
|
|
308
148
|
}
|
|
309
149
|
|
|
310
|
-
|
|
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
|
+
})
|
|
311
189
|
|
|
312
190
|
return handler
|
|
313
191
|
}
|
|
314
192
|
|
|
193
|
+
async function onFilesUpdated (app) {
|
|
194
|
+
// Reload the config as well, otherwise we will have problems
|
|
195
|
+
// in case the files watcher triggers the config watcher too
|
|
196
|
+
const configManager = app.platformatic.configManager
|
|
197
|
+
try {
|
|
198
|
+
app.log.debug('files changed')
|
|
199
|
+
await configManager.parse()
|
|
200
|
+
await app.restart()
|
|
201
|
+
/* c8 ignore next 8 */
|
|
202
|
+
} catch (err) {
|
|
203
|
+
app.log.error({
|
|
204
|
+
err: {
|
|
205
|
+
message: err.message,
|
|
206
|
+
stack: err.stack
|
|
207
|
+
}
|
|
208
|
+
}, 'failed to reload server')
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function safeRestart (app) {
|
|
213
|
+
try {
|
|
214
|
+
await app.restart()
|
|
215
|
+
/* c8 ignore next 8 */
|
|
216
|
+
} catch (err) {
|
|
217
|
+
app.log.error({
|
|
218
|
+
err: {
|
|
219
|
+
message: err.message,
|
|
220
|
+
stack: err.stack
|
|
221
|
+
}
|
|
222
|
+
}, 'failed to reload server')
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
315
226
|
// This is for @platformatic/db to use
|
|
316
227
|
/* c8 ignore next 4 */
|
|
317
|
-
async function buildStart (loadConfig, buildServer) {
|
|
228
|
+
async function buildStart (loadConfig, buildServer, configManagerConfig) {
|
|
318
229
|
const { buildStart } = await import('./lib/start.mjs')
|
|
319
|
-
return buildStart(loadConfig, buildServer)
|
|
230
|
+
return buildStart(loadConfig, buildServer, configManagerConfig)
|
|
320
231
|
}
|
|
321
232
|
|
|
322
233
|
module.exports.buildServer = buildServer
|
|
323
234
|
module.exports.schema = require('./lib/schema')
|
|
324
|
-
module.exports.createServerConfig = createServerConfig
|
|
325
235
|
module.exports.platformaticService = platformaticService
|
|
326
|
-
module.exports.addLoggerToTheConfig = addLoggerToTheConfig
|
|
327
236
|
module.exports.loadConfig = loadConfig
|
|
237
|
+
module.exports.addLoggerToTheConfig = addLoggerToTheConfig
|
|
328
238
|
module.exports.generateConfigManagerConfig = generateDefaultConfig
|
|
329
239
|
module.exports.tsCompiler = compiler
|
|
330
240
|
module.exports.buildStart = buildStart
|
package/lib/compile.js
CHANGED
|
@@ -28,7 +28,7 @@ async function getTSCExecutablePath (cwd) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
async function setup (cwd) {
|
|
31
|
+
async function setup (cwd, config) {
|
|
32
32
|
const logger = pino(
|
|
33
33
|
pretty({
|
|
34
34
|
translateTime: 'SYS:HH:MM:ss',
|
|
@@ -36,6 +36,10 @@ async function setup (cwd) {
|
|
|
36
36
|
})
|
|
37
37
|
)
|
|
38
38
|
|
|
39
|
+
if (config?.server.logger) {
|
|
40
|
+
logger.level = config.server.logger.level
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
const { execa } = await import('execa')
|
|
40
44
|
|
|
41
45
|
const tscExecutablePath = await getTSCExecutablePath(cwd)
|
|
@@ -56,8 +60,8 @@ async function setup (cwd) {
|
|
|
56
60
|
return { execa, logger, tscExecutablePath }
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
async function compile (cwd) {
|
|
60
|
-
const { execa, logger, tscExecutablePath } = await setup(cwd)
|
|
63
|
+
async function compile (cwd, config) {
|
|
64
|
+
const { execa, logger, tscExecutablePath } = await setup(cwd, config)
|
|
61
65
|
/* c8 ignore next 3 */
|
|
62
66
|
if (!tscExecutablePath) {
|
|
63
67
|
return false
|
|
@@ -76,8 +80,8 @@ async function compile (cwd) {
|
|
|
76
80
|
// This path is tested but C8 does not see it that way given it needs to work
|
|
77
81
|
// through execa.
|
|
78
82
|
/* c8 ignore next 20 */
|
|
79
|
-
async function compileWatch (cwd) {
|
|
80
|
-
const { execa, logger, tscExecutablePath } = await setup(cwd)
|
|
83
|
+
async function compileWatch (cwd, config) {
|
|
84
|
+
const { execa, logger, tscExecutablePath } = await setup(cwd, config)
|
|
81
85
|
if (!tscExecutablePath) {
|
|
82
86
|
return false
|
|
83
87
|
}
|
package/lib/load-config.js
CHANGED
|
@@ -4,18 +4,8 @@ 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 { findConfigFile } = require('./utils.js')
|
|
8
7
|
const { schema } = require('./schema.js')
|
|
9
8
|
|
|
10
|
-
const ourConfigFiles = [
|
|
11
|
-
'platformatic.service.json',
|
|
12
|
-
'platformatic.service.json5',
|
|
13
|
-
'platformatic.service.yaml',
|
|
14
|
-
'platformatic.service.yml',
|
|
15
|
-
'platformatic.service.toml',
|
|
16
|
-
'platformatic.service.tml'
|
|
17
|
-
]
|
|
18
|
-
|
|
19
9
|
function generateDefaultConfig () {
|
|
20
10
|
return {
|
|
21
11
|
schema,
|
|
@@ -30,8 +20,13 @@ function generateDefaultConfig () {
|
|
|
30
20
|
|
|
31
21
|
// Unfortunately c8 does not see those on Windows
|
|
32
22
|
/* c8 ignore next 70 */
|
|
33
|
-
async function loadConfig (minimistConfig, _args, defaultConfig,
|
|
34
|
-
defaultConfig
|
|
23
|
+
async function loadConfig (minimistConfig, _args, defaultConfig, configType = 'service') {
|
|
24
|
+
if (defaultConfig === undefined) {
|
|
25
|
+
defaultConfig = generateDefaultConfig()
|
|
26
|
+
} else if (defaultConfig?.mergeDefaults) {
|
|
27
|
+
defaultConfig = { ...generateDefaultConfig(), ...defaultConfig }
|
|
28
|
+
}
|
|
29
|
+
|
|
35
30
|
const args = parseArgs(_args, deepmerge({ all: true })({
|
|
36
31
|
string: ['allow-env'],
|
|
37
32
|
boolean: ['hotReload'],
|
|
@@ -49,22 +44,25 @@ async function loadConfig (minimistConfig, _args, defaultConfig, configFileNames
|
|
|
49
44
|
|
|
50
45
|
try {
|
|
51
46
|
if (!args.config) {
|
|
52
|
-
args.config = await findConfigFile(process.cwd(),
|
|
47
|
+
args.config = await ConfigManager.findConfigFile(process.cwd(), configType)
|
|
53
48
|
}
|
|
54
49
|
await access(args.config)
|
|
55
50
|
} catch (err) {
|
|
51
|
+
const configFiles = ConfigManager.listConfigFiles(configType)
|
|
56
52
|
console.error(`
|
|
57
53
|
Missing config file!
|
|
58
|
-
Be sure to have a config file with one of the following names: ${
|
|
54
|
+
Be sure to have a config file with one of the following names: ${configFiles.join('\n')}
|
|
59
55
|
In alternative run "npm create platformatic@latest" to generate a basic plt service config.
|
|
60
56
|
Error: ${err}
|
|
61
57
|
`)
|
|
62
58
|
process.exit(1)
|
|
63
59
|
}
|
|
64
60
|
|
|
61
|
+
const envWhitelist = args.allowEnv ? args.allowEnv : defaultConfig.envWhitelist
|
|
65
62
|
const configManager = new ConfigManager({
|
|
66
63
|
source: args.config,
|
|
67
|
-
...defaultConfig
|
|
64
|
+
...defaultConfig,
|
|
65
|
+
envWhitelist
|
|
68
66
|
})
|
|
69
67
|
|
|
70
68
|
const parsingResult = await configManager.parse()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fp = require('fastify-plugin')
|
|
4
|
+
|
|
5
|
+
async function setupClients (app, opts) {
|
|
6
|
+
const clientsConfig = opts
|
|
7
|
+
for (const { path, url } of clientsConfig) {
|
|
8
|
+
app.register(require(path), { url })
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = fp(setupClients)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fp = require('fastify-plugin')
|
|
4
|
+
|
|
5
|
+
function originToRegexp (origin) {
|
|
6
|
+
if (typeof origin === 'object') {
|
|
7
|
+
if (origin.regexp) {
|
|
8
|
+
origin = new RegExp(origin.regexp)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return origin
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function setupClients (app, opts) {
|
|
16
|
+
const cors = opts
|
|
17
|
+
|
|
18
|
+
let origin = cors.origin
|
|
19
|
+
if (Array.isArray(origin)) {
|
|
20
|
+
origin = origin.map(originToRegexp)
|
|
21
|
+
} else {
|
|
22
|
+
origin = originToRegexp(origin)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
cors.origin = origin
|
|
26
|
+
app.register(require('@fastify/cors'), cors)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = fp(setupClients)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { FileWatcher } = require('@platformatic/utils')
|
|
4
|
+
const fp = require('fastify-plugin')
|
|
5
|
+
|
|
6
|
+
async function setupFileWatcher (app, opts) {
|
|
7
|
+
// TODO: move params to opts
|
|
8
|
+
|
|
9
|
+
const configManager = app.platformatic.configManager
|
|
10
|
+
const config = configManager.current
|
|
11
|
+
|
|
12
|
+
const isRestartableApp = app.restarted !== undefined
|
|
13
|
+
|
|
14
|
+
// to run the plugin without restartable
|
|
15
|
+
/* c8 ignore next 1 */
|
|
16
|
+
const persistentRef = isRestartableApp ? app.persistentRef : app
|
|
17
|
+
|
|
18
|
+
let fileWatcher = persistentRef.fileWatcher
|
|
19
|
+
if (!fileWatcher) {
|
|
20
|
+
fileWatcher = new FileWatcher({
|
|
21
|
+
path: configManager.dirname,
|
|
22
|
+
allowToWatch: config.watch?.allow,
|
|
23
|
+
watchIgnore: config.watch?.ignore
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
fileWatcher.on('update', () => {
|
|
27
|
+
opts.onFilesUpdated(persistentRef)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
app.log.debug('start watching files')
|
|
31
|
+
fileWatcher.startWatching()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
app.decorate('fileWatcher', fileWatcher)
|
|
35
|
+
|
|
36
|
+
app.addHook('onClose', async () => {
|
|
37
|
+
if (!isRestartableApp || app.closingRestartable) {
|
|
38
|
+
app.fileWatcher.stopWatching()
|
|
39
|
+
app.log.debug('stop watching files')
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = fp(setupFileWatcher)
|