@platformatic/composer 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.
- package/config.d.ts +2 -30
- package/eslint.config.js +11 -2
- package/index.d.ts +58 -17
- package/index.js +29 -212
- package/lib/application.js +186 -0
- package/lib/commands/index.js +15 -0
- package/lib/commands/openapi-fetch-schemas.js +47 -0
- package/lib/composer-hook.js +9 -9
- package/lib/errors.js +15 -10
- package/lib/generator.js +127 -0
- package/lib/graphql-fetch.js +44 -46
- package/lib/graphql-generator.js +12 -10
- package/lib/graphql.js +13 -9
- package/lib/{proxy/not-host-constraints.js → not-host-constraints.js} +3 -5
- package/lib/openapi-composer.js +39 -41
- package/lib/openapi-config-schema.js +26 -30
- package/lib/openapi-generator.js +115 -112
- package/lib/openapi-load-config.js +14 -14
- package/lib/openapi-modifier.js +12 -21
- package/lib/openapi-scalar.js +3 -5
- package/lib/proxy.js +13 -12
- package/lib/{root-endpoint/index.js → root.js} +12 -12
- package/lib/schema.js +41 -25
- package/lib/stackable.js +29 -39
- package/lib/upgrade.js +6 -8
- package/lib/utils.js +5 -16
- package/lib/versions/2.0.0.js +4 -6
- package/lib/versions/3.0.0.js +14 -0
- package/package.json +15 -18
- package/schema.json +8 -153
- package/.c8rc +0 -6
- package/composer.mjs +0 -54
- package/help/create.txt +0 -11
- package/help/help.txt +0 -7
- package/help/openapi schemas fetch.txt +0 -9
- package/help/start.txt +0 -54
- package/index.test-d.ts +0 -23
- package/lib/create.mjs +0 -84
- package/lib/generator/README.md +0 -30
- package/lib/generator/composer-generator.d.ts +0 -11
- package/lib/generator/composer-generator.js +0 -128
- package/lib/openapi-fetch-schemas.mjs +0 -61
- /package/{lib/root-endpoint/public → public}/images/dark_mode.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/ellipse.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/external-link.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/favicon.ico +0 -0
- /package/{lib/root-endpoint/public → public}/images/graphiql.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/graphql.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/light_mode.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/openapi.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/platformatic-logo-dark.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/platformatic-logo-light.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/reverse-proxy.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/triangle_dark.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/triangle_light.svg +0 -0
- /package/{lib/root-endpoint/public → public}/index.html +0 -0
- /package/{lib/root-endpoint/public → public}/index.njk +0 -0
- /package/{lib/root-endpoint/public → public}/main.css +0 -0
package/lib/openapi-generator.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const { prefixWithSlash } = require('./utils.js')
|
|
13
|
-
const openApiScalar = require('./openapi-scalar')
|
|
1
|
+
import fastifyReplyFrom from '@fastify/reply-from'
|
|
2
|
+
import fastifySwagger from '@fastify/swagger'
|
|
3
|
+
import fp from 'fastify-plugin'
|
|
4
|
+
import { readFile } from 'node:fs/promises'
|
|
5
|
+
import { getGlobalDispatcher, request } from 'undici'
|
|
6
|
+
import { CouldNotReadOpenAPIConfigError } from './errors.js'
|
|
7
|
+
import { composeOpenApi } from './openapi-composer.js'
|
|
8
|
+
import { loadOpenApiConfig } from './openapi-load-config.js'
|
|
9
|
+
import { modifyOpenApiSchema, originPathSymbol } from './openapi-modifier.js'
|
|
10
|
+
import { openApiScalar } from './openapi-scalar.js'
|
|
11
|
+
import { prefixWithSlash } from './utils.js'
|
|
14
12
|
|
|
15
13
|
async function fetchOpenApiSchema (openApiUrl) {
|
|
16
14
|
const { body } = await request(openApiUrl)
|
|
@@ -31,95 +29,35 @@ async function getOpenApiSchema (origin, openapi) {
|
|
|
31
29
|
return readOpenApiSchema(openapi.file)
|
|
32
30
|
}
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
const { services } = opts
|
|
38
|
-
|
|
39
|
-
const openApiSchemas = []
|
|
40
|
-
const apiByApiRoutes = {}
|
|
41
|
-
|
|
42
|
-
for (const { id, origin, openapi } of services) {
|
|
43
|
-
if (!openapi) continue
|
|
44
|
-
|
|
45
|
-
let openapiConfig = null
|
|
46
|
-
if (openapi.config) {
|
|
47
|
-
try {
|
|
48
|
-
openapiConfig = await loadOpenApiConfig(openapi.config)
|
|
49
|
-
} catch (error) {
|
|
50
|
-
app.log.error(error)
|
|
51
|
-
throw new errors.CouldNotReadOpenAPIConfigError(id)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
let originSchema = null
|
|
56
|
-
try {
|
|
57
|
-
originSchema = await getOpenApiSchema(origin, openapi)
|
|
58
|
-
} catch (error) {
|
|
59
|
-
app.log.error(error, `failed to fetch schema for "${id} service"`)
|
|
60
|
-
continue
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const schema = modifyOpenApiSchema(app, originSchema, openapiConfig)
|
|
64
|
-
|
|
65
|
-
const prefix = openapi.prefix ? prefixWithSlash(openapi.prefix) : ''
|
|
66
|
-
for (const path in schema.paths) {
|
|
67
|
-
apiByApiRoutes[prefix + path] = {
|
|
68
|
-
origin,
|
|
69
|
-
prefix,
|
|
70
|
-
schema: schema.paths[path],
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
openApiSchemas.push({ id, prefix, schema, originSchema, config: openapiConfig })
|
|
32
|
+
function createPathMapper (originOpenApiPath, renamedOpenApiPath, prefix) {
|
|
33
|
+
if (prefix + originOpenApiPath === renamedOpenApiPath) {
|
|
34
|
+
return path => path.slice(prefix.length)
|
|
75
35
|
}
|
|
76
36
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
exposeRoute: true,
|
|
84
|
-
openapi: {
|
|
85
|
-
info: {
|
|
86
|
-
title: opts.openapi?.title || 'Platformatic Composer',
|
|
87
|
-
version: opts.openapi?.version || '1.0.0'
|
|
88
|
-
},
|
|
89
|
-
servers: [{ url: globalThis.platformatic?.runtimeBasePath ?? '/' }],
|
|
90
|
-
components: app.composedOpenApiSchema.components
|
|
91
|
-
},
|
|
92
|
-
transform ({ schema, url }) {
|
|
93
|
-
for (const service of opts.services) {
|
|
94
|
-
if (!service.proxy) continue
|
|
95
|
-
|
|
96
|
-
const prefix = service.proxy.prefix ?? ''
|
|
97
|
-
const proxyPrefix = prefix.at(-1) === '/' ? prefix.slice(0, -1) : prefix
|
|
98
|
-
|
|
99
|
-
const proxyUrls = [proxyPrefix + '/', proxyPrefix + '/*']
|
|
100
|
-
if (proxyUrls.includes(url)) {
|
|
101
|
-
schema = schema ?? {}
|
|
102
|
-
schema.hide = true
|
|
103
|
-
break
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return { schema, url }
|
|
107
|
-
}
|
|
108
|
-
})
|
|
37
|
+
const extractParamsRegexp = generateRouteRegex(renamedOpenApiPath)
|
|
38
|
+
return path => {
|
|
39
|
+
const routeParams = path.match(extractParamsRegexp).slice(1)
|
|
40
|
+
return generateRenamedPath(originOpenApiPath, routeParams)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
109
43
|
|
|
110
|
-
|
|
44
|
+
function generateRouteRegex (route) {
|
|
45
|
+
const regex = route.replace(/{(.*?)}/g, '(.*)')
|
|
46
|
+
return new RegExp(regex)
|
|
47
|
+
}
|
|
111
48
|
|
|
112
|
-
|
|
49
|
+
function generateRenamedPath (renamedOpenApiPath, routeParams) {
|
|
50
|
+
return renamedOpenApiPath.replace(/{(.*?)}/g, () => routeParams.shift())
|
|
113
51
|
}
|
|
114
52
|
|
|
115
|
-
async function
|
|
53
|
+
async function openApiComposerPlugin (app, { opts, generated }) {
|
|
116
54
|
const { apiByApiRoutes } = generated
|
|
117
55
|
|
|
118
56
|
const dispatcher = getGlobalDispatcher()
|
|
119
57
|
|
|
120
|
-
await app.register(
|
|
58
|
+
await app.register(fastifyReplyFrom, {
|
|
121
59
|
undici: dispatcher,
|
|
122
|
-
destroyAgent: false
|
|
60
|
+
destroyAgent: false
|
|
123
61
|
})
|
|
124
62
|
|
|
125
63
|
await app.register(await import('@platformatic/fastify-openapi-glue'), {
|
|
@@ -152,7 +90,11 @@ async function openApiComposer (app, { opts, generated }) {
|
|
|
152
90
|
const rewriteRequestHeaders = (request, headers) => {
|
|
153
91
|
const targetUrl = `${origin}${request.url}`
|
|
154
92
|
const context = request.span?.context
|
|
155
|
-
const { span, telemetryHeaders } = app.openTelemetry?.startHTTPSpanClient(
|
|
93
|
+
const { span, telemetryHeaders } = app.openTelemetry?.startHTTPSpanClient(
|
|
94
|
+
targetUrl,
|
|
95
|
+
request.method,
|
|
96
|
+
context
|
|
97
|
+
) || { span: null, telemetryHeaders: {} }
|
|
156
98
|
// We need to store the span in a different object
|
|
157
99
|
// to correctly close it in the onResponse hook
|
|
158
100
|
// Note that we have 2 spans:
|
|
@@ -164,7 +106,7 @@ async function openApiComposer (app, { opts, generated }) {
|
|
|
164
106
|
...headers,
|
|
165
107
|
...telemetryHeaders,
|
|
166
108
|
'x-forwarded-for': request.ip,
|
|
167
|
-
'x-forwarded-host': request.host
|
|
109
|
+
'x-forwarded-host': request.host
|
|
168
110
|
}
|
|
169
111
|
|
|
170
112
|
return headers
|
|
@@ -173,38 +115,99 @@ async function openApiComposer (app, { opts, generated }) {
|
|
|
173
115
|
replyOptions.rewriteRequestHeaders = rewriteRequestHeaders
|
|
174
116
|
|
|
175
117
|
reply.from(origin + newRoutePath, replyOptions)
|
|
176
|
-
}
|
|
118
|
+
}
|
|
177
119
|
}
|
|
178
|
-
}
|
|
120
|
+
}
|
|
179
121
|
})
|
|
180
122
|
|
|
181
|
-
app.addHook('preValidation', async
|
|
123
|
+
app.addHook('preValidation', async req => {
|
|
182
124
|
if (typeof req.query.fields === 'string') {
|
|
183
125
|
req.query.fields = req.query.fields.split(',')
|
|
184
126
|
}
|
|
185
127
|
})
|
|
186
128
|
}
|
|
187
129
|
|
|
188
|
-
function
|
|
189
|
-
if (
|
|
190
|
-
return
|
|
130
|
+
export async function openApiGenerator (app, opts) {
|
|
131
|
+
if (!opts.services.some(s => s.openapi)) {
|
|
132
|
+
return
|
|
191
133
|
}
|
|
192
134
|
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
135
|
+
const { services } = opts
|
|
136
|
+
|
|
137
|
+
const openApiSchemas = []
|
|
138
|
+
const apiByApiRoutes = {}
|
|
139
|
+
|
|
140
|
+
for (const { id, origin, openapi } of services) {
|
|
141
|
+
if (!openapi) continue
|
|
142
|
+
|
|
143
|
+
let openapiConfig = null
|
|
144
|
+
if (openapi.config) {
|
|
145
|
+
try {
|
|
146
|
+
openapiConfig = await loadOpenApiConfig(openapi.config)
|
|
147
|
+
} catch (error) {
|
|
148
|
+
app.log.error(error)
|
|
149
|
+
throw new CouldNotReadOpenAPIConfigError(id)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let originSchema = null
|
|
154
|
+
try {
|
|
155
|
+
originSchema = await getOpenApiSchema(origin, openapi)
|
|
156
|
+
} catch (error) {
|
|
157
|
+
app.log.error(error, `failed to fetch schema for "${id} service"`)
|
|
158
|
+
continue
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const schema = modifyOpenApiSchema(app, originSchema, openapiConfig)
|
|
162
|
+
|
|
163
|
+
const prefix = openapi.prefix ? prefixWithSlash(openapi.prefix) : ''
|
|
164
|
+
for (const path in schema.paths) {
|
|
165
|
+
apiByApiRoutes[prefix + path] = {
|
|
166
|
+
origin,
|
|
167
|
+
prefix,
|
|
168
|
+
schema: schema.paths[path]
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
openApiSchemas.push({ id, prefix, schema, originSchema, config: openapiConfig })
|
|
197
173
|
}
|
|
198
|
-
}
|
|
199
174
|
|
|
200
|
-
|
|
201
|
-
const regex = route.replace(/{(.*?)}/g, '(.*)')
|
|
202
|
-
return new RegExp(regex)
|
|
203
|
-
}
|
|
175
|
+
const composedOpenApiSchema = composeOpenApi(openApiSchemas, opts.openapi)
|
|
204
176
|
|
|
205
|
-
|
|
206
|
-
|
|
177
|
+
app.decorate('openApiSchemas', openApiSchemas)
|
|
178
|
+
app.decorate('composedOpenApiSchema', composedOpenApiSchema)
|
|
179
|
+
|
|
180
|
+
await app.register(fastifySwagger, {
|
|
181
|
+
exposeRoute: true,
|
|
182
|
+
openapi: {
|
|
183
|
+
info: {
|
|
184
|
+
title: opts.openapi?.title || 'Platformatic Composer',
|
|
185
|
+
version: opts.openapi?.version || '1.0.0'
|
|
186
|
+
},
|
|
187
|
+
servers: [{ url: globalThis.platformatic?.runtimeBasePath ?? '/' }],
|
|
188
|
+
components: app.composedOpenApiSchema.components
|
|
189
|
+
},
|
|
190
|
+
transform ({ schema, url }) {
|
|
191
|
+
for (const service of opts.services) {
|
|
192
|
+
if (!service.proxy) continue
|
|
193
|
+
|
|
194
|
+
const prefix = service.proxy.prefix ?? ''
|
|
195
|
+
const proxyPrefix = prefix.at(-1) === '/' ? prefix.slice(0, -1) : prefix
|
|
196
|
+
|
|
197
|
+
const proxyUrls = [proxyPrefix + '/', proxyPrefix + '/*']
|
|
198
|
+
if (proxyUrls.includes(url)) {
|
|
199
|
+
schema = schema ?? {}
|
|
200
|
+
schema.hide = true
|
|
201
|
+
break
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return { schema, url }
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
await app.register(openApiScalar, opts)
|
|
209
|
+
|
|
210
|
+
return { apiByApiRoutes }
|
|
207
211
|
}
|
|
208
212
|
|
|
209
|
-
|
|
210
|
-
module.exports.openApiComposer = fp(openApiComposer)
|
|
213
|
+
export const openApiComposer = fp(openApiComposerPlugin)
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const openApiConfigSchema = require('./openapi-config-schema')
|
|
6
|
-
const errors = require('./errors')
|
|
1
|
+
import Ajv from 'ajv'
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
import { ValidationErrors } from './errors.js'
|
|
4
|
+
import { openApiConfigSchema } from './openapi-config-schema.js'
|
|
7
5
|
|
|
8
6
|
const ajv = new Ajv()
|
|
9
7
|
const ajvValidate = ajv.compile(openApiConfigSchema)
|
|
10
8
|
|
|
11
|
-
async function loadOpenApiConfig (pathToConfig) {
|
|
9
|
+
export async function loadOpenApiConfig (pathToConfig) {
|
|
12
10
|
const openApiConfigFile = await readFile(pathToConfig, 'utf-8')
|
|
13
11
|
const openApiConfig = JSON.parse(openApiConfigFile)
|
|
14
12
|
|
|
15
13
|
if (!ajvValidate(openApiConfig)) {
|
|
16
|
-
const validationErrors = ajvValidate.errors.map(
|
|
14
|
+
const validationErrors = ajvValidate.errors.map(err => {
|
|
17
15
|
return {
|
|
18
16
|
/* c8 ignore next 1 */
|
|
19
17
|
path: err.instancePath === '' ? '/' : err.instancePath,
|
|
20
|
-
message: err.message + ' ' + JSON.stringify(err.params)
|
|
18
|
+
message: err.message + ' ' + JSON.stringify(err.params)
|
|
21
19
|
}
|
|
22
20
|
})
|
|
23
|
-
throw new
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
throw new ValidationErrors(
|
|
22
|
+
validationErrors
|
|
23
|
+
.map(err => {
|
|
24
|
+
return err.message
|
|
25
|
+
})
|
|
26
|
+
.join('\n')
|
|
27
|
+
)
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
return openApiConfig
|
|
29
31
|
}
|
|
30
|
-
|
|
31
|
-
module.exports = loadOpenApiConfig
|
package/lib/openapi-modifier.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import traverse from 'json-schema-traverse'
|
|
2
|
+
import rfdc from 'rfdc'
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
-
const clone = require('rfdc')()
|
|
4
|
+
const clone = rfdc()
|
|
5
5
|
|
|
6
|
-
const originPathSymbol = Symbol('originPath')
|
|
7
6
|
const MODIFICATION_KEYWORDS = ['rename']
|
|
8
7
|
|
|
8
|
+
export const originPathSymbol = Symbol('originPath')
|
|
9
|
+
|
|
9
10
|
function findDataBySchemaPointer (schemaPointer, schema, data, parentData, callback) {
|
|
10
11
|
const schemaPointerParts = schemaPointer.split('/').slice(1)
|
|
11
12
|
|
|
@@ -34,9 +35,7 @@ function getModificationRules (modificationSchema) {
|
|
|
34
35
|
|
|
35
36
|
function getModificationRules (schema, jsonPointer) {
|
|
36
37
|
const schemaKeys = Object.keys(schema)
|
|
37
|
-
const modificationKeys = schemaKeys.filter(
|
|
38
|
-
key => MODIFICATION_KEYWORDS.includes(key)
|
|
39
|
-
)
|
|
38
|
+
const modificationKeys = schemaKeys.filter(key => MODIFICATION_KEYWORDS.includes(key))
|
|
40
39
|
|
|
41
40
|
if (modificationKeys.length === 0) return
|
|
42
41
|
modificationRules[jsonPointer] = schema
|
|
@@ -70,22 +69,16 @@ function modifyPayload (payload, originSchema, modificationRules) {
|
|
|
70
69
|
for (const schemaJsonPointer in modificationRules) {
|
|
71
70
|
const rule = modificationRules[schemaJsonPointer]
|
|
72
71
|
|
|
73
|
-
findDataBySchemaPointer(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
null,
|
|
78
|
-
(data, parentData) => {
|
|
79
|
-
if (rule.rename) {
|
|
80
|
-
parentData[rule.rename] = data
|
|
81
|
-
delete parentData[schemaJsonPointer.split('/').pop()]
|
|
82
|
-
}
|
|
72
|
+
findDataBySchemaPointer(schemaJsonPointer, originSchema, payload, null, (data, parentData) => {
|
|
73
|
+
if (rule.rename) {
|
|
74
|
+
parentData[rule.rename] = data
|
|
75
|
+
delete parentData[schemaJsonPointer.split('/').pop()]
|
|
83
76
|
}
|
|
84
|
-
)
|
|
77
|
+
})
|
|
85
78
|
}
|
|
86
79
|
}
|
|
87
80
|
|
|
88
|
-
function modifyOpenApiSchema (app, schema, config) {
|
|
81
|
+
export function modifyOpenApiSchema (app, schema, config) {
|
|
89
82
|
const newSchemaPaths = {}
|
|
90
83
|
const { paths } = clone(schema)
|
|
91
84
|
|
|
@@ -133,5 +126,3 @@ function modifyOpenApiSchema (app, schema, config) {
|
|
|
133
126
|
|
|
134
127
|
return { ...schema, paths: newSchemaPaths }
|
|
135
128
|
}
|
|
136
|
-
|
|
137
|
-
module.exports = { modifyOpenApiSchema, originPathSymbol }
|
package/lib/openapi-scalar.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import fp from 'fastify-plugin'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
async function openApiScalar (app, opts) {
|
|
3
|
+
async function openApiScalarPlugin (app, opts) {
|
|
6
4
|
const { default: scalarTheme } = await import('@platformatic/scalar-theme')
|
|
7
5
|
const { default: scalarApiReference } = await import('@scalar/fastify-api-reference')
|
|
8
6
|
|
|
@@ -21,4 +19,4 @@ async function openApiScalar (app, opts) {
|
|
|
21
19
|
})
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
export const openApiScalar = fp(openApiScalarPlugin)
|
package/lib/proxy.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const { initMetrics } = require('./metrics')
|
|
1
|
+
import httpProxy from '@fastify/http-proxy'
|
|
2
|
+
import { ensureLoggableError, loadModule } from '@platformatic/foundation'
|
|
3
|
+
import fp from 'fastify-plugin'
|
|
4
|
+
import { createRequire } from 'node:module'
|
|
5
|
+
import { workerData } from 'node:worker_threads'
|
|
6
|
+
import { getGlobalDispatcher } from 'undici'
|
|
7
|
+
import { initMetrics } from './metrics.js'
|
|
9
8
|
|
|
10
9
|
const kITC = Symbol.for('plt.runtime.itc')
|
|
11
10
|
const kProxyRoute = Symbol('plt.composer.proxy.route')
|
|
@@ -36,7 +35,7 @@ async function resolveServiceProxyParameters (service) {
|
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
if (service.proxy?.ws?.hooks) {
|
|
39
|
-
const hooks =
|
|
38
|
+
const hooks = await loadModule(createRequire(import.meta.filename), service.proxy.ws.hooks.path)
|
|
40
39
|
service.proxy.ws.hooks = hooks
|
|
41
40
|
}
|
|
42
41
|
|
|
@@ -55,7 +54,7 @@ async function resolveServiceProxyParameters (service) {
|
|
|
55
54
|
|
|
56
55
|
let metrics
|
|
57
56
|
|
|
58
|
-
|
|
57
|
+
async function proxyPlugin (app, opts) {
|
|
59
58
|
const meta = { proxies: {} }
|
|
60
59
|
const hostnameLessProxies = []
|
|
61
60
|
|
|
@@ -260,5 +259,7 @@ module.exports = fp(async function (app, opts) {
|
|
|
260
259
|
await app.register(httpProxy, options)
|
|
261
260
|
}
|
|
262
261
|
|
|
263
|
-
opts.
|
|
264
|
-
}
|
|
262
|
+
opts.stackable?.registerMeta(meta)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export const proxy = fp(proxyPlugin)
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
import fastifyStatic from '@fastify/static'
|
|
2
|
+
import fastifyView from '@fastify/view'
|
|
3
|
+
import userAgentParser from 'my-ua-parser'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import nunjucks from 'nunjucks'
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
const fastifyStatic = require('@fastify/static')
|
|
5
|
-
const userAgentParser = require('my-ua-parser')
|
|
6
|
-
|
|
7
|
-
module.exports = async (app, opts) => {
|
|
7
|
+
export default function root (app) {
|
|
8
8
|
app.register(fastifyStatic, {
|
|
9
|
-
root: join(
|
|
9
|
+
root: join(import.meta.dirname, '../public')
|
|
10
10
|
})
|
|
11
|
-
app.register(
|
|
11
|
+
app.register(fastifyView, {
|
|
12
12
|
engine: {
|
|
13
|
-
nunjucks
|
|
13
|
+
nunjucks
|
|
14
14
|
},
|
|
15
|
-
root: join(
|
|
15
|
+
root: join(import.meta.dirname, '../public')
|
|
16
16
|
})
|
|
17
17
|
// root endpoint
|
|
18
18
|
app.route({
|
|
@@ -44,7 +44,7 @@ module.exports = async (app, opts) => {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
app.platformatic.config.composer.services.forEach(
|
|
47
|
+
app.platformatic.config.composer.services.forEach(s => {
|
|
48
48
|
if (s.openapi) {
|
|
49
49
|
hasOpenAPIServices = true
|
|
50
50
|
serviceTypes.openapi.services.push(s)
|
|
@@ -70,6 +70,6 @@ module.exports = async (app, opts) => {
|
|
|
70
70
|
}
|
|
71
71
|
// Load services
|
|
72
72
|
return { message: 'Welcome to Platformatic! Please visit https://docs.platformatic.dev' }
|
|
73
|
-
}
|
|
73
|
+
}
|
|
74
74
|
})
|
|
75
75
|
}
|
package/lib/schema.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
|
-
'use strict'
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
import { schemaComponents as basicSchemaComponents } from '@platformatic/basic'
|
|
4
|
+
import {
|
|
5
|
+
fastifyServer as server,
|
|
6
|
+
schemaComponents as utilsSchemaComponents,
|
|
7
|
+
watch,
|
|
8
|
+
wrappedRuntime
|
|
9
|
+
} from '@platformatic/foundation'
|
|
10
|
+
import { schemaComponents as serviceSchemaComponents } from '@platformatic/service'
|
|
11
|
+
import { readFileSync } from 'node:fs'
|
|
12
|
+
import { resolve } from 'node:path'
|
|
9
13
|
|
|
10
|
-
const
|
|
14
|
+
const { $defs, graphqlBase, openApiBase, plugins } = serviceSchemaComponents
|
|
15
|
+
|
|
16
|
+
export const packageJson = JSON.parse(readFileSync(resolve(import.meta.dirname, '../package.json'), 'utf8'))
|
|
17
|
+
export const version = packageJson.version
|
|
18
|
+
|
|
19
|
+
export const openApiService = {
|
|
11
20
|
type: 'object',
|
|
12
21
|
properties: {
|
|
13
22
|
url: { type: 'string' },
|
|
@@ -19,7 +28,7 @@ const openApiService = {
|
|
|
19
28
|
additionalProperties: false
|
|
20
29
|
}
|
|
21
30
|
|
|
22
|
-
const entityResolver = {
|
|
31
|
+
export const entityResolver = {
|
|
23
32
|
type: 'object',
|
|
24
33
|
properties: {
|
|
25
34
|
name: { type: 'string' },
|
|
@@ -34,7 +43,7 @@ const entityResolver = {
|
|
|
34
43
|
additionalProperties: false
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
const entities = {
|
|
46
|
+
export const entities = {
|
|
38
47
|
type: 'object',
|
|
39
48
|
patternProperties: {
|
|
40
49
|
'^.*$': {
|
|
@@ -77,7 +86,7 @@ const entities = {
|
|
|
77
86
|
}
|
|
78
87
|
}
|
|
79
88
|
|
|
80
|
-
const graphqlService = {
|
|
89
|
+
export const graphqlService = {
|
|
81
90
|
anyOf: [
|
|
82
91
|
{ type: 'boolean' },
|
|
83
92
|
{
|
|
@@ -94,7 +103,7 @@ const graphqlService = {
|
|
|
94
103
|
]
|
|
95
104
|
}
|
|
96
105
|
|
|
97
|
-
const graphqlComposerOptions = {
|
|
106
|
+
export const graphqlComposerOptions = {
|
|
98
107
|
type: 'object',
|
|
99
108
|
properties: {
|
|
100
109
|
...graphqlBase.properties,
|
|
@@ -109,7 +118,7 @@ const graphqlComposerOptions = {
|
|
|
109
118
|
additionalProperties: false
|
|
110
119
|
}
|
|
111
120
|
|
|
112
|
-
const composer = {
|
|
121
|
+
export const composer = {
|
|
113
122
|
type: 'object',
|
|
114
123
|
properties: {
|
|
115
124
|
services: {
|
|
@@ -179,7 +188,7 @@ const composer = {
|
|
|
179
188
|
additionalProperties: false
|
|
180
189
|
}
|
|
181
190
|
|
|
182
|
-
const types = {
|
|
191
|
+
export const types = {
|
|
183
192
|
type: 'object',
|
|
184
193
|
properties: {
|
|
185
194
|
autogenerate: {
|
|
@@ -195,10 +204,20 @@ const types = {
|
|
|
195
204
|
additionalProperties: false
|
|
196
205
|
}
|
|
197
206
|
|
|
198
|
-
const
|
|
199
|
-
|
|
207
|
+
export const schemaComponents = {
|
|
208
|
+
openApiService,
|
|
209
|
+
entityResolver,
|
|
210
|
+
entities,
|
|
211
|
+
graphqlService,
|
|
212
|
+
graphqlComposerOptions,
|
|
213
|
+
composer,
|
|
214
|
+
types
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export const schema = {
|
|
218
|
+
$id: `https://schemas.platformatic.dev/@platformatic/composer/${packageJson.version}.json`,
|
|
200
219
|
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
201
|
-
title: 'Platformatic Composer',
|
|
220
|
+
title: 'Platformatic Composer Config',
|
|
202
221
|
type: 'object',
|
|
203
222
|
properties: {
|
|
204
223
|
basePath: {
|
|
@@ -206,12 +225,11 @@ const platformaticComposerSchema = {
|
|
|
206
225
|
},
|
|
207
226
|
server,
|
|
208
227
|
composer,
|
|
209
|
-
metrics,
|
|
210
228
|
types,
|
|
211
229
|
plugins,
|
|
212
|
-
|
|
213
|
-
runtime:
|
|
214
|
-
telemetry,
|
|
230
|
+
application: basicSchemaComponents.application,
|
|
231
|
+
runtime: wrappedRuntime,
|
|
232
|
+
telemetry: utilsSchemaComponents.telemetry,
|
|
215
233
|
watch: {
|
|
216
234
|
anyOf: [
|
|
217
235
|
watch,
|
|
@@ -231,12 +249,10 @@ const platformaticComposerSchema = {
|
|
|
231
249
|
}
|
|
232
250
|
},
|
|
233
251
|
additionalProperties: false,
|
|
234
|
-
$defs
|
|
252
|
+
$defs
|
|
235
253
|
}
|
|
236
254
|
|
|
237
|
-
module.exports.schema = platformaticComposerSchema
|
|
238
|
-
|
|
239
255
|
/* c8 ignore next 3 */
|
|
240
|
-
if (
|
|
241
|
-
console.log(JSON.stringify(
|
|
256
|
+
if (process.argv[1] === import.meta.filename) {
|
|
257
|
+
console.log(JSON.stringify(schema, null, 2))
|
|
242
258
|
}
|