@platformatic/runtime 3.13.1 → 3.15.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
@@ -20,7 +20,16 @@ export type PlatformaticRuntimeConfig = {
20
20
  id: string;
21
21
  config?: string;
22
22
  useHttp?: boolean;
23
- workers?: number | string;
23
+ reuseTcpPorts?: boolean;
24
+ workers?:
25
+ | number
26
+ | string
27
+ | {
28
+ static?: number;
29
+ minimum?: number;
30
+ maximum?: number;
31
+ [k: string]: unknown;
32
+ };
24
33
  health?: {
25
34
  enabled?: boolean | string;
26
35
  interval?: number | string;
@@ -76,7 +85,20 @@ export type PlatformaticRuntimeConfig = {
76
85
  web?: {
77
86
  [k: string]: unknown;
78
87
  }[];
79
- workers?: number | string;
88
+ workers?:
89
+ | number
90
+ | string
91
+ | {
92
+ static?: number;
93
+ dynamic?: boolean;
94
+ minimum?: number;
95
+ maximum?: number;
96
+ total?: number;
97
+ maxMemory?: number;
98
+ cooldown?: number;
99
+ gracePeriod?: number;
100
+ [k: string]: unknown;
101
+ };
80
102
  workersRestartDelay?: number | string;
81
103
  logger?: {
82
104
  level: (
@@ -161,6 +183,7 @@ export type PlatformaticRuntimeConfig = {
161
183
  rejectUnauthorized?: boolean;
162
184
  };
163
185
  };
186
+ reuseTcpPorts?: boolean;
164
187
  startTimeout?: number;
165
188
  restartOnError?: boolean | number;
166
189
  exitOnUnhandledErrors?: boolean;
@@ -285,6 +308,37 @@ export type PlatformaticRuntimeConfig = {
285
308
  };
286
309
  plugins?: string[];
287
310
  timeout?: number | string;
311
+ /**
312
+ * Configuration for exporting metrics to an OTLP endpoint
313
+ */
314
+ otlpExporter?: {
315
+ /**
316
+ * Enable or disable OTLP metrics export
317
+ */
318
+ enabled?: boolean | string;
319
+ /**
320
+ * OTLP endpoint URL (e.g., http://collector:4318/v1/metrics)
321
+ */
322
+ endpoint: string;
323
+ /**
324
+ * Interval in milliseconds between metric pushes
325
+ */
326
+ interval?: number | string;
327
+ /**
328
+ * Additional HTTP headers for authentication
329
+ */
330
+ headers?: {
331
+ [k: string]: string;
332
+ };
333
+ /**
334
+ * Service name for OTLP resource attributes
335
+ */
336
+ serviceName?: string;
337
+ /**
338
+ * Service version for OTLP resource attributes
339
+ */
340
+ serviceVersion?: string;
341
+ };
288
342
  };
289
343
  telemetry?: {
290
344
  enabled?: boolean | string;
@@ -368,13 +422,28 @@ export type PlatformaticRuntimeConfig = {
368
422
  maxTotalMemory?: number;
369
423
  minWorkers?: number;
370
424
  maxWorkers?: number;
425
+ cooldownSec?: number;
426
+ gracePeriod?: number;
427
+ /**
428
+ * @deprecated
429
+ */
371
430
  scaleUpELU?: number;
431
+ /**
432
+ * @deprecated
433
+ */
372
434
  scaleDownELU?: number;
435
+ /**
436
+ * @deprecated
437
+ */
373
438
  timeWindowSec?: number;
439
+ /**
440
+ * @deprecated
441
+ */
374
442
  scaleDownTimeWindowSec?: number;
375
- cooldownSec?: number;
443
+ /**
444
+ * @deprecated
445
+ */
376
446
  scaleIntervalSec?: number;
377
- gracePeriod?: number;
378
447
  applications?: {
379
448
  [k: string]: {
380
449
  minWorkers?: number;
package/index.d.ts CHANGED
@@ -55,9 +55,11 @@ export module symbols {
55
55
  export declare const kWorkerId: unique symbol
56
56
  export declare const kITC: unique symbol
57
57
  export declare const kHealthCheckTimer: unique symbol
58
+ export declare const kHealthMetricsTimer: unique symbol
58
59
  export declare const kLastHealthCheckELU: unique symbol
59
60
  export declare const kLastVerticalScalerELU: unique symbol
60
61
  export declare const kWorkerStatus: unique symbol
62
+ export declare const kWorkerHealthSignals: unique symbol
61
63
  export declare const kStderrMarker: string
62
64
  export declare const kInterceptors: unique symbol
63
65
  export declare const kWorkersBroadcast: unique symbol
@@ -90,6 +92,8 @@ export function create (
90
92
  context?: ConfigurationOptions
91
93
  ): Promise<Runtime>
92
94
 
95
+ export declare function prepareApplication (config: RuntimeConfiguration, application: object): object
96
+
93
97
  export declare function transform (config: RuntimeConfiguration): Promise<RuntimeConfiguration>
94
98
 
95
99
  export declare function loadApplicationsCommands (): Promise<ApplicationsCommands>
package/index.js CHANGED
@@ -127,6 +127,7 @@ export async function loadApplicationsCommands () {
127
127
  }
128
128
 
129
129
  export async function create (configOrRoot, sourceOrConfig, context) {
130
+ const setupSignals = context?.setupSignals ?? true
130
131
  const config = await loadConfiguration(configOrRoot, sourceOrConfig, context)
131
132
 
132
133
  if (inspector.url() && !config[kMetadata].env.VSCODE_INSPECTOR_OPTIONS) {
@@ -134,7 +135,9 @@ export async function create (configOrRoot, sourceOrConfig, context) {
134
135
  }
135
136
 
136
137
  let runtime = new Runtime(config, context)
137
- handleSignal(runtime, config)
138
+ if (setupSignals) {
139
+ handleSignal(runtime, config)
140
+ }
138
141
 
139
142
  // Handle port handling
140
143
  if (context?.start) {
@@ -160,7 +163,9 @@ export async function create (configOrRoot, sourceOrConfig, context) {
160
163
 
161
164
  config.server.port = ++port
162
165
  runtime = new Runtime(config, context)
163
- handleSignal(runtime, config)
166
+ if (setupSignals) {
167
+ handleSignal(runtime, config)
168
+ }
164
169
  }
165
170
  }
166
171
  }
@@ -168,7 +173,7 @@ export async function create (configOrRoot, sourceOrConfig, context) {
168
173
  return runtime
169
174
  }
170
175
 
171
- export { transform, wrapInRuntimeConfig } from './lib/config.js'
176
+ export { prepareApplication, transform, wrapInRuntimeConfig } from './lib/config.js'
172
177
  export * as errors from './lib/errors.js'
173
178
  export { RuntimeGenerator as Generator, WrappedGenerator } from './lib/generator.js'
174
179
  export { Runtime } from './lib/runtime.js'
package/lib/config.js CHANGED
@@ -45,6 +45,42 @@ function raiseInvalidWorkersError (location, received, hint) {
45
45
  throw new InvalidArgumentError(`${location} workers must be a positive integer; received "${received}"${extra}`)
46
46
  }
47
47
 
48
+ function parseWorkers (config, prefix, defaultWorkers = 1) {
49
+ if (typeof config.workers !== 'undefined') {
50
+ // Number
51
+ if (typeof config.workers !== 'object') {
52
+ const coerced = coercePositiveInteger(config.workers)
53
+
54
+ if (coerced === null) {
55
+ const raw = config.workers
56
+ const hint = typeof raw === 'string' && /\{.*\}/.test(raw) ? 'check your environment variable' : ''
57
+ raiseInvalidWorkersError(prefix, config.workers, hint)
58
+ } else {
59
+ config.workers = { static: coerced, dynamic: false }
60
+ }
61
+ // Object
62
+ } else {
63
+ for (const key of ['minimum', 'maximum', 'static']) {
64
+ if (typeof config.workers[key] === 'undefined') {
65
+ continue
66
+ }
67
+
68
+ const coerced = coercePositiveInteger(config.workers[key])
69
+ if (coerced === null) {
70
+ const raw = config.workers
71
+ const hint = typeof raw === 'string' && /\{.*\}/.test(raw) ? 'check your environment variable' : ''
72
+ raiseInvalidWorkersError(`${prefix} ${key}`, config.workers, hint)
73
+ } else {
74
+ config.workers[key] = coerced
75
+ }
76
+ }
77
+ }
78
+ // No value, inherit from runtime
79
+ } else {
80
+ config.workers = { static: defaultWorkers }
81
+ }
82
+ }
83
+
48
84
  export function pprofCapturePreloadPath () {
49
85
  const require = createRequire(import.meta.url)
50
86
 
@@ -162,6 +198,64 @@ export function parseInspectorOptions (config, inspect, inspectBreak) {
162
198
  config.watch = false
163
199
  }
164
200
 
201
+ export async function prepareApplication (config, application, defaultWorkers) {
202
+ // We need to have absolute paths here, ot the `loadConfig` will fail
203
+ // Make sure we don't resolve if env var was not replaced
204
+ if (application.path && !isAbsolute(application.path) && !application.path.match(/^\{.*\}$/)) {
205
+ application.path = resolvePath(config[kMetadata].root, application.path)
206
+ }
207
+
208
+ if (application.path && application.config) {
209
+ application.config = resolvePath(application.path, application.config)
210
+ }
211
+
212
+ try {
213
+ let pkg
214
+
215
+ if (application.config) {
216
+ const config = await loadConfiguration(application.config)
217
+ pkg = await loadConfigurationModule(application.path, config)
218
+
219
+ application.type = extractModuleFromSchemaUrl(config, true).module
220
+ application.skipTelemetryHooks = pkg.skipTelemetryHooks
221
+ } else {
222
+ const { moduleName, capability } = await importCapabilityAndConfig(application.path)
223
+ pkg = capability
224
+
225
+ application.type = moduleName
226
+ }
227
+
228
+ application.skipTelemetryHooks = pkg.skipTelemetryHooks
229
+
230
+ // This is needed to work around Rust bug on dylibs:
231
+ // https://github.com/rust-lang/rust/issues/91979
232
+ // https://github.com/rollup/rollup/issues/5761
233
+ const _require = createRequire(application.path)
234
+ for (const m of pkg.modulesToLoad ?? []) {
235
+ const toLoad = _require.resolve(m)
236
+ loadModule(_require, toLoad).catch(() => {})
237
+ }
238
+ } catch (err) {
239
+ // This should not happen, it happens on running some unit tests if we prepare the runtime
240
+ // when not all the applications configs are available. Given that we are running this only
241
+ // to ddetermine the type of the application, it's safe to ignore this error and default to unknown
242
+ application.type = 'unknown'
243
+ }
244
+
245
+ // Validate and coerce per-service workers
246
+ parseWorkers(application, `Service "${application.id}"`, defaultWorkers)
247
+
248
+ application.entrypoint = application.id === config.entrypoint
249
+ application.dependencies ??= []
250
+ application.localUrl = `http://${application.id}.plt.local`
251
+
252
+ if (typeof application.watch === 'undefined') {
253
+ application.watch = config.watch
254
+ }
255
+
256
+ return application
257
+ }
258
+
165
259
  export async function transform (config, _, context) {
166
260
  const production = context?.isProduction ?? context?.production
167
261
  const applications = [...(config.applications ?? []), ...(config.services ?? []), ...(config.web ?? [])]
@@ -173,6 +267,27 @@ export async function transform (config, _, context) {
173
267
  config.watch = !production
174
268
  }
175
269
 
270
+ // Migrate the old verticalScaler property, only applied if the new settings are not set, otherwise workers takes precedence
271
+ // TODO: Remove in the next major version
272
+ if (config.verticalScaler) {
273
+ config.workers ??= {}
274
+ config.workers.dynamic ??= config.verticalScaler.enabled
275
+ config.workers.minimum ??= config.verticalScaler.minWorkers
276
+ config.workers.maximum ??= config.verticalScaler.maxWorkers
277
+ config.workers.total ??= config.verticalScaler.maxTotalWorkers
278
+ config.workers.maxMemory ??= config.verticalScaler.maxTotalMemory
279
+
280
+ if (typeof config.workers.cooldown === 'undefined' && typeof config.verticalScaler.cooldownSec === 'number') {
281
+ config.workers.cooldown = config.verticalScaler.cooldownSec * 1000
282
+ }
283
+
284
+ if (typeof config.workers.gracePeriod === 'undefined' && typeof config.verticalScaler.gracePeriod === 'number') {
285
+ config.workers.gracePeriod = config.verticalScaler.gracePeriod
286
+ }
287
+
288
+ config.verticalScaler = undefined
289
+ }
290
+
176
291
  if (config.autoload) {
177
292
  const { exclude = [], mappings = {} } = config.autoload
178
293
  let { path } = config.autoload
@@ -215,80 +330,11 @@ export async function transform (config, _, context) {
215
330
  let hasValidEntrypoint = false
216
331
 
217
332
  // Root-level workers
218
- if (typeof config.workers !== 'undefined') {
219
- const coerced = coercePositiveInteger(config.workers)
220
- if (coerced === null) {
221
- const raw = config.workers
222
- const hint = typeof raw === 'string' && /\{.*\}/.test(raw) ? 'check your environment variable' : ''
223
- raiseInvalidWorkersError('Runtime', config.workers, hint)
224
- }
225
- config.workers = coerced
226
- }
333
+ parseWorkers(config, 'Runtime')
334
+ const defaultWorkers = config.workers.static
227
335
 
228
336
  for (let i = 0; i < applications.length; ++i) {
229
- const application = applications[i]
230
-
231
- // We need to have absolute paths here, ot the `loadConfig` will fail
232
- // Make sure we don't resolve if env var was not replaced
233
- if (application.path && !isAbsolute(application.path) && !application.path.match(/^\{.*\}$/)) {
234
- application.path = resolvePath(config[kMetadata].root, application.path)
235
- }
236
-
237
- if (application.path && application.config) {
238
- application.config = resolvePath(application.path, application.config)
239
- }
240
-
241
- try {
242
- let pkg
243
-
244
- if (application.config) {
245
- const config = await loadConfiguration(application.config)
246
- pkg = await loadConfigurationModule(application.path, config)
247
-
248
- application.type = extractModuleFromSchemaUrl(config, true).module
249
- application.skipTelemetryHooks = pkg.skipTelemetryHooks
250
- } else {
251
- const { moduleName, capability } = await importCapabilityAndConfig(application.path)
252
- pkg = capability
253
-
254
- application.type = moduleName
255
- }
256
-
257
- application.skipTelemetryHooks = pkg.skipTelemetryHooks
258
-
259
- // This is needed to work around Rust bug on dylibs:
260
- // https://github.com/rust-lang/rust/issues/91979
261
- // https://github.com/rollup/rollup/issues/5761
262
- const _require = createRequire(application.path)
263
- for (const m of pkg.modulesToLoad ?? []) {
264
- const toLoad = _require.resolve(m)
265
- loadModule(_require, toLoad).catch(() => {})
266
- }
267
- } catch (err) {
268
- // This should not happen, it happens on running some unit tests if we prepare the runtime
269
- // when not all the applications configs are available. Given that we are running this only
270
- // to ddetermine the type of the application, it's safe to ignore this error and default to unknown
271
- application.type = 'unknown'
272
- }
273
-
274
- // Validate and coerce per-service workers
275
- if (typeof application.workers !== 'undefined') {
276
- const coerced = coercePositiveInteger(application.workers)
277
- if (coerced === null) {
278
- const raw = config.application?.[i]?.workers
279
- const hint = typeof raw === 'string' && /\{.*\}/.test(raw) ? 'check your environment variable' : ''
280
- raiseInvalidWorkersError(`Service "${application.id}"`, application.workers, hint)
281
- }
282
- application.workers = coerced
283
- }
284
-
285
- application.entrypoint = application.id === config.entrypoint
286
- application.dependencies ??= []
287
- application.localUrl = `http://${application.id}.plt.local`
288
-
289
- if (typeof application.watch === 'undefined') {
290
- application.watch = config.watch
291
- }
337
+ const application = await prepareApplication(config, applications[i], defaultWorkers)
292
338
 
293
339
  if (application.entrypoint) {
294
340
  hasValidEntrypoint = true
@@ -0,0 +1,218 @@
1
+ import { features } from '@platformatic/foundation'
2
+ import { availableParallelism } from 'node:os'
3
+ import { getMemoryInfo } from './metrics.js'
4
+ import { ScalingAlgorithm, scaleUpELUThreshold } from './scaling-algorithm.js'
5
+ import { kApplicationId, kId, kLastVerticalScalerELU, kWorkerStartTime, kWorkerStatus } from './worker/symbols.js'
6
+
7
+ const healthCheckInterval = 1000
8
+ export const kOriginalWorkers = Symbol('plt.runtime.application.dynamicWorkersScalerOriginalWorkers')
9
+
10
+ const defaultCooldown = 60_000
11
+ const defaultGracePeriod = 30_000
12
+ const scaleIntervalPeriod = 60_000
13
+
14
+ export class DynamicWorkersScaler {
15
+ #status
16
+ #runtime
17
+ #algorithm
18
+
19
+ #maxTotalMemory
20
+ #maxTotalWorkers
21
+ #maxWorkers
22
+ #minWorkers
23
+ #cooldown
24
+ #gracePeriod
25
+
26
+ #initialUpdates
27
+ #memoryInfo
28
+ #healthCheckTimeout
29
+ #checkScalingInterval
30
+ #isScaling
31
+ #lastScaling
32
+
33
+ constructor (runtime, config) {
34
+ this.#runtime = runtime
35
+
36
+ this.#maxTotalMemory = config.maxMemory // This is defaulted in start()
37
+ this.#maxTotalWorkers = config.total ?? availableParallelism()
38
+ this.#maxWorkers = config.maximum ?? this.#maxTotalWorkers
39
+ this.#minWorkers = config.minimum ?? 1
40
+ this.#cooldown = config.cooldown ?? defaultCooldown
41
+ this.#gracePeriod = config.gracePeriod ?? defaultGracePeriod
42
+
43
+ this.#algorithm = new ScalingAlgorithm({ maxTotalWorkers: this.#maxTotalWorkers })
44
+
45
+ this.#isScaling = false
46
+ this.#lastScaling = 0
47
+ this.#initialUpdates = []
48
+ this.#status = 'init'
49
+ }
50
+
51
+ getConfig () {
52
+ return {
53
+ maxTotalMemory: this.#maxTotalMemory,
54
+ maxTotalWorkers: this.#maxTotalWorkers,
55
+ maxWorkers: this.#maxWorkers,
56
+ minWorkers: this.#minWorkers,
57
+ cooldown: this.#cooldown,
58
+ gracePeriod: this.#gracePeriod
59
+ }
60
+ }
61
+
62
+ async start () {
63
+ this.#memoryInfo = await getMemoryInfo()
64
+ this.#maxTotalMemory ??= this.#memoryInfo.total * 0.9
65
+
66
+ this.#checkScalingInterval = setInterval(this.#checkScaling.bind(this), scaleIntervalPeriod)
67
+ this.#healthCheckTimeout = setTimeout(this.#chechHealth.bind(this), healthCheckInterval)
68
+
69
+ if (this.#initialUpdates.length > 0) {
70
+ await this.#runtime.updateApplicationsResources(this.#initialUpdates)
71
+ this.#initialUpdates = []
72
+ }
73
+
74
+ this.#status = 'started'
75
+ }
76
+
77
+ stop () {
78
+ clearTimeout(this.#healthCheckTimeout)
79
+ clearInterval(this.#checkScalingInterval)
80
+ this.#status = 'stopped'
81
+ }
82
+
83
+ async add (application) {
84
+ const config = {}
85
+
86
+ if (application.entrypoint && !features.node.reusePort) {
87
+ this.#runtime.logger.warn(
88
+ `The "${application.id}" application cannot be scaled because it is an entrypoint and the "reusePort" feature is not available in your OS.`
89
+ )
90
+
91
+ config.minWorkers = 1
92
+ config.maxWorkers = 1
93
+ } else if (application.workers.dynamic === false) {
94
+ this.#runtime.logger.warn(
95
+ `The "${application.id}" application cannot be scaled because it has a fixed number of workers (${application.workers.static}).`
96
+ )
97
+
98
+ config.minWorkers = application.workers.static
99
+ config.maxWorkers = application.workers.static
100
+ } else {
101
+ config.minWorkers = application.workers.minimum
102
+ config.maxWorkers = application.workers.maximum
103
+ }
104
+
105
+ config.minWorkers ??= this.#minWorkers
106
+ config.maxWorkers ??= this.#maxWorkers
107
+
108
+ if (config.minWorkers > 1) {
109
+ const update = { application: application.id, workers: config.minWorkers }
110
+
111
+ if (!this.#status === 'started') {
112
+ await this.#runtime.updateApplicationsResources([update])
113
+ } else {
114
+ this.#initialUpdates.push(update)
115
+ }
116
+ }
117
+
118
+ this.#algorithm.addApplication(application.id, config)
119
+ }
120
+
121
+ remove (application) {
122
+ this.#algorithm.removeApplication(application.id)
123
+ }
124
+
125
+ async #chechHealth () {
126
+ let shouldCheckForScaling = false
127
+
128
+ const now = Date.now()
129
+
130
+ const workers = await this.#runtime.getWorkers(true)
131
+
132
+ for (const { raw: worker } of Object.values(workers)) {
133
+ if (worker[kWorkerStatus] !== 'started' || worker[kWorkerStartTime] + this.#gracePeriod > now) {
134
+ continue
135
+ }
136
+
137
+ try {
138
+ const health = await this.#runtime.getWorkerHealth(worker, { previousELU: worker[kLastVerticalScalerELU] })
139
+
140
+ if (!health) {
141
+ continue
142
+ }
143
+
144
+ worker[kLastVerticalScalerELU] = health.currentELU
145
+
146
+ this.#algorithm.addWorkerHealthInfo({
147
+ workerId: worker[kId],
148
+ applicationId: worker[kApplicationId],
149
+ elu: health.elu,
150
+ heapUsed: health.heapUsed,
151
+ heapTotal: health.heapTotal
152
+ })
153
+
154
+ if (health.elu > scaleUpELUThreshold) {
155
+ shouldCheckForScaling = true
156
+ }
157
+ } catch (err) {
158
+ this.logger.error({ err }, 'Failed to get health for worker')
159
+ }
160
+ }
161
+
162
+ if (shouldCheckForScaling) {
163
+ await this.#checkScaling()
164
+ }
165
+
166
+ this.#healthCheckTimeout.refresh()
167
+ }
168
+
169
+ async #checkScaling () {
170
+ const isInCooldown = Date.now() < this.#lastScaling + this.#cooldown
171
+ if (this.#isScaling || isInCooldown) {
172
+ return
173
+ }
174
+
175
+ this.#isScaling = true
176
+
177
+ try {
178
+ const workersInfo = await this.#runtime.getWorkers()
179
+ const mem = await getMemoryInfo({ scope: this.#memoryInfo.scope })
180
+
181
+ const appsWorkersInfo = {}
182
+ for (const worker of Object.values(workersInfo)) {
183
+ if (worker.status === 'exited') {
184
+ continue
185
+ }
186
+
187
+ const applicationId = worker.application
188
+ appsWorkersInfo[applicationId] ??= 0
189
+ appsWorkersInfo[applicationId]++
190
+ }
191
+
192
+ const availableMemory = this.#maxTotalMemory - mem.used
193
+ const recommendations = this.#algorithm.getRecommendations(appsWorkersInfo, { availableMemory })
194
+
195
+ if (recommendations.length > 0) {
196
+ await this.#applyRecommendations(recommendations)
197
+ this.#lastScaling = Date.now()
198
+ }
199
+ } catch (err) {
200
+ this.#runtime.logger.error({ err }, 'Failed to scale applications')
201
+ } finally {
202
+ this.#isScaling = false
203
+ }
204
+ }
205
+
206
+ async #applyRecommendations (recommendations) {
207
+ const resourcesUpdates = []
208
+
209
+ for (const recommendation of recommendations) {
210
+ const { applicationId, workersCount, direction } = recommendation
211
+ this.#runtime.logger.info(`Scaling ${direction} the "${applicationId}" app to ${workersCount} workers`)
212
+
213
+ resourcesUpdates.push({ application: applicationId, workers: workersCount })
214
+ }
215
+
216
+ return this.#runtime.updateApplicationsResources(resourcesUpdates)
217
+ }
218
+ }
package/lib/errors.js CHANGED
@@ -127,7 +127,24 @@ export const MissingPprofCapture = createError(
127
127
  `${ERROR_PREFIX}_MISSING_PPROF_CAPTURE`,
128
128
  'Please install @platformatic/wattpm-pprof-capture'
129
129
  )
130
+
130
131
  export const GetHeapStatisticUnavailable = createError(
131
132
  `${ERROR_PREFIX}_GET_HEAP_STATISTIC_UNAVAILABLE`,
132
133
  'The getHeapStatistics method is not available in your Node version'
133
134
  )
135
+ export const FailedToSendHealthSignalsError = createError(
136
+ `${ERROR_PREFIX}_FAILED_TO_SEND_HEALTH_SIGNALS`,
137
+ 'Cannot send health signals from application "%s": %s'
138
+ )
139
+ export const HealthSignalMustBeObjectError = createError(
140
+ `${ERROR_PREFIX}_HEALTH_SIGNAL_MUST_BE_OBJECT`,
141
+ 'Health signal must be an object'
142
+ )
143
+ export const HealthSignalTypeMustBeStringError = createError(
144
+ `${ERROR_PREFIX}_HEALTH_SIGNAL_TYPE_MUST_BE_STRING`,
145
+ 'Health signal type must be a string, received "%s"'
146
+ )
147
+ export const CannotRemoveEntrypointError = createError(
148
+ `${ERROR_PREFIX}_CANNOT_REMOVE_ENTRYPOINT`,
149
+ 'Cannot remove the entrypoint application.'
150
+ )