@platformatic/service 0.15.1 → 0.17.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 (40) hide show
  1. package/fixtures/hello/no-server-logger.json +2 -2
  2. package/fixtures/hello/platformatic.service.json +2 -2
  3. package/fixtures/options/platformatic.service.yml +5 -4
  4. package/index.js +64 -66
  5. package/lib/compile.js +64 -91
  6. package/lib/config.js +2 -5
  7. package/lib/load-config.js +6 -2
  8. package/lib/sandbox-wrapper.js +26 -0
  9. package/lib/schema.js +37 -48
  10. package/lib/start.mjs +97 -68
  11. package/lib/utils.js +7 -4
  12. package/package.json +19 -19
  13. package/service.mjs +2 -2
  14. package/test/autoload.test.js +33 -21
  15. package/test/cli/compile.test.mjs +45 -3
  16. package/test/cli/gen-schema.test.mjs +4 -2
  17. package/test/cli/watch.test.mjs +59 -15
  18. package/test/config.test.js +70 -50
  19. package/test/fixtures/bad-typescript-plugin/dist/tsconfig.tsbuildinfo +1 -0
  20. package/test/fixtures/bad-typescript-plugin/platformatic.service.json +3 -5
  21. package/test/fixtures/directories/platformatic.service.json +2 -2
  22. package/test/fixtures/not-load.service.json +2 -3
  23. package/test/fixtures/typescript-plugin/platformatic.service.json +3 -5
  24. package/test/fixtures/typescript-plugin-nocompile/platformatic.service.json +3 -6
  25. package/test/load-and-reload-files.test.js +20 -20
  26. package/test/routes.test.js +3 -13
  27. package/test/schema.test.js +12 -0
  28. package/test/tmp/typescript-plugin-clone-1/platformatic.service.json +3 -5
  29. package/test/tmp/typescript-plugin-clone-2/platformatic.service.json +3 -6
  30. package/test/tmp/typescript-plugin-clone-3/dist/tsconfig.tsbuildinfo +1 -0
  31. package/test/tmp/typescript-plugin-clone-3/platformatic.service.json +3 -5
  32. package/test/tmp/typescript-plugin-clone-4/dist/plugin.js +18 -0
  33. package/test/tmp/typescript-plugin-clone-4/dist/plugin.js.map +1 -0
  34. package/test/tmp/typescript-plugin-clone-4/dist/tsconfig.tsbuildinfo +1 -0
  35. package/test/tmp/typescript-plugin-clone-4/platformatic.service.json +3 -5
  36. package/test/tmp/typescript-plugin-clone-4/tsconfig.json +22 -0
  37. package/test/tmp/typescript-plugin-clone-5/platformatic.service.json +3 -5
  38. package/test/tmp/typescript-plugin-clone-6/platformatic.service.json +3 -6
  39. package/test/utils.test.js +10 -3
  40. package/lib/autoload-wrapper.js +0 -11
@@ -6,8 +6,8 @@
6
6
  "level": "info"
7
7
  }
8
8
  },
9
- "plugin": {
10
- "path": "./plugin.js"
9
+ "plugins": {
10
+ "paths": ["./plugin.js"]
11
11
  },
12
12
  "metrics": false
13
13
  }
@@ -6,8 +6,8 @@
6
6
  "level": "info"
7
7
  }
8
8
  },
9
- "plugin": {
10
- "path": "./plugin.js"
9
+ "plugins": {
10
+ "paths": ["./plugin.js"]
11
11
  },
12
12
  "metrics": false
13
13
  }
@@ -1,8 +1,9 @@
1
1
  server:
2
2
  hostname: "127.0.0.1"
3
3
  port: 0
4
- plugin:
5
- path: "./plugin.js"
6
- options:
7
- something: 'else'
4
+ plugins:
5
+ paths:
6
+ - path: "./plugin.js"
7
+ options:
8
+ something: 'else'
8
9
  metrics: false
