@platformatic/runtime 3.0.0-alpha.6 → 3.0.0-rc.1
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 -0
- package/index.js +24 -3
- package/lib/config.js +81 -2
- package/lib/errors.js +5 -0
- package/lib/management-api.js +57 -26
- package/lib/prom-server.js +11 -3
- package/lib/runtime.js +142 -96
- package/lib/worker/controller.js +23 -8
- package/lib/worker/itc.js +38 -32
- package/lib/worker/main.js +17 -9
- package/package.json +18 -17
- package/schema.json +32 -1
- package/lib/dependencies.js +0 -63
package/lib/worker/itc.js
CHANGED
|
@@ -69,8 +69,8 @@ export async function waitEventFromITC (worker, event) {
|
|
|
69
69
|
return safeHandleInITC(worker, () => once(worker[kITC], event))
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
export function setupITC (
|
|
73
|
-
const messaging = new MessagingITC(
|
|
72
|
+
export function setupITC (controller, application, dispatcher, sharedContext) {
|
|
73
|
+
const messaging = new MessagingITC(controller.appConfig.id, workerData.config)
|
|
74
74
|
|
|
75
75
|
Object.assign(globalThis.platformatic ?? {}, {
|
|
76
76
|
messaging: {
|
|
@@ -80,55 +80,61 @@ export function setupITC (instance, application, dispatcher, sharedContext) {
|
|
|
80
80
|
})
|
|
81
81
|
|
|
82
82
|
const itc = new ITC({
|
|
83
|
-
name:
|
|
83
|
+
name: controller.appConfig.id + '-worker',
|
|
84
84
|
port: parentPort,
|
|
85
85
|
handlers: {
|
|
86
86
|
async start () {
|
|
87
|
-
const status =
|
|
87
|
+
const status = controller.getStatus()
|
|
88
88
|
|
|
89
89
|
if (status === 'starting') {
|
|
90
|
-
await once(
|
|
90
|
+
await once(controller, 'start')
|
|
91
91
|
} else {
|
|
92
92
|
// This gives a chance to a capability to perform custom logic
|
|
93
93
|
globalThis.platformatic.events.emit('start')
|
|
94
94
|
|
|
95
95
|
try {
|
|
96
|
-
await
|
|
96
|
+
await controller.start()
|
|
97
97
|
} catch (e) {
|
|
98
|
-
await
|
|
99
|
-
|
|
98
|
+
await controller.stop(true)
|
|
99
|
+
|
|
100
|
+
// Reply to the runtime that the start failed, so it can cleanup
|
|
101
|
+
once(itc, 'application:worker:start:processed').then(() => {
|
|
102
|
+
closeITC(dispatcher, itc, messaging).catch(() => {})
|
|
103
|
+
})
|
|
100
104
|
|
|
101
105
|
throw ensureLoggableError(e)
|
|
102
106
|
}
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
if (application.entrypoint) {
|
|
106
|
-
await
|
|
110
|
+
await controller.listen()
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
dispatcher.replaceServer(await
|
|
110
|
-
return application.entrypoint ?
|
|
113
|
+
dispatcher.replaceServer(await controller.capability.getDispatchTarget())
|
|
114
|
+
return application.entrypoint ? controller.capability.getUrl() : null
|
|
111
115
|
},
|
|
112
116
|
|
|
113
|
-
async stop () {
|
|
114
|
-
const status =
|
|
117
|
+
async stop ({ force, dependents }) {
|
|
118
|
+
const status = controller.getStatus()
|
|
115
119
|
|
|
116
|
-
if (status === 'starting') {
|
|
117
|
-
await once(
|
|
120
|
+
if (!force && status === 'starting') {
|
|
121
|
+
await once(controller, 'start')
|
|
118
122
|
}
|
|
119
123
|
|
|
120
|
-
if (status.startsWith('start')) {
|
|
124
|
+
if (force || status.startsWith('start')) {
|
|
121
125
|
// This gives a chance to a capability to perform custom logic
|
|
122
126
|
globalThis.platformatic.events.emit('stop')
|
|
123
127
|
|
|
124
|
-
await
|
|
128
|
+
await controller.stop(force, dependents)
|
|
125
129
|
}
|
|
126
130
|
|
|
127
|
-
|
|
131
|
+
once(itc, 'application:worker:stop:processed').then(() => {
|
|
132
|
+
closeITC(dispatcher, itc, messaging).catch(() => {})
|
|
133
|
+
})
|
|
128
134
|
},
|
|
129
135
|
|
|
130
136
|
async build () {
|
|
131
|
-
return
|
|
137
|
+
return controller.capability.build()
|
|
132
138
|
},
|
|
133
139
|
|
|
134
140
|
async removeFromMesh () {
|
|
@@ -136,7 +142,7 @@ export function setupITC (instance, application, dispatcher, sharedContext) {
|
|
|
136
142
|
},
|
|
137
143
|
|
|
138
144
|
inject (injectParams) {
|
|
139
|
-
return
|
|
145
|
+
return controller.capability.inject(injectParams)
|
|
140
146
|
},
|
|
141
147
|
|
|
142
148
|
async updateUndiciInterceptors (undiciConfig) {
|
|
@@ -150,27 +156,27 @@ export function setupITC (instance, application, dispatcher, sharedContext) {
|
|
|
150
156
|
},
|
|
151
157
|
|
|
152
158
|
getStatus () {
|
|
153
|
-
return
|
|
159
|
+
return controller.getStatus()
|
|
154
160
|
},
|
|
155
161
|
|
|
156
162
|
getApplicationInfo () {
|
|
157
|
-
return
|
|
163
|
+
return controller.capability.getInfo()
|
|
158
164
|
},
|
|
159
165
|
|
|
160
166
|
async getApplicationConfig () {
|
|
161
|
-
const current = await
|
|
167
|
+
const current = await controller.capability.getConfig()
|
|
162
168
|
// Remove all undefined keys from the config
|
|
163
169
|
return JSON.parse(JSON.stringify(current))
|
|
164
170
|
},
|
|
165
171
|
|
|
166
172
|
async getApplicationEnv () {
|
|
167
173
|
// Remove all undefined keys from the config
|
|
168
|
-
return JSON.parse(JSON.stringify({ ...process.env, ...(await
|
|
174
|
+
return JSON.parse(JSON.stringify({ ...process.env, ...(await controller.capability.getEnv()) }))
|
|
169
175
|
},
|
|
170
176
|
|
|
171
177
|
async getApplicationOpenAPISchema () {
|
|
172
178
|
try {
|
|
173
|
-
return await
|
|
179
|
+
return await controller.capability.getOpenapiSchema()
|
|
174
180
|
} catch (err) {
|
|
175
181
|
throw new FailedToRetrieveOpenAPISchemaError(application.id, err.message)
|
|
176
182
|
}
|
|
@@ -178,7 +184,7 @@ export function setupITC (instance, application, dispatcher, sharedContext) {
|
|
|
178
184
|
|
|
179
185
|
async getApplicationGraphQLSchema () {
|
|
180
186
|
try {
|
|
181
|
-
return await
|
|
187
|
+
return await controller.capability.getGraphqlSchema()
|
|
182
188
|
} catch (err) {
|
|
183
189
|
throw new FailedToRetrieveGraphQLSchemaError(application.id, err.message)
|
|
184
190
|
}
|
|
@@ -186,7 +192,7 @@ export function setupITC (instance, application, dispatcher, sharedContext) {
|
|
|
186
192
|
|
|
187
193
|
async getApplicationMeta () {
|
|
188
194
|
try {
|
|
189
|
-
return await
|
|
195
|
+
return await controller.capability.getMeta()
|
|
190
196
|
} catch (err) {
|
|
191
197
|
throw new FailedToRetrieveMetaError(application.id, err.message)
|
|
192
198
|
}
|
|
@@ -194,7 +200,7 @@ export function setupITC (instance, application, dispatcher, sharedContext) {
|
|
|
194
200
|
|
|
195
201
|
async getMetrics (format) {
|
|
196
202
|
try {
|
|
197
|
-
return await
|
|
203
|
+
return await controller.getMetrics({ format })
|
|
198
204
|
} catch (err) {
|
|
199
205
|
throw new FailedToRetrieveMetricsError(application.id, err.message)
|
|
200
206
|
}
|
|
@@ -202,7 +208,7 @@ export function setupITC (instance, application, dispatcher, sharedContext) {
|
|
|
202
208
|
|
|
203
209
|
async getHealth () {
|
|
204
210
|
try {
|
|
205
|
-
return await
|
|
211
|
+
return await controller.getHealth()
|
|
206
212
|
} catch (err) {
|
|
207
213
|
throw new FailedToRetrieveHealthError(application.id, err.message)
|
|
208
214
|
}
|
|
@@ -210,7 +216,7 @@ export function setupITC (instance, application, dispatcher, sharedContext) {
|
|
|
210
216
|
|
|
211
217
|
async getCustomHealthCheck () {
|
|
212
218
|
try {
|
|
213
|
-
return await
|
|
219
|
+
return await controller.capability.getCustomHealthCheck()
|
|
214
220
|
} catch (err) {
|
|
215
221
|
throw new FailedToPerformCustomHealthCheckError(application.id, err.message)
|
|
216
222
|
}
|
|
@@ -218,7 +224,7 @@ export function setupITC (instance, application, dispatcher, sharedContext) {
|
|
|
218
224
|
|
|
219
225
|
async getCustomReadinessCheck () {
|
|
220
226
|
try {
|
|
221
|
-
return await
|
|
227
|
+
return await controller.capability.getCustomReadinessCheck()
|
|
222
228
|
} catch (err) {
|
|
223
229
|
throw new FailedToPerformCustomReadinessCheckError(application.id, err.message)
|
|
224
230
|
}
|
|
@@ -234,7 +240,7 @@ export function setupITC (instance, application, dispatcher, sharedContext) {
|
|
|
234
240
|
}
|
|
235
241
|
})
|
|
236
242
|
|
|
237
|
-
|
|
243
|
+
controller.on('changed', () => {
|
|
238
244
|
itc.notify('changed')
|
|
239
245
|
})
|
|
240
246
|
|
package/lib/worker/main.js
CHANGED
|
@@ -166,7 +166,7 @@ async function main () {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
// Create the application
|
|
169
|
-
const
|
|
169
|
+
const controller = new Controller(
|
|
170
170
|
application,
|
|
171
171
|
workerData.worker.count > 1 ? workerData.worker.index : undefined,
|
|
172
172
|
application.telemetry,
|
|
@@ -177,13 +177,23 @@ async function main () {
|
|
|
177
177
|
!!config.watch
|
|
178
178
|
)
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
180
|
+
if (config.exitOnUnhandledErrors) {
|
|
181
|
+
process.on('uncaughtException', handleUnhandled.bind(null, controller, 'uncaught exception'))
|
|
182
|
+
process.on('unhandledRejection', handleUnhandled.bind(null, controller, 'unhandled rejection'))
|
|
182
183
|
|
|
183
|
-
|
|
184
|
+
process.on('newListener', event => {
|
|
185
|
+
if (event === 'uncaughtException' || event === 'unhandledRejection') {
|
|
186
|
+
globalThis.platformatic.logger.warn(
|
|
187
|
+
`A listener has been added for the "process.${event}" event. This listener will be never triggered as Watt default behavior will kill the process before.\n To disable this behavior, set "exitOnUnhandledErrors" to false in the runtime config.`
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await controller.init()
|
|
184
194
|
|
|
185
195
|
if (application.entrypoint && config.basePath) {
|
|
186
|
-
const meta = await
|
|
196
|
+
const meta = await controller.capability.getMeta()
|
|
187
197
|
if (!meta.gateway.wantsAbsoluteUrls) {
|
|
188
198
|
stripBasePath(config.basePath)
|
|
189
199
|
}
|
|
@@ -197,12 +207,10 @@ async function main () {
|
|
|
197
207
|
}
|
|
198
208
|
|
|
199
209
|
// Setup interaction with parent port
|
|
200
|
-
const itc = setupITC(
|
|
210
|
+
const itc = setupITC(controller, application, threadDispatcher, sharedContext)
|
|
201
211
|
globalThis[kITC] = itc
|
|
202
212
|
|
|
203
|
-
|
|
204
|
-
const dependencies = await app.getBootstrapDependencies()
|
|
205
|
-
itc.notify('init', { dependencies })
|
|
213
|
+
itc.notify('init')
|
|
206
214
|
}
|
|
207
215
|
|
|
208
216
|
function stripBasePath (basePath) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "3.0.0-
|
|
3
|
+
"version": "3.0.0-rc.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -34,23 +34,24 @@
|
|
|
34
34
|
"typescript": "^5.5.4",
|
|
35
35
|
"undici-oidc-interceptor": "^0.5.0",
|
|
36
36
|
"why-is-node-running": "^2.2.2",
|
|
37
|
-
"@platformatic/
|
|
38
|
-
"@platformatic/
|
|
39
|
-
"@platformatic/
|
|
40
|
-
"@platformatic/
|
|
41
|
-
"@platformatic/sql-graphql": "3.0.0-
|
|
42
|
-
"@platformatic/service": "3.0.0-
|
|
37
|
+
"@platformatic/db": "3.0.0-rc.1",
|
|
38
|
+
"@platformatic/composer": "3.0.0-rc.1",
|
|
39
|
+
"@platformatic/gateway": "3.0.0-rc.1",
|
|
40
|
+
"@platformatic/node": "3.0.0-rc.1",
|
|
41
|
+
"@platformatic/sql-graphql": "3.0.0-rc.1",
|
|
42
|
+
"@platformatic/service": "3.0.0-rc.1",
|
|
43
|
+
"@platformatic/sql-mapper": "3.0.0-rc.1",
|
|
44
|
+
"@platformatic/wattpm-pprof-capture": "3.0.0-rc.1"
|
|
43
45
|
},
|
|
44
46
|
"dependencies": {
|
|
45
47
|
"@fastify/accepts": "^5.0.0",
|
|
46
48
|
"@fastify/error": "^4.0.0",
|
|
47
49
|
"@fastify/websocket": "^11.0.0",
|
|
48
|
-
"@hapi/topo": "^6.0.2",
|
|
49
50
|
"@opentelemetry/api": "^1.9.0",
|
|
50
51
|
"@platformatic/undici-cache-memory": "^0.8.1",
|
|
51
52
|
"@watchable/unpromise": "^1.0.2",
|
|
52
53
|
"change-case-all": "^2.1.0",
|
|
53
|
-
"close-with-grace": "^2.
|
|
54
|
+
"close-with-grace": "^2.2.0",
|
|
54
55
|
"colorette": "^2.0.20",
|
|
55
56
|
"cron": "^4.1.0",
|
|
56
57
|
"debounce": "^2.0.0",
|
|
@@ -69,18 +70,18 @@
|
|
|
69
70
|
"undici": "^7.0.0",
|
|
70
71
|
"undici-thread-interceptor": "^0.14.0",
|
|
71
72
|
"ws": "^8.16.0",
|
|
72
|
-
"@platformatic/basic": "3.0.0-
|
|
73
|
-
"@platformatic/
|
|
74
|
-
"@platformatic/
|
|
75
|
-
"@platformatic/metrics": "3.0.0-
|
|
76
|
-
"@platformatic/
|
|
77
|
-
"@platformatic/
|
|
73
|
+
"@platformatic/basic": "3.0.0-rc.1",
|
|
74
|
+
"@platformatic/foundation": "3.0.0-rc.1",
|
|
75
|
+
"@platformatic/generators": "3.0.0-rc.1",
|
|
76
|
+
"@platformatic/metrics": "3.0.0-rc.1",
|
|
77
|
+
"@platformatic/telemetry": "3.0.0-rc.1",
|
|
78
|
+
"@platformatic/itc": "3.0.0-rc.1"
|
|
78
79
|
},
|
|
79
80
|
"engines": {
|
|
80
81
|
"node": ">=22.18.0"
|
|
81
82
|
},
|
|
82
83
|
"scripts": {
|
|
83
|
-
"test": "
|
|
84
|
+
"test": "npm run test:main && npm run test:api && npm run test:cli && npm run test:start && npm run test:multiple-workers",
|
|
84
85
|
"test:main": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/*.test.js test/versions/*.test.js",
|
|
85
86
|
"test:api": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/api/*.test.js test/management-api/*.test.js",
|
|
86
87
|
"test:cli": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/cli/*.test.js test/cli/**/*.test.js",
|
|
@@ -88,7 +89,7 @@
|
|
|
88
89
|
"test:multiple-workers": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/multiple-workers/*.test.js",
|
|
89
90
|
"gen-schema": "node lib/schema.js > schema.json",
|
|
90
91
|
"gen-types": "json2ts > config.d.ts < schema.json",
|
|
91
|
-
"build": "
|
|
92
|
+
"build": "npm run gen-schema && npm run gen-types",
|
|
92
93
|
"lint": "eslint"
|
|
93
94
|
}
|
|
94
95
|
}
|
package/schema.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.0.0-
|
|
2
|
+
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.0.0-rc.1.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"title": "Platformatic Runtime Config",
|
|
5
5
|
"type": "object",
|
|
@@ -186,6 +186,12 @@
|
|
|
186
186
|
}
|
|
187
187
|
]
|
|
188
188
|
},
|
|
189
|
+
"dependencies": {
|
|
190
|
+
"type": "array",
|
|
191
|
+
"items": {
|
|
192
|
+
"type": "string"
|
|
193
|
+
}
|
|
194
|
+
},
|
|
189
195
|
"arguments": {
|
|
190
196
|
"type": "array",
|
|
191
197
|
"items": {
|
|
@@ -347,6 +353,13 @@
|
|
|
347
353
|
},
|
|
348
354
|
"additionalProperties": false
|
|
349
355
|
},
|
|
356
|
+
"dependencies": {
|
|
357
|
+
"type": "array",
|
|
358
|
+
"items": {
|
|
359
|
+
"type": "string"
|
|
360
|
+
},
|
|
361
|
+
"default": []
|
|
362
|
+
},
|
|
350
363
|
"arguments": {
|
|
351
364
|
"type": "array",
|
|
352
365
|
"items": {
|
|
@@ -576,6 +589,13 @@
|
|
|
576
589
|
},
|
|
577
590
|
"additionalProperties": false
|
|
578
591
|
},
|
|
592
|
+
"dependencies": {
|
|
593
|
+
"type": "array",
|
|
594
|
+
"items": {
|
|
595
|
+
"type": "string"
|
|
596
|
+
},
|
|
597
|
+
"default": []
|
|
598
|
+
},
|
|
579
599
|
"arguments": {
|
|
580
600
|
"type": "array",
|
|
581
601
|
"items": {
|
|
@@ -805,6 +825,13 @@
|
|
|
805
825
|
},
|
|
806
826
|
"additionalProperties": false
|
|
807
827
|
},
|
|
828
|
+
"dependencies": {
|
|
829
|
+
"type": "array",
|
|
830
|
+
"items": {
|
|
831
|
+
"type": "string"
|
|
832
|
+
},
|
|
833
|
+
"default": []
|
|
834
|
+
},
|
|
808
835
|
"arguments": {
|
|
809
836
|
"type": "array",
|
|
810
837
|
"items": {
|
|
@@ -1190,6 +1217,10 @@
|
|
|
1190
1217
|
}
|
|
1191
1218
|
]
|
|
1192
1219
|
},
|
|
1220
|
+
"exitOnUnhandledErrors": {
|
|
1221
|
+
"default": true,
|
|
1222
|
+
"type": "boolean"
|
|
1223
|
+
},
|
|
1193
1224
|
"gracefulShutdown": {
|
|
1194
1225
|
"type": "object",
|
|
1195
1226
|
"properties": {
|
package/lib/dependencies.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { Sorter } from '@hapi/topo'
|
|
2
|
-
import { closest } from 'fastest-levenshtein'
|
|
3
|
-
import { MissingDependencyError } from './errors.js'
|
|
4
|
-
import { RoundRobinMap } from './worker/round-robin-map.js'
|
|
5
|
-
|
|
6
|
-
function missingDependencyErrorMessage (clientName, application, applications) {
|
|
7
|
-
const allNames = applications
|
|
8
|
-
.map(s => s.id)
|
|
9
|
-
.filter(id => id !== application.id)
|
|
10
|
-
.sort()
|
|
11
|
-
const closestName = closest(clientName, allNames)
|
|
12
|
-
let errorMsg = `application '${application.id}' has unknown dependency: '${clientName}'.`
|
|
13
|
-
if (closestName) {
|
|
14
|
-
errorMsg += ` Did you mean '${closestName}'?`
|
|
15
|
-
}
|
|
16
|
-
if (allNames.length) {
|
|
17
|
-
errorMsg += ` Known applications are: ${allNames.join(', ')}.`
|
|
18
|
-
}
|
|
19
|
-
return errorMsg
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function checkDependencies (applications) {
|
|
23
|
-
const allApplications = new Set(applications.map(s => s.id))
|
|
24
|
-
|
|
25
|
-
for (const application of applications) {
|
|
26
|
-
for (const dependency of application.dependencies) {
|
|
27
|
-
if (dependency.local && !allApplications.has(dependency.id)) {
|
|
28
|
-
throw new MissingDependencyError(missingDependencyErrorMessage(dependency.id, application, applications))
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function topologicalSort (workers, config) {
|
|
35
|
-
const topo = new Sorter()
|
|
36
|
-
|
|
37
|
-
for (const application of config.applications) {
|
|
38
|
-
const localDependencyIds = Array.from(application.dependencies)
|
|
39
|
-
.filter(dep => dep.local)
|
|
40
|
-
.map(dep => dep.id)
|
|
41
|
-
|
|
42
|
-
topo.add(application, {
|
|
43
|
-
group: application.id,
|
|
44
|
-
after: localDependencyIds,
|
|
45
|
-
manual: true
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
config.applications = topo.sort()
|
|
50
|
-
|
|
51
|
-
return new RoundRobinMap(
|
|
52
|
-
Array.from(workers.entries()).sort((a, b) => {
|
|
53
|
-
if (a[0] === b[0]) {
|
|
54
|
-
return 0
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const aIndex = config.applications.findIndex(s => s.id === a[0])
|
|
58
|
-
const bIndex = config.applications.findIndex(s => s.id === b[0])
|
|
59
|
-
return aIndex - bIndex
|
|
60
|
-
}),
|
|
61
|
-
workers.configuration
|
|
62
|
-
)
|
|
63
|
-
}
|