@platformatic/service 2.0.0-alpha.2 → 2.0.0-alpha.3

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.
@@ -0,0 +1,210 @@
1
+ 'use strict'
2
+
3
+ const { dirname } = require('node:path')
4
+ const { printSchema } = require('graphql')
5
+ const pino = require('pino')
6
+
7
+ class ServiceStackable {
8
+ constructor (options) {
9
+ this.app = null
10
+ this._init = options.init
11
+ this.stackable = options.stackable
12
+
13
+ this.configManager = options.configManager
14
+ this.config = this.configManager.current
15
+ this.context = options.context
16
+
17
+ this.#updateConfig()
18
+ }
19
+
20
+ async init () {
21
+ this.#initLogger()
22
+
23
+ if (this.app === null) {
24
+ this.app = await this._init()
25
+ }
26
+ return this.app
27
+ }
28
+
29
+ async start (options = {}) {
30
+ await this.init()
31
+
32
+ if (options.listen === false) {
33
+ await this.app.ready()
34
+ return
35
+ }
36
+ await this.app.start()
37
+ }
38
+
39
+ async stop () {
40
+ if (this.app === null) return
41
+ await this.app.close()
42
+ }
43
+
44
+ getUrl () {
45
+ return this.app !== null ? this.app.url : null
46
+ }
47
+
48
+ async getInfo () {
49
+ const type = this.stackable.configType
50
+ const version = this.stackable.configManagerConfig.version ?? null
51
+ return { type, version }
52
+ }
53
+
54
+ async getConfig () {
55
+ const config = this.configManager.current
56
+ const logger = config.server.logger
57
+
58
+ if (logger) {
59
+ config.server.logger = {}
60
+
61
+ if (logger.level) {
62
+ config.server.logger.level = logger.level
63
+ }
64
+ }
65
+
66
+ return config
67
+ }
68
+
69
+ async getWatchConfig () {
70
+ const config = this.configManager.current
71
+
72
+ const enabled =
73
+ config.watch?.enabled !== false &&
74
+ config.plugins !== undefined
75
+
76
+ return {
77
+ enabled,
78
+ path: dirname(this.configManager.fullPath),
79
+ allow: config.watch?.allow,
80
+ ignore: config.watch?.ignore,
81
+ }
82
+ }
83
+
84
+ async getDispatchFunc () {
85
+ await this.init()
86
+ return this.app
87
+ }
88
+
89
+ async getOpenapiSchema () {
90
+ await this.init()
91
+ await this.app.ready()
92
+ return this.app.swagger ? this.app.swagger() : null
93
+ }
94
+
95
+ async getGraphqlSchema () {
96
+ await this.init()
97
+ await this.app.ready()
98
+ return this.app.graphql ? printSchema(this.app.graphql.schema) : null
99
+ }
100
+
101
+ async getMetrics ({ format }) {
102
+ await this.init()
103
+
104
+ const promRegister = this.app.metrics?.client?.register
105
+ if (!promRegister) return null
106
+
107
+ return format === 'json'
108
+ ? promRegister.getMetricsAsJSON()
109
+ : promRegister.metrics()
110
+ }
111
+
112
+ async inject (injectParams) {
113
+ await this.init()
114
+
115
+ const { statusCode, statusMessage, headers, body } = await this.app.inject(injectParams)
116
+ return { statusCode, statusMessage, headers, body }
117
+ }
118
+
119
+ async log (options = {}) {
120
+ await this.init()
121
+
122
+ const logLevel = options.level ?? 'info'
123
+
124
+ const message = options.message
125
+ if (!message) return
126
+
127
+ this.app.log[logLevel](message)
128
+ }
129
+
130
+ async updateContext (context) {
131
+ this.context = { ...this.context, ...context }
132
+ this.#updateConfig()
133
+ }
134
+
135
+ #updateConfig () {
136
+ if (!this.context) return
137
+
138
+ const {
139
+ serviceId,
140
+ telemetryConfig,
141
+ metricsConfig,
142
+ serverConfig,
143
+ hasManagementApi,
144
+ isEntrypoint,
145
+ } = this.context
146
+
147
+ const configManager = this.configManager
148
+
149
+ configManager.on('error', (err) => {
150
+ /* c8 ignore next */
151
+ this.stackable.log({ message: 'error reloading the configuration' + err, level: 'error' })
152
+ })
153
+
154
+ configManager.update({
155
+ ...configManager.current,
156
+ telemetry: telemetryConfig,
157
+ metrics: metricsConfig,
158
+ })
159
+
160
+ if (this.context.serverConfig) {
161
+ configManager.update({
162
+ ...configManager.current,
163
+ server: serverConfig,
164
+ })
165
+ }
166
+
167
+ if (
168
+ (hasManagementApi && configManager.current.metrics === undefined) ||
169
+ configManager.current.metrics
170
+ ) {
171
+ const labels = configManager.current.metrics?.labels || {}
172
+ configManager.update({
173
+ ...configManager.current,
174
+ metrics: {
175
+ server: 'hide',
176
+ defaultMetrics: { enabled: isEntrypoint },
177
+ ...configManager.current.metrics,
178
+ labels: { serviceId, ...labels },
179
+ },
180
+ })
181
+ }
182
+
183
+ if (!isEntrypoint) {
184
+ configManager.update({
185
+ ...configManager.current,
186
+ server: {
187
+ ...(configManager.current.server || {}),
188
+ trustProxy: true,
189
+ },
190
+ })
191
+ }
192
+ }
193
+
194
+ #initLogger () {
195
+ this.configManager.current.server = this.configManager.current.server || {}
196
+ const level = this.configManager.current.server.logger?.level
197
+
198
+ const pinoOptions = {
199
+ level: level ?? 'trace',
200
+ }
201
+
202
+ if (this.context?.serviceId) {
203
+ pinoOptions.name = this.context.serviceId
204
+ }
205
+
206
+ this.configManager.current.server.logger = pino(pinoOptions)
207
+ }
208
+ }
209
+
210
+ module.exports = { ServiceStackable }
package/lib/start.js CHANGED
@@ -4,8 +4,8 @@ const { readFile } = require('fs/promises')
4
4
  const close = require('close-with-grace')
