@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.
Files changed (170) hide show
  1. package/config.d.ts +285 -0
  2. package/eslint.config.js +8 -0
  3. package/fixtures/botched-start/platformatic.runtime.json +1 -1
  4. package/fixtures/botched-start/services/a/platformatic.service.json +1 -1
  5. package/fixtures/composerApp/platformatic.composer.json +1 -1
  6. package/fixtures/configs/invalid-autoload-with-services.json +1 -1
  7. package/fixtures/configs/invalid-entrypoint.json +1 -1
  8. package/fixtures/configs/invalid-schema-type.config.json +1 -1
  9. package/fixtures/configs/missing-property.config.json +1 -1
  10. package/fixtures/configs/missing-service-config.json +1 -1
  11. package/fixtures/configs/monorepo-composer-no-autoload.json +2 -2
  12. package/fixtures/configs/monorepo-composer.json +2 -2
  13. package/fixtures/configs/monorepo-create-cycle.json +2 -2
  14. package/fixtures/configs/monorepo-missing-dependencies.json +2 -2
  15. package/fixtures/configs/monorepo-no-cycles.json +2 -2
  16. package/fixtures/configs/monorepo-openapi.json +2 -2
  17. package/fixtures/configs/{monorepo-hotreload-env.json → monorepo-watch-env.json} +2 -2
  18. package/fixtures/configs/monorepo-watch-single.json +12 -0
  19. package/fixtures/configs/monorepo-watch.json +26 -9
  20. package/fixtures/configs/monorepo-with-dependencies.json +2 -2
  21. package/fixtures/configs/monorepo-with-management-api-without-metrics.json +21 -0
  22. package/fixtures/configs/monorepo-with-management-api.json +2 -2
  23. package/fixtures/configs/{monorepo-hotreload.json → monorepo-with-metrics.json} +5 -4
  24. package/fixtures/configs/monorepo.json +2 -2
  25. package/fixtures/configs/no-services.config.json +1 -1
  26. package/fixtures/configs/no-sources.config.json +1 -1
  27. package/fixtures/configs/service-throws-on-start.json +1 -1
  28. package/fixtures/configs/service-with-env-port.json +2 -2
  29. package/fixtures/configs/service-with-stdio.json +12 -0
  30. package/fixtures/configs/{hotreload.json → watch.json} +2 -2
  31. package/fixtures/dbApp/platformatic.db.json +1 -1
  32. package/fixtures/dbAppNoName/platformatic.db.json +1 -1
  33. package/fixtures/dbAppNoPackageJson/platformatic.db.json +1 -1
  34. package/fixtures/dbAppWithMigrationError/platformatic.db.json +1 -1
  35. package/fixtures/do-not-reload-dependencies/platformatic.service.json +1 -1
  36. package/fixtures/do-not-restart-on-crash/platformatic.runtime.json +3 -2
  37. package/fixtures/do-not-restart-on-crash/services/a/platformatic.service.json +1 -1
  38. package/fixtures/express/platformatic.runtime.json +1 -1
  39. package/fixtures/express/services/a/platformatic.service.json +1 -1
  40. package/fixtures/express/services/b/platformatic.service.json +1 -1
  41. package/fixtures/external-client/platformatic.service.json +1 -1
  42. package/fixtures/interceptors/idp.js +2 -2
  43. package/fixtures/interceptors/platformatic.runtime.json +1 -1
  44. package/fixtures/interceptors/services/a/platformatic.service.json +1 -1
  45. package/fixtures/interceptors-2/platformatic.runtime.json +1 -1
  46. package/fixtures/interceptors-2/services/a/platformatic.service.json +1 -1
  47. package/fixtures/leven/platformatic.runtime.json +2 -2
  48. package/fixtures/leven/services/deeply-spittle/platformatic.service.json +1 -1
  49. package/fixtures/leven/services/rainy-empire/platformatic.composer.json +1 -1
  50. package/fixtures/management-api/platformatic.json +3 -3
  51. package/fixtures/management-api/services/service-1/platformatic.json +1 -1
  52. package/fixtures/management-api/services/service-1/plugin.js +4 -3
  53. package/fixtures/management-api/services/service-2/platformatic.json +1 -1
  54. package/fixtures/management-api/services/service-db/platformatic.db.json +1 -1
  55. package/fixtures/management-api-custom-labels/platformatic.json +2 -2
  56. package/fixtures/management-api-custom-labels/services/service-1/platformatic.json +1 -1
  57. package/fixtures/management-api-custom-labels/services/service-1/plugin.js +4 -3
  58. package/fixtures/management-api-custom-labels/services/service-2/platformatic.json +1 -1
  59. package/fixtures/management-api-custom-labels/services/service-db/platformatic.db.json +1 -1
  60. package/fixtures/management-api-without-metrics/platformatic.json +3 -2
  61. package/fixtures/management-api-without-metrics/services/service-1/platformatic.json +1 -1
  62. package/fixtures/monorepo/composerApp/platformatic.composer.json +1 -1
  63. package/fixtures/monorepo/dbApp/platformatic.db.json +1 -1
  64. package/fixtures/monorepo/serviceApp/platformatic.service.json +3 -2
  65. package/fixtures/monorepo/serviceApp/with-logger/with-logger.cjs +2 -2
  66. package/fixtures/monorepo/serviceApp/with-logger/with-logger.d.ts +7 -7
  67. package/fixtures/monorepo/serviceAppWithLogger/platformatic.service.json +1 -1
  68. package/fixtures/monorepo/serviceAppWithLogger/plugin.js +12 -0
  69. package/fixtures/monorepo/serviceAppWithMultiplePlugins/platformatic.service.json +3 -2
  70. package/fixtures/monorepo-missing-dependencies/composer/platformatic.json +1 -1
  71. package/fixtures/monorepo-openapi/serviceAppWithoutOpenapi/platformatic.service.json +1 -1
  72. package/fixtures/monorepo-watch/service1/platformatic.service.json +1 -1
  73. package/fixtures/monorepo-with-dependencies/main/platformatic.json +1 -1
  74. package/fixtures/monorepo-with-dependencies/service-1/platformatic.json +1 -1
  75. package/fixtures/monorepo-with-dependencies/service-2/platformatic.json +1 -1
  76. package/fixtures/no-env.service.json +1 -1
  77. package/fixtures/preload/platformatic.runtime.json +1 -1
  78. package/fixtures/preload/services/a/platformatic.service.json +1 -1
  79. package/fixtures/prom-server/platformatic.json +2 -2
  80. package/fixtures/prom-server/services/service-1/platformatic.json +1 -1
  81. package/fixtures/prom-server/services/service-2/platformatic.json +1 -1
  82. package/fixtures/restart-on-crash/platformatic.runtime.json +1 -1
  83. package/fixtures/restart-on-crash/services/a/platformatic.service.json +1 -1
  84. package/fixtures/sample-runtime/package.json +1 -1
  85. package/fixtures/sample-runtime/platformatic.json +2 -2
  86. package/fixtures/sample-runtime/services/rival/package.json +1 -1
  87. package/fixtures/sample-runtime/services/rival/platformatic.json +1 -1
  88. package/fixtures/sample-runtime-with-2-services/package.json +1 -1
  89. package/fixtures/sample-runtime-with-2-services/platformatic.json +2 -2
  90. package/fixtures/sample-runtime-with-2-services/services/foobar/package.json +1 -1
  91. package/fixtures/sample-runtime-with-2-services/services/foobar/platformatic.json +1 -1
  92. package/fixtures/sample-runtime-with-2-services/services/rival/package.json +1 -1
  93. package/fixtures/sample-runtime-with-2-services/services/rival/platformatic.json +1 -1
  94. package/fixtures/server/logger-transport/platformatic.runtime.json +2 -2
  95. package/fixtures/server/logger-transport/services/echo/platformatic.service.json +1 -1
  96. package/fixtures/server/overrides-service/platformatic.runtime.json +2 -2
  97. package/fixtures/server/overrides-service/services/echo/platformatic.service.json +1 -1
  98. package/fixtures/server/runtime-server/platformatic.runtime.json +2 -2
  99. package/fixtures/server/runtime-server/services/echo/platformatic.service.json +1 -1
  100. package/fixtures/serviceAppThrowsOnStart/platformatic.service.json +1 -1
  101. package/fixtures/stackables/node_modules/foo/foo.js +2 -1
  102. package/fixtures/start-command-in-runtime.js +1 -1
  103. package/fixtures/stdio/platformatic.service.json +6 -0
  104. package/fixtures/stdio/plugin.js +24 -0
  105. package/fixtures/telemetry/platformatic.runtime.json +2 -2
  106. package/fixtures/telemetry/services/echo/platformatic.service.json +1 -1
  107. package/fixtures/typescript/platformatic.runtime.json +2 -2
  108. package/fixtures/typescript/services/composer/platformatic.composer.json +1 -1
  109. package/fixtures/typescript/services/movies/global.d.ts +2 -3
  110. package/fixtures/typescript/services/movies/platformatic.db.json +1 -1
  111. package/fixtures/typescript/services/movies/types/Movie.d.ts +3 -3
  112. package/fixtures/typescript/services/movies/types/index.d.ts +6 -6
  113. package/fixtures/typescript/services/titles/client/client.d.ts +35 -35
  114. package/fixtures/typescript/services/titles/platformatic.service.json +1 -1
  115. package/fixtures/typescript-custom-flags/platformatic.runtime.json +2 -2
  116. package/fixtures/typescript-custom-flags/services/composer/platformatic.composer.json +1 -1
  117. package/fixtures/typescript-custom-flags/services/movies/global.d.ts +2 -3
  118. package/fixtures/typescript-custom-flags/services/movies/platformatic.db.json +1 -1
  119. package/fixtures/typescript-custom-flags/services/movies/types/Movie.d.ts +3 -3
  120. package/fixtures/typescript-custom-flags/services/movies/types/index.d.ts +6 -6
  121. package/fixtures/typescript-custom-flags/services/titles/client/client.d.ts +35 -35
  122. package/fixtures/typescript-custom-flags/services/titles/platformatic.service.json +1 -1
  123. package/fixtures/typescript-no-env/platformatic.runtime.json +2 -2
  124. package/fixtures/typescript-no-env/services/composer/platformatic.composer.json +1 -1
  125. package/fixtures/typescript-no-env/services/movies/global.d.ts +2 -3
  126. package/fixtures/typescript-no-env/services/movies/platformatic.db.json +1 -1
  127. package/fixtures/typescript-no-env/services/movies/types/Movie.d.ts +3 -3
  128. package/fixtures/typescript-no-env/services/movies/types/index.d.ts +6 -6
  129. package/fixtures/typescript-no-env/services/titles/client/client.d.ts +35 -35
  130. package/fixtures/typescript-no-env/services/titles/platformatic.service.json +1 -1
  131. package/index.d.ts +7 -8
  132. package/index.js +14 -10
  133. package/index.test-d.ts +10 -12
  134. package/lib/build-server.js +5 -11
  135. package/lib/compile.js +11 -10
  136. package/lib/config.js +21 -14
  137. package/lib/dependencies.js +2 -1
  138. package/lib/errors.js +3 -2
  139. package/lib/generator/errors.js +1 -1
  140. package/lib/generator/runtime-generator.d.ts +15 -15
  141. package/lib/generator/runtime-generator.js +92 -63
  142. package/lib/logger.js +55 -0
  143. package/lib/management-api.js +29 -44
  144. package/lib/prom-server.js +5 -9
  145. package/lib/runtime.js +885 -0
  146. package/lib/schema.js +79 -76
  147. package/lib/start.js +35 -113
  148. package/lib/streams/message-port-writable.js +44 -0
  149. package/lib/streams/pino-writable.js +30 -0
  150. package/lib/upgrade.js +4 -3
  151. package/lib/utils.js +49 -1
  152. package/lib/versions/v1.36.0.js +1 -1
  153. package/lib/versions/v1.5.0.js +1 -1
  154. package/lib/versions/v2.0.0.js +17 -0
  155. package/lib/worker/app.js +224 -0
  156. package/lib/worker/default-stackable.js +27 -0
  157. package/lib/worker/itc.js +128 -0
  158. package/lib/worker/main.js +120 -0
  159. package/lib/worker/symbols.js +7 -0
  160. package/package.json +23 -25
  161. package/runtime.mjs +4 -4
  162. package/schema.json +824 -0
  163. package/lib/api-client.js +0 -500
  164. package/lib/api.js +0 -420
  165. package/lib/app.js +0 -397
  166. package/lib/load-config.js +0 -12
  167. package/lib/loader.mjs +0 -103
  168. package/lib/message-port-writable.js +0 -50
  169. package/lib/worker.js +0 -182
  170. /package/lib/{interceptors.js → worker/interceptors.js} +0 -0
