@platformatic/runtime 1.27.0 → 1.28.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/fixtures/do-not-reload-dependencies/platformatic.service.json +1 -1
- package/fixtures/interceptors/idp.js +3 -0
- package/fixtures/interceptors/platformatic.runtime.json +2 -1
- package/fixtures/interceptors-2/platformatic.runtime.json +25 -0
- package/fixtures/interceptors-2/services/a/platformatic.service.json +17 -0
- package/fixtures/interceptors-2/services/a/plugin.js +15 -0
- package/fixtures/management-api/services/service-2/platformatic.json +2 -1
- package/lib/api-client.js +118 -0
- package/lib/api.js +39 -6
- package/lib/app.js +11 -1
- package/lib/config.js +4 -1
- package/lib/errors.js +2 -1
- package/lib/generator/runtime-generator.js +3 -3
- package/lib/interceptors.js +17 -0
- package/lib/logs.js +112 -0
- package/lib/management-api.js +47 -47
- package/lib/schema.js +49 -17
- package/lib/start.js +3 -14
- package/lib/upgrade.js +25 -0
- package/lib/versions/v1.5.0.js +11 -0
- package/lib/worker.js +8 -16
- package/package.json +24 -19
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const fastify = require('fastify')
|
|
4
|
+
const formBody = require('@fastify/formbody')
|
|
4
5
|
const { createSigner } = require('fast-jwt')
|
|
5
6
|
|
|
6
7
|
async function start (opts) {
|
|
@@ -15,6 +16,8 @@ async function start (opts) {
|
|
|
15
16
|
app.decorate('refreshToken', '')
|
|
16
17
|
app.decorate('signSync', signSync)
|
|
17
18
|
|
|
19
|
+
app.register(formBody)
|
|
20
|
+
|
|
18
21
|
// Dump bearer token implementation
|
|
19
22
|
app.post('/token', async (req, res) => {
|
|
20
23
|
return { access_token: signSync({}) }
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
"undici": {
|
|
15
15
|
"interceptors": {
|
|
16
16
|
"Agent": [{
|
|
17
|
-
"module": "undici-
|
|
17
|
+
"module": "undici-oidc-interceptor",
|
|
18
18
|
"options": {
|
|
19
|
+
"idpTokenUrl": "{{PLT_IDP_TOKEN_URL}}",
|
|
19
20
|
"refreshToken": "{{PLT_REFRESH_TOKEN}}",
|
|
20
21
|
"origins": ["{{PLT_EXTERNAL_SERVICE}}"],
|
|
21
22
|
"clientId": "my-client-id"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://platformatic.dev/schemas/v1.20.0/runtime",
|
|
3
|
+
"entrypoint": "a",
|
|
4
|
+
"autoload": {
|
|
5
|
+
"path": "./services"
|
|
6
|
+
},
|
|
7
|
+
"server": {
|
|
8
|
+
"hostname": "127.0.0.1",
|
|
9
|
+
"port": "{{PORT}}",
|
|
10
|
+
"logger": {
|
|
11
|
+
"level": "info"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"undici": {
|
|
15
|
+
"interceptors": [{
|
|
16
|
+
"module": "undici-oidc-interceptor",
|
|
17
|
+
"options": {
|
|
18
|
+
"idpTokenUrl": "{{PLT_IDP_TOKEN_URL}}",
|
|
19
|
+
"refreshToken": "{{PLT_REFRESH_TOKEN}}",
|
|
20
|
+
"origins": ["{{PLT_EXTERNAL_SERVICE}}"],
|
|
21
|
+
"clientId": "my-client-id"
|
|
22
|
+
}
|
|
23
|
+
}]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://platformatic.dev/schemas/v1.3.0/service",
|
|
3
|
+
"server": {
|
|
4
|
+
"logger": {
|
|
5
|
+
"level": "warn"
|
|
6
|
+
}
|
|
7
|
+
},
|
|
8
|
+
"plugins": {
|
|
9
|
+
"paths": [{
|
|
10
|
+
"path": "plugin.js",
|
|
11
|
+
"encapsulate": false,
|
|
12
|
+
"options": {
|
|
13
|
+
"externalService": "{{PLT_EXTERNAL_SERVICE}}"
|
|
14
|
+
}
|
|
15
|
+
}]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { request } = require('undici')
|
|
4
|
+
|
|
5
|
+
module.exports = async function (fastify, options) {
|
|
6
|
+
fastify.get('/hello', async (_, reply) => {
|
|
7
|
+
const res = await request(`${options.externalService}/hello`)
|
|
8
|
+
if (res.statusCode !== 200) {
|
|
9
|
+
reply.code(res.statusCode)
|
|
10
|
+
}
|
|
11
|
+
reply.log.info('response received')
|
|
12
|
+
const data = await res.body.json()
|
|
13
|
+
return data
|
|
14
|
+
})
|
|
15
|
+
}
|
package/lib/api-client.js
CHANGED
|
@@ -6,10 +6,14 @@ const errors = require('./errors')
|
|
|
6
6
|
const { setTimeout: sleep } = require('node:timers/promises')
|
|
7
7
|
|
|
8
8
|
const MAX_LISTENERS_COUNT = 100
|
|
9
|
+
const MAX_METRICS_QUEUE_LENGTH = 5 * 60 // 5 minutes in seconds
|
|
10
|
+
const COLLECT_METRICS_TIMEOUT = 1000
|
|
9
11
|
|
|
10
12
|
class RuntimeApiClient extends EventEmitter {
|
|
11
13
|
#exitCode
|
|
12
14
|
#exitPromise
|
|
15
|
+
#metrics
|
|
16
|
+
#metricsTimeout
|
|
13
17
|
|
|
14
18
|
constructor (worker) {
|
|
15
19
|
super()
|
|
@@ -78,6 +82,10 @@ class RuntimeApiClient extends EventEmitter {
|
|
|
78
82
|
return this.#sendCommand('plt:get-service-graphql-schema', { id })
|
|
79
83
|
}
|
|
80
84
|
|
|
85
|
+
async getMetrics (format = 'json') {
|
|
86
|
+
return this.#sendCommand('plt:get-metrics', { format })
|
|
87
|
+
}
|
|
88
|
+
|
|
81
89
|
async startService (id) {
|
|
82
90
|
return this.#sendCommand('plt:start-service', { id })
|
|
83
91
|
}
|
|
@@ -90,6 +98,115 @@ class RuntimeApiClient extends EventEmitter {
|
|
|
90
98
|
return this.#sendCommand('plt:inject', { id, injectParams })
|
|
91
99
|
}
|
|
92
100
|
|
|
101
|
+
getCachedMetrics () {
|
|
102
|
+
return this.#metrics
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async getFormattedMetrics () {
|
|
106
|
+
const { metrics } = await this.getMetrics()
|
|
107
|
+
|
|
108
|
+
const entrypointDetails = await this.getEntrypointDetails()
|
|
109
|
+
const entrypointConfig = await this.getServiceConfig(entrypointDetails.id)
|
|
110
|
+
const entrypointMetricsPrefix = entrypointConfig.metrics?.prefix
|
|
111
|
+
|
|
112
|
+
const cpuMetric = metrics.find(
|
|
113
|
+
(metric) => metric.name === 'process_cpu_percent_usage'
|
|
114
|
+
)
|
|
115
|
+
const rssMetric = metrics.find(
|
|
116
|
+
(metric) => metric.name === 'process_resident_memory_bytes'
|
|
117
|
+
)
|
|
118
|
+
const totalHeapSizeMetric = metrics.find(
|
|
119
|
+
(metric) => metric.name === 'nodejs_heap_size_total_bytes'
|
|
120
|
+
)
|
|
121
|
+
const usedHeapSizeMetric = metrics.find(
|
|
122
|
+
(metric) => metric.name === 'nodejs_heap_size_used_bytes'
|
|
123
|
+
)
|
|
124
|
+
const heapSpaceSizeTotalMetric = metrics.find(
|
|
125
|
+
(metric) => metric.name === 'nodejs_heap_space_size_total_bytes'
|
|
126
|
+
)
|
|
127
|
+
const newSpaceSizeTotalMetric = heapSpaceSizeTotalMetric.values.find(
|
|
128
|
+
(value) => value.labels.space === 'new'
|
|
129
|
+
)
|
|
130
|
+
const oldSpaceSizeTotalMetric = heapSpaceSizeTotalMetric.values.find(
|
|
131
|
+
(value) => value.labels.space === 'old'
|
|
132
|
+
)
|
|
133
|
+
const eventLoopUtilizationMetric = metrics.find(
|
|
134
|
+
(metric) => metric.name === 'nodejs_eventloop_utilization'
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
let p90Value = 0
|
|
138
|
+
let p95Value = 0
|
|
139
|
+
let p99Value = 0
|
|
140
|
+
|
|
141
|
+
if (entrypointMetricsPrefix) {
|
|
142
|
+
const metricName = entrypointMetricsPrefix + 'http_request_all_summary_seconds'
|
|
143
|
+
const httpLatencyMetrics = metrics.find((metric) => metric.name === metricName)
|
|
144
|
+
|
|
145
|
+
p90Value = httpLatencyMetrics.values.find(
|
|
146
|
+
(value) => value.labels.quantile === 0.9
|
|
147
|
+
).value || 0
|
|
148
|
+
p95Value = httpLatencyMetrics.values.find(
|
|
149
|
+
(value) => value.labels.quantile === 0.95
|
|
150
|
+
).value || 0
|
|
151
|
+
p99Value = httpLatencyMetrics.values.find(
|
|
152
|
+
(value) => value.labels.quantile === 0.99
|
|
153
|
+
).value || 0
|
|
154
|
+
|
|
155
|
+
p90Value = Math.round(p90Value * 1000)
|
|
156
|
+
p95Value = Math.round(p95Value * 1000)
|
|
157
|
+
p99Value = Math.round(p99Value * 1000)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const cpu = cpuMetric.values[0].value
|
|
161
|
+
const rss = rssMetric.values[0].value
|
|
162
|
+
const elu = eventLoopUtilizationMetric.values[0].value
|
|
163
|
+
const totalHeapSize = totalHeapSizeMetric.values[0].value
|
|
164
|
+
const usedHeapSize = usedHeapSizeMetric.values[0].value
|
|
165
|
+
const newSpaceSize = newSpaceSizeTotalMetric.value
|
|
166
|
+
const oldSpaceSize = oldSpaceSizeTotalMetric.value
|
|
167
|
+
|
|
168
|
+
const formattedMetrics = {
|
|
169
|
+
version: 1,
|
|
170
|
+
date: new Date().toISOString(),
|
|
171
|
+
cpu,
|
|
172
|
+
elu,
|
|
173
|
+
rss,
|
|
174
|
+
totalHeapSize,
|
|
175
|
+
usedHeapSize,
|
|
176
|
+
newSpaceSize,
|
|
177
|
+
oldSpaceSize,
|
|
178
|
+
entrypoint: {
|
|
179
|
+
latency: {
|
|
180
|
+
p90: p90Value,
|
|
181
|
+
p95: p95Value,
|
|
182
|
+
p99: p99Value
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return formattedMetrics
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
startCollectingMetrics () {
|
|
190
|
+
this.#metrics = []
|
|
191
|
+
this.#metricsTimeout = setInterval(async () => {
|
|
192
|
+
let metrics = null
|
|
193
|
+
try {
|
|
194
|
+
metrics = await this.getFormattedMetrics()
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (!(error instanceof errors.RuntimeExitedError)) {
|
|
197
|
+
console.error('Error collecting metrics', error)
|
|
198
|
+
}
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
this.emit('metrics', metrics)
|
|
203
|
+
this.#metrics.push(metrics)
|
|
204
|
+
if (this.#metrics.length > MAX_METRICS_QUEUE_LENGTH) {
|
|
205
|
+
this.#metrics.shift()
|
|
206
|
+
}
|
|
207
|
+
}, COLLECT_METRICS_TIMEOUT).unref()
|
|
208
|
+
}
|
|
209
|
+
|
|
93
210
|
async #sendCommand (command, params = {}) {
|
|
94
211
|
const operationId = randomUUID()
|
|
95
212
|
|
|
@@ -113,6 +230,7 @@ class RuntimeApiClient extends EventEmitter {
|
|
|
113
230
|
async #exitHandler () {
|
|
114
231
|
this.#exitCode = undefined
|
|
115
232
|
return once(this.worker, 'exit').then((msg) => {
|
|
233
|
+
clearInterval(this.#metricsTimeout)
|
|
116
234
|
this.#exitCode = msg[0]
|
|
117
235
|
return msg
|
|
118
236
|
})
|
package/lib/api.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { getGlobalDispatcher, setGlobalDispatcher } = require('undici')
|
|
4
|
-
const
|
|
4
|
+
const { createFastifyInterceptor } = require('fastify-undici-dispatcher')
|
|
5
5
|
const { PlatformaticApp } = require('./app')
|
|
6
6
|
const errors = require('./errors')
|
|
7
7
|
const { printSchema } = require('graphql')
|
|
@@ -9,9 +9,10 @@ const { printSchema } = require('graphql')
|
|
|
9
9
|
class RuntimeApi {
|
|
10
10
|
#services
|
|
11
11
|
#dispatcher
|
|
12
|
+
#interceptor
|
|
12
13
|
#logger
|
|
13
14
|
|
|
14
|
-
constructor (config, logger, loaderPort) {
|
|
15
|
+
constructor (config, logger, loaderPort, composedInterceptors = []) {
|
|
15
16
|
this.#services = new Map()
|
|
16
17
|
this.#logger = logger
|
|
17
18
|
const telemetryConfig = config.telemetry
|
|
@@ -37,11 +38,14 @@ class RuntimeApi {
|
|
|
37
38
|
this.#services.set(service.id, app)
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
this.#
|
|
41
|
-
dispatcher: getGlobalDispatcher(),
|
|
41
|
+
this.#interceptor = createFastifyInterceptor({
|
|
42
42
|
// setting the domain here allows for fail-fast scenarios
|
|
43
43
|
domain: '.plt.local'
|
|
44
44
|
})
|
|
45
|
+
|
|
46
|
+
composedInterceptors.unshift(this.#interceptor)
|
|
47
|
+
|
|
48
|
+
this.#dispatcher = getGlobalDispatcher().compose(composedInterceptors)
|
|
45
49
|
setGlobalDispatcher(this.#dispatcher)
|
|
46
50
|
}
|
|
47
51
|
|
|
@@ -122,6 +126,8 @@ class RuntimeApi {
|
|
|
122
126
|
return this.#getServiceOpenapiSchema(params)
|
|
123
127
|
case 'plt:get-service-graphql-schema':
|
|
124
128
|
return this.#getServiceGraphqlSchema(params)
|
|
129
|
+
case 'plt:get-metrics':
|
|
130
|
+
return this.#getMetrics(params)
|
|
125
131
|
case 'plt:start-service':
|
|
126
132
|
return this.#startService(params)
|
|
127
133
|
case 'plt:stop-service':
|
|
@@ -144,7 +150,7 @@ class RuntimeApi {
|
|
|
144
150
|
}
|
|
145
151
|
|
|
146
152
|
const serviceUrl = new URL(service.appConfig.localUrl)
|
|
147
|
-
this.#
|
|
153
|
+
this.#interceptor.route(serviceUrl.host, service.server)
|
|
148
154
|
}
|
|
149
155
|
return entrypointUrl
|
|
150
156
|
}
|
|
@@ -172,7 +178,7 @@ class RuntimeApi {
|
|
|
172
178
|
}
|
|
173
179
|
|
|
174
180
|
const serviceUrl = new URL(service.appConfig.localUrl)
|
|
175
|
-
this.#
|
|
181
|
+
this.#interceptor.route(serviceUrl.host, service.server)
|
|
176
182
|
}
|
|
177
183
|
return entrypointUrl
|
|
178
184
|
}
|
|
@@ -277,6 +283,33 @@ class RuntimeApi {
|
|
|
277
283
|
}
|
|
278
284
|
}
|
|
279
285
|
|
|
286
|
+
async #getMetrics ({ format }) {
|
|
287
|
+
let entrypoint = null
|
|
288
|
+
for (const service of this.#services.values()) {
|
|
289
|
+
if (service.appConfig.entrypoint) {
|
|
290
|
+
entrypoint = service
|
|
291
|
+
break
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (!entrypoint.config) {
|
|
296
|
+
throw new errors.ServiceNotStartedError(entrypoint.id)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const promRegister = entrypoint.server.metrics?.client?.register
|
|
300
|
+
if (!promRegister) {
|
|
301
|
+
return null
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// All runtime services shares the same metrics registry.
|
|
305
|
+
// Getting metrics from the entrypoint returns all metrics.
|
|
306
|
+
const metrics = format === 'json'
|
|
307
|
+
? await promRegister.getMetricsAsJSON()
|
|
308
|
+
: await promRegister.metrics()
|
|
309
|
+
|
|
310
|
+
return { metrics }
|
|
311
|
+
}
|
|
312
|
+
|
|
280
313
|
async #startService ({ id }) {
|
|
281
314
|
const service = this.#getServiceById(id)
|
|
282
315
|
await service.start()
|
package/lib/app.js
CHANGED
|
@@ -4,6 +4,7 @@ const { once } = require('node:events')
|
|
|
4
4
|
const { dirname } = require('node:path')
|
|
5
5
|
const { FileWatcher } = require('@platformatic/utils')
|
|
6
6
|
const debounce = require('debounce')
|
|
7
|
+
const { snakeCase } = require('change-case-all')
|
|
7
8
|
const { buildServer } = require('./build-server')
|
|
8
9
|
const { loadConfig } = require('./load-config')
|
|
9
10
|
const errors = require('./errors')
|
|
@@ -106,6 +107,15 @@ class PlatformaticApp {
|
|
|
106
107
|
})
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
configManager.update({
|
|
111
|
+
...configManager.current,
|
|
112
|
+
metrics: {
|
|
113
|
+
...configManager.current.metrics,
|
|
114
|
+
defaultMetrics: { enabled: this.appConfig.entrypoint },
|
|
115
|
+
prefix: snakeCase(this.appConfig.id) + '_'
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
|
|
109
119
|
if (!this.appConfig.entrypoint) {
|
|
110
120
|
configManager.update({
|
|
111
121
|
...configManager.current,
|
|
@@ -205,7 +215,7 @@ class PlatformaticApp {
|
|
|
205
215
|
onMissingEnv (key) {
|
|
206
216
|
return appConfig.localServiceEnvVars.get(key)
|
|
207
217
|
}
|
|
208
|
-
})
|
|
218
|
+
}, true, this.#logger)
|
|
209
219
|
} catch (err) {
|
|
210
220
|
this.#logAndExit(err)
|
|
211
221
|
}
|
package/lib/config.js
CHANGED
|
@@ -6,6 +6,7 @@ const Topo = require('@hapi/topo')
|
|
|
6
6
|
const ConfigManager = require('@platformatic/config')
|
|
7
7
|
const { schema } = require('./schema')
|
|
8
8
|
const errors = require('./errors')
|
|
9
|
+
const upgrade = require('./upgrade')
|
|
9
10
|
|
|
10
11
|
async function _transformConfig (configManager) {
|
|
11
12
|
const config = configManager.current
|
|
@@ -245,6 +246,7 @@ platformaticRuntime[Symbol.for('skip-override')] = true
|
|
|
245
246
|
platformaticRuntime.schema = schema
|
|
246
247
|
platformaticRuntime.configType = 'runtime'
|
|
247
248
|
platformaticRuntime.configManagerConfig = {
|
|
249
|
+
version: require('../package.json').version,
|
|
248
250
|
schema,
|
|
249
251
|
allowToWatch: ['.env'],
|
|
250
252
|
schemaOptions: {
|
|
@@ -256,7 +258,8 @@ platformaticRuntime.configManagerConfig = {
|
|
|
256
258
|
envWhitelist: ['DATABASE_URL', 'PORT', 'HOSTNAME'],
|
|
257
259
|
async transformConfig () {
|
|
258
260
|
await _transformConfig(this)
|
|
259
|
-
}
|
|
261
|
+
},
|
|
262
|
+
upgrade
|
|
260
263
|
}
|
|
261
264
|
|
|
262
265
|
async function wrapConfigInRuntimeConfig ({ configManager, args }) {
|
package/lib/errors.js
CHANGED
|
@@ -22,5 +22,6 @@ module.exports = {
|
|
|
22
22
|
InspectorHostError: createError(`${ERROR_PREFIX}_INSPECTOR_HOST`, 'Inspector host cannot be empty'),
|
|
23
23
|
CannotMapSpecifierToAbsolutePathError: createError(`${ERROR_PREFIX}_CANNOT_MAP_SPECIFIER_TO_ABSOLUTE_PATH`, 'Cannot map "%s" to an absolute path'),
|
|
24
24
|
NodeInspectorFlagsNotSupportedError: createError(`${ERROR_PREFIX}_NODE_INSPECTOR_FLAGS_NOT_SUPPORTED`, 'The Node.js inspector flags are not supported. Please use \'platformatic start --inspect\' instead.'),
|
|
25
|
-
FailedToUnlinkManagementApiSocket: createError(`${ERROR_PREFIX}_FAILED_TO_UNLINK_MANAGEMENT_API_SOCKET`, 'Failed to unlink management API socket "%s"')
|
|
25
|
+
FailedToUnlinkManagementApiSocket: createError(`${ERROR_PREFIX}_FAILED_TO_UNLINK_MANAGEMENT_API_SOCKET`, 'Failed to unlink management API socket "%s"'),
|
|
26
|
+
LogFileNotFound: createError(`${ERROR_PREFIX}_LOG_FILE_NOT_FOUND`, 'Log file with index %s not found', 404)
|
|
26
27
|
}
|
|
@@ -54,11 +54,11 @@ class RuntimeGenerator extends BaseGenerator {
|
|
|
54
54
|
const template = {
|
|
55
55
|
name: `${this.runtimeName}`,
|
|
56
56
|
scripts: {
|
|
57
|
-
start: 'platformatic start'
|
|
58
|
-
test: 'node --test test/*/*.test.js'
|
|
57
|
+
start: 'platformatic start'
|
|
59
58
|
},
|
|
60
59
|
devDependencies: {
|
|
61
|
-
fastify: `^${this.fastifyVersion}
|
|
60
|
+
fastify: `^${this.fastifyVersion}`,
|
|
61
|
+
borp: `${this.pkgData.devDependencies.borp}`
|
|
62
62
|
},
|
|
63
63
|
dependencies: {
|
|
64
64
|
platformatic: `^${this.platformaticVersion}`,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { pathToFileURL } = require('node:url')
|
|
4
|
+
|
|
5
|
+
async function loadInterceptor (_require, module, options) {
|
|
6
|
+
const url = pathToFileURL(_require.resolve(module))
|
|
7
|
+
const interceptor = (await import(url)).default
|
|
8
|
+
return interceptor(options)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function loadInterceptors (_require, interceptors) {
|
|
12
|
+
return Promise.all(interceptors.map(async ({ module, options }) => {
|
|
13
|
+
return loadInterceptor(_require, module, options)
|
|
14
|
+
}))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = loadInterceptors
|
package/lib/logs.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { tmpdir } = require('node:os')
|
|
4
|
+
const { join } = require('node:path')
|
|
5
|
+
const { createReadStream, watch } = require('node:fs')
|
|
6
|
+
const { readdir } = require('node:fs/promises')
|
|
7
|
+
const ts = require('tail-file-stream')
|
|
8
|
+
|
|
9
|
+
const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'runtimes')
|
|
10
|
+
const runtimeTmpDir = join(PLATFORMATIC_TMP_DIR, process.pid.toString())
|
|
11
|
+
|
|
12
|
+
async function getLogFiles () {
|
|
13
|
+
const runtimeTmpFiles = await readdir(runtimeTmpDir)
|
|
14
|
+
const runtimeLogFiles = runtimeTmpFiles
|
|
15
|
+
.filter((file) => file.startsWith('logs'))
|
|
16
|
+
.sort((log1, log2) => {
|
|
17
|
+
const index1 = parseInt(log1.slice('logs.'.length))
|
|
18
|
+
const index2 = parseInt(log2.slice('logs.'.length))
|
|
19
|
+
return index1 - index2
|
|
20
|
+
})
|
|
21
|
+
return runtimeLogFiles
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function pipeLiveLogs (writableStream, logger, startLogIndex) {
|
|
25
|
+
const runtimeLogFiles = await getLogFiles()
|
|
26
|
+
if (runtimeLogFiles.length === 0) {
|
|
27
|
+
writableStream.end()
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let latestFileIndex = parseInt(runtimeLogFiles.at(-1).slice('logs.'.length))
|
|
32
|
+
|
|
33
|
+
let waiting = false
|
|
34
|
+
let fileStream = null
|
|
35
|
+
let fileIndex = startLogIndex ?? latestFileIndex
|
|
36
|
+
|
|
37
|
+
const watcher = watch(runtimeTmpDir, async (event, filename) => {
|
|
38
|
+
if (event === 'rename' && filename.startsWith('logs')) {
|
|
39
|
+
const logFileIndex = parseInt(filename.slice('logs.'.length))
|
|
40
|
+
if (logFileIndex > latestFileIndex) {
|
|
41
|
+
latestFileIndex = logFileIndex
|
|
42
|
+
if (waiting) {
|
|
43
|
+
streamLogFile(++fileIndex)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}).unref()
|
|
48
|
+
|
|
49
|
+
const streamLogFile = () => {
|
|
50
|
+
const fileName = 'logs.' + fileIndex
|
|
51
|
+
const filePath = join(runtimeTmpDir, fileName)
|
|
52
|
+
|
|
53
|
+
const prevFileStream = fileStream
|
|
54
|
+
|
|
55
|
+
fileStream = ts.createReadStream(filePath)
|
|
56
|
+
fileStream.pipe(writableStream, { end: false })
|
|
57
|
+
|
|
58
|
+
if (prevFileStream) {
|
|
59
|
+
prevFileStream.unpipe(writableStream)
|
|
60
|
+
prevFileStream.destroy()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
fileStream.on('error', (err) => {
|
|
64
|
+
logger.log.error(err, 'Error streaming log file')
|
|
65
|
+
fileStream.destroy()
|
|
66
|
+
watcher.close()
|
|
67
|
+
writableStream.end()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
fileStream.on('data', () => {
|
|
71
|
+
waiting = false
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
fileStream.on('eof', () => {
|
|
75
|
+
if (latestFileIndex > fileIndex) {
|
|
76
|
+
streamLogFile(++fileIndex)
|
|
77
|
+
} else {
|
|
78
|
+
waiting = true
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return fileStream
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
streamLogFile(fileIndex)
|
|
86
|
+
|
|
87
|
+
writableStream.on('close', () => {
|
|
88
|
+
watcher.close()
|
|
89
|
+
fileStream.destroy()
|
|
90
|
+
})
|
|
91
|
+
writableStream.on('error', () => {
|
|
92
|
+
watcher.close()
|
|
93
|
+
fileStream.destroy()
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function getLogIndexes () {
|
|
98
|
+
const runtimeLogFiles = await getLogFiles()
|
|
99
|
+
return runtimeLogFiles
|
|
100
|
+
.map((file) => parseInt(file.slice('logs.'.length)))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function getLogFileStream (logFileIndex) {
|
|
104
|
+
const filePath = join(runtimeTmpDir, `logs.${logFileIndex}`)
|
|
105
|
+
return createReadStream(filePath)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
pipeLiveLogs,
|
|
110
|
+
getLogFileStream,
|
|
111
|
+
getLogIndexes
|
|
112
|
+
}
|
package/lib/management-api.js
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
const { tmpdir, platform } = require('node:os')
|
|
4
4
|
const { join } = require('node:path')
|
|
5
|
-
const {
|
|
6
|
-
const { readFile, readdir, mkdir, unlink } = require('node:fs/promises')
|
|
5
|
+
const { readFile, mkdir, unlink } = require('node:fs/promises')
|
|
7
6
|
const fastify = require('fastify')
|
|
8
7
|
const errors = require('./errors')
|
|
8
|
+
const { pipeLiveLogs, getLogFileStream, getLogIndexes } = require('./logs')
|
|
9
9
|
const platformaticVersion = require('../package.json').version
|
|
10
10
|
|
|
11
11
|
const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'runtimes')
|
|
12
12
|
const runtimeTmpDir = join(PLATFORMATIC_TMP_DIR, process.pid.toString())
|
|
13
13
|
|
|
14
|
-
async function createManagementApi (configManager, runtimeApiClient
|
|
14
|
+
async function createManagementApi (configManager, runtimeApiClient) {
|
|
15
15
|
const app = fastify()
|
|
16
16
|
app.log.warn(
|
|
17
17
|
'Runtime Management API is in the experimental stage. ' +
|
|
@@ -115,64 +115,65 @@ async function createManagementApi (configManager, runtimeApiClient, loggingPort
|
|
|
115
115
|
.send(res.body)
|
|
116
116
|
})
|
|
117
117
|
|
|
118
|
-
app.get('/
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
app.get('/metrics/live', { websocket: true }, async (connection) => {
|
|
119
|
+
const cachedMetrics = runtimeApiClient.getCachedMetrics()
|
|
120
|
+
const serializedMetrics = cachedMetrics
|
|
121
|
+
.map((metric) => JSON.stringify(metric))
|
|
122
|
+
.join('\n')
|
|
123
|
+
connection.socket.send(serializedMetrics)
|
|
124
|
+
|
|
125
|
+
const eventHandler = (metrics) => {
|
|
126
|
+
const serializedMetrics = JSON.stringify(metrics)
|
|
127
|
+
connection.socket.send(serializedMetrics)
|
|
123
128
|
}
|
|
124
129
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
connection.socket.on('error', () => {
|
|
130
|
-
loggingPort.off('message', handler)
|
|
130
|
+
runtimeApiClient.on('metrics', eventHandler)
|
|
131
|
+
|
|
132
|
+
connection.on('error', () => {
|
|
133
|
+
runtimeApiClient.off('metrics', eventHandler)
|
|
131
134
|
})
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
|
|
136
|
+
connection.on('close', () => {
|
|
137
|
+
runtimeApiClient.off('metrics', eventHandler)
|
|
134
138
|
})
|
|
135
139
|
})
|
|
136
140
|
|
|
137
|
-
app.get('/logs/
|
|
138
|
-
const
|
|
139
|
-
const runtimeLogFiles = runtimeTmpFiles
|
|
140
|
-
.filter((file) => file.startsWith('logs'))
|
|
141
|
-
.map((file) => join(runtimeTmpDir, file))
|
|
142
|
-
.sort()
|
|
141
|
+
app.get('/logs/live', { websocket: true }, async (connection, req) => {
|
|
142
|
+
const startLogIndex = req.query.start ? parseInt(req.query.start) : null
|
|
143
143
|
|
|
144
|
-
if (
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
if (startLogIndex) {
|
|
145
|
+
const logIndexes = await getLogIndexes()
|
|
146
|
+
if (!logIndexes.includes(startLogIndex)) {
|
|
147
|
+
throw new errors.LogFileNotFound(startLogIndex)
|
|
148
|
+
}
|
|
147
149
|
}
|
|
148
150
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
streamLogFile(fileIndex + 1)
|
|
166
|
-
})
|
|
151
|
+
pipeLiveLogs(connection, req.log, startLogIndex)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
app.get('/logs/indexes', async () => {
|
|
155
|
+
const logIndexes = await getLogIndexes()
|
|
156
|
+
return { indexes: logIndexes }
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
app.get('/logs/:id', async (req) => {
|
|
160
|
+
const { id } = req.params
|
|
161
|
+
|
|
162
|
+
const logIndex = parseInt(id)
|
|
163
|
+
const logIndexes = await getLogIndexes()
|
|
164
|
+
if (!logIndexes.includes(logIndex)) {
|
|
165
|
+
throw new errors.LogFileNotFound(logIndex)
|
|
167
166
|
}
|
|
168
|
-
|
|
167
|
+
|
|
168
|
+
const logFileStream = await getLogFileStream(logIndex)
|
|
169
|
+
return logFileStream
|
|
169
170
|
})
|
|
170
171
|
}, { prefix: '/api/v1' })
|
|
171
172
|
|
|
172
173
|
return app
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
async function startManagementApi (configManager, runtimeApiClient
|
|
176
|
+
async function startManagementApi (configManager, runtimeApiClient) {
|
|
176
177
|
const runtimePID = process.pid
|
|
177
178
|
|
|
178
179
|
let socketPath = null
|
|
@@ -192,8 +193,7 @@ async function startManagementApi (configManager, runtimeApiClient, loggingPort)
|
|
|
192
193
|
|
|
193
194
|
const managementApi = await createManagementApi(
|
|
194
195
|
configManager,
|
|
195
|
-
runtimeApiClient
|
|
196
|
-
loggingPort
|
|
196
|
+
runtimeApiClient
|
|
197
197
|
)
|
|
198
198
|
|
|
199
199
|
if (platform() !== 'win32') {
|
package/lib/schema.js
CHANGED
|
@@ -106,24 +106,41 @@ const platformaticRuntimeSchema = {
|
|
|
106
106
|
}
|
|
107
107
|
},
|
|
108
108
|
undici: {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
},
|
|
113
|
-
interceptors: {
|
|
114
|
-
type: 'array',
|
|
115
|
-
items: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties: {
|
|
111
|
+
agentOptions: {
|
|
116
112
|
type: 'object',
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
113
|
+
additionalProperties: true
|
|
114
|
+
},
|
|
115
|
+
interceptors: {
|
|
116
|
+
anyOf: [{
|
|
117
|
+
type: 'array',
|
|
118
|
+
items: {
|
|
119
|
+
$ref: '#/$defs/undiciInterceptor'
|
|
124
120
|
}
|
|
125
|
-
},
|
|
126
|
-
|
|
121
|
+
}, {
|
|
122
|
+
type: 'object',
|
|
123
|
+
properties: {
|
|
124
|
+
Client: {
|
|
125
|
+
type: 'array',
|
|
126
|
+
items: {
|
|
127
|
+
$ref: '#/$defs/undiciInterceptor'
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
Pool: {
|
|
131
|
+
type: 'array',
|
|
132
|
+
items: {
|
|
133
|
+
$ref: '#/$defs/undiciInterceptor'
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
Agent: {
|
|
137
|
+
type: 'array',
|
|
138
|
+
items: {
|
|
139
|
+
$ref: '#/$defs/undiciInterceptor'
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}]
|
|
127
144
|
}
|
|
128
145
|
}
|
|
129
146
|
},
|
|
@@ -144,7 +161,22 @@ const platformaticRuntimeSchema = {
|
|
|
144
161
|
{ required: ['autoload', 'entrypoint'] },
|
|
145
162
|
{ required: ['services', 'entrypoint'] }
|
|
146
163
|
],
|
|
147
|
-
additionalProperties: false
|
|
164
|
+
additionalProperties: false,
|
|
165
|
+
$defs: {
|
|
166
|
+
undiciInterceptor: {
|
|
167
|
+
type: 'object',
|
|
168
|
+
properties: {
|
|
169
|
+
module: {
|
|
170
|
+
type: 'string'
|
|
171
|
+
},
|
|
172
|
+
options: {
|
|
173
|
+
type: 'object',
|
|
174
|
+
additionalProperties: true
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
required: ['module', 'options']
|
|
178
|
+
}
|
|
179
|
+
}
|
|
148
180
|
}
|
|
149
181
|
|
|
150
182
|
module.exports.schema = platformaticRuntimeSchema
|
package/lib/start.js
CHANGED
|
@@ -44,21 +44,10 @@ async function startWithConfig (configManager, env = process.env) {
|
|
|
44
44
|
// The configManager cannot be transferred to the worker, so remove it.
|
|
45
45
|
delete config.configManager
|
|
46
46
|
|
|
47
|
-
let mainLoggingPort = null
|
|
48
|
-
let childLoggingPort = config.loggingPort
|
|
49
|
-
|
|
50
|
-
if (!childLoggingPort && config.managementApi) {
|
|
51
|
-
const { port1, port2 } = new MessageChannel()
|
|
52
|
-
mainLoggingPort = port1
|
|
53
|
-
childLoggingPort = port2
|
|
54
|
-
|
|
55
|
-
config.loggingPort = childLoggingPort
|
|
56
|
-
}
|
|
57
|
-
|
|
58
47
|
const worker = new Worker(kWorkerFile, {
|
|
59
48
|
/* c8 ignore next */
|
|
60
49
|
execArgv: config.hotReload ? kWorkerExecArgv : [],
|
|
61
|
-
transferList:
|
|
50
|
+
transferList: config.loggingPort ? [config.loggingPort] : [],
|
|
62
51
|
workerData: { config, dirname },
|
|
63
52
|
env
|
|
64
53
|
})
|
|
@@ -116,10 +105,10 @@ async function startWithConfig (configManager, env = process.env) {
|
|
|
116
105
|
if (config.managementApi) {
|
|
117
106
|
managementApi = await startManagementApi(
|
|
118
107
|
configManager,
|
|
119
|
-
runtimeApiClient
|
|
120
|
-
mainLoggingPort
|
|
108
|
+
runtimeApiClient
|
|
121
109
|
)
|
|
122
110
|
runtimeApiClient.managementApi = managementApi
|
|
111
|
+
runtimeApiClient.startCollectingMetrics()
|
|
123
112
|
}
|
|
124
113
|
|
|
125
114
|
return runtimeApiClient
|
package/lib/upgrade.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { join } = require('path')
|
|
4
|
+
const pkg = require('../package.json')
|
|
5
|
+
|
|
6
|
+
module.exports = async function upgrade (config, version) {
|
|
7
|
+
const { semgrator } = await import('semgrator')
|
|
8
|
+
|
|
9
|
+
const iterator = semgrator({
|
|
10
|
+
version,
|
|
11
|
+
path: join(__dirname, 'versions'),
|
|
12
|
+
input: config,
|
|
13
|
+
logger: this.logger.child({ name: '@platformatic/runtime' })
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
let result
|
|
17
|
+
|
|
18
|
+
for await (const updated of iterator) {
|
|
19
|
+
result = updated.result
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
result.$schema = `https://platformatic.dev/schemas/v${pkg.version}/runtime`
|
|
23
|
+
|
|
24
|
+
return result
|
|
25
|
+
}
|
package/lib/worker.js
CHANGED
|
@@ -4,7 +4,6 @@ const inspector = require('node:inspector')
|
|
|
4
4
|
const { tmpdir } = require('node:os')
|
|
5
5
|
const { register, createRequire } = require('node:module')
|
|
6
6
|
const { isatty } = require('node:tty')
|
|
7
|
-
const { pathToFileURL } = require('node:url')
|
|
8
7
|
const { join } = require('node:path')
|
|
9
8
|
const {
|
|
10
9
|
MessageChannel,
|
|
@@ -17,6 +16,7 @@ const pretty = require('pino-pretty')
|
|
|
17
16
|
const { setGlobalDispatcher, Agent } = require('undici')
|
|
18
17
|
const RuntimeApi = require('./api')
|
|
19
18
|
const { MessagePortWritable } = require('./message-port-writable')
|
|
19
|
+
const loadInterceptors = require('./interceptors')
|
|
20
20
|
let loaderPort
|
|
21
21
|
|
|
22
22
|
const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'runtimes')
|
|
@@ -64,7 +64,7 @@ function createLogger (config) {
|
|
|
64
64
|
const logsPath = join(PLATFORMATIC_TMP_DIR, process.pid.toString(), 'logs')
|
|
65
65
|
const pinoRoll = pino.transport({
|
|
66
66
|
target: 'pino-roll',
|
|
67
|
-
options: { file: logsPath, size: '5m', mkdir: true }
|
|
67
|
+
options: { file: logsPath, mode: 0o600, size: '5m', mkdir: true }
|
|
68
68
|
})
|
|
69
69
|
multiStream.add({ level: 'trace', stream: pinoRoll })
|
|
70
70
|
}
|
|
@@ -105,18 +105,6 @@ process.on('unhandledRejection', (err) => {
|
|
|
105
105
|
}
|
|
106
106
|
})
|
|
107
107
|
|
|
108
|
-
async function loadInterceptor (_require, module, options) {
|
|
109
|
-
const url = pathToFileURL(_require.resolve(module))
|
|
110
|
-
const interceptor = (await import(url)).default
|
|
111
|
-
return interceptor(options)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function loadInterceptors (_require, interceptors) {
|
|
115
|
-
return Promise.all(interceptors.map(async ({ module, options }) => {
|
|
116
|
-
return loadInterceptor(_require, module, options)
|
|
117
|
-
}))
|
|
118
|
-
}
|
|
119
|
-
|
|
120
108
|
async function main () {
|
|
121
109
|
const { inspectorOptions } = workerData.config
|
|
122
110
|
|
|
@@ -130,7 +118,7 @@ async function main () {
|
|
|
130
118
|
}
|
|
131
119
|
|
|
132
120
|
const interceptors = {}
|
|
133
|
-
|
|
121
|
+
const composedInterceptors = []
|
|
134
122
|
if (config.undici?.interceptors) {
|
|
135
123
|
const _require = createRequire(join(workerData.dirname, 'package.json'))
|
|
136
124
|
for (const key of ['Agent', 'Pool', 'Client']) {
|
|
@@ -138,6 +126,10 @@ async function main () {
|
|
|
138
126
|
interceptors[key] = await loadInterceptors(_require, config.undici.interceptors[key])
|
|
139
127
|
}
|
|
140
128
|
}
|
|
129
|
+
|
|
130
|
+
if (Array.isArray(config.undici.interceptors)) {
|
|
131
|
+
composedInterceptors.push(...await loadInterceptors(_require, config.undici.interceptors))
|
|
132
|
+
}
|
|
141
133
|
}
|
|
142
134
|
|
|
143
135
|
const globalDispatcher = new Agent({
|
|
@@ -146,7 +138,7 @@ async function main () {
|
|
|
146
138
|
})
|
|
147
139
|
setGlobalDispatcher(globalDispatcher)
|
|
148
140
|
|
|
149
|
-
const runtime = new RuntimeApi(workerData.config, logger, loaderPort)
|
|
141
|
+
const runtime = new RuntimeApi(workerData.config, logger, loaderPort, composedInterceptors)
|
|
150
142
|
runtime.startListening(parentPort)
|
|
151
143
|
|
|
152
144
|
parentPort.postMessage('plt:init')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -18,51 +18,56 @@
|
|
|
18
18
|
"homepage": "https://github.com/platformatic/platformatic#readme",
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@fastify/express": "^2.3.0",
|
|
21
|
+
"@fastify/formbody": "^7.4.0",
|
|
21
22
|
"@matteo.collina/tspl": "^0.1.1",
|
|
22
23
|
"borp": "^0.10.0",
|
|
23
24
|
"c8": "^9.1.0",
|
|
24
25
|
"execa": "^8.0.1",
|
|
25
|
-
"express": "^4.18.
|
|
26
|
+
"express": "^4.18.3",
|
|
26
27
|
"fast-jwt": "^3.3.3",
|
|
27
28
|
"pino-abstract-transport": "^1.1.0",
|
|
28
29
|
"snazzy": "^9.0.0",
|
|
29
30
|
"split2": "^4.2.0",
|
|
30
31
|
"standard": "^17.1.0",
|
|
31
|
-
"tsd": "^0.30.
|
|
32
|
-
"typescript": "^5.
|
|
33
|
-
"undici-
|
|
32
|
+
"tsd": "^0.30.7",
|
|
33
|
+
"typescript": "^5.4.2",
|
|
34
|
+
"undici-oidc-interceptor": "^0.5.0",
|
|
35
|
+
"why-is-node-running": "^2.2.2",
|
|
34
36
|
"ws": "^8.16.0",
|
|
35
|
-
"@platformatic/sql-graphql": "1.
|
|
36
|
-
"@platformatic/sql-mapper": "1.
|
|
37
|
+
"@platformatic/sql-graphql": "1.28.0",
|
|
38
|
+
"@platformatic/sql-mapper": "1.28.0"
|
|
37
39
|
},
|
|
38
40
|
"dependencies": {
|
|
39
41
|
"@fastify/error": "^3.4.1",
|
|
40
42
|
"@fastify/websocket": "^9.0.0",
|
|
41
43
|
"@hapi/topo": "^6.0.2",
|
|
42
44
|
"boring-name-generator": "^1.0.3",
|
|
43
|
-
"
|
|
45
|
+
"change-case-all": "^2.1.0",
|
|
46
|
+
"close-with-grace": "^1.3.0",
|
|
44
47
|
"commist": "^3.2.0",
|
|
45
48
|
"debounce": "^2.0.0",
|
|
46
49
|
"desm": "^1.3.1",
|
|
47
50
|
"es-main": "^1.3.0",
|
|
48
51
|
"fastest-levenshtein": "^1.0.16",
|
|
49
|
-
"fastify": "^4.26.
|
|
50
|
-
"fastify-undici-dispatcher": "^0.
|
|
52
|
+
"fastify": "^4.26.2",
|
|
53
|
+
"fastify-undici-dispatcher": "^0.6.0",
|
|
51
54
|
"graphql": "^16.8.1",
|
|
52
55
|
"help-me": "^5.0.0",
|
|
53
56
|
"minimist": "^1.2.8",
|
|
54
|
-
"pino": "^8.
|
|
57
|
+
"pino": "^8.19.0",
|
|
55
58
|
"pino-pretty": "^10.3.1",
|
|
59
|
+
"semgrator": "^0.3.0",
|
|
56
60
|
"pino-roll": "1.0.0-rc.1",
|
|
57
|
-
"
|
|
61
|
+
"tail-file-stream": "^0.1.0",
|
|
62
|
+
"undici": "^6.9.0",
|
|
58
63
|
"why-is-node-running": "^2.2.2",
|
|
59
|
-
"@platformatic/
|
|
60
|
-
"@platformatic/config": "1.
|
|
61
|
-
"@platformatic/generators": "1.
|
|
62
|
-
"@platformatic/
|
|
63
|
-
"@platformatic/
|
|
64
|
-
"@platformatic/utils": "1.
|
|
65
|
-
"@platformatic/telemetry": "1.
|
|
64
|
+
"@platformatic/db": "1.28.0",
|
|
65
|
+
"@platformatic/config": "1.28.0",
|
|
66
|
+
"@platformatic/generators": "1.28.0",
|
|
67
|
+
"@platformatic/service": "1.28.0",
|
|
68
|
+
"@platformatic/composer": "1.28.0",
|
|
69
|
+
"@platformatic/utils": "1.28.0",
|
|
70
|
+
"@platformatic/telemetry": "1.28.0"
|
|
66
71
|
},
|
|
67
72
|
"standard": {
|
|
68
73
|
"ignore": [
|