@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.
Files changed (43) hide show
  1. package/fixtures/hello/warn-log.service.json +19 -0
  2. package/fixtures/hello-client/platformatic.service.json +1 -1
  3. package/fixtures/hello-client-ts/platformatic.service.json +3 -2
  4. package/index.js +139 -229
  5. package/lib/compile.js +9 -5
  6. package/lib/load-config.js +13 -15
  7. package/lib/plugins/clients.js +12 -0
  8. package/lib/plugins/cors.js +29 -0
  9. package/lib/plugins/file-watcher.js +44 -0
  10. package/lib/plugins/health-check.js +17 -0
  11. package/lib/plugins/plugins.js +56 -0
  12. package/lib/plugins/typescript.js +46 -0
  13. package/lib/root-endpoint/index.js +1 -0
  14. package/lib/schema.js +8 -1
  15. package/lib/start.mjs +27 -135
  16. package/lib/utils.js +2 -11
  17. package/package.json +24 -23
  18. package/test/autoload.test.js +77 -59
  19. package/test/cli/compile.test.mjs +45 -36
  20. package/test/cli/start.test.mjs +13 -1
  21. package/test/cli/watch.test.mjs +40 -2
  22. package/test/clients.test.js +25 -18
  23. package/test/config.test.js +67 -27
  24. package/test/cors.test.js +48 -30
  25. package/test/fixtures/bad-typescript-plugin/platformatic.service.json +3 -1
  26. package/test/fixtures/custom-port-placeholder.json +10 -0
  27. package/test/fixtures/typescript-autoload/platformatic.service.json +3 -1
  28. package/test/fixtures/typescript-plugin/platformatic.service.json +3 -1
  29. package/test/fixtures/typescript-plugin-nocompile/platformatic.service.json +2 -1
  30. package/test/graphql.test.js +18 -14
  31. package/test/healthcheck.test.js +22 -13
  32. package/test/https.test.js +13 -11
  33. package/test/lambda.test.js +103 -0
  34. package/test/load-and-reload-files.test.js +98 -109
  35. package/test/load-plugin.test.js +8 -4
  36. package/test/metrics.test.js +59 -39
  37. package/test/routes.test.js +43 -25
  38. package/test/utils.test.js +6 -15
  39. package/test/watch.test.js +182 -0
  40. /package/lib/{graphql.js → plugins/graphql.js} +0 -0
  41. /package/lib/{metrics-plugin.js → plugins/metrics.js} +0 -0
  42. /package/lib/{openapi.js → plugins/openapi.js} +0 -0
  43. /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
+ }
@@ -3,7 +3,7 @@
3
3
  "hostname": "127.0.0.1",
4
4
  "port": 0,
5
5
  "logger": {
6
- "level": "info",
6
+ "level": "warn",
7
7
  "name": "hello client"
8
8
  }
9
9
  },
@@ -3,9 +3,10 @@
3
3
  "hostname": "127.0.0.1",
4
4
  "port": 0,
5
5
  "logger": {
6
- "level": "info",
6
+ "level": "warn",
7
7
  "name": "hello client ts"
8
- }
8
+ },
9
+ "pluginTimeout": 30000
9
10
  },
10
11
  "plugins": {
11
12
  "paths": ["./plugin.ts"],
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
- function originToRegexp (origin) {
27
- if (typeof origin === 'object') {
28
- if (origin.regexp) {
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
- return origin
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
- if (isKeyEnabled('metrics', opts)) {
38
- app.register(require('./lib/metrics-plugin'), opts.metrics)
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, opts)
34
+ await app.register(plugin)
44
35
  }
45
36
  }
46
37
 
47
- if (!app.hasDecorator('platformatic')) {
48
- app.decorate('platformatic', {})
49
- }
38
+ const serviceConfig = config.service || {}
50
39
 
51
- {
52
- const fileWatcher = opts.fileWatcher
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
- const serviceConfig = app.platformatic.config?.service
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
- for (const plugin of (app.platformatic.config.clients || [])) {
84
- app.register(require(plugin.path), {
85
- url: plugin.url
86
- })
48
+ if (isKeyEnabled('clients', config)) {
49
+ app.register(setupClients, config.clients)
87
50
  }
88
51
 
89
- if (opts.plugins) {
90
- // if we don't have a fullPath, let's assume we are in a test and we can use the current working directory
91
- const configPath = app.platformatic.configManager.fullPath || join(process.cwd(), 'platformatic.db.json')
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
- // Enable CORS
144
- if (opts.cors) {
145
- let origin = opts.cors.origin
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
- app.register(require('@fastify/cors'), opts.cors)
63
+ if (config.server.cors) {
64
+ app.register(setupCors, config.server.cors)
155
65
  }
156
66
 
157
- if (isKeyEnabled('healthCheck', opts)) {
158
- app.register(underPressure, {
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'), opts)
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
- if (!options.configManager) {
116
+ let configManager = options.configManager
117
+ if (!configManager) {
243
118
  // instantiate a new config manager from current options
244
- const cm = new ConfigManager(defaultConfig(app, options))
245
- await cm.parseAndValidate()
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
- if (options.server.https) {
253
- options.server.https.key = await adjustHttpsKeyAndCert(options.server.https.key)
254
- options.server.https.cert = await adjustHttpsKeyAndCert(options.server.https.cert)
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
- const _restart = handler.restart
128
+ let url = null
278
129
 
279
- let debounce = null
280
- handler.restart = (opts) => {
281
- /* c8 ignore next 3 */
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
- addLoggerToTheConfig(opts)
287
- const configManager = handler.app.platformatic.configManager
134
+ root.decorate('platformatic', { configManager, config })
135
+ root.register(app)
288
136
 
289
- if (!opts) {
290
- opts = configManager.current
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
- // Ignore because not tested on Windows
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
- handler.app.restart = handler.restart
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
  }
@@ -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, configFileNames = ourConfigFiles) {
34
- defaultConfig ??= generateDefaultConfig()
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(), configFileNames)
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: ${ourConfigFiles.join('\n')}
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)