5
5
  const { loadConfig, ConfigManager, printConfigValidationErrors, printAndExitLoadConfigError } = require('@platformatic/config')
6
6
  const { addLoggerToTheConfig, isDocker } = require('./utils.js')
7
- const { restartable } = require('@fastify/restartable')
8
7
  const { randomUUID } = require('crypto')
8
+ const { fastify } = require('fastify')
9
9
 
10
10
  async function adjustHttpsKeyAndCert (arg) {
11
11
  if (typeof arg === 'string') {
@@ -25,14 +25,48 @@ async function adjustHttpsKeyAndCert (arg) {
25
25
  return arg
26
26
  }
27
27
 
28
- async function buildServer (options, app) {
28
+ async function createServer (context) {
29
+ const { app, configManager } = context
30
+ const config = configManager.current
31
+ let fastifyOptions = {}
32
+
33
+ if (config.server) {
34
+ // override hostname if it's docker
35
+ if (await isDocker()) {
36
+ config.server.hostname = '0.0.0.0'
37
+ }
38
+ fastifyOptions = {
39
+ ...config.server,
40
+ }
41
+ }
42
+ fastifyOptions.genReqId = function (req) { return randomUUID() }
43
+ const root = fastify(fastifyOptions)
44
+ root.decorate('platformatic', { configManager, config })
45
+ await root.register(app)
46
+ if (!root.hasRoute({ url: '/', method: 'GET' })) {
47
+ await root.register(require('./root-endpoint'))
48
+ }
49
+
50
+ root.decorate('url', {
51
+ getter () {
52
+ return context.url
53
+ },
54
+ })
55
+
56
+ return root
57
+ }
58
+ async function buildConfigManager (options, app) {
29
59
  let configManager = options.configManager
30
60
  if (!configManager) {
31
61
  // instantiate a new config manager from current options
32
62
  configManager = new ConfigManager({ ...app.configManagerConfig, source: options })
33
63
  await configManager.parseAndValidate()
34
64
  }
65
+ return configManager
66
+ }
35
67
 
68
+ async function buildServer (options, app) {
69
+ const configManager = await buildConfigManager(options, app)
36
70
  const config = configManager.current
37
71
 
38
72
  // The server now can be not present, so we might need to add logger
@@ -43,52 +77,24 @@ async function buildServer (options, app) {
43
77
  options = config
44
78
  }
45
79
 
46
- let url = null
47
-
48
- async function createRestartable (fastify) {
49
- const config = configManager.current
50
- let fastifyOptions = {}
51
-
52
- if (config.server) {
53
- // override hostname if it's docker
54
- if (await isDocker()) {
55
- config.server.hostname = '0.0.0.0'
56
- }
57
- fastifyOptions = {
58
- ...config.server
59
- }
60
- }
61
- fastifyOptions.genReqId = function (req) { return randomUUID() }
62
- const root = fastify(fastifyOptions)
63
- root.decorate('platformatic', { configManager, config })
64
- await root.register(app)
65
- if (!root.hasRoute({ url: '/', method: 'GET' })) {
66
- await root.register(require('./root-endpoint'))
67
- }
68
-
69
- root.decorate('url', {
70
- getter () {
71
- return url
72
- }
73
- })
74
-
75
- if (root.restarted) {
76
- root.log.info('restarted')
77
- }
78
-
79
- return root
80
- }
81
-
82
80
  if (options.server) {
83
81
  if (options.server.https) {
84
82
  options.server.https.key = await adjustHttpsKeyAndCert(options.server.https.key)
85
83
  options.server.https.cert = await adjustHttpsKeyAndCert(options.server.https.cert)
86
84
  }
87
85
  }
88
- const handler = await restartable(createRestartable)
86
+
87
+ const context = {
88
+ app: typeof app === 'function' ? app : app.app,
89
+ configManager,
90
+ }
91
+ const handler = await createServer(context)
89
92
  handler.decorate('start', async () => {
90
- url = await handler.listen({ host: options.server?.hostname || '127.0.0.1', port: options.server?.port || 0 })
91
- return url
93
+ context.url = await handler.listen({
94
+ host: options.server?.hostname || '127.0.0.1',
95
+ port: options.server?.port || 0,
96
+ })
97
+ return context.url
92
98
  })
93
99
  configManager.on('error', function (err) {
94
100
  /* c8 ignore next 1 */
@@ -98,20 +104,6 @@ async function buildServer (options, app) {
98
104
  return handler
99
105
  }
100
106
 
101
- /* c8 ignore next 12 */
102
- async function safeRestart (app) {
103
- try {
104
- await app.restart()
105
- } catch (err) {
106
- app.log.error({
107
- err: {
108
- message: err.message,
109
- stack: err.stack
110
- }
111
- }, 'failed to reload server')
112
- }
113
- }
114
-
115
107
  async function start (appType, _args) {
116
108
  /* c8 ignore next 55 */
117
109
  let configManager = null
@@ -145,22 +137,14 @@ async function start (appType, _args) {
145
137
  printAndExitLoadConfigError(err)
146
138
  }
147
139
 
148
- // Ignore from CI because SIGUSR2 is not available
149
- // on Windows
150
- process.on('SIGUSR2', function () {
151
- app.log.info('reloading configuration')
152
- safeRestart(app)
153
- return false
154
- })
155
-
156
140
  close(async ({ signal, err }) => {
157
141
  // Windows does not support trapping signals
158
142
  if (err) {
159
143
  app.log.error({
160
144
  err: {
161
145
  message: err.message,
162
- stack: err.stack
163
- }
146
+ stack: err.stack,
147
+ },
164
148
  }, 'exiting')
165
149
  } else if (signal) {
166
150
  app.log.info({ signal }, 'received signal')
@@ -172,5 +156,6 @@ async function start (appType, _args) {
172
156
  })
173
157
  }
174
158
 
159
+ module.exports.buildConfigManager = buildConfigManager
175
160
  module.exports.buildServer = buildServer
176
161
  module.exports.start = start
package/lib/upgrade.js CHANGED
@@ -10,7 +10,7 @@ module.exports = async function upgrade (config, version) {
10
10
  version,
11
11
  path: join(__dirname, 'versions'),
12
12
  input: config,
13
- logger: this.logger.child({ name: '@platformatic/service' })
13
+ logger: this.logger.child({ name: '@platformatic/service' }),
14
14
  })
15
15
 
16
16
  let result
@@ -19,7 +19,7 @@ module.exports = async function upgrade (config, version) {
19
19
  result = updated.result
20
20
  }
21
21
 
22
- result.$schema = `https://platformatic.dev/schemas/v${pkg.version}/service`
22
+ result.$schema = `https://schemas.platformatic.dev/@platformatic/service/${pkg.version}.json`
23
23
 
24
24
  return result
25
25
  }
package/lib/utils.js CHANGED
@@ -31,7 +31,7 @@ function addLoggerToTheConfig (config) {
31
31
  if (isatty(1)) {
32
32
  if (!logger.transport) {
33
33
  logger.transport = {
34
- target: 'pino-pretty'
34
+ target: 'pino-pretty',
35
35
  }
36
36
  }
37
37
  }
@@ -101,5 +101,5 @@ module.exports = {
101
101
  isDocker,
102
102
  isFileAccessible,
103
103
  getJSPluginPath,
104
- addLoggerToTheConfig
104
+ addLoggerToTheConfig,
105
105
  }
@@ -19,12 +19,12 @@ module.exports = {
19
19
  } else if (p.options) {
20
20
  return {
21
21
  path: p.path,
22
- options: p.options
22
+ options: p.options,
23
23
  }
24
24
  } else {
25
25
  return p.path
26
26
  }
27
- })
27
+ }),
28
28
  }
29
29
 
30
30
  if (typeof config.plugin[0] === 'object') {
@@ -43,12 +43,12 @@ module.exports = {
43
43
  config.plugins = {
44
44
  paths: [{
45
45
  path: config.plugin.path,
46
- options: config.plugin.options
47
- }]
46
+ options: config.plugin.options,
47
+ }],
48
48
  }
49
49
  } else {
50
50
  config.plugins = {
51
- paths: [config.plugin.path]
51
+ paths: [config.plugin.path],
52
52
  }
53
53
  }