package/index.js CHANGED
@@ -1,18 +1,17 @@
1
1
  'use strict'
2
2
 
3
3
  const { start } = require('@fastify/restartable')
4
- const autoload = require('@fastify/autoload')
5
4
  const sandbox = require('fastify-sandbox')
6
5
  const underPressure = require('@fastify/under-pressure')
7
6
  const { schema } = require('./lib/schema')
8
7
  const ConfigManager = require('./lib/config.js')
9
- const { addLoggerToTheConfig, getJSPluginPath } = require('./lib/utils')
8
+ const { addLoggerToTheConfig, getJSPluginPath, isFileAccessible } = require('./lib/utils')
10
9
  const loadConfig = require('./lib/load-config')
11
10
  const { isKeyEnabled, deepmerge } = require('@platformatic/utils')
12
11
  const compiler = require('./lib/compile')
13
- const { stat } = require('fs').promises
14
- const { join } = require('path')
15
- const wrapperPath = join(__dirname, 'lib', 'autoload-wrapper.js')
12
+ const { join, dirname, resolve } = require('path')
13
+ const { readFile } = require('fs/promises')
14
+ const wrapperPath = join(__dirname, 'lib', 'sandbox-wrapper.js')
16
15
 
17
16
  function createServerConfig (config) {
18
17
  // convert the config file to a new structure
@@ -59,15 +58,58 @@ async function platformaticService (app, opts, toLoad = []) {
59
58
  }
60
59
  }
61
60
 
