@platformatic/runtime 2.0.0-alpha.1 → 2.0.0-alpha.3
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 +285 -0
- package/eslint.config.js +8 -0
- package/fixtures/botched-start/platformatic.runtime.json +1 -1
- package/fixtures/botched-start/services/a/platformatic.service.json +1 -1
- package/fixtures/composerApp/platformatic.composer.json +1 -1
- package/fixtures/configs/invalid-autoload-with-services.json +1 -1
- package/fixtures/configs/invalid-entrypoint.json +1 -1
- package/fixtures/configs/invalid-schema-type.config.json +1 -1
- package/fixtures/configs/missing-property.config.json +1 -1
- package/fixtures/configs/missing-service-config.json +1 -1
- package/fixtures/configs/monorepo-composer-no-autoload.json +2 -2
- package/fixtures/configs/monorepo-composer.json +2 -2
- package/fixtures/configs/monorepo-create-cycle.json +2 -2
- package/fixtures/configs/monorepo-missing-dependencies.json +2 -2
- package/fixtures/configs/monorepo-no-cycles.json +2 -2
- package/fixtures/configs/monorepo-openapi.json +2 -2
- package/fixtures/configs/{monorepo-hotreload-env.json → monorepo-watch-env.json} +2 -2
- package/fixtures/configs/monorepo-watch-single.json +12 -0
- package/fixtures/configs/monorepo-watch.json +26 -9
- package/fixtures/configs/monorepo-with-dependencies.json +2 -2
- package/fixtures/configs/monorepo-with-management-api-without-metrics.json +21 -0
- package/fixtures/configs/monorepo-with-management-api.json +2 -2
- package/fixtures/configs/{monorepo-hotreload.json → monorepo-with-metrics.json} +5 -4
- package/fixtures/configs/monorepo.json +2 -2
- package/fixtures/configs/no-services.config.json +1 -1
- package/fixtures/configs/no-sources.config.json +1 -1
- package/fixtures/configs/service-throws-on-start.json +1 -1
- package/fixtures/configs/service-with-env-port.json +2 -2
- package/fixtures/configs/service-with-stdio.json +12 -0
- package/fixtures/configs/{hotreload.json → watch.json} +2 -2
- package/fixtures/dbApp/platformatic.db.json +1 -1
- package/fixtures/dbAppNoName/platformatic.db.json +1 -1
- package/fixtures/dbAppNoPackageJson/platformatic.db.json +1 -1
- package/fixtures/dbAppWithMigrationError/platformatic.db.json +1 -1
- package/fixtures/do-not-reload-dependencies/platformatic.service.json +1 -1
- package/fixtures/do-not-restart-on-crash/platformatic.runtime.json +3 -2
- package/fixtures/do-not-restart-on-crash/services/a/platformatic.service.json +1 -1
- package/fixtures/express/platformatic.runtime.json +1 -1
- package/fixtures/express/services/a/platformatic.service.json +1 -1
- package/fixtures/express/services/b/platformatic.service.json +1 -1
- package/fixtures/external-client/platformatic.service.json +1 -1
- package/fixtures/interceptors/idp.js +2 -2
- package/fixtures/interceptors/platformatic.runtime.json +1 -1
- package/fixtures/interceptors/services/a/platformatic.service.json +1 -1
- package/fixtures/interceptors-2/platformatic.runtime.json +1 -1
- package/fixtures/interceptors-2/services/a/platformatic.service.json +1 -1
- package/fixtures/leven/platformatic.runtime.json +2 -2
- package/fixtures/leven/services/deeply-spittle/platformatic.service.json +1 -1
- package/fixtures/leven/services/rainy-empire/platformatic.composer.json +1 -1
- package/fixtures/management-api/platformatic.json +3 -3
- package/fixtures/management-api/services/service-1/platformatic.json +1 -1
- package/fixtures/management-api/services/service-1/plugin.js +4 -3
- package/fixtures/management-api/services/service-2/platformatic.json +1 -1
- package/fixtures/management-api/services/service-db/platformatic.db.json +1 -1
- package/fixtures/management-api-custom-labels/platformatic.json +2 -2
- package/fixtures/management-api-custom-labels/services/service-1/platformatic.json +1 -1
- package/fixtures/management-api-custom-labels/services/service-1/plugin.js +4 -3
- package/fixtures/management-api-custom-labels/services/service-2/platformatic.json +1 -1
- package/fixtures/management-api-custom-labels/services/service-db/platformatic.db.json +1 -1
- package/fixtures/management-api-without-metrics/platformatic.json +3 -2
- package/fixtures/management-api-without-metrics/services/service-1/platformatic.json +1 -1
- package/fixtures/monorepo/composerApp/platformatic.composer.json +1 -1
- package/fixtures/monorepo/dbApp/platformatic.db.json +1 -1
- package/fixtures/monorepo/serviceApp/platformatic.service.json +3 -2
- package/fixtures/monorepo/serviceApp/with-logger/with-logger.cjs +2 -2
- package/fixtures/monorepo/serviceApp/with-logger/with-logger.d.ts +7 -7
- package/fixtures/monorepo/serviceAppWithLogger/platformatic.service.json +1 -1
- package/fixtures/monorepo/serviceAppWithLogger/plugin.js +12 -0
- package/fixtures/monorepo/serviceAppWithMultiplePlugins/platformatic.service.json +3 -2
- package/fixtures/monorepo-missing-dependencies/composer/platformatic.json +1 -1
- package/fixtures/monorepo-openapi/serviceAppWithoutOpenapi/platformatic.service.json +1 -1
- package/fixtures/monorepo-watch/service1/platformatic.service.json +1 -1
- package/fixtures/monorepo-with-dependencies/main/platformatic.json +1 -1
- package/fixtures/monorepo-with-dependencies/service-1/platformatic.json +1 -1
- package/fixtures/monorepo-with-dependencies/service-2/platformatic.json +1 -1
- package/fixtures/no-env.service.json +1 -1
- package/fixtures/preload/platformatic.runtime.json +1 -1
- package/fixtures/preload/services/a/platformatic.service.json +1 -1
- package/fixtures/prom-server/platformatic.json +2 -2
- package/fixtures/prom-server/services/service-1/platformatic.json +1 -1
- package/fixtures/prom-server/services/service-2/platformatic.json +1 -1
- package/fixtures/restart-on-crash/platformatic.runtime.json +1 -1
- package/fixtures/restart-on-crash/services/a/platformatic.service.json +1 -1
- package/fixtures/sample-runtime/package.json +1 -1
- package/fixtures/sample-runtime/platformatic.json +2 -2
- package/fixtures/sample-runtime/services/rival/package.json +1 -1
- package/fixtures/sample-runtime/services/rival/platformatic.json +1 -1
- package/fixtures/sample-runtime-with-2-services/package.json +1 -1
- package/fixtures/sample-runtime-with-2-services/platformatic.json +2 -2
- package/fixtures/sample-runtime-with-2-services/services/foobar/package.json +1 -1
- package/fixtures/sample-runtime-with-2-services/services/foobar/platformatic.json +1 -1
- package/fixtures/sample-runtime-with-2-services/services/rival/package.json +1 -1
- package/fixtures/sample-runtime-with-2-services/services/rival/platformatic.json +1 -1
- package/fixtures/server/logger-transport/platformatic.runtime.json +2 -2
- package/fixtures/server/logger-transport/services/echo/platformatic.service.json +1 -1
- package/fixtures/server/overrides-service/platformatic.runtime.json +2 -2
- package/fixtures/server/overrides-service/services/echo/platformatic.service.json +1 -1
- package/fixtures/server/runtime-server/platformatic.runtime.json +2 -2
- package/fixtures/server/runtime-server/services/echo/platformatic.service.json +1 -1
- package/fixtures/serviceAppThrowsOnStart/platformatic.service.json +1 -1
- package/fixtures/stackables/node_modules/foo/foo.js +2 -1
- package/fixtures/start-command-in-runtime.js +1 -1
- package/fixtures/stdio/platformatic.service.json +6 -0
- package/fixtures/stdio/plugin.js +24 -0
- package/fixtures/telemetry/platformatic.runtime.json +2 -2
- package/fixtures/telemetry/services/echo/platformatic.service.json +1 -1
- package/fixtures/typescript/platformatic.runtime.json +2 -2
- package/fixtures/typescript/services/composer/platformatic.composer.json +1 -1
- package/fixtures/typescript/services/movies/global.d.ts +2 -3
- package/fixtures/typescript/services/movies/platformatic.db.json +1 -1
- package/fixtures/typescript/services/movies/types/Movie.d.ts +3 -3
- package/fixtures/typescript/services/movies/types/index.d.ts +6 -6
- package/fixtures/typescript/services/titles/client/client.d.ts +35 -35
- package/fixtures/typescript/services/titles/platformatic.service.json +1 -1
- package/fixtures/typescript-custom-flags/platformatic.runtime.json +2 -2
- package/fixtures/typescript-custom-flags/services/composer/platformatic.composer.json +1 -1
- package/fixtures/typescript-custom-flags/services/movies/global.d.ts +2 -3
- package/fixtures/typescript-custom-flags/services/movies/platformatic.db.json +1 -1
- package/fixtures/typescript-custom-flags/services/movies/types/Movie.d.ts +3 -3
- package/fixtures/typescript-custom-flags/services/movies/types/index.d.ts +6 -6
- package/fixtures/typescript-custom-flags/services/titles/client/client.d.ts +35 -35
- package/fixtures/typescript-custom-flags/services/titles/platformatic.service.json +1 -1
- package/fixtures/typescript-no-env/platformatic.runtime.json +2 -2
- package/fixtures/typescript-no-env/services/composer/platformatic.composer.json +1 -1
- package/fixtures/typescript-no-env/services/movies/global.d.ts +2 -3
- package/fixtures/typescript-no-env/services/movies/platformatic.db.json +1 -1
- package/fixtures/typescript-no-env/services/movies/types/Movie.d.ts +3 -3
- package/fixtures/typescript-no-env/services/movies/types/index.d.ts +6 -6
- package/fixtures/typescript-no-env/services/titles/client/client.d.ts +35 -35
- package/fixtures/typescript-no-env/services/titles/platformatic.service.json +1 -1
- package/index.d.ts +7 -8
- package/index.js +14 -10
- package/index.test-d.ts +10 -12
- package/lib/build-server.js +5 -11
- package/lib/compile.js +11 -10
- package/lib/config.js +21 -14
- package/lib/dependencies.js +2 -1
- package/lib/errors.js +3 -2
- package/lib/generator/errors.js +1 -1
- package/lib/generator/runtime-generator.d.ts +15 -15
- package/lib/generator/runtime-generator.js +92 -63
- package/lib/logger.js +55 -0
- package/lib/management-api.js +29 -44
- package/lib/prom-server.js +5 -9
- package/lib/runtime.js +885 -0
- package/lib/schema.js +79 -76
- package/lib/start.js +35 -113
- package/lib/streams/message-port-writable.js +44 -0
- package/lib/streams/pino-writable.js +30 -0
- package/lib/upgrade.js +4 -3
- package/lib/utils.js +49 -1
- package/lib/versions/v1.36.0.js +1 -1
- package/lib/versions/v1.5.0.js +1 -1
- package/lib/versions/v2.0.0.js +17 -0
- package/lib/worker/app.js +224 -0
- package/lib/worker/default-stackable.js +27 -0
- package/lib/worker/itc.js +128 -0
- package/lib/worker/main.js +120 -0
- package/lib/worker/symbols.js +7 -0
- package/package.json +23 -25
- package/runtime.mjs +4 -4
- package/schema.json +824 -0
- package/lib/api-client.js +0 -500
- package/lib/api.js +0 -420
- package/lib/app.js +0 -397
- package/lib/load-config.js +0 -12
- package/lib/loader.mjs +0 -103
- package/lib/message-port-writable.js +0 -50
- package/lib/worker.js +0 -182
- /package/lib/{interceptors.js → worker/interceptors.js} +0 -0
package/lib/versions/v1.36.0.js
CHANGED
package/lib/versions/v1.5.0.js
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
version: '1.99.0', // This is to account alpha versions as well
|
|
5
|
+
up: function (config) {
|
|
6
|
+
if (config.restartOnError === undefined) {
|
|
7
|
+
config.restartOnError = true
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (config.hotReload) {
|
|
11
|
+
config.watch = config.hotReload
|
|
12
|
+
delete config.hotReload
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return config
|
|
16
|
+
},
|
|
17
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { EventEmitter } = require('node:events')
|
|
4
|
+
const { FileWatcher } = require('@platformatic/utils')
|
|
5
|
+
const debounce = require('debounce')
|
|
6
|
+
|
|
7
|
+
const errors = require('../errors')
|
|
8
|
+
const defaultStackable = require('./default-stackable')
|
|
9
|
+
const { getServiceUrl, loadConfig, loadEmptyConfig } = require('../utils')
|
|
10
|
+
|
|
11
|
+
class PlatformaticApp extends EventEmitter {
|
|
12
|
+
#starting
|
|
13
|
+
#started
|
|
14
|
+
#listening
|
|
15
|
+
#watch
|
|
16
|
+
#fileWatcher
|
|
17
|
+
#debouncedRestart
|
|
18
|
+
#context
|
|
19
|
+
|
|
20
|
+
constructor (appConfig, telemetryConfig, serverConfig, hasManagementApi, watch, metricsConfig) {
|
|
21
|
+
super()
|
|
22
|
+
this.appConfig = appConfig
|
|
23
|
+
this.#watch = watch
|
|
24
|
+
this.#starting = false
|
|
25
|
+
this.#started = false
|
|
26
|
+
this.#listening = false
|
|
27
|
+
this.stackable = null
|
|
28
|
+
this.#fileWatcher = null
|
|
29
|
+
|
|
30
|
+
this.#context = {
|
|
31
|
+
serviceId: this.appConfig.id,
|
|
32
|
+
directory: this.appConfig.path,
|
|
33
|
+
isEntrypoint: this.appConfig.entrypoint,
|
|
34
|
+
telemetryConfig,
|
|
35
|
+
metricsConfig,
|
|
36
|
+
serverConfig,
|
|
37
|
+
hasManagementApi: !!hasManagementApi,
|
|
38
|
+
localServiceEnvVars: this.appConfig.localServiceEnvVars,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getStatus () {
|
|
43
|
+
if (this.#starting) return 'starting'
|
|
44
|
+
if (this.#started) return 'started'
|
|
45
|
+
return 'stopped'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async updateContext (context) {
|
|
49
|
+
this.#context = { ...this.#context, ...context }
|
|
50
|
+
if (this.stackable) {
|
|
51
|
+
this.stackable.updateContext(context)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async getBootstrapDependencies () {
|
|
56
|
+
return this.stackable.getBootstrapDependencies()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async init () {
|
|
60
|
+
try {
|
|
61
|
+
const appConfig = this.appConfig
|
|
62
|
+
let loadedConfig
|
|
63
|
+
|
|
64
|
+
if (!appConfig.config) {
|
|
65
|
+
loadedConfig = await loadEmptyConfig(
|
|
66
|
+
appConfig.path,
|
|
67
|
+
{
|
|
68
|
+
onMissingEnv: this.#fetchServiceUrl,
|
|
69
|
+
context: appConfig,
|
|
70
|
+
},
|
|
71
|
+
true
|
|
72
|
+
)
|
|
73
|
+
} else {
|
|
74
|
+
loadedConfig = await loadConfig(
|
|
75
|
+
{},
|
|
76
|
+
['-c', appConfig.config],
|
|
77
|
+
{
|
|
78
|
+
onMissingEnv: this.#fetchServiceUrl,
|
|
79
|
+
context: appConfig,
|
|
80
|
+
},
|
|
81
|
+
true
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const app = loadedConfig.app
|
|
86
|
+
|
|
87
|
+
const stackable = await app.buildStackable({
|
|
88
|
+
onMissingEnv: this.#fetchServiceUrl,
|
|
89
|
+
config: this.appConfig.config,
|
|
90
|
+
context: this.#context,
|
|
91
|
+
})
|
|
92
|
+
this.stackable = this.#wrapStackable(stackable)
|
|
93
|
+
} catch (err) {
|
|
94
|
+
this.#logAndExit(err)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async start () {
|
|
99
|
+
if (this.#starting || this.#started) {
|
|
100
|
+
throw new errors.ApplicationAlreadyStartedError()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this.#starting = true
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
await this.stackable.init?.()
|
|
107
|
+
} catch (err) {
|
|
108
|
+
this.#logAndExit(err)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (this.#watch) {
|
|
112
|
+
const watchConfig = await this.stackable.getWatchConfig()
|
|
113
|
+
if (watchConfig.enabled !== false) {
|
|
114
|
+
/* c8 ignore next 4 */
|
|
115
|
+
this.#debouncedRestart = debounce(() => {
|
|
116
|
+
this.stackable.log({ message: 'files changed', level: 'debug' })
|
|
117
|
+
this.emit('changed')
|
|
118
|
+
}, 100) // debounce restart for 100ms
|
|
119
|
+
|
|
120
|
+
this.#startFileWatching(watchConfig)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const listen = !!this.appConfig.useHttp
|
|
125
|
+
try {
|
|
126
|
+
await this.stackable.start({ listen })
|
|
127
|
+
this.#listening = listen
|
|
128
|
+
/* c8 ignore next 5 */
|
|
129
|
+
} catch (err) {
|
|
130
|
+
this.stackable.log({ message: err.message, level: 'debug' })
|
|
131
|
+
this.#starting = false
|
|
132
|
+
throw err
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.#started = true
|
|
136
|
+
this.#starting = false
|
|
137
|
+
this.emit('start')
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async stop () {
|
|
141
|
+
if (!this.#started || this.#starting) {
|
|
142
|
+
throw new errors.ApplicationNotStartedError()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await this.#stopFileWatching()
|
|
146
|
+
await this.stackable.stop()
|
|
147
|
+
|
|
148
|
+
this.#started = false
|
|
149
|
+
this.#starting = false
|
|
150
|
+
this.#listening = false
|
|
151
|
+
this.emit('stop')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async listen () {
|
|
155
|
+
// This server is not an entrypoint or already listened in start. Behave as no-op.
|
|
156
|
+
if (!this.appConfig.entrypoint || this.appConfig.useHttp || this.#listening) {
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
await this.stackable.start({ listen: true })
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
#fetchServiceUrl (key, { parent, context: service }) {
|
|
164
|
+
if (service.localServiceEnvVars.has(key)) {
|
|
165
|
+
return service.localServiceEnvVars.get(key)
|
|
166
|
+
} else if (!key.endsWith('_URL') || !parent.serviceId) {
|
|
167
|
+
return null
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return getServiceUrl(parent.serviceId)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#startFileWatching (watch) {
|
|
174
|
+
if (this.#fileWatcher) {
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const fileWatcher = new FileWatcher({
|
|
179
|
+
path: watch.path,
|
|
180
|
+
/* c8 ignore next 2 */
|
|
181
|
+
allowToWatch: watch?.allow,
|
|
182
|
+
watchIgnore: watch?.ignore || [],
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
fileWatcher.on('update', this.#debouncedRestart)
|
|
186
|
+
|
|
187
|
+
fileWatcher.startWatching()
|
|
188
|
+
this.stackable.log({ message: 'start watching files', level: 'debug' })
|
|
189
|
+
this.#fileWatcher = fileWatcher
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async #stopFileWatching () {
|
|
193
|
+
const watcher = this.#fileWatcher
|
|
194
|
+
|
|
195
|
+
if (watcher) {
|
|
196
|
+
this.stackable.log({ message: 'stop watching files', level: 'debug' })
|
|
197
|
+
await watcher.stopWatching()
|
|
198
|
+
this.#fileWatcher = null
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#logAndExit (err) {
|
|
203
|
+
// Runtime logs here with console.error because stackable is not initialized
|
|
204
|
+
console.error(
|
|
205
|
+
JSON.stringify({
|
|
206
|
+
msg: err.message,
|
|
207
|
+
name: this.appConfig.id,
|
|
208
|
+
})
|
|
209
|
+
)
|
|
210
|
+
process.exit(1)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
#wrapStackable (stackable) {
|
|
214
|
+
const newStackable = {}
|
|
215
|
+
for (const method of Object.keys(defaultStackable)) {
|
|
216
|
+
newStackable[method] = stackable[method]
|
|
217
|
+
? stackable[method].bind(stackable)
|
|
218
|
+
: defaultStackable[method]
|
|
219
|
+
}
|
|
220
|
+
return newStackable
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = { PlatformaticApp }
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const defaultStackable = {
|
|
4
|
+
init: () => {},
|
|
5
|
+
start: () => {
|
|
6
|
+
throw new Error('Stackable start not implemented')
|
|
7
|
+
},
|
|
8
|
+
stop: () => {},
|
|
9
|
+
getUrl: () => null,
|
|
10
|
+
updateContext: () => {},
|
|
11
|
+
getConfig: () => null,
|
|
12
|
+
getInfo: () => null,
|
|
13
|
+
getDispatchFunc: () => null,
|
|
14
|
+
getOpenapiSchema: () => null,
|
|
15
|
+
getGraphqlSchema: () => null,
|
|
16
|
+
getMetrics: () => null,
|
|
17
|
+
inject: () => {
|
|
18
|
+
throw new Error('Stackable inject not implemented')
|
|
19
|
+
},
|
|
20
|
+
log: ({ message }) => {
|
|
21
|
+
console.log(message)
|
|
22
|
+
},
|
|
23
|
+
getBootstrapDependencies: () => [],
|
|
24
|
+
getWatchConfig: () => ({ enabled: false }),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = defaultStackable
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { once } = require('node:events')
|
|
4
|
+
const { parentPort } = require('node:worker_threads')
|
|
5
|
+
|
|
6
|
+
const { ITC } = require('@platformatic/itc')
|
|
7
|
+
|
|
8
|
+
const errors = require('../errors')
|
|
9
|
+
const { kITC, kId } = require('./symbols')
|
|
10
|
+
|
|
11
|
+
async function sendViaITC (worker, name, message) {
|
|
12
|
+
try {
|
|
13
|
+
// Make sure to catch when the worker exits, otherwise we're stuck forever
|
|
14
|
+
const ac = new AbortController()
|
|
15
|
+
let exitCode
|
|
16
|
+
|
|
17
|
+
const response = await Promise.race([
|
|
18
|
+
worker[kITC].send(name, message),
|
|
19
|
+
once(worker, 'exit', { signal: ac.signal }).then(([code]) => {
|
|
20
|
+
exitCode = code
|
|
21
|
+
}),
|
|
22
|
+
])
|
|
23
|
+
|
|
24
|
+
if (typeof exitCode === 'number') {
|
|
25
|
+
throw new errors.ServiceExitedError(worker[kId], exitCode)
|
|
26
|
+
} else {
|
|
27
|
+
ac.abort()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return response
|
|
31
|
+
} catch (error) {
|
|
32
|
+
if (!error.handlerError) {
|
|
33
|
+
throw error
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (error.handlerErrorCode && !error.handlerError.code) {
|
|
37
|
+
error.handlerError.code = error.handlerErrorCode
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
throw error.handlerError
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function setupITC (app, service, dispatcher) {
|
|
45
|
+
const itc = new ITC({ port: parentPort })
|
|
46
|
+
|
|
47
|
+
itc.handle('start', async () => {
|
|
48
|
+
const status = app.getStatus()
|
|
49
|
+
|
|
50
|
+
if (status === 'starting') {
|
|
51
|
+
await once(app, 'start')
|
|
52
|
+
} else {
|
|
53
|
+
await app.start()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (service.entrypoint) {
|
|
57
|
+
await app.listen()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const url = app.stackable.getUrl()
|
|
61
|
+
|
|
62
|
+
const dispatchFunc = await app.stackable.getDispatchFunc()
|
|
63
|
+
dispatcher.replaceServer(url ?? dispatchFunc)
|
|
64
|
+
|
|
65
|
+
return service.entrypoint ? url : null
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
itc.handle('stop', async () => {
|
|
69
|
+
const status = app.getStatus()
|
|
70
|
+
|
|
71
|
+
if (status === 'starting') {
|
|
72
|
+
await once(app, 'start')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (status !== 'stopped') {
|
|
76
|
+
await app.stop()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
dispatcher.interceptor.close()
|
|
80
|
+
itc.close()
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
itc.handle('getStatus', async () => {
|
|
84
|
+
return app.getStatus()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
itc.handle('getServiceInfo', async () => {
|
|
88
|
+
return app.stackable.getInfo()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
itc.handle('getServiceConfig', async () => {
|
|
92
|
+
const current = await app.stackable.getConfig()
|
|
93
|
+
// Remove all undefined keys from the config
|
|
94
|
+
return JSON.parse(JSON.stringify(current))
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
itc.handle('getServiceOpenAPISchema', async () => {
|
|
98
|
+
try {
|
|
99
|
+
return app.stackable.getOpenapiSchema()
|
|
100
|
+
} catch (err) {
|
|
101
|
+
throw new errors.FailedToRetrieveOpenAPISchemaError(service.id, err.message)
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
itc.handle('getServiceGraphQLSchema', async () => {
|
|
106
|
+
try {
|
|
107
|
+
return app.stackable.getGraphqlSchema()
|
|
108
|
+
} catch (err) {
|
|
109
|
+
throw new errors.FailedToRetrieveGraphQLSchemaError(service.id, err.message)
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
itc.handle('getMetrics', async format => {
|
|
114
|
+
return app.stackable.getMetrics({ format })
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
itc.handle('inject', async injectParams => {
|
|
118
|
+
return app.stackable.inject(injectParams)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
app.on('changed', () => {
|
|
122
|
+
itc.notify('changed')
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
return itc
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = { sendViaITC, setupITC }
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { createRequire } = require('node:module')
|
|
4
|
+
const { join } = require('node:path')
|
|
5
|
+
const { setTimeout: sleep } = require('node:timers/promises')
|
|
6
|
+
const { parentPort, workerData, threadId } = require('node:worker_threads')
|
|
7
|
+
const { pathToFileURL } = require('node:url')
|
|
8
|
+
|
|
9
|
+
const pino = require('pino')
|
|
10
|
+
const { fetch, setGlobalDispatcher, Agent } = require('undici')
|
|
11
|
+
const { wire } = require('undici-thread-interceptor')
|
|
12
|
+
|
|
13
|
+
const { PlatformaticApp } = require('./app')
|
|
14
|
+
const { setupITC } = require('./itc')
|
|
15
|
+
const loadInterceptors = require('./interceptors')
|
|
16
|
+
const { MessagePortWritable } = require('../streams/message-port-writable')
|
|
17
|
+
const { createPinoWritable } = require('../streams/pino-writable')
|
|
18
|
+
const { kId, kITC } = require('./symbols')
|
|
19
|
+
|
|
20
|
+
process.on('uncaughtException', handleUnhandled.bind(null, 'uncaught exception'))
|
|
21
|
+
process.on('unhandledRejection', handleUnhandled.bind(null, 'unhandled rejection'))
|
|
22
|
+
|
|
23
|
+
globalThis.fetch = fetch
|
|
24
|
+
globalThis[kId] = threadId
|
|
25
|
+
|
|
26
|
+
let app
|
|
27
|
+
const config = workerData.config
|
|
28
|
+
const logger = createLogger()
|
|
29
|
+
|
|
30
|
+
function handleUnhandled (type, err) {
|
|
31
|
+
logger.error({ err }, `application ${type}`)
|
|
32
|
+
|
|
33
|
+
Promise.race([app?.stop(), sleep(1000, 'timeout', { ref: false })])
|
|
34
|
+
.catch()
|
|
35
|
+
.finally(() => {
|
|
36
|
+
process.exit(1)
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createLogger () {
|
|
41
|
+
const destination = new MessagePortWritable({ port: workerData.loggingPort })
|
|
42
|
+
const loggerInstance = pino({ level: 'trace' }, destination)
|
|
43
|
+
|
|
44
|
+
Reflect.defineProperty(process, 'stdout', { value: createPinoWritable(loggerInstance, 'info') })
|
|
45
|
+
Reflect.defineProperty(process, 'stderr', { value: createPinoWritable(loggerInstance, 'error') })
|
|
46
|
+
|
|
47
|
+
return loggerInstance
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function main () {
|
|
51
|
+
if (config.preload) {
|
|
52
|
+
await import(pathToFileURL(config.preload))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const service = workerData.serviceConfig
|
|
56
|
+
|
|
57
|
+
// Setup undici
|
|
58
|
+
const interceptors = {}
|
|
59
|
+
const composedInterceptors = []
|
|
60
|
+
|
|
61
|
+
if (config.undici?.interceptors) {
|
|
62
|
+
const _require = createRequire(join(workerData.dirname, 'package.json'))
|
|
63
|
+
for (const key of ['Agent', 'Pool', 'Client']) {
|
|
64
|
+
if (config.undici.interceptors[key]) {
|
|
65
|
+
interceptors[key] = await loadInterceptors(_require, config.undici.interceptors[key])
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (Array.isArray(config.undici.interceptors)) {
|
|
70
|
+
composedInterceptors.push(...(await loadInterceptors(_require, config.undici.interceptors)))
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const globalDispatcher = new Agent({
|
|
75
|
+
...config.undici,
|
|
76
|
+
interceptors,
|
|
77
|
+
}).compose(composedInterceptors)
|
|
78
|
+
|
|
79
|
+
setGlobalDispatcher(globalDispatcher)
|
|
80
|
+
|
|
81
|
+
// Setup mesh networker
|
|
82
|
+
const threadDispatcher = wire({ port: parentPort, useNetwork: service.useHttp })
|
|
83
|
+
|
|
84
|
+
// If the service is an entrypoint and runtime server config is defined, use it.
|
|
85
|
+
let serverConfig = null
|
|
86
|
+
if (config.server && service.entrypoint) {
|
|
87
|
+
serverConfig = config.server
|
|
88
|
+
} else if (service.useHttp) {
|
|
89
|
+
serverConfig = {
|
|
90
|
+
port: 0,
|
|
91
|
+
host: '127.0.0.1',
|
|
92
|
+
keepAliveTimeout: 5000,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Create the application
|
|
97
|
+
app = new PlatformaticApp(
|
|
98
|
+
service,
|
|
99
|
+
config.telemetry,
|
|
100
|
+
serverConfig,
|
|
101
|
+
!!config.managementApi,
|
|
102
|
+
!!config.watch,
|
|
103
|
+
config.metrics
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
await app.init()
|
|
107
|
+
|
|
108
|
+
// Setup interaction with parent port
|
|
109
|
+
const itc = setupITC(app, service, threadDispatcher)
|
|
110
|
+
|
|
111
|
+
// Get the dependencies
|
|
112
|
+
const dependencies = config.autoload ? await app.getBootstrapDependencies() : []
|
|
113
|
+
itc.notify('init', { dependencies })
|
|
114
|
+
itc.listen()
|
|
115
|
+
|
|
116
|
+
globalThis[kITC] = itc
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// No need to catch this because there is the unhadledRejection handler on top.
|
|
120
|
+
main()
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const kConfig = Symbol.for('plt.runtime.config')
|
|
4
|
+
const kId = Symbol.for('plt.runtime.id') // This is also used to detect if we are running in a Platformatic runtime thread
|
|
5
|
+
const kITC = Symbol.for('plt.runtime.itc')
|
|
6
|
+
|
|
7
|
+
module.exports = { kConfig, kId, kITC }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -19,26 +19,25 @@
|
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@fastify/express": "^3.0.0",
|
|
21
21
|
"@fastify/formbody": "^7.4.0",
|
|
22
|
-
"
|
|
23
|
-
"borp": "^0.16.0",
|
|
22
|
+
"borp": "^0.17.0",
|
|
24
23
|
"c8": "^10.0.0",
|
|
24
|
+
"eslint": "9",
|
|
25
25
|
"execa": "^8.0.1",
|
|
26
26
|
"express": "^4.18.3",
|
|
27
27
|
"fast-jwt": "^4.0.0",
|
|
28
28
|
"get-port": "^7.1.0",
|
|
29
|
+
"neostandard": "^0.11.1",
|
|
29
30
|
"pino-abstract-transport": "^1.1.0",
|
|
30
|
-
"snazzy": "^9.0.0",
|
|
31
31
|
"split2": "^4.2.0",
|
|
32
|
-
"standard": "^17.1.0",
|
|
33
32
|
"tsd": "^0.31.0",
|
|
34
|
-
"typescript": "^5.4
|
|
33
|
+
"typescript": "^5.5.4",
|
|
35
34
|
"undici-oidc-interceptor": "^0.5.0",
|
|
36
35
|
"why-is-node-running": "^2.2.2",
|
|
37
|
-
"@platformatic/
|
|
38
|
-
"@platformatic/
|
|
39
|
-
"@platformatic/
|
|
40
|
-
"@platformatic/
|
|
41
|
-
"@platformatic/
|
|
36
|
+
"@platformatic/composer": "2.0.0-alpha.3",
|
|
37
|
+
"@platformatic/service": "2.0.0-alpha.3",
|
|
38
|
+
"@platformatic/sql-graphql": "2.0.0-alpha.3",
|
|
39
|
+
"@platformatic/db": "2.0.0-alpha.3",
|
|
40
|
+
"@platformatic/sql-mapper": "2.0.0-alpha.3"
|
|
42
41
|
},
|
|
43
42
|
"dependencies": {
|
|
44
43
|
"@fastify/error": "^3.4.1",
|
|
@@ -62,24 +61,23 @@
|
|
|
62
61
|
"pino-pretty": "^11.0.0",
|
|
63
62
|
"pino-roll": "^1.0.0",
|
|
64
63
|
"semgrator": "^0.3.0",
|
|
65
|
-
"tail-file-stream": "^0.
|
|
64
|
+
"tail-file-stream": "^0.2.0",
|
|
66
65
|
"undici": "^6.9.0",
|
|
66
|
+
"undici-thread-interceptor": "^0.5.0",
|
|
67
67
|
"ws": "^8.16.0",
|
|
68
|
-
"@platformatic/
|
|
69
|
-
"@platformatic/
|
|
70
|
-
"@platformatic/
|
|
71
|
-
"@platformatic/
|
|
72
|
-
"@platformatic/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"ignore": [
|
|
76
|
-
"**/dist/*",
|
|
77
|
-
"**/test/tmp"
|
|
78
|
-
]
|
|
68
|
+
"@platformatic/basic": "2.0.0-alpha.3",
|
|
69
|
+
"@platformatic/itc": "2.0.0-alpha.3",
|
|
70
|
+
"@platformatic/telemetry": "2.0.0-alpha.3",
|
|
71
|
+
"@platformatic/generators": "2.0.0-alpha.3",
|
|
72
|
+
"@platformatic/config": "2.0.0-alpha.3",
|
|
73
|
+
"@platformatic/utils": "2.0.0-alpha.3",
|
|
74
|
+
"@platformatic/ts-compiler": "2.0.0-alpha.3"
|
|
79
75
|
},
|
|
80
76
|
"scripts": {
|
|
81
77
|
"test": "npm run lint && borp --concurrency=1 --timeout=180000 && tsd",
|
|
82
|
-
"coverage": "npm run lint && borp -X=fixtures -X=test -C --concurrency=1 --timeout=
|
|
83
|
-
"lint": "
|
|
78
|
+
"coverage": "npm run lint && borp -X=fixtures -X=test -C --concurrency=1 --timeout=180000 && tsd",
|
|
79
|
+
"lint": "eslint",
|
|
80
|
+
"gen-schema": "node lib/schema.js > schema.json",
|
|
81
|
+
"gen-types": "json2ts > config.d.ts < schema.json"
|
|
84
82
|
}
|
|
85
83
|
}
|
package/runtime.mjs
CHANGED
|
@@ -14,7 +14,7 @@ export const compile = compileCmd
|
|
|
14
14
|
const help = helpMe({
|
|
15
15
|
dir: join(import.meta.url, 'help'),
|
|
16
16
|
// the default
|
|
17
|
-
ext: '.txt'
|
|
17
|
+
ext: '.txt',
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
const program = commist({ maxDistance: 2 })
|
|
@@ -28,8 +28,8 @@ program.register('compile', compile)
|
|
|
28
28
|
export async function run (argv) {
|
|
29
29
|
const args = parseArgs(argv, {
|
|
30
30
|
alias: {
|
|
31
|
-
v: 'version'
|
|
32
|
-
}
|
|
31
|
+
v: 'version',
|
|
32
|
+
},
|
|
33
33
|
})
|
|
34
34
|
|
|
35
35
|
if (args.version) {
|
|
@@ -40,7 +40,7 @@ export async function run (argv) {
|
|
|
40
40
|
/* c8 ignore next 4 */
|
|
41
41
|
return {
|
|
42
42
|
output: await program.parseAsync(argv),
|
|
43
|
-
help
|
|
43
|
+
help,
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|