@platformatic/service 0.16.0 → 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 (37) 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 -68
  5. package/lib/compile.js +63 -88
  6. package/lib/config.js +2 -11
  7. package/lib/load-config.js +6 -2
  8. package/lib/sandbox-wrapper.js +26 -0
  9. package/lib/schema.js +34 -48
  10. package/lib/start.mjs +97 -69
  11. package/lib/utils.js +4 -0
  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 +12 -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/tsconfig.tsbuildinfo +1 -0
  33. package/test/tmp/typescript-plugin-clone-4/platformatic.service.json +3 -5
  34. package/test/tmp/typescript-plugin-clone-5/platformatic.service.json +3 -5
  35. package/test/tmp/typescript-plugin-clone-6/platformatic.service.json +3 -6
  36. package/test/utils.test.js +8 -1
  37. 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,61 +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 5 */
101
- if (pluginOptions.typescript !== undefined) {
102
- const configPath = app.platformatic.configManager.fullPath
103
- const pluginPath = getJSPluginPath(configPath, pluginOptions.path, pluginOptions.typescript.outDir)
104
- pluginOptions = { ...pluginOptions, path: pluginPath }
105
- }
106
-
107
- app.log.debug({ plugin: pluginOptions }, 'loading plugin')
108
-
109
- // if not defined, we defaults to true (which can happen only if config is set programmatically,
110
- // that's why we ignore the coverage of the `undefined` case, which cannot be covered in cli tests)
111
- // all individual plugin hot reload settings will be overloaded by global hot reload
112
- /* c8 ignore next 35 */
113
- const hotReload = config.hotReload ?? pluginOptions.hotReload !== false
114
- const isWatchEnabled = config.watch !== false
115
- if (isWatchEnabled && hotReload) {
116
- let options = pluginOptions
117
- if ((await stat(pluginOptions.path)).isDirectory()) {
118
- options = {
119
- path: wrapperPath,
120
- options: pluginOptions
121
- }
122
- }
123
- await app.register(sandbox, {
124
- ...options,
125
- customizeGlobalThis (_globalThis) {
126
- // Taken from https://github.com/nodejs/undici/blob/fa9fd9066569b6357acacffb806aa804b688c9d8/lib/global.js#L5
127
- const globalDispatcher = Symbol.for('undici.globalDispatcher.1')
128
- const dispatcher = globalThis[globalDispatcher]
129
- /* istanbul ignore else */
130
- if (dispatcher) {
131
- _globalThis[globalDispatcher] = dispatcher
132
- }
133
- }
134
- })
135
- // c8 fails in reporting the coverage of this else branch, so we ignore it
136
- } else {
137
- if ((await stat(pluginOptions.path)).isDirectory()) {
138
- const options = {
139
- ...pluginOptions.options,
140
- dir: pluginOptions.path
141
- }
142
- await app.register(autoload, options)
143
- } else {
144
- let plugin = await import(`file://${pluginOptions.path}`)
145
- if (plugin.__esModule === true) {
146
- plugin = plugin.default
147
- }
148
- /* c8 ignore next 4 */
149
- await app.register(plugin, pluginOptions.options)
150
- }
151
- }
152
- }
153
-
154
142
  platformaticService[Symbol.for('skip-override')] = true
155
143
  platformaticService.schema = schema
156
144
 
@@ -254,6 +242,13 @@ async function buildServer (options, app, ConfigManagerContructor) {
254
242
  return handler
255
243
  }
256
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
+
257
252
  module.exports.buildServer = buildServer
258
253
  module.exports.schema = require('./lib/schema')
259
254
  module.exports.createServerConfig = createServerConfig
@@ -262,3 +257,4 @@ module.exports.addLoggerToTheConfig = addLoggerToTheConfig
262
257
  module.exports.loadConfig = loadConfig
263
258
  module.exports.tsCompiler = compiler