62
- // TODO apparently c8 is not able to mark
63
- // this as covered even if it is
64
- /* c8 ignore next 7 */
65
- if (Array.isArray(opts.plugin)) {
66
- for (const plugin of opts.plugin) {
67
- await loadPlugin(app, opts, plugin)
61
+ if (opts.plugins) {
62
+ // if we don't have a fullPath, let's assume we are in a test and we can use the current working directory
63
+ const configPath = app.platformatic.configManager.fullPath || process.cwd()
64
+ const tsConfigPath = join(dirname(configPath), 'tsconfig.json')
65
+ /* c8 ignore next 21 */
66
+ if (await isFileAccessible(tsConfigPath)) {
67
+ const tsConfig = JSON.parse(await readFile(tsConfigPath, 'utf8'))
68
+ const outDir = resolve(dirname(tsConfigPath), tsConfig.compilerOptions.outDir)
69
+ opts.plugins.paths = opts.plugins.paths.map((plugin) => {
70
+ if (typeof plugin === 'string') {
71
+ return getJSPluginPath(configPath, plugin, outDir)
72
+ } else {
73
+ return {
74
+ path: getJSPluginPath(configPath, plugin.path, outDir),
75
+ options: plugin.options
76
+ }
77
+ }
78
+ })
79
+ } else {
80
+ for (const plugin of opts.plugins.paths) {
81
+ const path = typeof plugin === 'string' ? plugin : plugin.path
82
+ if (path.endsWith('.ts')) {
83
+ throw new Error(`Cannot load plugin ${path}, tsconfig.json not found`)
84
+ }
85
+ }
86
+ }
87
+
88
+ // if not defined, we default to true (which can happen only if config is set programmatically,
89
+ // that's why we ignore the coverage of the `undefined` case, which cannot be covered in cli tests)
90
+ // all individual plugin hot reload settings will be overloaded by global hot reload
91
+ /* c8 ignore next 1 */
92
+ const hotReload = opts.plugins.hotReload !== false
93
+ const isWatchEnabled = app.platformatic.config.watch !== false
94
+ app.log.debug({ plugins: opts.plugins.path }, 'loading plugin')
95
+
96
+ if (isWatchEnabled && hotReload) {
97
+ await app.register(sandbox, {
98
+ path: wrapperPath,
99
+ options: { paths: opts.plugins.paths },
100
+ customizeGlobalThis (_globalThis) {
101
+ // Taken from https://github.com/nodejs/undici/blob/fa9fd9066569b6357acacffb806aa804b688c9d8/lib/global.js#L5
102
+ const globalDispatcher = Symbol.for('undici.globalDispatcher.1')
103
+ const dispatcher = globalThis[globalDispatcher]
104
+ /* istanbul ignore else */
105
+ if (dispatcher) {
106
+ _globalThis[globalDispatcher] = dispatcher
107
+ }
108
+ }
109
+ })
110
+ } else {
111
+ await app.register(require(wrapperPath), { paths: opts.plugins.paths })
68
112
  }
69
- } else if (opts.plugin) {
70
- await loadPlugin(app, opts, opts.plugin)
71
113
  }
72
114
 
73
115
  // Enable CORS
@@ -87,6 +129,7 @@ async function platformaticService (app, opts, toLoad = []) {
87
129
  app.register(underPressure, {
88
130
  exposeStatusRoute: '/status',
89
131
  healthCheckInterval: opts.healthCheck.interval !== undefined ? opts.healthCheck.interval : 5000,
132
+ ...opts.healthCheck,
90
133
  healthCheck: opts.healthCheck.fn
91
134
  })
92
135
  }
@@ -96,59 +139,6 @@ async function platformaticService (app, opts, toLoad = []) {
96
139
  }
97
140
  }
98
141
 
99
- async function loadPlugin (app, config, pluginOptions) {
100
- /* c8 ignore next 4 */
101
- if (pluginOptions.typescript !== undefined) {
102
- const pluginPath = getJSPluginPath(pluginOptions.path, pluginOptions.typescript.outDir)
103
- pluginOptions = { ...pluginOptions, path: pluginPath }
104
- }
105
-
106
- app.log.debug({ plugin: pluginOptions }, 'loading plugin')
107
-
108
- // if not defined, we defaults to true (which can happen only if config is set programmatically,
109
- // that's why we ignore the coverage of the `undefined` case, which cannot be covered in cli tests)
110
- /* c8 ignore next 35 */
111
- const hotReload = pluginOptions.hotReload !== false
112
- const isWatchEnabled = config.watch !== false
113
- if (isWatchEnabled && hotReload) {
114
- let options = pluginOptions
115
- if ((await stat(pluginOptions.path)).isDirectory()) {
116
- options = {
117
- path: wrapperPath,
118
- options: pluginOptions
119
- }
120
- }
121
- await app.register(sandbox, {
122
- ...options,
123
- customizeGlobalThis (_globalThis) {
124
- // Taken from https://github.com/nodejs/undici/blob/fa9fd9066569b6357acacffb806aa804b688c9d8/lib/global.js#L5
125
- const globalDispatcher = Symbol.for('undici.globalDispatcher.1')
126
- const dispatcher = globalThis[globalDispatcher]
127
- /* istanbul ignore else */
128
- if (dispatcher) {
129
- _globalThis[globalDispatcher] = dispatcher
130
- }
131
- }
132
- })
133
- // c8 fails in reporting the coverage of this else branch, so we ignore it
134
- } else {
135
- if ((await stat(pluginOptions.path)).isDirectory()) {
136
- const options = {
137
- ...pluginOptions.options,
138
- dir: pluginOptions.path
139
- }
140
- await app.register(autoload, options)
141
- } else {
142
- let plugin = await import(`file://${pluginOptions.path}`)
143
- if (plugin.__esModule === true) {
144
- plugin = plugin.default
145
- }
146
- /* c8 ignore next 4 */
147
- await app.register(plugin, pluginOptions.options)
148
- }
149
- }
150
- }
151
-
152
142
  platformaticService[Symbol.for('skip-override')] = true
153
143
  platformaticService.schema = schema
154
144
 
@@ -252,6 +242,13 @@ async function buildServer (options, app, ConfigManagerContructor) {
252
242
  return handler
253
243
  }
254
244
 
245
+ // This is for @platformatic/db to use
246
+ /* c8 ignore next 4 */
247
+ async function buildStart (loadConfig, buildServer) {
248
+ const { buildStart } = await import('./lib/start.mjs')
249
+ return buildStart(loadConfig, buildServer)
250
+ }
251
+
255
252
  module.exports.buildServer = buildServer
256
253
  module.exports.schema = require('./lib/schema')
257
254
  module.exports.createServerConfig = createServerConfig
@@ -260,3 +257,4 @@ module.exports.addLoggerToTheConfig = addLoggerToTheConfig
260
257
  module.exports.loadConfig = loadConfig
261
258
  module.exports.tsCompiler = compiler
262
259
  module.exports.ConfigManager = ConfigManager
260
+ module.exports.buildStart = buildStart
package/lib/compile.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { resolve, join } = require('path')
3
+ const { resolve, join, dirname } = require('path')
4
4
  const pino = require('pino')
5
5
  const pretty = require('pino-pretty')
6
6
  const loadConfig = require('./load-config.js')
@@ -28,125 +28,98 @@ async function getTSCExecutablePath (cwd) {
28
28
  }
29
29
  }
