@platformatic/runtime 1.53.3 → 2.0.0-alpha.10

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 (178) hide show
  1. package/config.d.ts +284 -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/{monorepo-hotreload.json → invalid-autoload-with-services.json} +8 -8
  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 +32 -0
  12. package/fixtures/configs/monorepo-composer.json +2 -2
  13. package/fixtures/configs/monorepo-create-cycle.json +2 -3
  14. package/fixtures/configs/monorepo-missing-dependencies.json +9 -0
  15. package/fixtures/configs/monorepo-no-cycles.json +2 -3
  16. package/fixtures/configs/monorepo-openapi.json +2 -2
  17. package/fixtures/configs/{hotreload.json → monorepo-watch-env.json} +2 -3
  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-client-without-id.json → monorepo-with-management-api-without-metrics.json} +5 -8
  22. package/fixtures/configs/monorepo-with-management-api.json +2 -3
  23. package/fixtures/configs/monorepo-with-metrics.json +1 -2
  24. package/fixtures/configs/monorepo.json +2 -3
  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 -3
  29. package/fixtures/configs/service-with-stdio.json +12 -0
  30. package/fixtures/configs/{monorepo-hotreload-env.json → watch.json} +2 -3
  31. package/fixtures/crash-on-bootstrap/platformatic.runtime.json +15 -0
  32. package/fixtures/crash-on-bootstrap/services/service-1/platformatic.service.json +14 -0
  33. package/fixtures/crash-on-bootstrap/services/service-1/plugin.js +5 -0
  34. package/fixtures/crash-on-bootstrap/services/service-2/platformatic.service.json +14 -0
  35. package/fixtures/crash-on-bootstrap/services/service-2/plugin.js +5 -0
  36. package/fixtures/dbApp/platformatic.db.json +1 -1
  37. package/fixtures/dbAppNoName/platformatic.db.json +1 -1
  38. package/fixtures/dbAppNoPackageJson/platformatic.db.json +1 -1
  39. package/fixtures/dbAppWithMigrationError/platformatic.db.json +1 -1
  40. package/fixtures/do-not-reload-dependencies/platformatic.service.json +1 -1
  41. package/fixtures/do-not-restart-on-crash/platformatic.runtime.json +3 -2
  42. package/fixtures/do-not-restart-on-crash/services/a/platformatic.service.json +1 -1
  43. package/fixtures/express/platformatic.runtime.json +1 -1
  44. package/fixtures/express/services/a/platformatic.service.json +1 -1
  45. package/fixtures/express/services/b/platformatic.service.json +1 -1
  46. package/fixtures/external-client/platformatic.service.json +1 -1
  47. package/fixtures/interceptors/idp.js +2 -2
  48. package/fixtures/interceptors/platformatic.runtime.json +1 -1
  49. package/fixtures/interceptors/services/a/platformatic.service.json +1 -1
  50. package/fixtures/interceptors-2/platformatic.runtime.json +1 -1
  51. package/fixtures/interceptors-2/services/a/platformatic.service.json +1 -1
  52. package/fixtures/leven/platformatic.runtime.json +2 -3
  53. package/fixtures/leven/services/deeply-spittle/platformatic.service.json +1 -1
  54. package/fixtures/leven/services/rainy-empire/platformatic.composer.json +1 -1
  55. package/fixtures/management-api/platformatic.json +8 -4
  56. package/fixtures/management-api/services/service-1/platformatic.json +1 -1
  57. package/fixtures/management-api/services/service-1/plugin.js +4 -3
  58. package/fixtures/management-api/services/service-2/platformatic.json +1 -1
  59. package/fixtures/management-api/services/service-db/platformatic.db.json +1 -1
  60. package/fixtures/management-api-custom-labels/platformatic.json +2 -3
  61. package/fixtures/management-api-custom-labels/services/service-1/platformatic.json +1 -1
  62. package/fixtures/management-api-custom-labels/services/service-1/plugin.js +4 -3
  63. package/fixtures/management-api-custom-labels/services/service-2/platformatic.json +1 -1
  64. package/fixtures/management-api-custom-labels/services/service-db/platformatic.db.json +1 -1
  65. package/fixtures/management-api-without-metrics/platformatic.json +3 -3
  66. package/fixtures/management-api-without-metrics/services/service-1/platformatic.json +1 -1
  67. package/fixtures/monorepo/composerApp/platformatic.composer.json +1 -2
  68. package/fixtures/monorepo/dbApp/platformatic.db.json +1 -1
  69. package/fixtures/monorepo/serviceApp/platformatic.service.json +3 -2
  70. package/fixtures/monorepo/serviceApp/with-logger/with-logger.cjs +2 -2
  71. package/fixtures/monorepo/serviceApp/with-logger/with-logger.d.ts +7 -7
  72. package/fixtures/monorepo/serviceAppWithLogger/platformatic.service.json +1 -1
  73. package/fixtures/monorepo/serviceAppWithLogger/plugin.js +12 -0
  74. package/fixtures/monorepo/serviceAppWithMultiplePlugins/platformatic.service.json +3 -2
  75. package/fixtures/monorepo-missing-dependencies/composer/platformatic.json +24 -0
  76. package/fixtures/monorepo-openapi/serviceAppWithoutOpenapi/platformatic.service.json +1 -1
  77. package/fixtures/monorepo-watch/service1/platformatic.service.json +1 -1
  78. package/fixtures/monorepo-with-dependencies/main/platformatic.json +1 -1
  79. package/fixtures/monorepo-with-dependencies/service-1/platformatic.json +1 -1
  80. package/fixtures/monorepo-with-dependencies/service-2/platformatic.json +1 -1
  81. package/fixtures/no-env.service.json +1 -1
  82. package/fixtures/preload/platformatic.runtime.json +1 -1
  83. package/fixtures/preload/services/a/platformatic.service.json +1 -1
  84. package/fixtures/prom-server/platformatic.json +2 -3
  85. package/fixtures/prom-server/services/service-1/platformatic.json +1 -1
  86. package/fixtures/prom-server/services/service-2/platformatic.json +1 -1
  87. package/fixtures/restart-on-crash/platformatic.runtime.json +1 -1
  88. package/fixtures/restart-on-crash/services/a/platformatic.service.json +1 -1
  89. package/fixtures/sample-runtime/package.json +1 -1
  90. package/fixtures/sample-runtime/platformatic.json +2 -3
  91. package/fixtures/sample-runtime/services/rival/package.json +1 -1
  92. package/fixtures/sample-runtime/services/rival/platformatic.json +1 -1
  93. package/fixtures/sample-runtime-with-2-services/package.json +1 -1
  94. package/fixtures/sample-runtime-with-2-services/platformatic.json +2 -3
  95. package/fixtures/sample-runtime-with-2-services/services/foobar/package.json +1 -1
  96. package/fixtures/sample-runtime-with-2-services/services/foobar/platformatic.json +1 -1
  97. package/fixtures/sample-runtime-with-2-services/services/rival/package.json +1 -1
  98. package/fixtures/sample-runtime-with-2-services/services/rival/platformatic.json +1 -1
  99. package/fixtures/server/logger-transport/platformatic.runtime.json +2 -3
  100. package/fixtures/server/logger-transport/services/echo/platformatic.service.json +1 -1
  101. package/fixtures/server/overrides-service/platformatic.runtime.json +2 -3
  102. package/fixtures/server/overrides-service/services/echo/platformatic.service.json +1 -1
  103. package/fixtures/server/runtime-server/platformatic.runtime.json +2 -3
  104. package/fixtures/server/runtime-server/services/echo/platformatic.service.json +1 -1
  105. package/fixtures/serviceAppThrowsOnStart/platformatic.service.json +1 -1
  106. package/fixtures/stackables/node_modules/foo/foo.js +2 -1
  107. package/fixtures/start-command-in-runtime.js +1 -1
  108. package/fixtures/stdio/platformatic.service.json +6 -0
  109. package/fixtures/stdio/plugin.js +24 -0
  110. package/fixtures/telemetry/platformatic.runtime.json +2 -3
  111. package/fixtures/telemetry/services/echo/platformatic.service.json +1 -1
  112. package/fixtures/telemetry/services/echo/routes/span.js +16 -2
  113. package/fixtures/telemetry/services/service-1/platformatic.service.json +19 -0
  114. package/fixtures/telemetry/services/service-1/routes/echo.js +7 -0
  115. package/fixtures/typescript/platformatic.runtime.json +2 -3
  116. package/fixtures/typescript/services/composer/platformatic.composer.json +1 -1
  117. package/fixtures/typescript/services/movies/global.d.ts +2 -3
  118. package/fixtures/typescript/services/movies/platformatic.db.json +1 -1
  119. package/fixtures/typescript/services/movies/types/Movie.d.ts +3 -3
  120. package/fixtures/typescript/services/movies/types/index.d.ts +6 -6
  121. package/fixtures/typescript/services/titles/client/client.d.ts +35 -35
  122. package/fixtures/typescript/services/titles/platformatic.service.json +1 -1
  123. package/fixtures/typescript-custom-flags/platformatic.runtime.json +2 -3
  124. package/fixtures/typescript-custom-flags/services/composer/platformatic.composer.json +1 -1
  125. package/fixtures/typescript-custom-flags/services/movies/global.d.ts +2 -3
  126. package/fixtures/typescript-custom-flags/services/movies/platformatic.db.json +1 -1
  127. package/fixtures/typescript-custom-flags/services/movies/types/Movie.d.ts +3 -3
  128. package/fixtures/typescript-custom-flags/services/movies/types/index.d.ts +6 -6
  129. package/fixtures/typescript-custom-flags/services/titles/client/client.d.ts +35 -35
  130. package/fixtures/typescript-custom-flags/services/titles/platformatic.service.json +1 -1
  131. package/fixtures/typescript-no-env/platformatic.runtime.json +2 -3
  132. package/fixtures/typescript-no-env/services/composer/platformatic.composer.json +1 -1
  133. package/fixtures/typescript-no-env/services/movies/global.d.ts +2 -3
  134. package/fixtures/typescript-no-env/services/movies/platformatic.db.json +1 -1
  135. package/fixtures/typescript-no-env/services/movies/types/Movie.d.ts +3 -3
  136. package/fixtures/typescript-no-env/services/movies/types/index.d.ts +6 -6
  137. package/fixtures/typescript-no-env/services/titles/client/client.d.ts +35 -35
  138. package/fixtures/typescript-no-env/services/titles/platformatic.service.json +1 -1
  139. package/index.d.ts +7 -8
  140. package/index.js +15 -10
  141. package/index.test-d.ts +10 -12
  142. package/lib/build-server.js +23 -15
  143. package/lib/compile.js +56 -13
  144. package/lib/config.js +27 -139
  145. package/lib/dependencies.js +59 -0
  146. package/lib/errors.js +6 -2
  147. package/lib/generator/errors.js +1 -1
  148. package/lib/generator/runtime-generator.d.ts +15 -15
  149. package/lib/generator/runtime-generator.js +81 -66
  150. package/lib/logger.js +55 -0
  151. package/lib/management-api.js +29 -44
  152. package/lib/prom-server.js +5 -9
  153. package/lib/runtime.js +1005 -0
  154. package/lib/schema.js +46 -44
  155. package/lib/start.js +49 -117
  156. package/lib/upgrade.js +4 -3
  157. package/lib/utils.js +49 -1
  158. package/lib/versions/v1.36.0.js +1 -1
  159. package/lib/versions/v1.5.0.js +1 -1
  160. package/lib/versions/v2.0.0.js +17 -0
  161. package/lib/worker/app.js +266 -0
  162. package/lib/worker/default-stackable.js +32 -0
  163. package/lib/worker/itc.js +149 -0
  164. package/lib/worker/main.js +129 -0
  165. package/lib/worker/metrics.js +106 -0
  166. package/lib/worker/symbols.js +7 -0
  167. package/package.json +44 -44
  168. package/runtime.mjs +11 -6
  169. package/schema.json +25 -13
  170. package/fixtures/monorepo/serviceApp/platformatic.service-client-without-id.json +0 -21
  171. package/lib/api-client.js +0 -497
  172. package/lib/api.js +0 -397
  173. package/lib/app.js +0 -365
  174. package/lib/load-config.js +0 -20
  175. package/lib/loader.mjs +0 -103
  176. package/lib/message-port-writable.js +0 -50
  177. package/lib/worker.js +0 -182
  178. /package/lib/{interceptors.js → worker/interceptors.js} +0 -0