264
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,123 +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
 
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 */
56
79
  async function compileWatch (cwd) {
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 tscExecutablePath = await getTSCExecutablePath(cwd)
66
- /* c8 ignore next 4 */
67
- if (tscExecutablePath === undefined) {
68
- logger.error('The tsc executable was not found.')
69
- process.exit(1)
80
+ const { execa, logger, tscExecutablePath } = await setup(cwd)
81
+ if (!tscExecutablePath) {
82
+ return false
70
83
  }
71
84
 
72
- const tsconfigPath = resolve(cwd, 'tsconfig.json')
73
- const tsconfigExists = await isFileAccessible(tsconfigPath)
74
-
75
- if (!tsconfigExists) {
76
- logger.error('The tsconfig.json file was not found.')
77
- 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)
78
90
  }
79
91
 
80
- let isCompiled = false
81
- return new Promise((resolve, reject) => {
82
- const child = execa(tscExecutablePath, ['--project', 'tsconfig.json', '--watch'], { cwd })
83
- process.on('SIGINT', () => child.kill('SIGKILL'))
84
- process.on('SIGTERM', () => child.kill('SIGKILL'))
85
-
86
- let tsCompilationMessages = []
87
-
88
- child.stdout.on('data', (data) => {
89
- let tsCompilerMessage = data.toString().trim()
90
- if (tsCompilerMessage.startsWith('\u001bc')) {
91
- tsCompilerMessage = tsCompilerMessage.slice(2)
92
- }
93
-
94
- if (tsCompilerMessage === '') return
95
-
96
- const startMessage = tsCompilerMessage.match(/.*Starting compilation in watch mode.../)
97
- if (startMessage !== null) {
98
- logger.info(tsCompilerMessage)
99
- return
100
- }
101
-
102
- tsCompilationMessages.push(tsCompilerMessage)
103
-
104
- const resultMessage = tsCompilerMessage.match(/.*Found (\d+) error.*/)
105
- if (resultMessage !== null) {
106
- const errorsCount = parseInt(resultMessage[1])
107
- const compilerOutput = tsCompilationMessages.join('\n')
108
- /* c8 ignore next 6 */
109
- if (errorsCount === 0) {
110
- logger.info(compilerOutput)
111
- if (!isCompiled) {
112
- isCompiled = true
113
- resolve()
114
- }
115
- } else {
116
- logger.error(compilerOutput)
117
- if (!isCompiled) {
118
- reject(new Error('Typescript compilation failed.'))
119
- }
120
- }
121
- tsCompilationMessages = []
122
- }
123
- })
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())
124
96
  })
125
- }
126
-
127
- async function compile (_args) {
128
- const logger = pino(
129
- pretty({
130
- translateTime: 'SYS:HH:MM:ss',
131
- ignore: 'hostname,pid'
132
- })
133
- )
134
97
 
135
- const { configManager, args } = await loadConfig({}, _args)
136
- await configManager.parseAndValidate()
137
- const config = configManager.current
98
+ return { child }
99
+ }
138
100
 
139
- try {
140
- await execute(logger, args, config)
141
- process.exit(0)
142
- } catch (error) {
143
- logger.error(error.message)
144
- 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
+ }
145
117
  }
146
118
  }
147
119
 
120
+ const compileCmd = buildCompileCmd(loadConfig)
121
+
148
122
  module.exports.compile = compile
149
123
  module.exports.compileWatch = compileWatch
150
- module.exports.execute = execute
124
+ module.exports.buildCompileCmd = buildCompileCmd
125
+ module.exports.compileCmd = compileCmd
package/lib/config.js CHANGED
@@ -32,17 +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)
40
- }
41
-
42
- // TODO: handle arrays
43
- if (this.current.plugin?.typescript) {
44
- const typescript = this.current.plugin.typescript
45
- typescript.outDir = this._fixRelativePath(typescript.outDir)
35
+ if (this.current.plugins?.paths) {
36
+ this.current.plugins.paths = this.current.plugins.paths.map(fixPluginPath)
46
37
  }
47
38
  }
48
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: {
@@ -261,6 +244,9 @@ module.exports.schema = platformaticServiceSchema
261
244
  module.exports.metrics = metrics
262
245
  module.exports.cors = cors
263
246
  module.exports.server = server
264
- module.exports.plugin = plugin
265
- module.exports.pluginTypes = pluginTypes
247
+ module.exports.plugins = plugins
266
248
  module.exports.watch = watch
249
+
250
+ if (require.main === module) {
251
+ console.log(JSON.stringify(platformaticServiceSchema, null, 2))
252
+ }