@platformatic/runtime 2.9.1 → 2.11.0

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 CHANGED
@@ -5,7 +5,7 @@
5
5
  * and run json-schema-to-typescript to regenerate this file.
6
6
  */
7
7
 
8
- export type HttpsSchemasPlatformaticDevPlatformaticRuntime291Json = {
8
+ export type HttpsSchemasPlatformaticDevPlatformaticRuntime2110Json = {
9
9
  [k: string]: unknown;
10
10
  } & {
11
11
  $schema?: string;
@@ -20,6 +20,16 @@ export type HttpsSchemasPlatformaticDevPlatformaticRuntime291Json = {
20
20
  id: string;
21
21
  config?: string;
22
22
  useHttp?: boolean;
23
+ workers?: number | string;
24
+ health?: {
25
+ enabled?: boolean | string;
26
+ interval?: number | string;
27
+ gracePeriod?: number | string;
28
+ maxUnhealthyChecks?: number | string;
29
+ maxELU?: number | string;
30
+ maxHeapUsed?: number | string;
31
+ maxHeapTotal?: number | string;
32
+ };
23
33
  };
24
34
  };
25
35
  };
@@ -52,8 +62,6 @@ export type HttpsSchemasPlatformaticDevPlatformaticRuntime291Json = {
52
62
  [k: string]: unknown;
53
63
  };
54
64
  level?: string;
55
- additionalProperties?: never;
56
- [k: string]: unknown;
57
65
  }[];
58
66
  options?: {
59
67
  [k: string]: unknown;
@@ -96,11 +104,21 @@ export type HttpsSchemasPlatformaticDevPlatformaticRuntime291Json = {
96
104
  )[];
97
105
  };
98
106
  };
107
+ startTimeout?: number;
99
108
  restartOnError?: boolean | number;
100
109
  gracefulShutdown?: {
101
110
  runtime: number | string;
102
111
  service: number | string;
103
112
  };