54
54
 
@@ -66,7 +66,7 @@ module.exports = {
66
66
  }
67
67
  } else {
68
68
  config.plugins = {
69
- paths: [config.plugin]
69
+ paths: [config.plugin],
70
70
  }
71
71
  }
72
72
 
@@ -78,5 +78,5 @@ module.exports = {
78
78
  config.$schema = 'https://platformatic.dev/schemas/v0.17.0/' + kind
79
79
 
80
80
  return config
81
- }
81
+ },
82
82
  }
@@ -7,10 +7,10 @@ module.exports.migration = {
7
7
  toVersion: version,
8
8
  up: function (config) {
9
9
  if (config.watch !== false) {
10
- config.watch = typeof config.watch === 'object' ? config.watch : {}
10
+ config.watch = typeof config.watch === 'object' ? config.watch : true
11
11
  }
12
- delete config.plugins?.hotReload
12
+ delete config.plugins?.watch
13
13
 
14
14
  return config
15
- }
15
+ },
16
16
  }
@@ -3,7 +3,7 @@
3
3
  const { version } = require('../../package.json')
4
4
 
5
5
  module.exports.migration = {
6
- version: '2.0.0',
6
+ version: '1.99.0', // This is to account alpha versions as well
7
7
  toVersion: version,
8
8
  up: function (config) {
9
9
  if (typeof config.allowCycles === 'boolean') {
@@ -11,5 +11,5 @@ module.exports.migration = {
11
11
  }
12
12
 
13
13
  return config
14
- }
14
+ },
15
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/service",
3
- "version": "2.0.0-alpha.2",
3
+ "version": "2.0.0-alpha.3",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -20,18 +20,17 @@
20
20
  "@fastify/aws-lambda": "^4.0.0",
21
21
  "@fastify/compress": "^7.0.0",
22
22
  "bindings": "^1.5.0",
23
- "borp": "^0.16.0",
24
- "json-schema-to-typescript": "^14.0.0",
23
+ "borp": "^0.17.0",
24
+ "eslint": "9",
25
+ "json-schema-to-typescript": "^15.0.0",
26
+ "neostandard": "^0.11.1",
25
27
  "openapi-types": "^12.1.3",
26
28
  "pino-abstract-transport": "^1.1.0",
27
29
  "self-cert": "^2.0.0",
28
- "snazzy": "^9.0.0",
29
30
  "split2": "^4.2.0",
30
- "standard": "^17.1.0",
31
31
  "strip-ansi": "^7.1.0",
32
- "ts-standard": "^12.0.2",
33
32
  "tsd": "^0.31.0",
34
- "typescript": "^5.4.2",
33
+ "typescript": "^5.5.4",
35
34
  "undici": "^6.0.0",
36
35
  "vscode-json-languageservice": "^5.3.9",
37
36
  "why-is-node-running": "^2.2.2",
@@ -44,7 +43,6 @@
44
43
  "@fastify/cors": "^9.0.1",
45
44
  "@fastify/deepmerge": "^1.3.0",
46
45
  "@fastify/error": "^3.4.1",
47
- "@fastify/restartable": "^2.3.1",
48
46
  "@fastify/static": "^7.0.1",
49
47
  "@fastify/swagger": "^8.14.0",
50
48
  "@fastify/under-pressure": "^8.3.0",
@@ -66,7 +64,7 @@
66
64
  "fastify-metrics": "^11.0.0",
67
65
  "fastify-openapi-glue": "^4.4.3",
68
66
  "fastify-plugin": "^4.5.1",
69
- "graphql": "^16.8.1",
67
+ "graphql": "^16.9.0",
70
68
  "help-me": "^5.0.0",
71
69
  "mercurius": "^14.0.0",
72
70
  "minimist": "^1.2.8",
@@ -78,32 +76,20 @@
78
76
  "rfdc": "^1.3.1",
79
77
  "semgrator": "^0.3.0",
80
78
  "undici": "^6.9.0",
81
- "@platformatic/client": "2.0.0-alpha.2",
82
- "@platformatic/config": "2.0.0-alpha.2",
83
- "@platformatic/generators": "2.0.0-alpha.2",
84
- "@platformatic/telemetry": "2.0.0-alpha.2",
85
- "@platformatic/utils": "2.0.0-alpha.2",
86
- "@platformatic/scalar-theme": "2.0.0-alpha.2",
87
- "@platformatic/ts-compiler": "2.0.0-alpha.2"
88
- },
89
- "standard": {
90
- "ignore": [
91
- "**/dist/*"
92
- ]
93
- },
94
- "ts-standard": {
95
- "ignore": [
96
- "**/dist/*",
97
- "fixtures/**/*",
98
- "test/**/*"
99
- ]
79
+ "@platformatic/client": "2.0.0-alpha.3",
80
+ "@platformatic/generators": "2.0.0-alpha.3",
81
+ "@platformatic/scalar-theme": "2.0.0-alpha.3",
82
+ "@platformatic/config": "2.0.0-alpha.3",
83
+ "@platformatic/ts-compiler": "2.0.0-alpha.3",
84
+ "@platformatic/utils": "2.0.0-alpha.3",
85
+ "@platformatic/telemetry": "2.0.0-alpha.3"
100
86
  },
