@platformatic/runtime 3.14.0 → 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 +2 -0
- package/index.js +7 -2
- package/lib/logger.js +141 -10
- package/lib/runtime.js +32 -14
- package/package.json +15 -15
- package/schema.json +21 -1
package/config.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export type PlatformaticRuntimeConfig = {
|
|
|
20
20
|
id: string;
|
|
21
21
|
config?: string;
|
|
22
22
|
useHttp?: boolean;
|
|
23
|
+
reuseTcpPorts?: boolean;
|
|
23
24
|
workers?:
|
|
24
25
|
| number
|
|
25
26
|
| string
|
|
@@ -182,6 +183,7 @@ export type PlatformaticRuntimeConfig = {
|
|
|
182
183
|
rejectUnauthorized?: boolean;
|
|
183
184
|
};
|
|
184
185
|
};
|
|
186
|
+
reuseTcpPorts?: boolean;
|
|
185
187
|
startTimeout?: number;
|
|
186
188
|
restartOnError?: boolean | number;
|
|
187
189
|
exitOnUnhandledErrors?: boolean;
|
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
|
-
|
|
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
|
-
|
|
166
|
+
if (setupSignals) {
|
|
167
|
+
handleSignal(runtime, config)
|
|
168
|
+
}
|
|
164
169
|
}
|
|
165
170
|
}
|
|
166
171
|
}
|
package/lib/logger.js
CHANGED
|
@@ -5,19 +5,150 @@ import pretty from 'pino-pretty'
|
|
|
5
5
|
|
|
6
6
|
export { abstractLogger } from '@platformatic/foundation'
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
// A valid color in the ANSI 256 color palette - Adiacent colors are purposely different
|
|
9
|
+
const colors = [
|
|
10
|
+
196, // bright red
|
|
11
|
+
46, // bright green
|
|
12
|
+
33, // light blue
|
|
13
|
+
226, // bright yellow
|
|
14
|
+
201, // bright magenta
|
|
15
|
+
51, // cyan
|
|
16
|
+
208, // orange
|
|
17
|
+
118, // lime
|
|
18
|
+
39, // deep sky blue
|
|
19
|
+
220, // gold
|
|
20
|
+
129, // violet
|
|
21
|
+
82, // spring green
|
|
22
|
+
33, // blue
|
|
23
|
+
214, // amber
|
|
24
|
+
99, // orchid
|
|
25
|
+
190, // light yellow-green
|
|
26
|
+
45, // turquoise
|
|
27
|
+
197, // rose
|
|
28
|
+
50, // aqua
|
|
29
|
+
202, // orange-red
|
|
30
|
+
141, // lavender
|
|
31
|
+
154, // pale green
|
|
32
|
+
93, // pink
|
|
33
|
+
33, // light blue again (for spacing)
|
|
34
|
+
220, // gold
|
|
35
|
+
201, // magenta
|
|
36
|
+
46, // green
|
|
37
|
+
27, // navy blue
|
|
38
|
+
214, // amber
|
|
39
|
+
99, // orchid
|
|
40
|
+
190, // light yellow-green
|
|
41
|
+
39, // cyan-blue
|
|
42
|
+
200, // violet
|
|
43
|
+
82, // neon green
|
|
44
|
+
208, // orange
|
|
45
|
+
135, // purple
|
|
46
|
+
118, // lime
|
|
47
|
+
33, // bright blue
|
|
48
|
+
220, // gold
|
|
49
|
+
201, // bright magenta
|
|
50
|
+
46, // bright green
|
|
51
|
+
21, // bright blue
|
|
52
|
+
202, // orange-red
|
|
53
|
+
141, // purple
|
|
54
|
+
118, // spring green
|
|
55
|
+
208, // orange
|
|
56
|
+
93, // pink
|
|
57
|
+
190, // yellow-green
|
|
58
|
+
39, // cyan
|
|
59
|
+
196, // bright red
|
|
60
|
+
226 // bright yellow
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
function createLoggerContext () {
|
|
64
|
+
const context = {
|
|
65
|
+
colors: {},
|
|
66
|
+
maxLength: 0,
|
|
67
|
+
updatePrefixes (ids) {
|
|
68
|
+
context.colors = {}
|
|
69
|
+
context.maxLength = 0
|
|
70
|
+
|
|
71
|
+
for (const id of ids) {
|
|
72
|
+
context.maxLength = Math.max(context.maxLength, id.length)
|
|
73
|
+
let hash = 0
|
|
74
|
+
|
|
75
|
+
if (!pretty.isColorSupported && process.env.FORCE_COLOR !== 'true') {
|
|
76
|
+
context.colors[id] = ''
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Calculate the hash of the id to pick a color
|
|
81
|
+
for (const char of id) {
|
|
82
|
+
for (let i = 0; i < char.length; i++) {
|
|
83
|
+
hash = ((hash << 5) - hash) ^ char.charCodeAt(i)
|
|
84
|
+
hash = Math.abs(hash) % Number.MAX_SAFE_INTEGER
|
|
85
|
+
}
|
|
86
|
+
}
|
|
14
87
|
|
|
15
|
-
|
|
88
|
+
context.colors[id] = `\u001B[38;5;${colors[hash % colors.length]}m`
|
|
89
|
+
}
|
|
90
|
+
}
|
|
16
91
|
}
|
|
92
|
+
|
|
93
|
+
return context
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function createPrettifier (context) {
|
|
97
|
+
return pretty({
|
|
98
|
+
messageFormat (log, key) {
|
|
99
|
+
const { name, pid, hostname, caller, worker } = log
|
|
100
|
+
|
|
101
|
+
context.current = {
|
|
102
|
+
name,
|
|
103
|
+
pid,
|
|
104
|
+
hostname,
|
|
105
|
+
caller,
|
|
106
|
+
worker
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let prefix = ''
|
|
110
|
+
let color = ''
|
|
111
|
+
|
|
112
|
+
if (name) {
|
|
113
|
+
prefix = name.match(/:\d+$/) ? name : `${name}:${worker}`
|
|
114
|
+
color = context.colors[prefix] ?? ''
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
context.current.prefix = `(${pid}) ` + prefix.padStart(context.maxLength, ' ')
|
|
118
|
+
context.current.color = color
|
|
119
|
+
|
|
120
|
+
// We need to nullify all these so that prettifierMetadata in pino-pretty returns an empty string
|
|
121
|
+
log.name = undefined
|
|
122
|
+
log.pid = undefined
|
|
123
|
+
log.hostname = undefined
|
|
124
|
+
log.caller = undefined
|
|
125
|
+
log.worker = undefined
|
|
126
|
+
|
|
127
|
+
return log[key]
|
|
128
|
+
},
|
|
129
|
+
customPrettifiers: {
|
|
130
|
+
time (time) {
|
|
131
|
+
return `${context.current.color}[${time}]`
|
|
132
|
+
},
|
|
133
|
+
level (_u1, _u2, _u3, { label, labelColorized }) {
|
|
134
|
+
// No applications registered yet, no need to pad
|
|
135
|
+
if (context.maxLength === 0) {
|
|
136
|
+
return context.current.prefix + labelColorized
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const current = context.current
|
|
140
|
+
const level = current.caller ? current.caller : labelColorized.replace(label, label.padStart(6, ' '))
|
|
141
|
+
|
|
142
|
+
return `${current.prefix} | \u001B[0m ${level}`
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
})
|
|
17
146
|
}
|
|
18
147
|
|
|
19
148
|
// Create the runtime logger
|
|
20
149
|
export async function createLogger (config) {
|
|
150
|
+
const context = createLoggerContext()
|
|
151
|
+
|
|
21
152
|
const loggerConfig = { ...config.logger, transport: undefined }
|
|
22
153
|
if (config.logger.base === null) {
|
|
23
154
|
loggerConfig.base = undefined
|
|
@@ -28,7 +159,7 @@ export async function createLogger (config) {
|
|
|
28
159
|
if (config.logger.transport) {
|
|
29
160
|
cliStream = pino.transport(config.logger.transport)
|
|
30
161
|
} else if ((process.env.FORCE_TTY || isatty(1)) && usePrettyPrint()) {
|
|
31
|
-
cliStream =
|
|
162
|
+
cliStream = createPrettifier(context)
|
|
32
163
|
} else {
|
|
33
164
|
cliStream = pino.destination(1)
|
|
34
165
|
}
|
|
@@ -42,7 +173,7 @@ export async function createLogger (config) {
|
|
|
42
173
|
}
|
|
43
174
|
|
|
44
175
|
if (!config.managementApi) {
|
|
45
|
-
return [pino(loggerConfig, cliStream), cliStream]
|
|
176
|
+
return [pino(loggerConfig, cliStream), cliStream, context]
|
|
46
177
|
}
|
|
47
178
|
|
|
48
179
|
const multiStream = pino.multistream([{ stream: cliStream, level: loggerConfig.level }])
|
|
@@ -55,5 +186,5 @@ export async function createLogger (config) {
|
|
|
55
186
|
logsLimitCount = 1
|
|
56
187
|
}
|
|
57
188
|
|
|
58
|
-
return [pino(loggerConfig, multiStream), multiStream]
|
|
189
|
+
return [pino(loggerConfig, multiStream), multiStream, context]
|
|
59
190
|
}
|
package/lib/runtime.js
CHANGED
|
@@ -47,9 +47,9 @@ import { startPrometheusServer } from './prom-server.js'
|
|
|
47
47
|
import { startScheduler } from './scheduler.js'
|
|
48
48
|
import { createSharedStore } from './shared-http-cache.js'
|
|
49
49
|
import { version } from './version.js'
|
|
50
|
+
import { HealthSignalsQueue } from './worker/health-signals.js'
|
|
50
51
|
import { sendViaITC, waitEventFromITC } from './worker/itc.js'
|
|
51
52
|
import { RoundRobinMap } from './worker/round-robin-map.js'
|
|
52
|
-
import { HealthSignalsQueue } from './worker/health-signals.js'
|
|
53
53
|
import {
|
|
54
54
|
kApplicationId,
|
|
55
55
|
kConfig,
|
|
@@ -60,11 +60,11 @@ import {
|
|
|
60
60
|
kITC,
|
|
61
61
|
kLastHealthCheckELU,
|
|
62
62
|
kStderrMarker,
|
|
63
|
+
kWorkerHealthSignals,
|
|
63
64
|
kWorkerId,
|
|
64
65
|
kWorkersBroadcast,
|
|
65
66
|
kWorkerStartTime,
|
|
66
|
-
kWorkerStatus
|
|
67
|
-
kWorkerHealthSignals
|
|
67
|
+
kWorkerStatus
|
|
68
68
|
} from './worker/symbols.js'
|
|
69
69
|
|
|
70
70
|
const kWorkerFile = join(import.meta.dirname, 'worker/main.js')
|
|
@@ -84,6 +84,7 @@ export class Runtime extends EventEmitter {
|
|
|
84
84
|
error
|
|
85
85
|
|
|
86
86
|
#loggerDestination
|
|
87
|
+
#loggerContext
|
|
87
88
|
#stdio
|
|
88
89
|
|
|
89
90
|
#status // starting, started, stopping, stopped, closed
|
|
@@ -201,9 +202,10 @@ export class Runtime extends EventEmitter {
|
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
// Create the logger
|
|
204
|
-
const [logger, destination] = await createLogger(config)
|
|
205
|
+
const [logger, destination, context] = await createLogger(config)
|
|
205
206
|
this.logger = logger
|
|
206
207
|
this.#loggerDestination = destination
|
|
208
|
+
this.#loggerContext = context
|
|
207
209
|
|
|
208
210
|
this.#createWorkersBroadcastChannel()
|
|
209
211
|
|
|
@@ -358,6 +360,7 @@ export class Runtime extends EventEmitter {
|
|
|
358
360
|
|
|
359
361
|
this.logger = abstractLogger
|
|
360
362
|
this.#loggerDestination = null
|
|
363
|
+
this.#loggerContext = null
|
|
361
364
|
}
|
|
362
365
|
|
|
363
366
|
this.#updateStatus('closed')
|
|
@@ -466,12 +469,15 @@ export class Runtime extends EventEmitter {
|
|
|
466
469
|
await executeInParallel(this.#setupApplication.bind(this), setupInvocations, this.#concurrency)
|
|
467
470
|
|
|
468
471
|
for (const application of applications) {
|
|
472
|
+
this.logger.info(`Added application "${application.id}"${application.entrypoint ? ' (entrypoint)' : ''}.`)
|
|
469
473
|
this.emitAndNotify('application:added', application)
|
|
470
474
|
}
|
|
471
475
|
|
|
472
476
|
if (start) {
|
|
473
477
|
await this.startApplications(toStart)
|
|
474
478
|
}
|
|
479
|
+
|
|
480
|
+
this.#updateLoggingPrefixes()
|
|
475
481
|
}
|
|
476
482
|
|
|
477
483
|
async removeApplications (applications, silent = false) {
|
|
@@ -487,8 +493,11 @@ export class Runtime extends EventEmitter {
|
|
|
487
493
|
}
|
|
488
494
|
|
|
489
495
|
for (const application of applications) {
|
|
496
|
+
this.logger.info(`Removed application "${application}".`)
|
|
490
497
|
this.emitAndNotify('application:removed', application)
|
|
491
498
|
}
|
|
499
|
+
|
|
500
|
+
this.#updateLoggingPrefixes()
|
|
492
501
|
}
|
|
493
502
|
|
|
494
503
|
async startApplications (applicationsToStart, silent = false) {
|
|
@@ -1666,13 +1675,7 @@ export class Runtime extends EventEmitter {
|
|
|
1666
1675
|
|
|
1667
1676
|
const healthConfig = worker[kConfig].health
|
|
1668
1677
|
|
|
1669
|
-
let {
|
|
1670
|
-
maxELU,
|
|
1671
|
-
maxHeapUsed,
|
|
1672
|
-
maxHeapTotal,
|
|
1673
|
-
maxUnhealthyChecks,
|
|
1674
|
-
interval
|
|
1675
|
-
} = worker[kConfig].health
|
|
1678
|
+
let { maxELU, maxHeapUsed, maxHeapTotal, maxUnhealthyChecks, interval } = worker[kConfig].health
|
|
1676
1679
|
|
|
1677
1680
|
if (typeof maxHeapTotal === 'string') {
|
|
1678
1681
|
maxHeapTotal = parseMemorySize(maxHeapTotal)
|
|
@@ -1682,7 +1685,7 @@ export class Runtime extends EventEmitter {
|
|
|
1682
1685
|
interval = 1000
|
|
1683
1686
|
this.logger.warn(
|
|
1684
1687
|
`The health check interval for the "${errorLabel}" is set to ${healthConfig.interval}ms. ` +
|
|
1685
|
-
|
|
1688
|
+
'The minimum health check interval is 1s. It will be set to 1000ms.'
|
|
1686
1689
|
)
|
|
1687
1690
|
}
|
|
1688
1691
|
|
|
@@ -1741,14 +1744,14 @@ export class Runtime extends EventEmitter {
|
|
|
1741
1744
|
|
|
1742
1745
|
this.logger.error(
|
|
1743
1746
|
{ elu: health.elu, maxELU, memoryUsage: health.heapUsed, maxMemoryUsage: maxHeapUsed },
|
|
1744
|
-
|
|
1747
|
+
`The ${errorLabel} is unhealthy. Replacing it ...`
|
|
1745
1748
|
)
|
|
1746
1749
|
|
|
1747
1750
|
await this.#replaceWorker(config, applicationConfig, workersCount, id, index, worker)
|
|
1748
1751
|
} catch (e) {
|
|
1749
1752
|
this.logger.error(
|
|
1750
1753
|
{ elu: health.elu, maxELU, memoryUsage: health.heapUsed, maxMemoryUsage: maxHeapUsed },
|
|
1751
|
-
|
|
1754
|
+
`Cannot replace the ${errorLabel}. Forcefully terminating it ...`
|
|
1752
1755
|
)
|
|
1753
1756
|
|
|
1754
1757
|
worker.terminate()
|
|
@@ -2320,6 +2323,8 @@ export class Runtime extends EventEmitter {
|
|
|
2320
2323
|
throw result.reason
|
|
2321
2324
|
}
|
|
2322
2325
|
}
|
|
2326
|
+
|
|
2327
|
+
this.#updateLoggingPrefixes()
|
|
2323
2328
|
}
|
|
2324
2329
|
|
|
2325
2330
|
async #updateApplicationConfigHealth (applicationId, health) {
|
|
@@ -2694,4 +2699,17 @@ export class Runtime extends EventEmitter {
|
|
|
2694
2699
|
worker[kWorkerHealthSignals] ??= new HealthSignalsQueue()
|
|
2695
2700
|
worker[kWorkerHealthSignals].add(signals)
|
|
2696
2701
|
}
|
|
2702
|
+
|
|
2703
|
+
#updateLoggingPrefixes () {
|
|
2704
|
+
if (!this.#loggerContext) {
|
|
2705
|
+
return
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
const ids = []
|
|
2709
|
+
for (const worker of this.#workers.values()) {
|
|
2710
|
+
ids.push(`${worker[kFullId]}`)
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
this.#loggerContext.updatePrefixes(ids)
|
|
2714
|
+
}
|
|
2697
2715
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -35,14 +35,14 @@
|
|
|
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/
|
|
39
|
-
"@platformatic/
|
|
40
|
-
"@platformatic/
|
|
41
|
-
"@platformatic/node": "3.
|
|
42
|
-
"@platformatic/
|
|
43
|
-
"@platformatic/
|
|
44
|
-
"@platformatic/sql-mapper": "3.
|
|
45
|
-
"@platformatic/wattpm-pprof-capture": "3.
|
|
38
|
+
"@platformatic/composer": "3.15.0",
|
|
39
|
+
"@platformatic/db": "3.15.0",
|
|
40
|
+
"@platformatic/gateway": "3.15.0",
|
|
41
|
+
"@platformatic/node": "3.15.0",
|
|
42
|
+
"@platformatic/sql-graphql": "3.15.0",
|
|
43
|
+
"@platformatic/service": "3.15.0",
|
|
44
|
+
"@platformatic/sql-mapper": "3.15.0",
|
|
45
|
+
"@platformatic/wattpm-pprof-capture": "3.15.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@fastify/accepts": "^5.0.0",
|
|
@@ -73,12 +73,12 @@
|
|
|
73
73
|
"undici": "^7.0.0",
|
|
74
74
|
"undici-thread-interceptor": "^0.15.0",
|
|
75
75
|
"ws": "^8.16.0",
|
|
76
|
-
"@platformatic/basic": "3.
|
|
77
|
-
"@platformatic/
|
|
78
|
-
"@platformatic/
|
|
79
|
-
"@platformatic/generators": "3.
|
|
80
|
-
"@platformatic/
|
|
81
|
-
"@platformatic/
|
|
76
|
+
"@platformatic/basic": "3.15.0",
|
|
77
|
+
"@platformatic/foundation": "3.15.0",
|
|
78
|
+
"@platformatic/metrics": "3.15.0",
|
|
79
|
+
"@platformatic/generators": "3.15.0",
|
|
80
|
+
"@platformatic/itc": "3.15.0",
|
|
81
|
+
"@platformatic/telemetry": "3.15.0"
|
|
82
82
|
},
|
|
83
83
|
"engines": {
|
|
84
84
|
"node": ">=22.19.0"
|
package/schema.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.
|
|
2
|
+
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.15.0.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"title": "Platformatic Runtime Config",
|
|
5
5
|
"type": "object",
|
|
@@ -64,6 +64,10 @@
|
|
|
64
64
|
"useHttp": {
|
|
65
65
|
"type": "boolean"
|
|
66
66
|
},
|
|
67
|
+
"reuseTcpPorts": {
|
|
68
|
+
"type": "boolean",
|
|
69
|
+
"default": true
|
|
70
|
+
},
|
|
67
71
|
"workers": {
|
|
68
72
|
"anyOf": [
|
|
69
73
|
{
|
|
@@ -342,6 +346,10 @@
|
|
|
342
346
|
"useHttp": {
|
|
343
347
|
"type": "boolean"
|
|
344
348
|
},
|
|
349
|
+
"reuseTcpPorts": {
|
|
350
|
+
"type": "boolean",
|
|
351
|
+
"default": true
|
|
352
|
+
},
|
|
345
353
|
"workers": {
|
|
346
354
|
"anyOf": [
|
|
347
355
|
{
|
|
@@ -618,6 +626,10 @@
|
|
|
618
626
|
"useHttp": {
|
|
619
627
|
"type": "boolean"
|
|
620
628
|
},
|
|
629
|
+
"reuseTcpPorts": {
|
|
630
|
+
"type": "boolean",
|
|
631
|
+
"default": true
|
|
632
|
+
},
|
|
621
633
|
"workers": {
|
|
622
634
|
"anyOf": [
|
|
623
635
|
{
|
|
@@ -894,6 +906,10 @@
|
|
|
894
906
|
"useHttp": {
|
|
895
907
|
"type": "boolean"
|
|
896
908
|
},
|
|
909
|
+
"reuseTcpPorts": {
|
|
910
|
+
"type": "boolean",
|
|
911
|
+
"default": true
|
|
912
|
+
},
|
|
897
913
|
"workers": {
|
|
898
914
|
"anyOf": [
|
|
899
915
|
{
|
|
@@ -1464,6 +1480,10 @@
|
|
|
1464
1480
|
},
|
|
1465
1481
|
"additionalProperties": false
|
|
1466
1482
|
},
|
|
1483
|
+
"reuseTcpPorts": {
|
|
1484
|
+
"type": "boolean",
|
|
1485
|
+
"default": true
|
|
1486
|
+
},
|
|
1467
1487
|
"startTimeout": {
|
|
1468
1488
|
"default": 30000,
|
|
1469
1489
|
"type": "number",
|