30
30
 
31
- async function execute (logger, args, config) {
31
+ async function setup (cwd) {
32
+ const logger = pino(
33
+ pretty({
34
+ translateTime: 'SYS:HH:MM:ss',
35
+ ignore: 'hostname,pid'
36
+ })
37
+ )
38
+
32
39
  const { execa } = await import('execa')
33
- const cwd = process.cwd()
34
40
 
35
41
  const tscExecutablePath = await getTSCExecutablePath(cwd)
36
42
  /* c8 ignore next 4 */
37
43
  if (tscExecutablePath === undefined) {
38
- throw new Error('The tsc executable was not found.')
44
+ const msg = 'The tsc executable was not found.'
45
+ logger.error(msg)
39
46
  }
40
47
 
41
48
  const tsconfigPath = resolve(cwd, 'tsconfig.json')
42
49
  const tsconfigExists = await isFileAccessible(tsconfigPath)
43
50
 
44
51
  if (!tsconfigExists) {
45
- throw new Error('The tsconfig.json file was not found.')
52
+ const msg = 'The tsconfig.json file was not found.'
53
+ logger.error(msg)
54
+ }
55
+
56
+ return { execa, logger, tscExecutablePath }
57
+ }
58
+
59
+ async function compile (cwd) {
60
+ const { execa, logger, tscExecutablePath } = await setup(cwd)
61
+ /* c8 ignore next 3 */
62
+ if (!tscExecutablePath) {
63
+ return false
46
64
  }
47
65
 
48
66
  try {
49
67
  await execa(tscExecutablePath, ['--project', 'tsconfig.json'], { cwd })
50
68
  logger.info('Typescript compilation completed successfully.')
69
+ return true
51
70
  } catch (error) {
52
- throw new Error('Failed to compile typescript files: ' + error)
71
+ logger.error(error.message)
72
+ return false
53
73
  }
54
74
  }
55
75
 
56
- async function compileWatch () {
57
- const { execa } = await import('execa')
58
- const logger = pino(
59
- pretty({
60
- translateTime: 'SYS:HH:MM:ss',
61
- ignore: 'hostname,pid'
62
- })
63
- )
64
-
65
- const cwd = process.cwd()
66
-
67
- const tscExecutablePath = await getTSCExecutablePath(cwd)
68
- /* c8 ignore next 4 */
69
- if (tscExecutablePath === undefined) {
70
- logger.error('The tsc executable was not found.')
71
- process.exit(1)
76
+ // This path is tested but C8 does not see it that way given it needs to work
77
+ // through execa.
78
+ /* c8 ignore next 20 */
79
+ async function compileWatch (cwd) {
80
+ const { execa, logger, tscExecutablePath } = await setup(cwd)
81
+ if (!tscExecutablePath) {
82
+ return false
72
83
  }
73
84
 
74
- const tsconfigPath = resolve(cwd, 'tsconfig.json')
75
- const tsconfigExists = await isFileAccessible(tsconfigPath)
76
-
77
- if (!tsconfigExists) {
78
- logger.error('The tsconfig.json file was not found.')
79
- process.exit(1)
85
+ try {
86
+ await execa(tscExecutablePath, ['--project', 'tsconfig.json', '--incremental'], { cwd })
87
+ logger.info('Typescript compilation completed successfully. Starting watch mode.')
88
+ } catch (error) {
89
+ throw new Error('Failed to compile typescript files: ' + error)
80
90
  }
81
91
 
82
- let isCompiled = false
83
- return new Promise((resolve, reject) => {
84
- const child = execa(tscExecutablePath, ['--project', 'tsconfig.json', '--watch'], { cwd })
85
- process.on('SIGINT', () => child.kill('SIGKILL'))
86
- process.on('SIGTERM', () => child.kill('SIGKILL'))
87
-
88
- let tsCompilationMessages = []
89
-
90
- child.stdout.on('data', (data) => {
91
- let tsCompilerMessage = data.toString().trim()
92
- if (tsCompilerMessage.startsWith('\u001bc')) {
93
- tsCompilerMessage = tsCompilerMessage.slice(2)
94
- }
95
-
96
- if (tsCompilerMessage === '') return
97
-
98
- const startMessage = tsCompilerMessage.match(/.*Starting compilation in watch mode.../)
99
- if (startMessage !== null) {
100
- logger.info(tsCompilerMessage)
101
- return
102
- }
103
-
104
- tsCompilationMessages.push(tsCompilerMessage)
105
-
106
- const resultMessage = tsCompilerMessage.match(/.*Found (\d+) error.*/)
107
- if (resultMessage !== null) {
108
- const errorsCount = parseInt(resultMessage[1])
109
- const compilerOutput = tsCompilationMessages.join('\n')
110
- /* c8 ignore next 6 */
111
- if (errorsCount === 0) {
112
- logger.info(compilerOutput)
113
- if (!isCompiled) {
114
- isCompiled = true
115
- resolve()
116
- }
117
- } else {
118
- logger.error(compilerOutput)
119
- if (!isCompiled) {
120
- reject(new Error('Typescript compilation failed.'))
121
- }
122
- }
123
- tsCompilationMessages = []
124
- }
125
- })
92
+ const child = execa(tscExecutablePath, ['--project', 'tsconfig.json', '--watch', '--incremental'], { cwd })
93
+ child.stdout.resume()
94
+ child.stderr.on('data', (data) => {
95
+ logger.error(data.toString())
126
96
  })
127
- }
128
97
 
129
- async function compile (_args) {
130
- const logger = pino(
131
- pretty({
132
- translateTime: 'SYS:HH:MM:ss',
133
- ignore: 'hostname,pid'
134
- })
135
- )
136
-
137
- const { configManager, args } = await loadConfig({}, _args)
138
- await configManager.parseAndValidate()
139
- const config = configManager.current
98
+ return { child }
99
+ }
140
100
 
141
- try {
142
- await execute(logger, args, config)
143
- process.exit(0)
144
- } catch (error) {
145
- logger.error(error.message)
146
- process.exit(1)
101
+ function buildCompileCmd (_loadConfig) {
102
+ return async function compileCmd (_args) {
103
+ let fullPath = null
104
+ try {
105
+ const { configManager } = await _loadConfig({}, _args)
106
+ await configManager.parseAndValidate()
107
+ fullPath = dirname(configManager.fullPath)
108
+ /* c8 ignore next 4 */
109
+ } catch (err) {
110
+ console.error(err)
111
+ process.exit(1)
112
+ }
113
+
114
+ if (!await compile(fullPath)) {
115
+ process.exit(1)
116
+ }
147
117
  }
148
118
  }
149
119
 
120
+ const compileCmd = buildCompileCmd(loadConfig)
121
+
150
122
  module.exports.compile = compile
151
123
  module.exports.compileWatch = compileWatch
152
- module.exports.execute = execute
124
+ module.exports.buildCompileCmd = buildCompileCmd
125
+ module.exports.compileCmd = compileCmd
package/lib/config.js CHANGED
@@ -32,11 +32,8 @@ class ServiceConfigManager extends ConfigManager {
32
32
  }
33
33
 
34
34
  // relative-to-absolute plugin path
35
- /* c8 ignore next 3 */
36
- if (Array.isArray(this.current.plugin)) {
37
- this.current.plugin = this.current.plugin.map(fixPluginPath)
38
- } else if (this.current.plugin) {
39
- this.current.plugin = fixPluginPath(this.current.plugin)
35
+ if (this.current.plugins?.paths) {
36
+ this.current.plugins.paths = this.current.plugins.paths.map(fixPluginPath)
40
37
  }
41
38
  }
42
39
 
@@ -18,15 +18,19 @@ const ourConfigFiles = [
18
18
  async function loadConfig (minimistConfig, _args, configOpts = {}, Manager = ConfigManager, configFileNames = ourConfigFiles) {
19
19
  const args = parseArgs(_args, deepmerge({ all: true })({
20
20
  string: ['allow-env'],
21
+ boolean: ['hotReload'],
21
22
  default: {
22
- allowEnv: '' // The default is set in ConfigManager
23
+ allowEnv: '', // The default is set in ConfigManager
24
+ hotReload: true
23
25
  },
24
26
  alias: {
25
27
  v: 'version',
26
28
  c: 'config',
27
- allowEnv: ['allow-env', 'E']
29
+ allowEnv: ['allow-env', 'E'],
30
+ hotReload: ['hot-reload']
28
31
  }
29
32
  }, minimistConfig))
33
+
30
34
  try {
31
35
  if (!args.config) {
32
36
  args.config = await findConfigFile(process.cwd(), configFileNames)
@@ -0,0 +1,26 @@
1
+ 'use strict'
2
+
3
+ const fp = require('fastify-plugin')
4
+ const autoload = require('@fastify/autoload')
5
+ const { stat } = require('fs').promises
6
+
7
+ module.exports = fp(async function (app, opts) {
8
+ for (let plugin of opts.paths) {
9
+ if (typeof plugin === 'string') {
10
+ plugin = { path: plugin }
11
+ }
12
+ if ((await stat(plugin.path)).isDirectory()) {
13
+ app.register(autoload, {
14
+ dir: plugin.path,
15
+ options: plugin.options
16
+ })
17
+ } else {
18
+ let loaded = await import(`file://${plugin.path}`)
19
+ /* c8 ignore next 3 */
20
+ if (loaded.__esModule === true) {
21
+ loaded = loaded.default
22
+ }
23
+ await app.register(loaded, plugin.options)
24
+ }
25
+ }
26
+ })
package/lib/schema.js CHANGED
@@ -1,5 +1,10 @@
1
+ #!/usr/bin/env node
2
+
1
3
  'use strict'
2
4
 
5
+ const pkg = require('../package.json')
6
+ const version = 'v' + pkg.version
7
+
3
8
  const cors = {
4
9
  type: 'object',
5
10
  $comment: 'See https://github.com/fastify/fastify-cors',
@@ -90,7 +95,6 @@ const cors = {
90
95
  }
91
96
 
92
97
  const server = {
93
- $id: 'https://schemas.platformatic.dev/service/server',
94
98
  type: 'object',
95
99
  properties: {
96
100
  // TODO add support for level
@@ -125,7 +129,6 @@ const server = {
125
129
  }
126
130
 
127
131
  const watch = {
128
- $id: 'https://schemas.platformatic.dev/service/watch',
129
132
  type: 'object',
130
133
  properties: {
131
134
  type: 'object',
@@ -152,29 +155,34 @@ const watch = {
152
155
  }
153
156
  }
154
157
 
155
- const plugin = {
156
- $id: '#plugin',
158
+ const plugins = {
159
+ $id: '#plugins',
157
160
  type: 'object',
158
161
  properties: {
159
- path: {
160
- type: 'string'
162
+ paths: {
163
+ type: 'array',
164
+ items: {
165
+ anyOf: [{
166
+ type: 'string'
167
+ }, {
168
+ type: 'object',
169
+ properties: {
170
+ path: {
171
+ type: 'string'
172
+ },
173
+ options: {
174
+ type: 'object',
175
+ additionalProperties: true
176
+ }
177
+ }
178
+ }]
179
+ }
161
180
  },
162
181
  stopTimeout: {
163
182
  type: 'integer'
164
183
  },
165
184
  typescript: {
166
- type: 'object',
167
- properties: {
168
- outDir: {
169
- type: 'string'
170
- },
171
- build: {
172
- type: 'boolean',
173
- default: true
174
- }
175
- },
176
- additionalProperties: false,
177
- required: ['outDir']
185
+ type: 'boolean'
178
186
  },
179
187
  fallback: {
180
188
  type: 'boolean'
@@ -182,38 +190,13 @@ const plugin = {
182
190
  hotReload: {
183
191
  type: 'boolean',
184
192
  default: true
185
- },
186
- options: {
187
- type: 'object'
188
193
  }
189
194
  },
190
195
  additionalProperties: false,
191
- required: ['path']
192
- }
193
-
194
- const pluginTypes = {
195
- $id: 'https://schemas.platformatic.dev/service/pluginTypes',
196
- $defs: {
197
- plugin
198
- },
199
- anyOf: [{
200
- type: 'array',
201
- items: {
202
- anyOf: [{
203
- $ref: '#plugin'
204
- }, {
205
- type: 'string'
206
- }]
207
- }
208
- }, {
209
- $ref: '#plugin'
210
- }, {
211
- type: 'string'
212
- }]
196
+ required: ['paths']
213
197
  }
214
198
 
215
199
  const metrics = {
216
- $id: 'https://schemas.platformatic.dev/service/metrics',
217
200
  anyOf: [
218
201
  { type: 'boolean' },
219
202
  {
@@ -237,11 +220,11 @@ const metrics = {
237
220
  }
238
221
 
239
222
  const platformaticServiceSchema = {
240
- $id: 'https://schemas.platformatic.dev/service',
223
+ $id: `https://platformatic.dev/schemas/${version}/service`,
241
224
  type: 'object',
242
225
  properties: {
243
226
  server,
244
- plugin: pluginTypes,
227
+ plugins,
245
228
  metrics
246
229
  },
247
230
  additionalProperties: {
@@ -249,6 +232,9 @@ const platformaticServiceSchema = {
249
232
  anyOf: [watch, {
250
233
  type: 'boolean'
251
234
  }]
235
+ },
236
+ hotReload: {
237
+ type: 'boolean'
252
238
  }
253
239
  },
254
240
  required: ['server']
@@ -258,6 +244,9 @@ module.exports.schema = platformaticServiceSchema
258
244
  module.exports.metrics = metrics
259
245
  module.exports.cors = cors
260
246
  module.exports.server = server
261
- module.exports.plugin = plugin
262
- module.exports.pluginTypes = pluginTypes
247
+ module.exports.plugins = plugins
263
248
  module.exports.watch = watch
249
+
250
+ if (require.main === module) {
251
+ console.log(JSON.stringify(platformaticServiceSchema, null, 2))
252
+ }