101
87
  "scripts": {
102
- "test": "pnpm run lint && borp -T --concurrency=1 --timeout 120000 && tsd",
103
- "unit": "borp --pattern 'test/**/*.test.{js,mjs}' --ignore 'fixtures/**/*' --concurrency=1 --timeout=120000 --no-typescript",
88
+ "test": "pnpm run lint && borp -T --concurrency=1 --timeout=180000 && tsd",
89
+ "unit": "borp --pattern 'test/**/*.test.{js,mjs}' --ignore 'fixtures/**/*' --concurrency=1 --timeout=180000 --no-typescript",
104
90
  "gen-schema": "node lib/schema.js > schema.json",
105
91
  "gen-types": "json2ts > config.d.ts < schema.json",
106
92
  "build": "pnpm run gen-schema && pnpm run gen-types",
107
- "lint": "standard | snazzy && ts-standard | snazzy && tsd"
93
+ "lint": "eslint && tsd"
108
94
  }
109
95
  }
package/schema.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "$id": "https://platformatic.dev/schemas/v2.0.0-alpha.2/service",
3
- "version": "2.0.0-alpha.2",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/service/2.0.0-alpha.3.json",
3
+ "version": "2.0.0-alpha.3",
4
4
  "title": "Platformatic Service",
5
5
  "type": "object",
