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