113
+ health?: {
114
+ enabled?: boolean | string;
115
+ interval?: number | string;
116
+ gracePeriod?: number | string;
117
+ maxUnhealthyChecks?: number | string;
118
+ maxELU?: number | string;
119
+ maxHeapUsed?: number | string;
120
+ maxHeapTotal?: number | string;
121
+ };
104
122
  undici?: {
105
123
  agentOptions?: {
106
124
  [k: string]: unknown;
package/lib/errors.js CHANGED
@@ -13,10 +13,12 @@ module.exports = {
13
13
  UnknownRuntimeAPICommandError: createError(`${ERROR_PREFIX}_UNKNOWN_RUNTIME_API_COMMAND`, 'Unknown Runtime API command "%s"'),
14
14
  ServiceNotFoundError: createError(`${ERROR_PREFIX}_SERVICE_NOT_FOUND`, 'Service %s not found. Available services are: %s'),
15
15
  ServiceNotStartedError: createError(`${ERROR_PREFIX}_SERVICE_NOT_STARTED`, "Service with id '%s' is not started"),
16
+ ServiceStartTimeoutError: createError(`${ERROR_PREFIX}_SERVICE_START_TIMEOUT`, "Service with id '%s' failed to start in %dms."),
16
17
  FailedToRetrieveOpenAPISchemaError: createError(`${ERROR_PREFIX}_FAILED_TO_RETRIEVE_OPENAPI_SCHEMA`, 'Failed to retrieve OpenAPI schema for service with id "%s": %s'),
17
18
  FailedToRetrieveGraphQLSchemaError: createError(`${ERROR_PREFIX}_FAILED_TO_RETRIEVE_GRAPHQL_SCHEMA`, 'Failed to retrieve GraphQL schema for service with id "%s": %s'),
18
19
  FailedToRetrieveMetaError: createError(`${ERROR_PREFIX}_FAILED_TO_RETRIEVE_META`, 'Failed to retrieve metadata for service with id "%s": %s'),
19
20
  FailedToRetrieveMetricsError: createError(`${ERROR_PREFIX}_FAILED_TO_RETRIEVE_METRICS`, 'Failed to retrieve metrics for service with id "%s": %s'),
21
+ FailedToRetrieveHealthError: createError(`${ERROR_PREFIX}_FAILED_TO_RETRIEVE_HEALTH`, 'Failed to retrieve health for service with id "%s": %s'),
20
22
  ApplicationAlreadyStartedError: createError(`${ERROR_PREFIX}_APPLICATION_ALREADY_STARTED`, 'Application is already started'),
21
23
  ApplicationNotStartedError: createError(`${ERROR_PREFIX}_APPLICATION_NOT_STARTED`, 'Application has not been started'),
22
24
  ConfigPathMustBeStringError: createError(`${ERROR_PREFIX}_CONFIG_PATH_MUST_BE_STRING`, 'Config path must be a string'),
package/lib/runtime.js CHANGED
@@ -7,7 +7,7 @@ const { join } = require('node:path')
7
7
  const { setTimeout: sleep } = require('node:timers/promises')
8
8
  const { Worker } = require('node:worker_threads')
9
9
  const { ITC } = require('@platformatic/itc')
10
- const { ensureLoggableError, executeWithTimeout } = require('@platformatic/utils')
10
+ const { ensureLoggableError, executeWithTimeout, deepmerge } = require('@platformatic/utils')
11
11
  const ts = require('tail-file-stream')
12
12
  const { createThreadInterceptor } = require('undici-thread-interceptor')
13
13
 
@@ -24,6 +24,7 @@ const {
24
24
  kServiceId,
25
25
  kWorkerId,
26
26
  kITC,
27
+ kHealthCheckTimer,
27
28
  kConfig,
28
29
  kLoggerDestination,
29
30
  kLoggingPort,
@@ -763,6 +764,9 @@ class Runtime extends EventEmitter {
763
764
  }
764
765
  }
765
766
 
767
+ const errorLabel = this.#workerExtendedLabel(serviceId, index, workersCount)
768
+ const health = deepmerge(config.health ?? {}, serviceConfig.health ?? {})
769
+
766
770
  const worker = new Worker(kWorkerFile, {
767
771
  workerData: {
768
772
  config,
@@ -783,6 +787,9 @@ class Runtime extends EventEmitter {
783
787
  execArgv: serviceConfig.isPLTService ? [] : ['--require', openTelemetrySetupPath],
784
788
  env: this.#env,
785
789
  transferList: [loggingPort],
790
+ resourceLimits: {
791
+ maxOldGenerationSizeMb: health.maxHeapTotal
792
+ },
786
793
  /*
787
794
  Important: always set stdout and stderr to true, so that worker's output is not automatically
788
795
  piped to the parent thread. We actually never output the thread output since we replace it
@@ -814,8 +821,6 @@ class Runtime extends EventEmitter {
814
821
 
815
822
  // Wait for the next tick so that crashed from the thread are logged first
816
823
  setImmediate(() => {
817
- const errorLabel = workersCount > 1 ? `worker ${index} of the service "${serviceId}"` : `service "${serviceId}"`
818
-
819
824
  if (started && (!config.watch || code !== 0)) {
820
825
  this.logger.warn(`The ${errorLabel} unexpectedly exited with code ${code}.`)
821
826
  }
@@ -837,7 +842,6 @@ class Runtime extends EventEmitter {
837
842
  worker[kId] = workersCount > 1 ? workerId : serviceId
838
843
  worker[kServiceId] = serviceId
839
844
  worker[kWorkerId] = workersCount > 1 ? index : undefined
840
- worker[kConfig] = serviceConfig
841
845
  worker[kLoggerDestination] = loggerDestination
842
846
  worker[kLoggingPort] = loggingPort
843
847
 
@@ -900,7 +904,6 @@ class Runtime extends EventEmitter {
900
904
 
901
905
  // Store dependencies
902
906
  const [{ dependencies }] = await waitEventFromITC(worker, 'init')
903
- worker[kWorkerStatus] = 'boot'
904
907
 
905
908
  if (autoload) {
906
909
  serviceConfig.dependencies = dependencies
@@ -910,11 +913,50 @@ class Runtime extends EventEmitter {
910
913
  }
911
914
  }
912
915
  }
916
+
917
+ // This must be done here as the dependencies are filled above
918
+ worker[kConfig] = { ...serviceConfig, health }
919
+ worker[kWorkerStatus] = 'boot'
920
+ }
921
+
922
+ #setupHealthCheck (worker, errorLabel) {
923
+ // Clear the timeout when exiting
924
+ worker.on('exit', () => clearTimeout(worker[kHealthCheckTimer]))
925
+
926
+ const { maxELU, maxHeapUsed, maxHeapTotal, maxUnhealthyChecks, interval } = worker[kConfig].health
927
+ let unhealthyChecks = 0
928
+
929
+ worker[kHealthCheckTimer] = setTimeout(async () => {
930
+ const health = await worker[kITC].send('getHealth')
931
+ this.emit('health', {
932
+ id: worker[kId],
933
+ service: worker[kServiceId],
934
+ worker: worker[kWorkerId],
935
+ currentHealth: health,
936
+ healthConfig: worker[kConfig].health
937
+ })
938
+
939
+ const { elu, heapUsed } = health
940
+ const memoryUsage = heapUsed / maxHeapTotal
941
+ if (elu > maxELU || memoryUsage > maxHeapUsed) {
942
+ unhealthyChecks++
943
+ } else {
944
+ unhealthyChecks = 0
945
+ }
946
+
947
+ if (unhealthyChecks >= maxUnhealthyChecks) {
948
+ this.logger.error(`The ${errorLabel} is unhealthy. Forcefully terminating it ...`)
949
+ worker.terminate()
950
+ return
951
+ }
952
+
953
+ worker[kHealthCheckTimer].refresh()
954
+ }, interval)
913
955
  }
914
956
 
915
957
  async #startWorker (config, serviceConfig, workersCount, id, index, silent, bootstrapAttempt = 0) {
916
958
  const workerId = `${id}:${index}`
917
- const label = workersCount > 1 ? `worker ${index} of the service "${id}"` : `service "${id}"`
959
+ const label = this.#workerExtendedLabel(id, index, workersCount)
918
960
 
919
961
  if (!silent) {
920
962
  this.logger?.info(`Starting the ${label}...`)
@@ -931,7 +973,19 @@ class Runtime extends EventEmitter {
931
973
  worker[kWorkerStatus] = 'starting'
932
974
 
933
975
  try {
934
- const workerUrl = await sendViaITC(worker, 'start')
976
+ let workerUrl
977
+ if (config.startTimeout > 0) {
978
+ workerUrl = await executeWithTimeout(sendViaITC(worker, 'start'), config.startTimeout)
979
+
980
+ if (workerUrl === 'timeout') {
981
+ this.logger.info(`The ${label} failed to start in ${config.startTimeout}ms. Forcefully killing the thread.`)
982
+ worker.terminate()
983
+ throw new errors.ServiceStartTimeoutError(id, config.startTimeout)
984
+ }
985
+ } else {
986
+ workerUrl = await sendViaITC(worker, 'start')
987
+ }
988
+
935
989
  if (workerUrl) {
936
990
  this.#url = workerUrl
937
991
  }
@@ -941,6 +995,16 @@ class Runtime extends EventEmitter {
941
995
  if (!silent) {
942
996
  this.logger?.info(`Started the ${label}...`)
943
997
  }
998
+
999
+ const { enabled, gracePeriod } = worker[kConfig].health
1000
+ if (enabled && config.restartOnError > 0) {
1001
+ worker[kHealthCheckTimer] = setTimeout(
1002
+ () => {
1003
+ this.#setupHealthCheck(worker, label)
1004
+ },
1005
+ gracePeriod > 0 ? gracePeriod : 1
1006
+ )
1007
+ }
944
1008
  } catch (error) {
945
1009
  // TODO: handle port allocation error here
946
1010
  if (error.code === 'EADDRINUSE') throw error
@@ -953,7 +1017,9 @@ class Runtime extends EventEmitter {
953
1017
  await worker.terminate()
954
1018
  }
955
1019
 
956
- this.logger.error({ err: ensureLoggableError(error) }, `Failed to start ${label}.`)
1020
+ if (error.code !== 'PLT_RUNTIME_SERVICE_START_TIMEOUT') {
1021
+ this.logger.error({ err: ensureLoggableError(error) }, `Failed to start ${label}.`)
1022
+ }
957
1023
 
958
1024
  const restartOnError = config.restartOnError
959
1025
 
@@ -983,7 +1049,7 @@ class Runtime extends EventEmitter {
983
1049
 
984
1050
  worker[kWorkerStatus] = 'stopping'
985
1051
 
986
- const label = workersCount > 1 ? `worker ${index} of the service "${id}"` : `service "${id}"`
1052
+ const label = this.#workerExtendedLabel(id, index, workersCount)
987
1053
 
988
1054
  if (!silent) {
989
1055
  this.logger?.info(`Stopping the ${label}...`)
@@ -1022,6 +1088,11 @@ class Runtime extends EventEmitter {
1022
1088
  worker[kITC].close()
1023
1089
  worker[kLoggerDestination].close()
1024
1090
  worker[kLoggingPort].close()
1091
+ clearTimeout(worker[kHealthCheckTimer])
1092
+ }
1093
+
1094
+ #workerExtendedLabel (serviceId, workerId, workersCount) {
1095
+ return workersCount > 1 ? `worker ${workerId} of the service "${serviceId}"` : `service "${serviceId}"`
1025
1096
  }
1026
1097
 
1027
1098
  async #restartCrashedWorker (config, serviceConfig, workersCount, id, index, silent, bootstrapAttempt) {
@@ -1037,6 +1108,12 @@ class Runtime extends EventEmitter {
1037
1108
  setTimeout(async () => {
1038
1109
  this.#restartingWorkers.delete(workerId)
1039
1110
 
1111
+ // If some processes were scheduled to restart
1112
+ // but the runtime is stopped, ignore it
1113
+ if (!this.#status.startsWith('start')) {
1114
+ return
1115
+ }
1116
+
1040
1117
  try {
1041
1118
  await this.#setupWorker(config, serviceConfig, workersCount, id, index)
1042
1119
  await this.#startWorker(config, serviceConfig, workersCount, id, index, silent, bootstrapAttempt)
package/lib/schema.js CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  const telemetry = require('@platformatic/telemetry').schema
5
5
  const {
6
- schemaComponents: { server, logger }
6
+ schemaComponents: { server, logger, health }
7
7
  } = require('@platformatic/utils')
8
8
 
9
9
  const workers = {
@@ -38,7 +38,8 @@ const services = {
38
38
  useHttp: {
39
39
  type: 'boolean'
40
40
  },
41
- workers
41
+ workers,
42
+ health: { ...health, default: undefined }
42
43
  }
43
44
  }
44
45
  }
@@ -94,7 +95,9 @@ const platformaticRuntimeSchema = {
94
95
  },
95
96
  useHttp: {
96
97
  type: 'boolean'
97
- }
98
+ },
99
+ workers,
100
+ health: { ...health, default: undefined }
98
101
  }
99
102
  }
100
103
  }
@@ -105,6 +108,11 @@ const platformaticRuntimeSchema = {
105
108
  web: services,
106
109
  logger,
107
110
  server,
111
+ startTimeout: {
112
+ default: 30000,
113
+ type: 'number',
114
+ minimum: 0
115
+ },
108
116
  restartOnError: {
109
117
  default: true,
110
118
  anyOf: [
@@ -143,6 +151,7 @@ const platformaticRuntimeSchema = {
143
151
  required: ['runtime', 'service'],
144
152
  additionalProperties: false
145
153
  },
154
+ health,
146
155
  undici: {
147
156
  type: 'object',
148
157
  properties: {
package/lib/worker/app.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const { existsSync } = require('node:fs')
4
4
  const { EventEmitter } = require('node:events')
5
5
  const { resolve } = require('node:path')
6
+ const { performance: { eventLoopUtilization } } = require('node:perf_hooks')
6
7
  const { workerData } = require('node:worker_threads')
7
8
  const { ConfigManager } = require('@platformatic/config')
8
9
  const { FileWatcher } = require('@platformatic/utils')
@@ -21,6 +22,7 @@ class PlatformaticApp extends EventEmitter {
21
22
  #fileWatcher
22
23
  #debouncedRestart
23
24
  #context
25
+ #lastELU
24
26
 
25
27
  constructor (
26
28
  appConfig,
@@ -42,6 +44,7 @@ class PlatformaticApp extends EventEmitter {
42
44
  this.#listening = false
43
45
  this.stackable = null
44
46
  this.#fileWatcher = null
47
+ this.#lastELU = eventLoopUtilization()
45
48
 
46
49
  this.#context = {
47
50
  serviceId: this.serviceId,
@@ -209,6 +212,20 @@ class PlatformaticApp extends EventEmitter {
209
212
  return this.stackable.getMetrics({ format })
210
213
  }
211
214
 
215
+ async getHealth () {
216
+ const currentELU = eventLoopUtilization()
217
+ const elu = eventLoopUtilization(currentELU, this.#lastELU).utilization
218
+ this.#lastELU = currentELU
219
+
220
+ const { heapUsed, heapTotal } = process.memoryUsage()
221
+
222
+ return {
223
+ elu,
224
+ heapUsed,
225
+ heapTotal
226
+ }
227
+ }
228
+
212
229
  #fetchServiceUrl (key, { parent, context: service }) {
213
230
  if (service.localServiceEnvVars.has(key)) {
214
231
  return service.localServiceEnvVars.get(key)
package/lib/worker/itc.js CHANGED
@@ -150,6 +150,14 @@ function setupITC (app, service, dispatcher) {
150
150
  }
151
151
  },
152
152
 
153
+ async getHealth () {
154
+ try {
155
+ return await app.getHealth()
156
+ } catch (err) {
157
+ throw new errors.FailedToRetrieveHealthError(service.id, err.message)
158
+ }
159
+ },
160
+
153
161
  inject (injectParams) {
154
162
  return app.stackable.inject(injectParams)
155
163
  }
@@ -5,8 +5,19 @@ const kId = Symbol.for('plt.runtime.id') // This is also used to detect if we ar
5
5
  const kServiceId = Symbol.for('plt.runtime.service.id')
6
6
  const kWorkerId = Symbol.for('plt.runtime.worker.id')
7
7
  const kITC = Symbol.for('plt.runtime.itc')
8
+ const kHealthCheckTimer = Symbol.for('plt.runtime.worker.healthCheckTimer')
8
9
  const kLoggerDestination = Symbol.for('plt.runtime.loggerDestination')
9
10
  const kLoggingPort = Symbol.for('plt.runtime.logginPort')
10
11
  const kWorkerStatus = Symbol('plt.runtime.worker.status')
11
12
 
12
- module.exports = { kConfig, kId, kServiceId, kWorkerId, kITC, kLoggerDestination, kLoggingPort, kWorkerStatus }
13
+ module.exports = {
14
+ kConfig,
15
+ kId,
16
+ kServiceId,
17
+ kWorkerId,
18
+ kITC,
19
+ kHealthCheckTimer,
20
+ kLoggerDestination,
21
+ kLoggingPort,
22
+ kWorkerStatus
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "2.9.1",
3
+ "version": "2.11.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -35,12 +35,12 @@
35
35
  "typescript": "^5.5.4",
36
36
  "undici-oidc-interceptor": "^0.5.0",
37
37
  "why-is-node-running": "^2.2.2",
38
- "@platformatic/db": "2.9.1",
39
- "@platformatic/composer": "2.9.1",
40
- "@platformatic/service": "2.9.1",
41
- "@platformatic/node": "2.9.1",
42
- "@platformatic/sql-graphql": "2.9.1",
43
- "@platformatic/sql-mapper": "2.9.1"
38
+ "@platformatic/composer": "2.11.0",
39
+ "@platformatic/db": "2.11.0",
40
+ "@platformatic/service": "2.11.0",
41
+ "@platformatic/node": "2.11.0",
42
+ "@platformatic/sql-graphql": "2.11.0",
43
+ "@platformatic/sql-mapper": "2.11.0"
44
44
  },
45
45
  "dependencies": {
46
46
  "@fastify/error": "^4.0.0",
@@ -62,22 +62,21 @@
62
62
  "help-me": "^5.0.0",
63
63
  "minimist": "^1.2.8",
64
64
  "pino": "^8.19.0",
65
- "pino-pretty": "^11.0.0",
65
+ "pino-pretty": "^12.0.0",
66
66
  "pino-roll": "^2.0.0",
67
67
  "prom-client": "^15.1.2",
68
68
  "semgrator": "^0.3.0",
69
69
  "tail-file-stream": "^0.2.0",
70
- "thread-cpu-usage": "^0.2.0",
71
70
  "undici": "^6.9.0",
72
- "undici-thread-interceptor": "^0.7.0",
71
+ "undici-thread-interceptor": "^0.8.0",
73
72
  "ws": "^8.16.0",
74
- "@platformatic/basic": "2.9.1",
75
- "@platformatic/config": "2.9.1",
76
- "@platformatic/itc": "2.9.1",
77
- "@platformatic/ts-compiler": "2.9.1",
78
- "@platformatic/generators": "2.9.1",
79
- "@platformatic/utils": "2.9.1",
80
- "@platformatic/telemetry": "2.9.1"
73
+ "@platformatic/basic": "2.11.0",
74
+ "@platformatic/config": "2.11.0",
75
+ "@platformatic/itc": "2.11.0",
76
+ "@platformatic/generators": "2.11.0",
77
+ "@platformatic/telemetry": "2.11.0",
78
+ "@platformatic/ts-compiler": "2.11.0",
79
+ "@platformatic/utils": "2.11.0"
81
80
  },
82
81
  "scripts": {
83
82
  "test": "npm run lint && borp --concurrency=1 --timeout=300000 && tsd",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.9.1.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.11.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "type": "object",
5
5
  "properties": {
@@ -51,6 +51,108 @@
51
51
  },
52
52
  "useHttp": {
53
53
  "type": "boolean"
54
+ },
55
+ "workers": {
56
+ "anyOf": [
57
+ {
58
+ "type": "number",
59
+ "minimum": 1
60
+ },
61
+ {
62
+ "type": "string"
63
+ }
64
+ ]
65
+ },
66
+ "health": {
67
+ "type": "object",
68
+ "properties": {
69
+ "enabled": {
70
+ "default": true,
71
+ "anyOf": [
72
+ {
73
+ "type": "boolean"
74
+ },
75
+ {
76
+ "type": "string"
77
+ }
78
+ ]
79
+ },
80
+ "interval": {
81
+ "default": 30000,
82
+ "anyOf": [
83
+ {
84
+ "type": "number",
85
+ "minimum": 0
86
+ },
87
+ {
88
+ "type": "string"
89
+ }
90
+ ]
91
+ },
92
+ "gracePeriod": {
93
+ "default": 30000,
94
+ "anyOf": [
95
+ {
96
+ "type": "number",
97
+ "minimum": 0
98
+ },
99
+ {
100
+ "type": "string"
101
+ }
102
+ ]
103
+ },
104
+ "maxUnhealthyChecks": {
105
+ "default": 3,
106
+ "anyOf": [
107
+ {
108
+ "type": "number",
109
+ "minimum": 1
110
+ },
111
+ {
112
+ "type": "string"
113
+ }
114
+ ]
115
+ },
116
+ "maxELU": {
117
+ "default": 0.95,
118
+ "anyOf": [
119
+ {
120
+ "type": "number",
121
+ "minimum": 0,
122
+ "maximum": 1
123
+ },
124
+ {
125
+ "type": "string"
126
+ }
127
+ ]
128
+ },
129
+ "maxHeapUsed": {
130
+ "default": 0.95,
131
+ "anyOf": [
132
+ {
133
+ "type": "number",
134
+ "minimum": 0,
135
+ "maximum": 1
136
+ },
137
+ {
138
+ "type": "string"
139
+ }
140
+ ]
141
+ },
142
+ "maxHeapTotal": {
143
+ "default": 4294967296,
144
+ "anyOf": [
145
+ {
146
+ "type": "number",
147
+ "minimum": 0
148
+ },
149
+ {
150
+ "type": "string"
151
+ }
152
+ ]
153
+ }
154
+ },
155
+ "additionalProperties": false
54
156
  }
55
157
  }
56
158
  }
@@ -102,6 +204,97 @@
102
204
  "type": "string"
103
205
  }
104
206
  ]
207
+ },
208
+ "health": {
209
+ "type": "object",
210
+ "properties": {
211
+ "enabled": {
212
+ "default": true,
213
+ "anyOf": [
214
+ {
215
+ "type": "boolean"
216
+ },
217
+ {
218
+ "type": "string"
219
+ }
220
+ ]
221
+ },
222
+ "interval": {
223
+ "default": 30000,
224
+ "anyOf": [
225
+ {
226
+ "type": "number",
227
+ "minimum": 0
228
+ },
229
+ {
230
+ "type": "string"
231
+ }
232
+ ]
233
+ },
234
+ "gracePeriod": {
235
+ "default": 30000,
236
+ "anyOf": [
237
+ {
238
+ "type": "number",
239
+ "minimum": 0
240
+ },
241
+ {
242
+ "type": "string"
243
+ }
244
+ ]
245
+ },
246
+ "maxUnhealthyChecks": {
247
+ "default": 3,
248
+ "anyOf": [
249
+ {
250
+ "type": "number",
251
+ "minimum": 1
252
+ },
253
+ {
254
+ "type": "string"
255
+ }
256
+ ]
257
+ },
258
+ "maxELU": {
259
+ "default": 0.95,
260
+ "anyOf": [
261
+ {
262
+ "type": "number",
263
+ "minimum": 0,
264
+ "maximum": 1
265
+ },
266
+ {
267
+ "type": "string"
268
+ }
269
+ ]
270
+ },
271
+ "maxHeapUsed": {
272
+ "default": 0.95,
273
+ "anyOf": [
274
+ {
275
+ "type": "number",
276
+ "minimum": 0,
277
+ "maximum": 1
278
+ },
279
+ {
280
+ "type": "string"
281
+ }
282
+ ]
283
+ },
284
+ "maxHeapTotal": {
285
+ "default": 4294967296,
286
+ "anyOf": [
287
+ {
288
+ "type": "number",
289
+ "minimum": 0
290
+ },
291
+ {
292
+ "type": "string"
293
+ }
294
+ ]
295
+ }
296
+ },
297
+ "additionalProperties": false
105
298
  }
106
299
  }
107
300
  }
@@ -163,6 +356,97 @@
163
356
  "type": "string"
164
357
  }
165
358
  ]
359
+ },
360
+ "health": {
361
+ "type": "object",
362
+ "properties": {
363
+ "enabled": {
364
+ "default": true,
365
+ "anyOf": [
366
+ {
367
+ "type": "boolean"
368
+ },
369
+ {
370
+ "type": "string"
371
+ }
372
+ ]
373
+ },
374
+ "interval": {
375
+ "default": 30000,
376
+ "anyOf": [
377
+ {
378
+ "type": "number",
379
+ "minimum": 0
380
+ },
381
+ {
382
+ "type": "string"
383
+ }
384
+ ]
385
+ },
386
+ "gracePeriod": {
387
+ "default": 30000,
388
+ "anyOf": [
389
+ {
390
+ "type": "number",
391
+ "minimum": 0
392
+ },
393
+ {
394
+ "type": "string"
395
+ }
396
+ ]
397
+ },
398
+ "maxUnhealthyChecks": {
399
+ "default": 3,
400
+ "anyOf": [
401
+ {
402
+ "type": "number",
403
+ "minimum": 1
404
+ },
405
+ {
406
+ "type": "string"
407
+ }
408
+ ]
409
+ },
410
+ "maxELU": {
411
+ "default": 0.95,
412
+ "anyOf": [
413
+ {
414
+ "type": "number",
415
+ "minimum": 0,
416
+ "maximum": 1
417
+ },
418
+ {
419
+ "type": "string"
420
+ }
421
+ ]
422
+ },
423
+ "maxHeapUsed": {
424
+ "default": 0.95,
425
+ "anyOf": [
426
+ {
427
+ "type": "number",
428
+ "minimum": 0,
429
+ "maximum": 1
430
+ },
431
+ {
432
+ "type": "string"
433
+ }
434
+ ]
435
+ },
436
+ "maxHeapTotal": {
437
+ "default": 4294967296,
438
+ "anyOf": [
439
+ {
440
+ "type": "number",
441
+ "minimum": 0
442
+ },
443
+ {
444
+ "type": "string"
445
+ }
446
+ ]
447
+ }
448
+ },
449
+ "additionalProperties": false
166
450
  }
167
451
  }
168
452
  }
@@ -214,17 +498,25 @@
214
498
  "type": "object",
215
499
  "properties": {
216
500
  "target": {
217
- "type": "string",
218
- "resolveModule": true
501
+ "anyOf": [
502
+ {
503
+ "type": "string",
504
+ "resolveModule": true
505
+ },
506
+ {
507
+ "type": "string",
508
+ "resolvePath": true
509
+ }
510
+ ]
219
511
  },
220
512
  "options": {
221
513
  "type": "object"
222
514
  },
223
515
  "level": {
224
516
  "type": "string"
225
- },
226
- "additionalProperties": false
227
- }
517
+ }
518
+ },
519
+ "additionalProperties": false
228
520
  }
229
521
  },
230
522
  "options": {
@@ -362,6 +654,11 @@
362
654
  },
363
655
  "additionalProperties": false
364
656
  },
657
+ "startTimeout": {
658
+ "default": 30000,
659
+ "type": "number",
660
+ "minimum": 0
661
+ },
365
662
  "restartOnError": {
366
663
  "default": true,
367
664
  "anyOf": [
@@ -409,6 +706,98 @@
409
706
  ],
410
707
  "additionalProperties": false
411
708
  },
709
+ "health": {
710
+ "type": "object",
711
+ "default": {},
712
+ "properties": {
713
+ "enabled": {
714
+ "default": true,
715
+ "anyOf": [
716
+ {
717
+ "type": "boolean"
718
+ },
719
+ {
720
+ "type": "string"
721
+ }
722
+ ]
723
+ },
724
+ "interval": {
725
+ "default": 30000,
726
+ "anyOf": [
727
+ {
728
+ "type": "number",
729
+ "minimum": 0
730
+ },
731
+ {
732
+ "type": "string"
733
+ }
734
+ ]
735
+ },
736
+ "gracePeriod": {
737
+ "default": 30000,
738
+ "anyOf": [
739
+ {
740
+ "type": "number",
741
+ "minimum": 0
742
+ },
743
+ {
744
+ "type": "string"
745
+ }
746
+ ]
747
+ },
748
+ "maxUnhealthyChecks": {
749
+ "default": 3,
750
+ "anyOf": [
751
+ {
752
+ "type": "number",
753
+ "minimum": 1
754
+ },
755
+ {
756
+ "type": "string"
757
+ }
758
+ ]
759
+ },
760
+ "maxELU": {
761
+ "default": 0.95,
762
+ "anyOf": [
763
+ {
764
+ "type": "number",
765
+ "minimum": 0,
766
+ "maximum": 1
767
+ },
768
+ {
769
+ "type": "string"
770
+ }
771
+ ]
772
+ },
773
+ "maxHeapUsed": {
774
+ "default": 0.95,
775
+ "anyOf": [
776
+ {
777
+ "type": "number",
778
+ "minimum": 0,
779
+ "maximum": 1
780
+ },
781
+ {
782
+ "type": "string"
783
+ }
784
+ ]
785
+ },
786
+ "maxHeapTotal": {
787
+ "default": 4294967296,
788
+ "anyOf": [
789
+ {
790
+ "type": "number",
791
+ "minimum": 0
792
+ },
793
+ {
794
+ "type": "string"
795
+ }
796
+ ]
797
+ }
798
+ },
799
+ "additionalProperties": false
800
+ },
412
801
  "undici": {
413
802
  "type": "object",
414
803
  "properties": {