package/lib/api-client.js DELETED
@@ -1,497 +0,0 @@
1
- 'use strict'
2
-
3
- const { tmpdir } = require('node:os')
4
- const { join } = require('node:path')
5
- const { once, EventEmitter } = require('node:events')
6
- const { randomUUID, createHash } = require('node:crypto')
7
- const { createReadStream, watch } = require('node:fs')
8
- const { readdir, readFile, stat, access } = require('node:fs/promises')
9
- const { setTimeout: sleep } = require('node:timers/promises')
10
- const errors = require('./errors')
11
- const { Unpromise } = require('@watchable/unpromise')
12
- const ts = require('tail-file-stream')
13
-
14
- const platformaticVersion = require('../package.json').version
15
-
16
- const MAX_LISTENERS_COUNT = 100
17
- const MAX_METRICS_QUEUE_LENGTH = 5 * 60 // 5 minutes in seconds
18
- const COLLECT_METRICS_TIMEOUT = 1000
19
-
20
- class RuntimeApiClient extends EventEmitter {
21
- #exitCode
22
- #exitPromise
23
- #configManager
24
- #runtimeTmpDir
25
- #metrics
26
- #metricsTimeout
27
-
28
- constructor (worker, configManager) {
29
- super()
30
- this.setMaxListeners(MAX_LISTENERS_COUNT)
31
-
32
- this.#configManager = configManager
33
- this.#runtimeTmpDir = getRuntimeTmpDir(configManager.dirname)
34
-
35
- this.setWorker(worker)
36
- }
37
-
38
- async setWorker (worker) {
39
- if (!worker) {
40
- throw new errors.WorkerIsRequired()
41
- }
42
- if (this.worker) {
43
- this.worker.removeAllListeners('message')
44
- // Ignore all things that happen after the worker has been replaced
45
- this.#exitPromise.catch(() => {})
46
- }
47
- this.worker = worker
48
- this.#exitPromise = this.#exitHandler()
49
- this.worker.on('message', (message) => {
50
- if (message.operationId) {
51
- this.emit(message.operationId, message)
52
- }
53
- })
54
-
55
- if (this.started) {
56
- await this.start()
57
- }
58
- }
59
-
60
- async #getRuntimePackageJson () {
61
- const runtimeDir = this.#configManager.dirname
62
- const packageJsonPath = join(runtimeDir, 'package.json')
63
- const packageJsonFile = await readFile(packageJsonPath, 'utf8')
64
- const packageJson = JSON.parse(packageJsonFile)
65
- return packageJson
66
- }
67
-
68
- async getRuntimeMetadata () {
69
- const packageJson = await this.#getRuntimePackageJson()
70
- const entrypointDetails = await this.getEntrypointDetails()
71
-
72
- return {
73
- pid: process.pid,
74
- cwd: process.cwd(),
75
- argv: process.argv,
76
- uptimeSeconds: Math.floor(process.uptime()),
77
- execPath: process.execPath,
78
- nodeVersion: process.version,
79
- projectDir: this.#configManager.dirname,
80
- packageName: packageJson.name ?? null,
81
- packageVersion: packageJson.version ?? null,
82
- url: entrypointDetails?.url ?? null,
83
- platformaticVersion
84
- }
85
- }
86
-
87
- getRuntimeConfig () {
88
- return this.#configManager.current
89
- }
90
-
91
- async start () {
92
- const address = await this.#sendCommand('plt:start-services')
93
- this.started = true
94
- this.emit('start', address)
95
- return address
96
- }
97
-
98
- async close () {
99
- this.started = false
100
- await this.#sendCommand('plt:stop-services')
101
-
102
- this.worker.postMessage({ command: 'plt:close' })
103
- const res = await Unpromise.race([
104
- this.#exitPromise,
105
- // We must kill the worker if it doesn't exit in 10 seconds
106
- // because it may be stuck in an infinite loop.
107
- // This is a workaround for
108
- // https://github.com/nodejs/node/issues/47748
109
- // https://github.com/nodejs/node/issues/49344
110
- // Remove once https://github.com/nodejs/node/pull/51290 is released
111
- // on all lines.
112
- // Likely to be removed when we drop support for Node.js 18.
113
- sleep(10000, 'timeout', { ref: false })
114
- ])
115
-
116
- if (res === 'timeout') {
117
- this.worker.unref()
118
- }
119
- }
120
-
121
- async restart () {
122
- return this.#sendCommand('plt:restart-services')
123
- }
124
-
125
- async getEntrypointDetails () {
126
- return this.#sendCommand('plt:get-entrypoint-details')
127
- }
128
-
129
- async getServices () {
130
- return this.#sendCommand('plt:get-services')
131
- }
132
-
133
- async getServiceDetails (id) {
134
- return this.#sendCommand('plt:get-service-details', { id })
135
- }
136
-
137
- async getServiceConfig (id) {
138
- return this.#sendCommand('plt:get-service-config', { id })
139
- }
140
-
141
- async getServiceOpenapiSchema (id) {
142
- return this.#sendCommand('plt:get-service-openapi-schema', { id })
143
- }
144
-
145
- async getServiceGraphqlSchema (id) {
146
- return this.#sendCommand('plt:get-service-graphql-schema', { id })
147
- }
148
-
149
- async getMetrics (format = 'json') {
150
- return this.#sendCommand('plt:get-metrics', { format })
151
- }
152
-
153
- async startService (id) {
154
- return this.#sendCommand('plt:start-service', { id })
155
- }
156
-
157
- async stopService (id) {
158
- return this.#sendCommand('plt:stop-service', { id })
159
- }
160
-
161
- async inject (id, injectParams) {
162
- return this.#sendCommand('plt:inject', { id, injectParams })
163
- }
164
-
165
- getCachedMetrics () {
166
- return this.#metrics
167
- }
168
-
169
- async getFormattedMetrics () {
170
- const { metrics } = await this.getMetrics()
171
-
172
- if (metrics === null) return null
173
-
174
- const cpuMetric = metrics.find(
175
- (metric) => metric.name === 'process_cpu_percent_usage'
176
- )
177
- const rssMetric = metrics.find(
178
- (metric) => metric.name === 'process_resident_memory_bytes'
179
- )
180
- const totalHeapSizeMetric = metrics.find(
181
- (metric) => metric.name === 'nodejs_heap_size_total_bytes'
182
- )
183
- const usedHeapSizeMetric = metrics.find(
184
- (metric) => metric.name === 'nodejs_heap_size_used_bytes'
185
- )
186
- const heapSpaceSizeTotalMetric = metrics.find(
187
- (metric) => metric.name === 'nodejs_heap_space_size_total_bytes'
188
- )
189
- const newSpaceSizeTotalMetric = heapSpaceSizeTotalMetric.values.find(
190
- (value) => value.labels.space === 'new'
191
- )
192
- const oldSpaceSizeTotalMetric = heapSpaceSizeTotalMetric.values.find(
193
- (value) => value.labels.space === 'old'
194
- )
195
- const eventLoopUtilizationMetric = metrics.find(
196
- (metric) => metric.name === 'nodejs_eventloop_utilization'
197
- )
198
-
199
- let p50Value = 0
200
- let p90Value = 0
201
- let p95Value = 0
202
- let p99Value = 0
203
-
204
- const metricName = 'http_request_all_summary_seconds'
205
- const httpLatencyMetrics =
206
- metrics.find((metric) => metric.name === metricName)
207
-
208
- p50Value = httpLatencyMetrics.values.find(
209
- (value) => value.labels.quantile === 0.5
210
- ).value || 0
211
- p90Value = httpLatencyMetrics.values.find(
212
- (value) => value.labels.quantile === 0.9
213
- ).value || 0
214
- p95Value = httpLatencyMetrics.values.find(
215
- (value) => value.labels.quantile === 0.95
216
- ).value || 0
217
- p99Value = httpLatencyMetrics.values.find(
218
- (value) => value.labels.quantile === 0.99
219
- ).value || 0
220
-
221
- p50Value = Math.round(p50Value * 1000)
222
- p90Value = Math.round(p90Value * 1000)
223
- p95Value = Math.round(p95Value * 1000)
224
- p99Value = Math.round(p99Value * 1000)
225
-
226
- const cpu = cpuMetric.values[0].value
227
- const rss = rssMetric.values[0].value
228
- const elu = eventLoopUtilizationMetric.values[0].value
229
- const totalHeapSize = totalHeapSizeMetric.values[0].value
230
- const usedHeapSize = usedHeapSizeMetric.values[0].value
231
- const newSpaceSize = newSpaceSizeTotalMetric.value
232
- const oldSpaceSize = oldSpaceSizeTotalMetric.value
233
-
234
- const formattedMetrics = {
235
- version: 1,
236
- date: new Date().toISOString(),
237
- cpu,
238
- elu,
239
- rss,
240
- totalHeapSize,
241
- usedHeapSize,
242
- newSpaceSize,
243
- oldSpaceSize,
244
- entrypoint: {
245
- latency: {
246
- p50: p50Value,
247
- p90: p90Value,
248
- p95: p95Value,
249
- p99: p99Value
250
- }
251
- }
252
- }
253
- return formattedMetrics
254
- }
255
-
256
- startCollectingMetrics () {
257
- this.#metrics = []
258
- this.#metricsTimeout = setInterval(async () => {
259
- let metrics = null
260
- try {
261
- metrics = await this.getFormattedMetrics()
262
- } catch (error) {
263
- if (!(error instanceof errors.RuntimeExitedError)) {
264
- // TODO(mcollina): use the logger
265
- console.error('Error collecting metrics', error)
266
- }
267
- return
268
- }
269
-
270
- this.emit('metrics', metrics)
271
- this.#metrics.push(metrics)
272
- if (this.#metrics.length > MAX_METRICS_QUEUE_LENGTH) {
273
- this.#metrics.shift()
274
- }
275
- }, COLLECT_METRICS_TIMEOUT).unref()
276
- }
277
-
278
- async pipeLogsStream (writableStream, logger, startLogId, endLogId, runtimePID) {
279
- endLogId = endLogId || Infinity
280
- runtimePID = runtimePID ?? process.pid
281
-
282
- const runtimeLogFiles = await this.#getRuntimeLogFiles(runtimePID)
283
- if (runtimeLogFiles.length === 0) {
284
- writableStream.end()
285
- return
286
- }
287
-
288
- let latestFileId = parseInt(runtimeLogFiles.at(-1).slice('logs.'.length))
289
-
290
- let fileStream = null
291
- let fileId = startLogId ?? latestFileId
292
-
293
- let isClosed = false
294
-
295
- const runtimeLogsDir = this.#getRuntimeLogsDir(runtimePID)
296
-
297
- const watcher = watch(runtimeLogsDir, async (event, filename) => {
298
- if (event === 'rename' && filename.startsWith('logs')) {
299
- const logFileId = parseInt(filename.slice('logs.'.length))
300
- if (logFileId > latestFileId) {
301
- latestFileId = logFileId
302
- fileStream.unwatch()
303
- }
304
- }
305
- }).unref()
306
-
307
- const streamLogFile = () => {
308
- if (fileId > endLogId) {
309
- writableStream.end()
310
- return
311
- }
312
-
313
- const fileName = 'logs.' + fileId
314
- const filePath = join(runtimeLogsDir, fileName)
315
-
316
- const prevFileStream = fileStream
317
-
318
- fileStream = ts.createReadStream(filePath)
319
- fileStream.pipe(writableStream, { end: false })
320
-
321
- if (prevFileStream) {
322
- prevFileStream.unpipe(writableStream)
323
- prevFileStream.destroy()
324
- }
325
-
326
- fileStream.on('error', (err) => {
327
- isClosed = true
328
- logger.error(err, 'Error streaming log file')
329
- fileStream.destroy()
330
- watcher.close()
331
- writableStream.end()
332
- })
333
-
334
- fileStream.on('close', () => {
335
- if (latestFileId > fileId && !isClosed) {
336
- streamLogFile(++fileId)
337
- }
338
- })
339
-
340
- fileStream.on('eof', () => {
341
- if (fileId >= endLogId) {
342
- writableStream.end()
343
- }
344
- if (latestFileId > fileId) {
345
- fileStream.unwatch()
346
- }
347
- })
348
-
349
- return fileStream
350
- }
351
-
352
- streamLogFile(fileId)
353
-
354
- writableStream.on('close', () => {
355
- isClosed = true
356
- watcher.close()
357
- fileStream.destroy()
358
- })
359
- writableStream.on('error', () => {
360
- isClosed = true
361
- watcher.close()
362
- fileStream.destroy()
363
- })
364
- }
365
-
366
- async getLogIds (runtimePID) {
367
- runtimePID = runtimePID ?? process.pid
368
-
369
- const runtimeLogFiles = await this.#getRuntimeLogFiles(runtimePID)
370
- const runtimeLogIds = []
371
-
372
- for (const logFile of runtimeLogFiles) {
373
- const logId = parseInt(logFile.slice('logs.'.length))
374
- runtimeLogIds.push(logId)
375
- }
376
- return runtimeLogIds
377
- }
378
-
379
- async getAllLogIds () {
380
- const runtimesLogFiles = await this.#getAllLogsFiles()
381
- const runtimesLogsIds = []
382
-
383
- for (const runtime of runtimesLogFiles) {
384
- const runtimeLogIds = []
385
- for (const logFile of runtime.runtimeLogFiles) {
386
- const logId = parseInt(logFile.slice('logs.'.length))
387
- runtimeLogIds.push(logId)
388
- }
389
- runtimesLogsIds.push({
390
- pid: runtime.runtimePID,
391
- indexes: runtimeLogIds
392
- })
393
- }
394
-
395
- return runtimesLogsIds
396
- }
397
-
398
- async getLogFileStream (logFileId, runtimePID) {
399
- const runtimeLogsDir = this.#getRuntimeLogsDir(runtimePID)
400
- const filePath = join(runtimeLogsDir, `logs.${logFileId}`)
401
- return createReadStream(filePath)
402
- }
403
-
404
- #getRuntimeLogsDir (runtimePID) {
405
- return join(this.#runtimeTmpDir, runtimePID.toString(), 'logs')
406
- }
407
-
408
- async #getRuntimeLogFiles (runtimePID) {
409
- const runtimeLogsDir = this.#getRuntimeLogsDir(runtimePID)
410
- const runtimeLogsFiles = await readdir(runtimeLogsDir)
411
- return runtimeLogsFiles
412
- .filter((file) => file.startsWith('logs'))
413
- .sort((log1, log2) => {
414
- const index1 = parseInt(log1.slice('logs.'.length))
415
- const index2 = parseInt(log2.slice('logs.'.length))
416
- return index1 - index2
417
- })
418
- }
419
-
420
- async #getAllLogsFiles () {
421
- try {
422
- await access(this.#runtimeTmpDir)
423
- } catch (error) {
424
- return []
425
- }
426
-
427
- const runtimePIDs = await readdir(this.#runtimeTmpDir)
428
- const runtimesLogFiles = []
429
-
430
- for (const runtimePID of runtimePIDs) {
431
- const runtimeLogsDir = this.#getRuntimeLogsDir(runtimePID)
432
- const runtimeLogsDirStat = await stat(runtimeLogsDir)
433
- const runtimeLogFiles = await this.#getRuntimeLogFiles(runtimePID)
434
- const lastModified = runtimeLogsDirStat.mtime
435
-
436
- runtimesLogFiles.push({
437
- runtimePID: parseInt(runtimePID),
438
- runtimeLogFiles,
439
- lastModified
440
- })
441
- }
442
-
443
- return runtimesLogFiles.sort(
444
- (runtime1, runtime2) => runtime1.lastModified - runtime2.lastModified
445
- )
446
- }
447
-
448
- async #sendCommand (command, params = {}) {
449
- const operationId = randomUUID()
450
- this.worker.postMessage({ operationId, command, params })
451
- const [message] = await Unpromise.race(
452
- [once(this, operationId), this.#exitPromise]
453
- )
454
-
455
- if (this.#exitCode !== undefined) {
456
- if (this.exitCode === 1) {
457
- throw new errors.AddressInUseError()
458
- }
459
- throw new errors.RuntimeExitedError()
460
- }
461
- const { error, data, code } = message
462
- if (error !== null) {
463
- const err = new Error(error)
464
- err.code = code
465
- throw err
466
- }
467
-
468
- return JSON.parse(data)
469
- }
470
-
471
- async #exitHandler () {
472
- this.#exitCode = undefined
473
- return once(this.worker, 'exit').then((msg) => {
474
- clearInterval(this.#metricsTimeout)
475
- this.#exitCode = msg[0]
476
- this.emit('close')
477
- return msg
478
- })
479
- }
480
- }
481
-
482
- function getRuntimeTmpDir (runtimeDir) {
483
- const platformaticTmpDir = join(tmpdir(), 'platformatic', 'applications')
484
- const runtimeDirHash = createHash('md5').update(runtimeDir).digest('hex')
485
- return join(platformaticTmpDir, runtimeDirHash)
486
- }
487
-
488
- function getRuntimeLogsDir (runtimeDir, runtimePID) {
489
- const runtimeTmpDir = getRuntimeTmpDir(runtimeDir)
490
- return join(runtimeTmpDir, runtimePID.toString(), 'logs')
491
- }
492
-
493
- module.exports = {
494
- RuntimeApiClient,
495
- getRuntimeTmpDir,
496
- getRuntimeLogsDir
497
- }