@platformatic/runtime 3.13.0 → 3.14.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/config.d.ts +71 -4
- package/index.d.ts +6 -1
- package/index.js +1 -1
- package/lib/config.js +118 -72
- package/lib/dynamic-workers-scaler.js +218 -0
- package/lib/errors.js +21 -0
- package/lib/logger.js +4 -2
- package/lib/prom-server.js +2 -4
- package/lib/runtime.js +448 -476
- package/lib/scaling-algorithm.js +26 -31
- package/lib/worker/controller.js +15 -8
- package/lib/worker/health-signals.js +80 -0
- package/lib/worker/main.js +8 -5
- package/lib/worker/messaging.js +0 -6
- package/lib/worker/round-robin-map.js +39 -41
- package/lib/worker/symbols.js +4 -1
- package/package.json +15 -15
- package/schema.json +180 -22
package/lib/scaling-algorithm.js
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export const scaleUpELUThreshold = 0.8
|
|
2
|
+
export const scaleDownELUThreshold = 0.2
|
|
3
|
+
export const scaleUpTimeWindow = 10_000
|
|
4
|
+
export const scaleDownTimeWindow = 60_000
|
|
5
|
+
|
|
6
|
+
export class ScalingAlgorithm {
|
|
4
7
|
#maxTotalWorkers
|
|
5
|
-
#scaleUpTimeWindowSec
|
|
6
|
-
#scaleDownTimeWindowSec
|
|
7
8
|
#appsMetrics
|
|
8
9
|
#appsConfigs
|
|
9
10
|
|
|
10
11
|
constructor (options = {}) {
|
|
11
|
-
this.#scaleUpELU = options.scaleUpELU ?? 0.8
|
|
12
|
-
this.#scaleDownELU = options.scaleDownELU ?? 0.2
|
|
13
12
|
this.#maxTotalWorkers = options.maxTotalWorkers ?? Infinity
|
|
14
|
-
this.#scaleUpTimeWindowSec = options.scaleUpTimeWindowSec ?? 10
|
|
15
|
-
this.#scaleDownTimeWindowSec = options.scaleDownTimeWindowSec ?? 60
|
|
16
13
|
this.#appsConfigs = options.applications ?? {}
|
|
17
|
-
|
|
18
14
|
this.#appsMetrics = {}
|
|
19
15
|
}
|
|
20
16
|
|
|
17
|
+
addApplication (id, config) {
|
|
18
|
+
this.#appsConfigs[id] = config
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
removeApplication (id) {
|
|
22
|
+
delete this.#appsConfigs[id]
|
|
23
|
+
delete this.#appsMetrics[id]
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
addWorkerHealthInfo (healthInfo) {
|
|
22
27
|
const { workerId, applicationId, elu, heapUsed } = healthInfo
|
|
23
28
|
const timestamp = Date.now()
|
|
@@ -50,7 +55,7 @@ class ScalingAlgorithm {
|
|
|
50
55
|
appsInfo.push({
|
|
51
56
|
applicationId,
|
|
52
57
|
workersCount,
|
|
53
|
-
avgHeapUsed: heapUsed
|
|
58
|
+
avgHeapUsed: heapUsed
|
|
54
59
|
})
|
|
55
60
|
|
|
56
61
|
totalWorkersCount += workersCount
|
|
@@ -112,9 +117,7 @@ class ScalingAlgorithm {
|
|
|
112
117
|
if (workersCount >= appMaxWorkers) continue
|
|
113
118
|
if (avgHeapUsed >= totalAvailableMemory) continue
|
|
114
119
|
|
|
115
|
-
const isScaled = recommendations.some(
|
|
116
|
-
r => r.applicationId === applicationId
|
|
117
|
-
)
|
|
120
|
+
const isScaled = recommendations.some(r => r.applicationId === applicationId)
|
|
118
121
|
if (isScaled) continue
|
|
119
122
|
|
|
120
123
|
const recommendation = this.#getApplicationScaleRecommendation(applicationId)
|
|
@@ -122,10 +125,8 @@ class ScalingAlgorithm {
|
|
|
122
125
|
|
|
123
126
|
if (
|
|
124
127
|
!scaleUpCandidate ||
|
|
125
|
-
|
|
126
|
-
(recommendation.scaleUpELU === scaleUpCandidate.scaleUpELU &&
|
|
127
|
-
workersCount < scaleUpCandidate.workersCount
|
|
128
|
-
)
|
|
128
|
+
recommendation.scaleUpELU > scaleUpCandidate.scaleUpELU ||
|
|
129
|
+
(recommendation.scaleUpELU === scaleUpCandidate.scaleUpELU && workersCount < scaleUpCandidate.workersCount)
|
|
129
130
|
) {
|
|
130
131
|
scaleUpCandidate = {
|
|
131
132
|
applicationId,
|
|
@@ -186,8 +187,8 @@ class ScalingAlgorithm {
|
|
|
186
187
|
count++
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
const elu = Math.round(eluSum / count * 100) / 100
|
|
190
|
-
const heapUsed = Math.round(heapUsedSum / count * 100) / 100
|
|
190
|
+
const elu = Math.round((eluSum / count) * 100) / 100
|
|
191
|
+
const heapUsed = Math.round((heapUsedSum / count) * 100) / 100
|
|
191
192
|
return { elu, heapUsed }
|
|
192
193
|
}
|
|
193
194
|
|
|
@@ -226,28 +227,22 @@ class ScalingAlgorithm {
|
|
|
226
227
|
}
|
|
227
228
|
|
|
228
229
|
#getMetricsTimeWindow () {
|
|
229
|
-
return Math.max(
|
|
230
|
+
return Math.max(scaleUpTimeWindow, scaleDownTimeWindow) * 1000
|
|
230
231
|
}
|
|
231
232
|
|
|
232
233
|
#getApplicationScaleRecommendation (applicationId) {
|
|
233
|
-
const { elu: scaleUpELU } = this.#calculateAppAvgMetrics(applicationId, {
|
|
234
|
-
|
|
235
|
-
})
|
|
236
|
-
const { elu: scaleDownELU } = this.#calculateAppAvgMetrics(applicationId, {
|
|
237
|
-
timeWindow: this.#scaleDownTimeWindowSec * 1000
|
|
238
|
-
})
|
|
234
|
+
const { elu: scaleUpELU } = this.#calculateAppAvgMetrics(applicationId, { timeWindow: scaleUpTimeWindow })
|
|
235
|
+
const { elu: scaleDownELU } = this.#calculateAppAvgMetrics(applicationId, { timeWindow: scaleDownTimeWindow })
|
|
239
236
|
const { heapUsed: avgHeapUsage } = this.#calculateAppAvgMetrics(applicationId)
|
|
240
237
|
|
|
241
238
|
let recommendation = null
|
|
242
|
-
if (scaleUpELU >
|
|
239
|
+
if (scaleUpELU > scaleUpELUThreshold) {
|
|
243
240
|
recommendation = 'scaleUp'
|
|
244
241
|
}
|
|
245
|
-
if (scaleDownELU <
|
|
242
|
+
if (scaleDownELU < scaleDownELUThreshold) {
|
|
246
243
|
recommendation = 'scaleDown'
|
|
247
244
|
}
|
|
248
245
|
|
|
249
246
|
return { recommendation, scaleUpELU, scaleDownELU, avgHeapUsage }
|
|
250
247
|
}
|
|
251
248
|
}
|
|
252
|
-
|
|
253
|
-
export default ScalingAlgorithm
|
package/lib/worker/controller.js
CHANGED
|
@@ -26,10 +26,7 @@ function fetchApplicationUrl (application, key) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function handleUnhandled (app, type, err) {
|
|
29
|
-
const label =
|
|
30
|
-
workerData.worker.count > 1
|
|
31
|
-
? `worker ${workerData.worker.index} of the application "${workerData.applicationConfig.id}"`
|
|
32
|
-
: `application "${workerData.applicationConfig.id}"`
|
|
29
|
+
const label = `worker ${workerData.worker.index} of the application "${workerData.applicationConfig.id}"`
|
|
33
30
|
|
|
34
31
|
globalThis.platformatic.logger.error({ err: ensureLoggableError(err) }, `The ${label} threw an ${type}.`)
|
|
35
32
|
|
|
@@ -164,7 +161,7 @@ export class Controller extends EventEmitter {
|
|
|
164
161
|
return
|
|
165
162
|
}
|
|
166
163
|
|
|
167
|
-
this
|
|
164
|
+
this.#updateCapabilityStatus('starting')
|
|
168
165
|
this.emit('starting')
|
|
169
166
|
|
|
170
167
|
if (this.#watch) {
|
|
@@ -188,7 +185,7 @@ export class Controller extends EventEmitter {
|
|
|
188
185
|
this.#listening = listen
|
|
189
186
|
/* c8 ignore next 5 */
|
|
190
187
|
} catch (err) {
|
|
191
|
-
this
|
|
188
|
+
this.#updateCapabilityStatus('start:error')
|
|
192
189
|
this.emit('start:error', err)
|
|
193
190
|
|
|
194
191
|
this.capability.log({ message: err.message, level: 'debug' })
|
|
@@ -199,7 +196,7 @@ export class Controller extends EventEmitter {
|
|
|
199
196
|
this.#started = true
|
|
200
197
|
this.#starting = false
|
|
201
198
|
|
|
202
|
-
this
|
|
199
|
+
this.#updateCapabilityStatus('started')
|
|
203
200
|
this.emit('started')
|
|
204
201
|
}
|
|
205
202
|
|
|
@@ -221,7 +218,7 @@ export class Controller extends EventEmitter {
|
|
|
221
218
|
this.#starting = false
|
|
222
219
|
this.#listening = false
|
|
223
220
|
|
|
224
|
-
this
|
|
221
|
+
this.#updateCapabilityStatus('stopped')
|
|
225
222
|
this.emit('stopped')
|
|
226
223
|
}
|
|
227
224
|
|
|
@@ -336,4 +333,14 @@ export class Controller extends EventEmitter {
|
|
|
336
333
|
}
|
|
337
334
|
})
|
|
338
335
|
}
|
|
336
|
+
|
|
337
|
+
#updateCapabilityStatus (status) {
|
|
338
|
+
if (typeof this.capability.updateStatus === 'function') {
|
|
339
|
+
this.capability.updateStatus(status)
|
|
340
|
+
} else {
|
|
341
|
+
// This is horrible but needed for backward compatibility
|
|
342
|
+
this.capability.status = status
|
|
343
|
+
this.capability.emit(status)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
339
346
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HealthSignalMustBeObjectError,
|
|
3
|
+
HealthSignalTypeMustBeStringError
|
|
4
|
+
} from '../errors.js'
|
|
5
|
+
|
|
6
|
+
export class HealthSignalsQueue {
|
|
7
|
+
#size
|
|
8
|
+
#values
|
|
9
|
+
|
|
10
|
+
constructor (options = {}) {
|
|
11
|
+
this.#size = options.size ?? 100
|
|
12
|
+
this.#values = []
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
add (value) {
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
for (const v of value) {
|
|
18
|
+
this.#values.push(v)
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
this.#values.push(value)
|
|
22
|
+
}
|
|
23
|
+
if (this.#values.length > this.#size) {
|
|
24
|
+
this.#values.splice(0, this.#values.length - this.#size)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getAll () {
|
|
29
|
+
const values = this.#values
|
|
30
|
+
this.#values = []
|
|
31
|
+
return values
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function initHealthSignalsApi (options = {}) {
|
|
36
|
+
const queue = new HealthSignalsQueue()
|
|
37
|
+
const timeout = options.timeout ?? 1000
|
|
38
|
+
const workerId = options.workerId
|
|
39
|
+
|
|
40
|
+
let isSending = false
|
|
41
|
+
let promise = null
|
|
42
|
+
|
|
43
|
+
async function sendHealthSignal (signal) {
|
|
44
|
+
if (typeof signal !== 'object') {
|
|
45
|
+
throw new HealthSignalMustBeObjectError()
|
|
46
|
+
}
|
|
47
|
+
if (typeof signal.type !== 'string') {
|
|
48
|
+
throw new HealthSignalTypeMustBeStringError(signal.type)
|
|
49
|
+
}
|
|
50
|
+
if (!signal.timestamp || typeof signal.timestamp !== 'number') {
|
|
51
|
+
signal.timestamp = Date.now()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
queue.add(signal)
|
|
55
|
+
|
|
56
|
+
if (!isSending) {
|
|
57
|
+
isSending = true
|
|
58
|
+
promise = new Promise((resolve, reject) => {
|
|
59
|
+
setTimeout(async () => {
|
|
60
|
+
isSending = false
|
|
61
|
+
try {
|
|
62
|
+
const signals = queue.getAll()
|
|
63
|
+
await globalThis.platformatic.itc.send('sendHealthSignals', {
|
|
64
|
+
workerId,
|
|
65
|
+
signals
|
|
66
|
+
})
|
|
67
|
+
} catch (err) {
|
|
68
|
+
reject(err)
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
resolve()
|
|
72
|
+
}, timeout)
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return promise
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
globalThis.platformatic.sendHealthSignal = sendHealthSignal
|
|
80
|
+
}
|
package/lib/worker/main.js
CHANGED
|
@@ -20,6 +20,7 @@ import { setDispatcher } from './interceptors.js'
|
|
|
20
20
|
import { setupITC } from './itc.js'
|
|
21
21
|
import { SharedContext } from './shared-context.js'
|
|
22
22
|
import { kId, kITC, kStderrMarker } from './symbols.js'
|
|
23
|
+
import { initHealthSignalsApi } from './health-signals.js'
|
|
23
24
|
|
|
24
25
|
class ForwardingEventEmitter extends EventEmitter {
|
|
25
26
|
emitAndNotify (event, ...args) {
|
|
@@ -59,13 +60,10 @@ function createLogger () {
|
|
|
59
60
|
const pinoOptions = {
|
|
60
61
|
level: 'trace',
|
|
61
62
|
name: workerData.applicationConfig.id,
|
|
63
|
+
base: { pid: process.pid, hostname: hostname(), worker: workerData.worker.index },
|
|
62
64
|
...workerData.config.logger
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
if (workerData.worker?.count > 1) {
|
|
66
|
-
pinoOptions.base = { pid: process.pid, hostname: hostname(), worker: workerData.worker.index }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
67
|
if (pinoOptions.formatters) {
|
|
70
68
|
pinoOptions.formatters = buildPinoFormatters(pinoOptions.formatters)
|
|
71
69
|
}
|
|
@@ -167,7 +165,7 @@ async function main () {
|
|
|
167
165
|
const controller = new Controller(
|
|
168
166
|
runtimeConfig,
|
|
169
167
|
applicationConfig,
|
|
170
|
-
workerData.worker.
|
|
168
|
+
workerData.worker.index,
|
|
171
169
|
serverConfig,
|
|
172
170
|
metricsConfig
|
|
173
171
|
)
|
|
@@ -193,6 +191,11 @@ async function main () {
|
|
|
193
191
|
globalThis[kITC] = itc
|
|
194
192
|
globalThis.platformatic.itc = itc
|
|
195
193
|
|
|
194
|
+
initHealthSignalsApi({
|
|
195
|
+
workerId: workerData.worker.id,
|
|
196
|
+
applicationId: applicationConfig.id
|
|
197
|
+
})
|
|
198
|
+
|
|
196
199
|
itc.notify('init')
|
|
197
200
|
}
|
|
198
201
|
|
package/lib/worker/messaging.js
CHANGED
|
@@ -179,12 +179,8 @@ export class MessagingITC extends ITC {
|
|
|
179
179
|
// Create a brand new map
|
|
180
180
|
this.#workers = new RoundRobinMap()
|
|
181
181
|
|
|
182
|
-
const instances = []
|
|
183
182
|
for (const [application, workers] of event.data) {
|
|
184
183
|
const count = workers.length
|
|
185
|
-
const next = Math.floor(Math.random() * count)
|
|
186
|
-
|
|
187
|
-
instances.push({ id: application, next, workers: count })
|
|
188
184
|
|
|
189
185
|
for (let i = 0; i < count; i++) {
|
|
190
186
|
const worker = workers[i]
|
|
@@ -194,8 +190,6 @@ export class MessagingITC extends ITC {
|
|
|
194
190
|
this.#workers.set(`${application}:${i}`, { ...worker, channel })
|
|
195
191
|
}
|
|
196
192
|
}
|
|
197
|
-
|
|
198
|
-
this.#workers.configure(instances)
|
|
199
193
|
}
|
|
200
194
|
|
|
201
195
|
async #handleNotification (messageEvent) {
|
|
@@ -1,62 +1,60 @@
|
|
|
1
1
|
export class RoundRobinMap extends Map {
|
|
2
2
|
#instances
|
|
3
3
|
|
|
4
|
-
constructor (
|
|
5
|
-
super(
|
|
6
|
-
this.#instances =
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
get configuration () {
|
|
10
|
-
return { ...this.#instances }
|
|
4
|
+
constructor () {
|
|
5
|
+
super()
|
|
6
|
+
this.#instances = {}
|
|
11
7
|
}
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
set (key, worker) {
|
|
10
|
+
const hasKey = super.has(key)
|
|
11
|
+
if (!hasKey) {
|
|
12
|
+
const application = key.split(':')[0]
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
if (!this.#instances[application]) {
|
|
15
|
+
this.#instances[application] = { keys: [] }
|
|
16
|
+
}
|
|
17
|
+
this.#instances[application].next = null
|
|
18
|
+
this.#instances[application].keys.push(key)
|
|
18
19
|
}
|
|
20
|
+
|
|
21
|
+
return super.set(key, worker)
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
delete (key) {
|
|
25
|
+
const removed = super.delete(key)
|
|
26
|
+
|
|
27
|
+
if (removed) {
|
|
28
|
+
const application = key.split(':')[0]
|
|
29
|
+
|
|
30
|
+
if (this.#instances[application]) {
|
|
31
|
+
const keys = this.#instances[application].keys
|
|
32
|
+
if (keys.length <= 1) {
|
|
33
|
+
delete this.#instances[application]
|
|
34
|
+
} else {
|
|
35
|
+
const keys = this.#instances[application].keys
|
|
36
|
+
keys.splice(keys.indexOf(key), 1)
|
|
37
|
+
this.#instances[application].next = null
|
|
38
|
+
}
|
|
39
|
+
}
|
|
24
40
|
}
|
|
25
41
|
|
|
26
|
-
return
|
|
42
|
+
return removed
|
|
27
43
|
}
|
|
28
44
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
throw new Error(`Application ${application} is not configured.`)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
this.#instances[application].count = count
|
|
45
|
+
getKeys (application) {
|
|
46
|
+
return this.#instances[application]?.keys ?? []
|
|
35
47
|
}
|
|
36
48
|
|
|
37
49
|
next (application) {
|
|
38
|
-
if (!this.#instances[application])
|
|
39
|
-
return null
|
|
40
|
-
}
|
|
50
|
+
if (!this.#instances[application]) return
|
|
41
51
|
|
|
42
|
-
let
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Try count times to get the next worker. This is to handle the case where a worker is being restarted.
|
|
46
|
-
for (let i = 0; i < count; i++) {
|
|
47
|
-
const current = next++
|
|
48
|
-
if (next >= count) {
|
|
49
|
-
next = 0
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
worker = this.get(`${application}:${current}`)
|
|
53
|
-
|
|
54
|
-
if (worker) {
|
|
55
|
-
break
|
|
56
|
-
}
|
|
52
|
+
let { next, keys } = this.#instances[application]
|
|
53
|
+
if (next === null) {
|
|
54
|
+
next = Math.floor(Math.random() * keys.length)
|
|
57
55
|
}
|
|
56
|
+
this.#instances[application].next = (next + 1) % keys.length
|
|
58
57
|
|
|
59
|
-
this
|
|
60
|
-
return worker
|
|
58
|
+
return this.get(keys[next])
|
|
61
59
|
}
|
|
62
60
|
}
|
package/lib/worker/symbols.js
CHANGED
|
@@ -5,10 +5,13 @@ export const kApplicationId = Symbol.for('plt.runtime.application.id')
|
|
|
5
5
|
export const kWorkerId = Symbol.for('plt.runtime.worker.id')
|
|
6
6
|
export const kITC = Symbol.for('plt.runtime.itc')
|
|
7
7
|
export const kHealthCheckTimer = Symbol.for('plt.runtime.worker.healthCheckTimer')
|
|
8
|
+
export const kHealthMetricsTimer = Symbol.for('plt.runtime.worker.healthMetricsTimer')
|
|
8
9
|
export const kWorkerStatus = Symbol('plt.runtime.worker.status')
|
|
10
|
+
export const kWorkerHealthSignals = Symbol.for('plt.runtime.worker.healthSignals')
|
|
9
11
|
export const kWorkerStartTime = Symbol.for('plt.runtime.worker.startTime')
|
|
10
12
|
export const kInterceptors = Symbol.for('plt.runtime.worker.interceptors')
|
|
11
|
-
export const
|
|
13
|
+
export const kLastHealthCheckELU = Symbol.for('plt.runtime.worker.lastHealthCheckELU')
|
|
14
|
+
export const kLastVerticalScalerELU = Symbol.for('plt.runtime.worker.lastVerticalScalerELU')
|
|
12
15
|
|
|
13
16
|
// This string marker should be safe to use since it belongs to Unicode private area
|
|
14
17
|
export const kStderrMarker = '\ue002'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"typescript": "^5.5.4",
|
|
36
36
|
"undici-oidc-interceptor": "^0.5.0",
|
|
37
37
|
"why-is-node-running": "^2.2.2",
|
|
38
|
-
"@platformatic/
|
|
39
|
-
"@platformatic/
|
|
40
|
-
"@platformatic/
|
|
41
|
-
"@platformatic/
|
|
42
|
-
"@platformatic/service": "3.
|
|
43
|
-
"@platformatic/sql-graphql": "3.
|
|
44
|
-
"@platformatic/sql-mapper": "3.
|
|
45
|
-
"@platformatic/wattpm-pprof-capture": "3.
|
|
38
|
+
"@platformatic/db": "3.14.0",
|
|
39
|
+
"@platformatic/gateway": "3.14.0",
|
|
40
|
+
"@platformatic/composer": "3.14.0",
|
|
41
|
+
"@platformatic/node": "3.14.0",
|
|
42
|
+
"@platformatic/service": "3.14.0",
|
|
43
|
+
"@platformatic/sql-graphql": "3.14.0",
|
|
44
|
+
"@platformatic/sql-mapper": "3.14.0",
|
|
45
|
+
"@platformatic/wattpm-pprof-capture": "3.14.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@fastify/accepts": "^5.0.0",
|
|
@@ -73,12 +73,12 @@
|
|
|
73
73
|
"undici": "^7.0.0",
|
|
74
74
|
"undici-thread-interceptor": "^0.15.0",
|
|
75
75
|
"ws": "^8.16.0",
|
|
76
|
-
"@platformatic/basic": "3.
|
|
77
|
-
"@platformatic/
|
|
78
|
-
"@platformatic/
|
|
79
|
-
"@platformatic/
|
|
80
|
-
"@platformatic/
|
|
81
|
-
"@platformatic/
|
|
76
|
+
"@platformatic/basic": "3.14.0",
|
|
77
|
+
"@platformatic/metrics": "3.14.0",
|
|
78
|
+
"@platformatic/foundation": "3.14.0",
|
|
79
|
+
"@platformatic/generators": "3.14.0",
|
|
80
|
+
"@platformatic/telemetry": "3.14.0",
|
|
81
|
+
"@platformatic/itc": "3.14.0"
|
|
82
82
|
},
|
|
83
83
|
"engines": {
|
|
84
84
|
"node": ">=22.19.0"
|