@@ -7,5 +7,5 @@ module.exports = {
7
7
  config.restartOnError = false
8
8
  }
9
9
  return config
10
- }
10
+ },
11
11
  }
@@ -7,5 +7,5 @@ module.exports = {
7
7
  delete config.watch
8
8
  }
9
9
  return config
10
- }
10
+ },
11
11
  }
@@ -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.1",
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
- "@matteo.collina/tspl": "^0.1.1",
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.2",
33
+ "typescript": "^5.5.4",
35
34
  "undici-oidc-interceptor": "^0.5.0",
36
35
  "why-is-node-running": "^2.2.2",
37
- "@platformatic/sql-graphql": "2.0.0-alpha.1",
38
- "@platformatic/sql-mapper": "2.0.0-alpha.1",
39
- "@platformatic/service": "2.0.0-alpha.1",
40
- "@platformatic/composer": "2.0.0-alpha.1",
41
- "@platformatic/db": "2.0.0-alpha.1"
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.1.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/config": "2.0.0-alpha.1",
69
- "@platformatic/ts-compiler": "2.0.0-alpha.1",
70
- "@platformatic/generators": "2.0.0-alpha.1",
71
- "@platformatic/telemetry": "2.0.0-alpha.1",
72
- "@platformatic/utils": "2.0.0-alpha.1"
73
- },
74
- "standard": {
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=120000 && tsd",
83
- "lint": "standard | snazzy"
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