@platformatic/service 2.74.3 → 3.0.0-alpha.2

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 (52) hide show
  1. package/config.d.ts +2 -30
  2. package/eslint.config.js +4 -6
  3. package/index.d.ts +55 -47
  4. package/index.js +44 -199
  5. package/lib/application.js +35 -0
  6. package/lib/compile.js +1 -52
  7. package/lib/generator.js +424 -0
  8. package/lib/plugins/cors.js +5 -8
  9. package/lib/plugins/graphql.js +16 -14
  10. package/lib/plugins/health-check.js +6 -8
  11. package/lib/plugins/openapi.js +40 -31
  12. package/lib/plugins/plugins.js +6 -53
  13. package/lib/{root-endpoint/index.js → plugins/root.js} +9 -8
  14. package/lib/plugins/sandbox-wrapper.js +62 -55
  15. package/lib/schema.js +1028 -203
  16. package/lib/stackable.js +171 -338
  17. package/lib/upgrade.js +6 -8
  18. package/lib/utils.js +30 -93
  19. package/lib/versions/0.16.0.js +14 -15
  20. package/lib/versions/{from-zero-twenty-eight-to-will-see.js → 0.28.0.js} +3 -6
  21. package/lib/versions/2.0.0.js +4 -7
  22. package/lib/versions/3.0.0.js +14 -0
  23. package/package.json +18 -25
  24. package/schema.json +10 -155
  25. package/tsconfig.json +16 -6
  26. package/.c8rc +0 -6
  27. package/help/compile.txt +0 -19
  28. package/help/create.txt +0 -11
  29. package/help/help.txt +0 -8
  30. package/help/schema.txt +0 -9
  31. package/help/start.txt +0 -23
  32. package/index.test-d.ts +0 -107
  33. package/lib/create.mjs +0 -85
  34. package/lib/gen-schema.js +0 -15
  35. package/lib/gen-types.mjs +0 -38
  36. package/lib/generator/README.md +0 -31
  37. package/lib/generator/service-generator.d.ts +0 -11
  38. package/lib/generator/service-generator.js +0 -126
  39. package/lib/openapi-schema-defs.js +0 -1108
  40. package/lib/plugins/clients.js +0 -16
  41. package/lib/plugins/metrics.js +0 -244
  42. package/lib/plugins/typescript.js +0 -20
  43. package/lib/start.js +0 -190
  44. package/service.mjs +0 -71
  45. /package/{lib/root-endpoint/public → public}/images/dark_mode.svg +0 -0
  46. /package/{lib/root-endpoint/public → public}/images/favicon.ico +0 -0
  47. /package/{lib/root-endpoint/public → public}/images/light_mode.svg +0 -0
  48. /package/{lib/root-endpoint/public → public}/images/platformatic-logo-dark.svg +0 -0
  49. /package/{lib/root-endpoint/public → public}/images/platformatic-logo-light.svg +0 -0
  50. /package/{lib/root-endpoint/public → public}/images/triangle_dark.svg +0 -0
  51. /package/{lib/root-endpoint/public → public}/images/triangle_light.svg +0 -0
  52. /package/{lib/root-endpoint/public → public}/index.html +0 -0