6
6
  "properties": {
@@ -635,9 +635,6 @@
635
635
  ],
636
636
  "additionalProperties": false
637
637
  },
638
- "prefix": {
639
- "type": "string"
640
- },
641
638
  "auth": {
642
639
  "type": "object",
643
640
  "properties": {
@@ -823,6 +820,9 @@
823
820
  "$schema": {
824
821
  "type": "string"
825
822
  },
823
+ "module": {
824
+ "type": "string"
825
+ },
826
826
  "service": {
827
827
  "type": "object",
828
828
  "properties": {
package/service.mjs CHANGED
@@ -12,12 +12,12 @@ import { generateJsonSchemaConfig } from './lib/gen-schema.js'
12
12
  import { generateTypes } from './lib/gen-types.mjs'
13
13
  import { createService } from './lib/create.mjs'
14
14
 
15
- import { start, platformaticService } from './index.js'
15
+ import platformaticService from './index.js'
16
16
 
17
17
  const help = helpMe({
18
18
  dir: join(import.meta.url, 'help'),
19
19
  // the default
20
- ext: '.txt'
20
+ ext: '.txt',
21
21
  })
22
22
 
23
23
  function wrapCommand (fn) {
@@ -38,7 +38,7 @@ program.register('help start', help.toStdout.bind(null, ['start']))
38
38
 
39
39
  program.register('start', (argv) => {
40
40
  /* c8 ignore next 1 */
41
- start(platformaticService, argv).catch(printAndExitLoadConfigError)
41
+ platformaticService.start(platformaticService, argv).catch(printAndExitLoadConfigError)
42
42
  })
43
43
 
44
44
  program.register('create', wrapCommand(createService))
@@ -50,8 +50,8 @@ program.register('schema', help.toStdout.bind(null, ['schema']))
50
50
  export async function runService (argv) {
51
51
  const args = parseArgs(argv, {
52
52
  alias: {
53
- v: 'version'
54
- }
53
+ v: 'version',
54
+ },
55
55
  })
56
56
 
57
57
  /* c8 ignore next 4 */
@@ -62,7 +62,7 @@ export async function runService (argv) {
62
62
 
63
63
  return {
64
64
  output: await program.parseAsync(argv),
65
- help
65
+ help,
66
66
  }
67
67
  }
68
68