@platformatic/service 0.10.0 → 0.12.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/help/help.txt CHANGED
@@ -3,3 +3,4 @@ Available commands:
3
3
  * `help` - show this help message.
4
4
  * `help <command>` - shows more information about a command.
5
5
  * `start` - start the server.
6
+ * `schema config` - generate the schema configuration file.
@@ -0,0 +1,9 @@
1
+ Update the config schema file:
2
+
3
+ * `schema config` - update the JSON schema config available on `platformatic.service.schema.json`
4
+
5
+ Your configuration on `platformatic.service.json` has a schema defined to improve the developer experience and avoid mistakes when updating the configuration of Platformatic Service.
6
+ When you run `platformatic service init`, a new JSON `$schema` property is added in `platformatic.service.json`. This can allow your IDE to add suggestions (f.e. mandatory/missing fields, types, default values) by opening the config in `platformatic.service.json`.
7
+ Running `platformatic service schema config` you can update your schema so that it matches well the latest changes available on your config.
8
+
9
+
package/index.js CHANGED
@@ -40,7 +40,6 @@ async function platformaticService (app, opts, toLoad = []) {
40
40
  {
41
41
  const fileWatcher = opts.fileWatcher
42
42
  const configManager = opts.configManager
43
- /* c8 ignore next 4 */
44
43
  if (fileWatcher !== undefined) {
45
44
  app.platformatic.fileWatcher = fileWatcher
46
45
  }
package/lib/config.js CHANGED
@@ -1,9 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const ConfigManager = require('@platformatic/config')
4
- const { dirname, resolve, relative } = require('path')
4
+ const { resolve } = require('path')
5
5
  const { schema } = require('./schema')
6
- const clone = require('rfdc')()
7
6
 
8
7
  class ServiceConfigManager extends ConfigManager {
9
8
  constructor (opts) {
@@ -41,33 +40,8 @@ class ServiceConfigManager extends ConfigManager {
41
40
  }
42
41
  }
43
42
 
44
- _sanitizeConfig () {
45
- const sanitizedConfig = clone(this.current)
46
- const dirOfConfig = dirname(this.fullPath)
47
-
48
- const fixPluginPath = (plugin) => {
49
- // for some reasons c8 does not detect these
50
- /* c8 ignore next 3 */
51
- if (typeof plugin === 'string') {
52
- plugin = { path: plugin }
53
- }
54
- plugin.path = relative(dirOfConfig, plugin.path)
55
- return plugin
56
- }
57
-
58
- // relative-to-absolute plugin path
59
- /* c8 ignore next 6 */
60
- if (Array.isArray(this.current.plugin)) {
61
- sanitizedConfig.plugin = sanitizedConfig.plugin.map(fixPluginPath)
62
- } else if (this.current.plugin) {
63
- sanitizedConfig.plugin = fixPluginPath(sanitizedConfig.plugin)
64
- }
65
-
66
- return sanitizedConfig
67
- }
68
-
69
43
  _fixRelativePath (path) {
70
- return resolve(dirname(this.fullPath), path)
44
+ return resolve(this.dirname, path)
71
45
  }
72
46
  }
73
47
 
@@ -0,0 +1,14 @@
1
+
2
+ const { writeFile } = require('fs/promises')
3
+ const { schema: platformaticServiceSchema } = require('./schema.js')
4
+
5
+ const filenameConfigJsonSchema = 'platformatic.service.schema.json'
6
+
7
+ async function generateJsonSchemaConfig () {
8
+ await writeFile(filenameConfigJsonSchema, JSON.stringify(platformaticServiceSchema, null, 2))
9
+ }
10
+
11
+ module.exports = {
12
+ generateJsonSchemaConfig,
13
+ filenameConfigJsonSchema
14
+ }
@@ -87,7 +87,7 @@ module.exports = fp(async function (app, opts) {
87
87
  const metricsEndpointOptions = {
88
88
  url: '/metrics',
89
89
  method: 'GET',
90
- logLevel: 'info',
90
+ logLevel: 'trace',
91
91
  handler: async (req, reply) => {
92
92
  const promRegistry = app.metrics.client.register
93
93
  const accepts = req.accepts()
package/lib/schema.js CHANGED
@@ -96,7 +96,7 @@ const server = {
96
96
  enabled: { type: 'boolean' },
97
97
  interval: { type: 'integer' }
98
98
  },
99
- additionalProperties: false
99
+ additionalProperties: true
100
100
  }
101
101
  ]
102
102
  },
@@ -134,7 +134,7 @@ const watch = {
134
134
  }
135
135
 
136
136
  const plugin = {
137
- $id: 'https://schemas.platformatic.dev/service/plugin',
137
+ $id: '#plugin',
138
138
  type: 'object',
139
139
  properties: {
140
140
  path: {
@@ -148,6 +148,10 @@ const plugin = {
148
148
  properties: {
149
149
  outDir: {
150
150
  type: 'string'
151
+ },
152
+ build: {
153
+ type: 'boolean',
154
+ default: true
151
155
  }
152
156
  },
153
157
  additionalProperties: false,
@@ -168,8 +172,29 @@ const plugin = {
168
172
  required: ['path']
169
173
  }
170
174
 
175
+ const pluginTypes = {
176
+ $id: 'https://schemas.platformatic.dev/service/pluginTypes',
177
+ $defs: {
178
+ plugin
179
+ },
180
+ anyOf: [{
181
+ type: 'array',
182
+ items: {
183
+ anyOf: [{
184
+ $ref: '#plugin'
185
+ }, {
186
+ type: 'string'
187
+ }]
188
+ }
189
+ }, {
190
+ $ref: '#plugin'
191
+ }, {
192
+ type: 'string'
193
+ }]
194
+ }
195
+
171
196
  const metrics = {
172
- $id: 'https://schemas.platformatic.dev/db/metrics',
197
+ $id: 'https://schemas.platformatic.dev/service/metrics',
173
198
  anyOf: [
174
199
  { type: 'boolean' },
175
200
  {
@@ -193,29 +218,11 @@ const metrics = {
193
218
  }
194
219
 
195
220
  const platformaticServiceSchema = {
196
- $id: 'https://schemas.platformatic.dev/db',
221
+ $id: 'https://schemas.platformatic.dev/service',
197
222
  type: 'object',
198
- $defs: {
199
- plugin
200
- },
201
223
  properties: {
202
224
  server,
203
- plugin: {
204
- anyOf: [{
205
- type: 'array',
206
- items: {
207
- anyOf: [{
208
- $ref: '#/$defs/plugin'
209
- }, {
210
- type: 'string'
211
- }]
212
- }
213
- }, {
214
- $ref: '#/$defs/plugin'
215
- }, {
216
- type: 'string'
217
- }]
218
- },
225
+ plugin: pluginTypes,
219
226
  metrics
220
227
  },
221
228
  additionalProperties: {
@@ -233,4 +240,5 @@ module.exports.metrics = metrics
233
240
  module.exports.cors = cors
234
241
  module.exports.server = server
235
242
  module.exports.plugin = plugin
243
+ module.exports.pluginTypes = pluginTypes
236
244
  module.exports.watch = watch
package/lib/start.mjs CHANGED
@@ -19,13 +19,17 @@ async function start (_args) {
19
19
 
20
20
  if (
21
21
  config.plugin?.typescript !== undefined &&
22
- config.plugin?.watch !== false
22
+ config.plugin?.watch !== false &&
23
+ config.plugin?.typescript?.build !== false
23
24
  ) {
24
25
  try {
25
26
  await compileWatch()
26
27
  } catch (error) {
27
28
  process.exit(1)
28
29
  }
30
+ } else if (config.plugin?.typescript !== undefined && config.plugin?.typescript?.build === false) {
31
+ // we don't have the logger here, shall we create one just for this message?
32
+ console.log(`TS build is disabled, expecting compiled js files in ${config.plugin.typescript.outDir} folder`)
29
33
  }
30
34
 
31
35
  // Set the location of the config
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/service",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "author": "Matteo Collina <hello@matteocollina.com>",
@@ -14,47 +14,49 @@
14
14
  },
15
15
  "homepage": "https://github.com/platformatic/platformatic#readme",
16
16
  "devDependencies": {
17
- "c8": "^7.11.0",
17
+ "bindings": "^1.5.0",
18
+ "c8": "^7.12.0",
18
19
  "snazzy": "^9.0.0",
19
20
  "split2": "^4.1.0",
20
21
  "standard": "^17.0.0",
21
22
  "strip-ansi": "^7.0.1",
22
- "tap": "^16.0.0",
23
- "typescript": "^4.8.4",
24
- "undici": "^5.8.0",
23
+ "tap": "^16.3.2",
24
+ "typescript": "^4.9.4",
25
+ "undici": "^5.14.0",
26
+ "vscode-json-languageservice": "^5.1.3",
25
27
  "why-is-node-running": "^2.2.2",
26
- "yaml": "^2.1.1"
28
+ "yaml": "^2.1.3"
27
29
  },
28
30
  "dependencies": {
29
- "@fastify/accepts": "^4.0.1",
30
- "@fastify/autoload": "^5.5.0",
31
+ "@fastify/accepts": "^4.1.0",
32
+ "@fastify/autoload": "^5.6.0",
31
33
  "@fastify/basic-auth": "^5.0.0",
32
- "@fastify/cors": "^8.0.0",
33
- "@fastify/deepmerge": "^1.1.0",
34
- "@fastify/restartable": "^1.3.1",
35
- "@fastify/static": "^6.5.0",
36
- "@fastify/swagger": "^8.0.0",
37
- "@fastify/under-pressure": "^8.0.0",
38
- "@platformatic/config": "0.10.0",
39
- "@platformatic/utils": "0.10.0",
34
+ "@fastify/cors": "^8.2.0",
35
+ "@fastify/deepmerge": "^1.3.0",
36
+ "@fastify/restartable": "^1.4.0",
37
+ "@fastify/static": "^6.6.0",
38
+ "@fastify/swagger": "^8.2.1",
39
+ "@fastify/under-pressure": "^8.2.0",
40
40
  "close-with-grace": "^1.1.0",
41
- "commist": "^3.1.2",
42
- "desm": "^1.2.0",
43
- "env-schema": "^5.0.0",
41
+ "commist": "^3.2.0",
42
+ "desm": "^1.3.0",
43
+ "env-schema": "^5.2.0",
44
44
  "es-main": "^1.2.0",
45
45
  "execa": "^6.1.0",
46
- "fastify": "^4.6.0",
46
+ "fastify": "^4.10.2",
47
47
  "fastify-metrics": "^10.0.0",
48
- "fastify-plugin": "^4.1.0",
48
+ "fastify-plugin": "^4.4.0",
49
49
  "fastify-sandbox": "^0.11.0",
50
50
  "graphql": "^16.6.0",
51
- "help-me": "^4.1.0",
52
- "mercurius": "^11.3.0",
53
- "minimatch": "^5.1.0",
54
- "minimist": "^1.2.6",
55
- "pino": "^8.4.1",
56
- "pino-pretty": "^9.0.0",
57
- "rfdc": "^1.3.0"
51
+ "help-me": "^4.2.0",
52
+ "mercurius": "^11.4.0",
53
+ "minimatch": "^5.1.1",
54
+ "minimist": "^1.2.7",
55
+ "pino": "^8.8.0",
56
+ "pino-pretty": "^9.1.1",
57
+ "rfdc": "^1.3.0",
58
+ "@platformatic/config": "0.12.0",
59
+ "@platformatic/utils": "0.12.0"
58
60
  },
59
61
  "standard": {
60
62
  "ignore": [
package/service.mjs CHANGED
@@ -6,6 +6,7 @@ import isMain from 'es-main'
6
6
  import helpMe from 'help-me'
7
7
  import { readFile } from 'fs/promises'
8
8
  import { join } from 'desm'
9
+ import { generateJsonSchemaConfig } from './lib/gen-schema.js'
9
10
 
10
11
  import start from './lib/start.mjs'
11
12
  import { compile } from './lib/compile.js'
@@ -16,13 +17,15 @@ const help = helpMe({
16
17
  ext: '.txt'
17
18
  })
18
19
 
19
- const program = commist({ maxDistance: 4 })
20
+ const program = commist({ maxDistance: 2 })
20
21
 
21
22
  program.register('help', help.toStdout)
22
23
  program.register('help start', help.toStdout.bind(null, ['start']))
23
24
 
24
25
  program.register('start', start)
25
26
  program.register('compile', compile)
27
+ program.register('schema config', generateJsonSchemaConfig)
28
+ program.register('schema', help.toStdout.bind(null, ['schema']))
26
29
 
27
30
  export async function runService (argv) {
28
31
  const args = parseArgs(argv, {
@@ -36,11 +39,9 @@ export async function runService (argv) {
36
39
  process.exit(0)
37
40
  }
38
41
 
39
- const result = program.parse(argv)
40
-
41
- if (result) {
42
- // We did have a command we did not match
43
- return start(result)
42
+ return {
43
+ output: await program.parseAsync(argv),
44
+ help
44
45
  }
45
46
  }
46
47
 
@@ -37,6 +37,31 @@ t.test('should compile typescript plugin', async (t) => {
37
37
  t.pass()
38
38
  })
39
39
 
40
+ t.test('should compile typescript plugin even if build is `false`', async (t) => {
41
+ const testDir = path.join(urlDirname(import.meta.url), '..', 'fixtures', 'typescript-plugin-nocompile')
42
+ const cwd = path.join(urlDirname(import.meta.url), '..', 'tmp', 'typescript-plugin-clone-2')
43
+
44
+ await cp(testDir, cwd, { recursive: true })
45
+
46
+ try {
47
+ const child = await execa('node', [cliPath, 'compile'], { cwd })
48
+ t.equal(child.stdout.includes('Typescript compilation completed successfully.'), true)
49
+ } catch (err) {
50
+ console.log(err.stdout)
51
+ console.log(err.stderr)
52
+ t.fail(err.stderr)
53
+ }
54
+
55
+ const jsPluginPath = path.join(cwd, 'dist', 'plugin.js')
56
+ try {
57
+ await access(jsPluginPath)
58
+ } catch (err) {
59
+ t.fail(err)
60
+ }
61
+
62
+ t.pass()
63
+ })
64
+
40
65
  t.test('should compile typescript plugin with start command', async (t) => {
41
66
  const testDir = path.join(urlDirname(import.meta.url), '..', 'fixtures', 'typescript-plugin')
42
67
  const cwd = path.join(urlDirname(import.meta.url), '..', 'tmp', 'typescript-plugin-clone-3')
@@ -91,7 +116,7 @@ t.test('should not compile bad typescript plugin', async (t) => {
91
116
 
92
117
  t.test('missing tsconfig file', async (t) => {
93
118
  const testDir = path.join(urlDirname(import.meta.url), '..', 'fixtures', 'typescript-plugin')
94
- const cwd = path.join(urlDirname(import.meta.url), '..', 'tmp', 'typescript-plugin-clone-2')
119
+ const cwd = path.join(urlDirname(import.meta.url), '..', 'tmp', 'typescript-plugin-clone-4')
95
120
 
96
121
  await cp(testDir, cwd, { recursive: true })
97
122
 
@@ -138,7 +163,7 @@ t.test('start command should not compile typescript plugin with errors', async (
138
163
 
139
164
  t.test('should not compile typescript plugin with start without tsconfig', async (t) => {
140
165
  const testDir = path.join(urlDirname(import.meta.url), '..', 'fixtures', 'typescript-plugin')
141
- const cwd = path.join(urlDirname(import.meta.url), '..', 'tmp', 'typescript-plugin-clone-4')
166
+ const cwd = path.join(urlDirname(import.meta.url), '..', 'tmp', 'typescript-plugin-clone-5')
142
167
 
143
168
  await cp(testDir, cwd, { recursive: true })
144
169
 
@@ -155,3 +180,34 @@ t.test('should not compile typescript plugin with start without tsconfig', async
155
180
  t.equal(err.stdout.includes('The tsconfig.json file was not found.'), true)
156
181
  }
157
182
  })
183
+
184
+ t.test('start command should not compile typescript if `build` is false', async (t) => {
185
+ const testDir = path.join(urlDirname(import.meta.url), '..', 'fixtures', 'typescript-plugin-nocompile')
186
+ const cwd = path.join(urlDirname(import.meta.url), '..', 'tmp', 'typescript-plugin-clone-6')
187
+
188
+ await cp(testDir, cwd, { recursive: true })
189
+
190
+ const child = execa('node', [cliPath, 'start'], { cwd })
191
+ t.teardown(async () => {
192
+ if (os.platform() === 'win32') {
193
+ try {
194
+ await execa('taskkill', ['/pid', child.pid, '/f', '/t'])
195
+ } catch (err) {
196
+ console.error(`Failed to kill process ${child.pid})`)
197
+ }
198
+ } else {
199
+ child.kill('SIGINT')
200
+ }
201
+ })
202
+
203
+ const jsPluginPath = path.join(cwd, 'dist', 'plugin.js')
204
+ try {
205
+ await access(jsPluginPath)
206
+ t.fail("should not have created 'dist/plugin.js'")
207
+ } catch (err) {
208
+ // cannot start because the plugin is not compiled
209
+ t.equal(err.code, 'ENOENT')
210
+ t.equal(err.path, jsPluginPath)
211
+ t.pass()
212
+ }
213
+ })
@@ -0,0 +1,43 @@
1
+ import { test } from 'tap'
2
+ import fs from 'fs/promises'
3
+ import { mkdtempSync } from 'fs'
4
+ import { tmpdir } from 'os'
5
+ import { generateJsonSchemaConfig } from '../../lib/gen-schema.js'
6
+ import { join } from 'path'
7
+ import jsonLanguageService from 'vscode-json-languageservice'
8
+
9
+ test('generateJsonSchemaConfig generates the file', async (t) => {
10
+ const tmpDir = await mkdtempSync(join(tmpdir(), 'test-create-platformatic-'))
11
+ process.chdir(tmpDir)
12
+ await generateJsonSchemaConfig()
13
+
14
+ const configSchema = await fs.readFile('platformatic.service.schema.json', 'utf8')
15
+ const schema = JSON.parse(configSchema)
16
+ const { required, additionalProperties } = schema
17
+ t.has(required, ['server'])
18
+ t.has(additionalProperties, { watch: {} })
19
+ const { $id, type } = schema
20
+ t.equal($id, 'https://schemas.platformatic.dev/service')
21
+ t.equal(type, 'object')
22
+
23
+ const languageservice = jsonLanguageService.getLanguageService({
24
+ async schemaRequestService (uri) {
25
+ return configSchema
26
+ }
27
+ })
28
+
29
+ languageservice.configure({ allowComments: false, schemas: [{ fileMatch: ['*.data.json'], uri: $id }] })
30
+
31
+ const jsonContent = `{
32
+ "$schema": "https://schemas.platformatic.dev/service",
33
+ "server": {
34
+ "hostname": "127.0.0.1",
35
+ "port": 3000
36
+ }
37
+ }`
38
+ const jsonContentUri = 'foo://server/example.data.json'
39
+ const textDocument = jsonLanguageService.TextDocument.create(jsonContentUri, 'json', 1, jsonContent)
40
+ const jsonDocument = languageservice.parseJSONDocument(textDocument)
41
+ const diagnostics = await languageservice.doValidation(textDocument, jsonDocument)
42
+ t.equal(diagnostics.length, 0)
43
+ })
@@ -23,7 +23,7 @@ setGlobalDispatcher(new Agent({
23
23
  export const cliPath = join(import.meta.url, '..', '..', 'service.mjs')
24
24
 
25
25
  export async function start (...args) {
26
- const child = execa('node', [cliPath, ...args])
26
+ const child = execa('node', [cliPath, 'start', ...args])
27
27
  child.stderr.pipe(process.stdout)
28
28
 
29
29
  const output = child.stdout.pipe(split(function (line) {
@@ -12,13 +12,13 @@ test('version', async (t) => {
12
12
  })
13
13
 
14
14
  test('missing config', async (t) => {
15
- await t.rejects(execa('node', [cliPath]))
15
+ await t.rejects(execa('node', [cliPath, 'start']))
16
16
  })
17
17
 
18
18
  test('print validation errors', async ({ equal, plan }) => {
19
19
  plan(2)
20
20
  try {
21
- await execa('node', [cliPath, '--config', join(import.meta.url, '..', 'fixtures', 'missing-property.config.json')])
21
+ await execa('node', [cliPath, 'start', '--config', join(import.meta.url, '..', 'fixtures', 'missing-property.config.json')])
22
22
  } catch (err) {
23
23
  equal(err.exitCode, 1)
24
24
  equal(err.stdout, `
@@ -136,7 +136,7 @@ test('should not watch ignored file', async ({ teardown, equal }) => {
136
136
  equal(version, 'v1')
137
137
  })
138
138
 
139
- test('should not loop forever when doing ESM', { skip: true }, async ({ comment, fail }) => {
139
+ test('should not loop forever when doing ESM', async ({ comment, fail }) => {
140
140
  const tmpDir = await mkdtemp(join(os.tmpdir(), 'watch-esm-'))
141
141
  const pluginFilePath = join(tmpDir, 'plugin.mjs')
142
142
  const configFilePath = join(tmpDir, 'platformatic.service.json')
@@ -0,0 +1,16 @@
1
+ {
2
+ "server": {
3
+ "logger": {
4
+ "level": "info"
5
+ },
6
+ "hostname": "127.0.0.1",
7
+ "port": "0"
8
+ },
9
+ "plugin": {
10
+ "path": "plugin.ts",
11
+ "typescript": {
12
+ "outDir": "dist",
13
+ "build": false
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,5 @@
1
+ import { FastifyInstance } from 'fastify'
2
+
3
+ export default async function (app: FastifyInstance) {
4
+ app.log.info('Typescript plugin loaded')
5
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "esModuleInterop": true,
5
+ "target": "es6",
6
+ "moduleResolution": "node",
7
+ "sourceMap": true,
8
+ "pretty": true,
9
+ "noEmitOnError": true,
10
+ "outDir": "dist"
11
+ },
12
+ "watchOptions": {
13
+ "watchFile": "fixedPollingInterval",
14
+ "watchDirectory": "fixedPollingInterval",
15
+ "fallbackPolling": "dynamicPriority",
16
+ "synchronousWatchDirectory": true,
17
+ "excludeDirectories": [
18
+ "**/node_modules",
19
+ "dist"
20
+ ]
21
+ }
22
+ }
@@ -201,7 +201,7 @@ test('load and reload with the fallback', async ({ teardown, equal, pass, same }
201
201
  }
202
202
  })
203
203
 
204
- test('load and reload ESM', { skip: true }, async ({ teardown, equal, pass, same }) => {
204
+ test('load and reload ESM', async ({ teardown, equal, pass, same }) => {
205
205
  const file = join(os.tmpdir(), `some-plugin-${process.pid}.mjs`)
206
206
 
207
207
  await writeFile(file, `
@@ -0,0 +1,263 @@
1
+ {
2
+ "$id": "https://schemas.platformatic.dev/service",
3
+ "type": "object",
4
+ "properties": {
5
+ "server": {
6
+ "$id": "https://schemas.platformatic.dev/service/server",
7
+ "type": "object",
8
+ "properties": {
9
+ "hostname": {
10
+ "type": "string"
11
+ },
12
+ "port": {
13
+ "anyOf": [
14
+ {
15
+ "type": "integer"
16
+ },
17
+ {
18
+ "type": "string"
19
+ }
20
+ ]
21
+ },
22
+ "pluginTimeout": {
23
+ "type": "integer"
24
+ },
25
+ "healthCheck": {
26
+ "anyOf": [
27
+ {
28
+ "type": "boolean"
29
+ },
30
+ {
31
+ "type": "object",
32
+ "properties": {
33
+ "enabled": {
34
+ "type": "boolean"
35
+ },
36
+ "interval": {
37
+ "type": "integer"
38
+ }
39
+ },
40
+ "additionalProperties": true
41
+ }
42
+ ]
43
+ },
44
+ "cors": {
45
+ "type": "object",
46
+ "$comment": "See https://github.com/fastify/fastify-cors",
47
+ "properties": {
48
+ "origin": {
49
+ "anyOf": [
50
+ {
51
+ "type": "boolean"
52
+ },
53
+ {
54
+ "type": "string"
55
+ },
56
+ {
57
+ "type": "array",
58
+ "items": {
59
+ "type": "string"
60
+ }
61
+ }
62
+ ]
63
+ },
64
+ "methods": {
65
+ "type": "array",
66
+ "items": {
67
+ "type": "string"
68
+ }
69
+ },
70
+ "allowedHeaders": {
71
+ "type": "string",
72
+ "description": "Comma separated string of allowed headers."
73
+ },
74
+ "exposedHeaders": {
75
+ "anyOf": [
76
+ {
77
+ "type": "array",
78
+ "items": {
79
+ "type": "string"
80
+ }
81
+ },
82
+ {
83
+ "type": "string",
84
+ "description": "Comma separated string of exposed headers."
85
+ }
86
+ ]
87
+ },
88
+ "credentials": {
89
+ "type": "boolean"
90
+ },
91
+ "maxAge": {
92
+ "type": "integer"
93
+ },
94
+ "preflightContinue": {
95
+ "type": "boolean",
96
+ "default": false
97
+ },
98
+ "optionsSuccessStatus": {
99
+ "type": "integer",
100
+ "default": 204
101
+ },
102
+ "preflight": {
103
+ "type": "boolean",
104
+ "default": true
105
+ },
106
+ "strictPreflight": {
107
+ "type": "boolean",
108
+ "default": true
109
+ },
110
+ "hideOptionsRoute": {
111
+ "type": "boolean",
112
+ "default": true
113
+ }
114
+ },
115
+ "additionalProperties": false
116
+ }
117
+ },
118
+ "required": [
119
+ "hostname",
120
+ "port"
121
+ ]
122
+ },
123
+ "plugin": {
124
+ "$id": "https://schemas.platformatic.dev/service/pluginTypes",
125
+ "$defs": {
126
+ "plugin": {
127
+ "$id": "#plugin",
128
+ "type": "object",
129
+ "properties": {
130
+ "path": {
131
+ "type": "string"
132
+ },
133
+ "stopTimeout": {
134
+ "type": "integer"
135
+ },
136
+ "typescript": {
137
+ "type": "object",
138
+ "properties": {
139
+ "outDir": {
140
+ "type": "string"
141
+ }
142
+ },
143
+ "additionalProperties": false,
144
+ "required": [
145
+ "outDir"
146
+ ]
147
+ },
148
+ "fallback": {
149
+ "type": "boolean"
150
+ },
151
+ "hotReload": {
152
+ "type": "boolean",
153
+ "default": true
154
+ },
155
+ "options": {
156
+ "type": "object"
157
+ }
158
+ },
159
+ "additionalProperties": false,
160
+ "required": [
161
+ "path"
162
+ ]
163
+ }
164
+ },
165
+ "anyOf": [
166
+ {
167
+ "type": "array",
168
+ "items": {
169
+ "anyOf": [
170
+ {
171
+ "$ref": "#plugin"
172
+ },
173
+ {
174
+ "type": "string"
175
+ }
176
+ ]
177
+ }
178
+ },
179
+ {
180
+ "$ref": "#plugin"
181
+ },
182
+ {
183
+ "type": "string"
184
+ }
185
+ ]
186
+ },
187
+ "metrics": {
188
+ "$id": "https://schemas.platformatic.dev/service/metrics",
189
+ "anyOf": [
190
+ {
191
+ "type": "boolean"
192
+ },
193
+ {
194
+ "type": "object",
195
+ "properties": {
196
+ "port": {
197
+ "type": "integer"
198
+ },
199
+ "hostname": {
200
+ "type": "string"
201
+ },
202
+ "auth": {
203
+ "type": "object",
204
+ "properties": {
205
+ "username": {
206
+ "type": "string"
207
+ },
208
+ "password": {
209
+ "type": "string"
210
+ }
211
+ },
212
+ "additionalProperties": false,
213
+ "required": [
214
+ "username",
215
+ "password"
216
+ ]
217
+ }
218
+ },
219
+ "additionalProperties": false
220
+ }
221
+ ]
222
+ }
223
+ },
224
+ "additionalProperties": {
225
+ "watch": {
226
+ "anyOf": [
227
+ {
228
+ "$id": "https://schemas.platformatic.dev/service/watch",
229
+ "type": "object",
230
+ "properties": {
231
+ "type": "object",
232
+ "properties": {
233
+ "allow": {
234
+ "type": "array",
235
+ "items": {
236
+ "type": "string"
237
+ },
238
+ "minItems": 1,
239
+ "nullable": true,
240
+ "default": null
241
+ },
242
+ "ignore": {
243
+ "type": "array",
244
+ "items": {
245
+ "type": "string"
246
+ },
247
+ "nullable": true,
248
+ "default": null
249
+ }
250
+ },
251
+ "additionalProperties": false
252
+ }
253
+ },
254
+ {
255
+ "type": "boolean"
256
+ }
257
+ ]
258
+ }
259
+ },
260
+ "required": [
261
+ "server"
262
+ ]
263
+ }