@@ -1,16 +0,0 @@
1
- 'use strict'
2
-
3
- const fp = require('fastify-plugin')
4
- const client = require('@platformatic/client')
5
-
6
- async function setupClients (app, opts) {
7
- for (const { path, url, serviceId, name, schema, type, fullRequest, fullResponse, validateResponse } of opts) {
8
- if (path) {
9
- app.register(require(path), { url, serviceId })
10
- } else {
11
- app.register(client, { url, serviceId, name, path: schema, type, fullRequest, fullResponse, validateResponse })
12
- }
13
- }
14
- }
15
-
16
- module.exports = fp(setupClients)
@@ -1,244 +0,0 @@
1
- 'use strict'
2
-
3
- const os = require('node:os')
4
- const http = require('node:http')
5
- const { eventLoopUtilization } = require('node:perf_hooks').performance
6
- const fastify = require('fastify')
7
- const fp = require('fastify-plugin')
8
-
9
- const metricsPlugin = fp(async function (app, opts = {}) {
10
- const promClient = require('prom-client')
11
-
12
- const register = new promClient.Registry()
13
-
14
- const defaultMetrics = opts.defaultMetrics ?? { enabled: true }
15
-
16
- if (opts.labels) {
17
- const labels = opts.labels ?? {}
18
- register.setDefaultLabels(labels)
19
- }
20
-
21
- app.register(require('fastify-metrics'), {
22
- defaultMetrics: {
23
- ...defaultMetrics,
24
- register,
25
- },
26
- endpoint: null,
27
- name: 'metrics',
28
- clearRegisterOnInit: false,
29
- promClient: {
30
- ...promClient,
31
- register,
32
- },
33
- routeMetrics: {
34
- enabled: true,
35
- customLabels: {
36
- // TODO: check if this is set in prom
37
- telemetry_id: (req) => req.headers['x-plt-telemetry-id'] ?? 'unknown',
38
- },
39
- overrides: {
40
- histogram: {
41
- name: 'http_request_duration_seconds',
42
- registers: [register],
43
- },
44
- summary: {
45
- name: 'http_request_summary_seconds',
46
- registers: [register],
47
- },
48
- },
49
- },
50
- })
51
-
52
- app.register(fp(async (app) => {
53
- const httpLatencyMetric = new app.metrics.client.Summary({
54
- name: 'http_request_all_summary_seconds',
55
- help: 'request duration in seconds summary for all requests',
56
- collect: () => {
57
- process.nextTick(() => httpLatencyMetric.reset())
58
- },
59
- registers: [register],
60
- })
61
-
62
- const ignoredMethods = ['HEAD', 'OPTIONS', 'TRACE', 'CONNECT']
63
- const timers = new WeakMap()
64
- app.addHook('onRequest', async (req) => {
65
- if (ignoredMethods.includes(req.method)) return
66
- const timer = httpLatencyMetric.startTimer()
67
- timers.set(req, timer)
68
- })
69
- app.addHook('onResponse', async (req) => {
70
- if (ignoredMethods.includes(req.method)) return
71
- const timer = timers.get(req)
72
- if (timer) {
73
- timer()
74
- timers.delete(req)
75
- }
76
- })
77
- }, {
78
- encapsulate: false,
79
- }))
80
-
81
- if (defaultMetrics.enabled) {
82
- app.register(async (app) => {
83
- let startELU = eventLoopUtilization()
84
- const eluMetric = new app.metrics.client.Gauge({
85
- name: 'nodejs_eventloop_utilization',
86
- help: 'The event loop utilization as a fraction of the loop time. 1 is fully utilized, 0 is fully idle.',
87
- collect: () => {
88
- const endELU = eventLoopUtilization()
89
- const result = eventLoopUtilization(endELU, startELU).utilization
90
- eluMetric.set(result)
91
- startELU = endELU
92
- },
93
- registers: [register],
94
- })
95
- app.metrics.client.register.registerMetric(eluMetric)
96
-
97
- let previousIdleTime = 0
98
- let previousTotalTime = 0
99
- const cpuMetric = new app.metrics.client.Gauge({
100
- name: 'process_cpu_percent_usage',
101
- help: 'The process CPU percent usage.',
102
- collect: () => {
103
- const cpus = os.cpus()
104
- let idleTime = 0
105
- let totalTime = 0
106
-
107
- cpus.forEach(cpu => {
108
- for (const type in cpu.times) {
109
- totalTime += cpu.times[type]
110
- if (type === 'idle') {
111
- idleTime += cpu.times[type]
112
- }
113
- }
114
- })
115
-
116
- const idleDiff = idleTime - previousIdleTime
117
- const totalDiff = totalTime - previousTotalTime
118
-
119
- const usagePercent = 100 - ((100 * idleDiff) / totalDiff)
120
- const roundedUsage = Math.round(usagePercent * 100) / 100
121
- cpuMetric.set(roundedUsage)
122
-
123
- previousIdleTime = idleTime
124
- previousTotalTime = totalTime
125
- },
126
- registers: [register],
127
- })
128
- app.metrics.client.register.registerMetric(cpuMetric)
129
- })
130
- }
131
-
132
- function cleanMetrics () {
133
- const httpMetrics = ['http_request_duration_seconds', 'http_request_summary_seconds', 'http_request_all_summary_seconds']
134
- const metrics = app.metrics.client.register._metrics
135
- for (const metricName in metrics) {
136
- if (defaultMetrics.enabled || httpMetrics.includes(metricName)) {
137
- delete metrics[metricName]
138
- }
139
- }
140
- }
141
-
142
- app.addHook('onClose', async () => {
143
- cleanMetrics()
144
- })
145
- }, {
146
- encapsulate: false,
147
- })
148
-
149
- // This is a global httpServer to match global
150
- // prometheus. It's an antipattern, so do
151
- // not use it elsewhere.
152
- let httpServer = null
153
-
154
- async function createMetricsServer (app, hostname, port) {
155
- if (httpServer && httpServer.address().port !== port) {
156
- await closeMetricsServer()
157
- }
158
-
159
- if (!httpServer) {
160
- httpServer = http.createServer()
161
- httpServer.listen(port, hostname)
162
- httpServer.unref()
163
- }
164
-
165
- const promServer = fastify({
166
- name: 'Prometheus server',
167
- serverFactory: (handler) => {
168
- httpServer.removeAllListeners('request')
169
- httpServer.removeAllListeners('clientError')
170
- httpServer.on('request', handler)
171
- return httpServer
172
- },
173
- loggerInstance: app.log.child({ name: 'prometheus' }),
174
- })
175
-
176
- app.addHook('onClose', async () => {
177
- await promServer.close()
178
- })
179
-
180
- return promServer
181
- }
182
-
183
- async function closeMetricsServer () {
184
- if (httpServer) {
185
- await new Promise((resolve) => httpServer.close(resolve))
186
- httpServer = null
187
- }
188
- }
189
-
190
- module.exports = fp(async function (app, opts) {
191
- const server = opts.server ?? 'own'
192
- const hostname = opts.hostname ?? '0.0.0.0'
193
- const port = opts.port ?? 9090
194
- const metricsEndpoint = opts.endpoint ?? '/metrics'
195
- const auth = opts.auth ?? null
196
-
197
- app.register(metricsPlugin, opts)
198
-
199
- let metricsServer = app
200
- if (server === 'own') {
201
- metricsServer = await createMetricsServer(app, hostname, port)
202
- } else {
203
- await closeMetricsServer()
204
- }
205
-
206
- if (server !== 'hide') {
207
- let onRequestHook
208
- if (auth) {
209
- const { username, password } = auth
210
-
211
- await metricsServer.register(require('@fastify/basic-auth'), {
212
- validate: function (user, pass, req, reply, done) {
213
- if (username !== user || password !== pass) {
214
- return reply.code(401).send({ message: 'Unauthorized' })
215
- }
216
- return done()
217
- },
218
- })
219
- onRequestHook = metricsServer.basicAuth
220
- }
221
-
222
- metricsServer.register(require('@fastify/accepts'))
223
-
224
- metricsServer.route({
225
- url: metricsEndpoint,
226
- method: 'GET',
227
- logLevel: 'warn',
228
- onRequest: onRequestHook,
229
- handler: async (req, reply) => {
230
- const promRegistry = app.metrics.client.register
231
- const accepts = req.accepts()
232
- if (!accepts.type('text/plain') && accepts.type('application/json')) {
233
- return promRegistry.getMetricsAsJSON()
234
- }
235
- reply.type('text/plain')
236
- return promRegistry.metrics()
237
- },
238
- })
239
- }
240
-
241
- if (server === 'own') {
242
- await metricsServer.ready()
243
- }
244
- })
@@ -1,20 +0,0 @@
1
- 'use strict'
2
-
3
- const fp = require('fastify-plugin')
4
- const compiler = require('@platformatic/ts-compiler')
5
- const { extractTypeScriptCompileOptionsFromConfig } = require('../compile')
6
-
7
- async function setupTsCompiler (app, opts) {
8
- const configManager = app.platformatic.configManager
9
- const config = configManager.current
10
- const workingDir = opts?.context?.directory ?? configManager.dirname
11
-
12
- await compiler.compile({
13
- ...extractTypeScriptCompileOptionsFromConfig(config),
14
- cwd: workingDir,
15
- clean: false,
16
- logger: app.log,
17
- })
18
- }
19
-
20
- module.exports = fp(setupTsCompiler)
package/lib/start.js DELETED
@@ -1,190 +0,0 @@
1
- 'use strict'
2
-
3
- const { readFile } = require('fs/promises')
4
- const close = require('close-with-grace')
5
- const {
6
- loadConfig,
7
- ConfigManager,
8
- printConfigValidationErrors,
9
- printAndExitLoadConfigError
10
- } = require('@platformatic/config')
11
- const { addLoggerToTheConfig, isDocker } = require('./utils.js')
12
- const { randomUUID } = require('crypto')
13
- const { fastify } = require('fastify')
14
- const { features } = require('@platformatic/utils')
15
-
16
- async function adjustHttpsKeyAndCert (arg) {
17
- if (typeof arg === 'string') {
18
- return arg
19
- }
20
-
21
- if (!Array.isArray(arg)) {
22
- // { path: pathToKeyOrCert }
23
- return readFile(arg.path)
24
- }
25
-
26
- // Array of strings or objects.
27
- for (let i = 0; i < arg.length; ++i) {
28
- arg[i] = await adjustHttpsKeyAndCert(arg[i])
29
- }
30
-
31
- return arg
32
- }
33
-
34
- async function createServer (serverContext) {
35
- const { app, configManager, context } = serverContext
36
- const config = configManager.current
37
- let fastifyOptions = {}
38
-
39
- if (config.server) {
40
- // override hostname if it's docker
41
- if (await isDocker()) {
42
- config.server.hostname = '0.0.0.0'
43
- }
44
- fastifyOptions = {
45
- ...config.server
46
- }
47
- }
48
-
49
- Object.assign(fastifyOptions, context?.fastifyOptions ?? {})
50
-
51
- fastifyOptions.genReqId = function (req) {
52
- return randomUUID()
53
- }
54
- const root = fastify(fastifyOptions)
55
- root.decorate('platformatic', { configManager, config })
56
- await root.register(app, { context })
57
- if (!root.hasRoute({ url: '/', method: 'GET' }) && !root.hasRoute({ url: '/*', method: 'GET' })) {
58
- await root.register(require('./root-endpoint'))
59
- }
60
-
61
- root.decorate('url', {
62
- getter () {
63
- return serverContext.url
64
- }
65
- })
66
-
67
- return root
68
- }
69
- async function buildConfigManager (options, app) {
70
- const loggerInstance = options.server?.loggerInstance
71
- if (loggerInstance) {
72
- delete options.server.loggerInstance
73
- options.server ||= {}
74
- options.server.logger = { level: loggerInstance.level }
75
- }
76
-
77
- let configManager = options.configManager
78
- if (!configManager) {
79
- // instantiate a new config manager from current options
80
- configManager = new ConfigManager({ ...app.configManagerConfig, source: options })
81
- await configManager.parseAndValidate()
82
- }
83
-
84
- if (loggerInstance) {
85
- configManager.current.server ||= {}
86
- delete configManager.current.server.logger
87
- configManager.current.server.loggerInstance = loggerInstance
88
- }
89
- return configManager
90
- }
91
-
92
- async function buildServer (options, app, context) {
93
- const configManager = await buildConfigManager(options, app)
94
- const config = configManager.current
95
-
96
- // The server now can be not present, so we might need to add logger
97
- addLoggerToTheConfig(config)
98
-
99
- // options is a path
100
- if (typeof options === 'string') {
101
- options = config
102
- }
103
-
104
- if (options.server) {
105
- if (options.server.https) {
106
- options.server.https.key = await adjustHttpsKeyAndCert(options.server.https.key)
107
- options.server.https.cert = await adjustHttpsKeyAndCert(options.server.https.cert)
108
- }
109
- }
110
-
111
- const serverContext = {
112
- app: typeof app === 'function' ? app : app.app,
113
- configManager,
114
- context
115
- }
116
- const handler = await createServer(serverContext)
117
- handler.start = async function () {
118
- serverContext.url = await handler.listen({
119
- host: options.server?.hostname || '127.0.0.1',
120
- port: options.server?.port || 0,
121
- reusePort: features.node.reusePort && context?.isProduction
122
- })
123
- return serverContext.url
124
- }
125
- configManager.on('error', function (err) {
126
- /* c8 ignore next 1 */
127
- handler.log.error({ err }, 'error reloading the configuration')
128
- })
129
-
130
- return handler
131
- }
132
-
133
- async function start (appType, _args) {
134
- /* c8 ignore next 55 */
135
- let configManager = null
136
- try {
137
- configManager = (await loadConfig({}, _args, appType)).configManager
138
- } catch (err) {
139
- if (err.validationErrors) {
140
- printConfigValidationErrors(err)
141
- process.exit(1)
142
- } else {
143
- throw err
144
- }
145
- }
146
-
147
- const config = configManager.current
148
-
149
- const _transformConfig = configManager._transformConfig.bind(configManager)
150
- configManager._transformConfig = function () {
151
- const config = configManager.current
152
- addLoggerToTheConfig(config)
153
- return _transformConfig(config)
154
- }
155
-
156
- let app = null
157
-
158
- try {
159
- // Set the location of the config
160
- app = await buildServer({ ...config, configManager }, appType)
161
- await app.start()
162
- } catch (err) {
163
- printAndExitLoadConfigError(err)
164
- }
165
-
166
- close(async ({ signal, err }) => {
167
- // Windows does not support trapping signals
168
- if (err) {
169
- app.log.error(
170
- {
171
- err: {
172
- message: err.message,
173
- stack: err.stack
174
- }
175
- },
176
- 'exiting'
177
- )
178
- } else if (signal) {
179
- app.log.info({ signal }, 'received signal')
180
- }
181
-
182
- // Weird coverage issue in c8
183
- /* c8 ignore next 2 */
184
- await app.close()
185
- })
186
- }
187
-
188
- module.exports.buildConfigManager = buildConfigManager
189
- module.exports.buildServer = buildServer
190
- module.exports.start = start
package/service.mjs DELETED
@@ -1,71 +0,0 @@
1
- #! /usr/bin/env node
2
-
3
- import commist from 'commist'
4
- import parseArgs from 'minimist'
5
- import isMain from 'es-main'
6
- import helpMe from 'help-me'
7
- import { readFile } from 'fs/promises'
8
- import { join } from 'desm'
9
- import { printAndExitLoadConfigError } from '@platformatic/config'
10
- import { buildCompileCmd } from './lib/compile.js'
11
- import { generateJsonSchemaConfig } from './lib/gen-schema.js'
12
- import { generateTypes } from './lib/gen-types.mjs'
13
- import { createService } from './lib/create.mjs'
14
-
15
- import platformaticService from './index.js'
16
-
17
- const help = helpMe({
18
- dir: join(import.meta.url, 'help'),
19
- // the default
20
- ext: '.txt',
21
- })
22
-
23
- function wrapCommand (fn) {
24
- return async function (...args) {
25
- try {
26
- return await fn(...args)
27
- /* c8 ignore next 3 */
28
- } catch (err) {
29
- printAndExitLoadConfigError(err)
30
- }
31
- }
32
- }
33
-
34
- const program = commist({ maxDistance: 2 })
35
-
36
- program.register('help', help.toStdout)
37
- program.register('help start', help.toStdout.bind(null, ['start']))
38
-
39
- program.register('start', (argv) => {
40
- /* c8 ignore next 1 */
41
- platformaticService.start(platformaticService, argv).catch(printAndExitLoadConfigError)
42
- })
43
-
44
- program.register('create', wrapCommand(createService))
45
- program.register('compile', buildCompileCmd(platformaticService))
46
- program.register('types', wrapCommand(generateTypes))
47
- program.register('schema config', wrapCommand(generateJsonSchemaConfig))
48
- program.register('schema', help.toStdout.bind(null, ['schema']))
49
-
50
- export async function runService (argv) {
51
- const args = parseArgs(argv, {
52
- alias: {
53
- v: 'version',
54
- },
55
- })
56
-
57
- /* c8 ignore next 4 */
58
- if (args.version) {
59
- console.log('v' + JSON.parse(await readFile(join(import.meta.url, 'package.json'), 'utf-8')).version)
60
- process.exit(0)
61
- }
62
-
63
- return {
64
- output: await program.parseAsync(argv),
65
- help,
66
- }
67
- }
68
-
69
- if (isMain(import.meta)) {
70
- await runService(process.argv.splice(2))
71
- }
File without changes