@platformatic/runtime 3.66.0 → 3.68.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 +3 -0
- package/index.d.ts +15 -0
- package/lib/config.js +56 -2
- package/lib/errors.js +9 -0
- package/lib/runtime.js +66 -11
- package/lib/worker/controller.js +14 -5
- package/lib/worker/main.js +1 -1
- package/lib/worker/management.js +4 -0
- package/package.json +17 -17
- package/schema.json +21 -1
package/config.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export type PlatformaticRuntimeConfig = {
|
|
|
17
17
|
options?: {
|
|
18
18
|
[k: string]: unknown;
|
|
19
19
|
};
|
|
20
|
+
build?: boolean;
|
|
20
21
|
}
|
|
21
22
|
| (
|
|
22
23
|
| string
|
|
@@ -25,6 +26,7 @@ export type PlatformaticRuntimeConfig = {
|
|
|
25
26
|
options?: {
|
|
26
27
|
[k: string]: unknown;
|
|
27
28
|
};
|
|
29
|
+
build?: boolean;
|
|
28
30
|
}
|
|
29
31
|
)[];
|
|
30
32
|
entrypoint?: string;
|
|
@@ -43,6 +45,7 @@ export type PlatformaticRuntimeConfig = {
|
|
|
43
45
|
};
|
|
44
46
|
config?: string;
|
|
45
47
|
useHttp?: boolean;
|
|
48
|
+
websocket?: boolean;
|
|
46
49
|
reuseTcpPorts?: boolean;
|
|
47
50
|
workers?:
|
|
48
51
|
| number
|
package/index.d.ts
CHANGED
|
@@ -250,6 +250,7 @@ export declare class ManagementClient {
|
|
|
250
250
|
getApplicationOpenapiSchema (id: string): Promise<unknown>
|
|
251
251
|
getApplicationGraphqlSchema (id: string): Promise<unknown>
|
|
252
252
|
getMetrics (format?: string): Promise<{ metrics: unknown }>
|
|
253
|
+
getScheduler (): Promise<SchedulerJob[]>
|
|
253
254
|
getSchedulerJobs (): Promise<SchedulerJob[]>
|
|
254
255
|
pauseSchedulerJob (name: string): Promise<SchedulerJob>
|
|
255
256
|
resumeSchedulerJob (name: string): Promise<SchedulerJob>
|
|
@@ -344,7 +345,20 @@ export interface RuntimeExtensionContext {
|
|
|
344
345
|
health: RuntimeExtensionHealth
|
|
345
346
|
}
|
|
346
347
|
|
|
348
|
+
export interface RuntimeExtensionBuildContext {
|
|
349
|
+
applicationId: string
|
|
350
|
+
applicationPath: string
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export type RuntimeExtensionBuild = () => Promise<unknown>
|
|
354
|
+
|
|
347
355
|
export interface RuntimeExtensionInstance {
|
|
356
|
+
preBuild?: (context: RuntimeExtensionBuildContext) => void | Promise<void>
|
|
357
|
+
onBuild?: (
|
|
358
|
+
context: RuntimeExtensionBuildContext,
|
|
359
|
+
build: RuntimeExtensionBuild
|
|
360
|
+
) => unknown | Promise<unknown>
|
|
361
|
+
postBuild?: (context: RuntimeExtensionBuildContext, result: unknown) => void | Promise<void>
|
|
348
362
|
start?: () => void | Promise<void>
|
|
349
363
|
stop?: () => void | Promise<void>
|
|
350
364
|
close?: () => void | Promise<void>
|
|
@@ -449,6 +463,7 @@ export declare class Runtime extends EventEmitter {
|
|
|
449
463
|
off (event: 'application:worker:health:metrics', listener: (event: HealthMetricsEvent) => void): this
|
|
450
464
|
off (event: string | symbol, listener: (...args: any[]) => void): this
|
|
451
465
|
|
|
466
|
+
getScheduler (): SchedulerJob[]
|
|
452
467
|
getSchedulerJobs (): SchedulerJob[]
|
|
453
468
|
pauseSchedulerJob (name: string): Promise<SchedulerJob>
|
|
454
469
|
resumeSchedulerJob (name: string): Promise<SchedulerJob>
|
package/lib/config.js
CHANGED
|
@@ -9,11 +9,13 @@ import {
|
|
|
9
9
|
omitProperties,
|
|
10
10
|
runtimeUnwrappablePropertiesList
|
|
11
11
|
} from '@platformatic/foundation'
|
|
12
|
+
import { realpathSync } from 'node:fs'
|
|
12
13
|
import { readdir, readFile } from 'node:fs/promises'
|
|
13
14
|
import { createRequire } from 'node:module'
|
|
14
15
|
import { isAbsolute, join, resolve as resolvePath } from 'node:path'
|
|
15
16
|
|
|
16
17
|
import {
|
|
18
|
+
ApplicationIdCollisionError,
|
|
17
19
|
InspectAndInspectBrkError,
|
|
18
20
|
InspectorHostError,
|
|
19
21
|
InspectorPortError,
|
|
@@ -295,6 +297,40 @@ export async function prepareApplication (config, application, defaultWorkers) {
|
|
|
295
297
|
return application
|
|
296
298
|
}
|
|
297
299
|
|
|
300
|
+
function canonicalPath (path) {
|
|
301
|
+
try {
|
|
302
|
+
return realpathSync(path)
|
|
303
|
+
} catch {
|
|
304
|
+
return resolvePath(path)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// An autoloaded directory and an explicitly configured entry with the same id are the same application
|
|
309
|
+
// only when they point to the same place: either the configured path resolves to the autoloaded
|
|
310
|
+
// directory, or the entry is external and the autoloaded directory is where "resolve" would put it.
|
|
311
|
+
// When they don't, returns a description of the configured entry to report in the error.
|
|
312
|
+
function conflictingApplicationSource (root, resolvedApplicationsPath, existing, entryPath) {
|
|
313
|
+
if (existing.path) {
|
|
314
|
+
// The path still contains a placeholder, which means the environment variable was not replaced.
|
|
315
|
+
// There is nothing to compare in that case.
|
|
316
|
+
if (existing.path.match(/^\{.*\}$/)) {
|
|
317
|
+
return null
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const existingPath = isAbsolute(existing.path) ? existing.path : resolvePath(root, existing.path)
|
|
321
|
+
|
|
322
|
+
return canonicalPath(existingPath) === canonicalPath(entryPath) ? null : `the path "${existing.path}"`
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (existing.url) {
|
|
326
|
+
const resolvedPath = join(resolvedApplicationsPath, existing.id)
|
|
327
|
+
|
|
328
|
+
return canonicalPath(resolvedPath) === canonicalPath(entryPath) ? null : `the URL "${existing.url}"`
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return null
|
|
332
|
+
}
|
|
333
|
+
|
|
298
334
|
function isApplicationEnabled (application, environment) {
|
|
299
335
|
const { enabled } = application
|
|
300
336
|
|
|
@@ -378,7 +414,11 @@ export async function transform (config, _, context) {
|
|
|
378
414
|
const { exclude = [], mappings = {} } = config.autoload
|
|
379
415
|
let { path } = config.autoload
|
|
380
416
|
|
|
381
|
-
|
|
417
|
+
// Capture these before the loop, as config is shadowed in its body
|
|
418
|
+
const root = config[kMetadata].root
|
|
419
|
+
const resolvedApplicationsPath = resolvePath(root, config.resolvedApplicationsBasePath ?? 'external')
|
|
420
|
+
|
|
421
|
+
path = resolvePath(root, path)
|
|
382
422
|
const entries = await readdir(path, { withFileTypes: true })
|
|
383
423
|
|
|
384
424
|
for (let i = 0; i < entries.length; ++i) {
|
|
@@ -403,7 +443,21 @@ export async function transform (config, _, context) {
|
|
|
403
443
|
const existingApplicationId = applications.findIndex(application => application.id === id)
|
|
404
444
|
|
|
405
445
|
if (existingApplicationId !== -1) {
|
|
406
|
-
|
|
446
|
+
const existing = applications[existingApplicationId]
|
|
447
|
+
|
|
448
|
+
// Merging on the id alone can boot local code where the configuration named a repository, as
|
|
449
|
+
// the autoloaded path survives next to the configured url. Reject the duplicate instead: an id
|
|
450
|
+
// is the mesh hostname, the injected PLT_<ID>_URL name, the metrics label and the argument of
|
|
451
|
+
// "wattpm inject", so two different applications cannot share one.
|
|
452
|
+
if (isApplicationEnabled(existing, environment)) {
|
|
453
|
+
const source = conflictingApplicationSource(root, resolvedApplicationsPath, existing, entryPath)
|
|
454
|
+
|
|
455
|
+
if (source) {
|
|
456
|
+
throw new ApplicationIdCollisionError(id, entryPath, source)
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
applications[existingApplicationId] = { ...application, ...existing }
|
|
407
461
|
} else {
|
|
408
462
|
applications.push(application)
|
|
409
463
|
}
|
package/lib/errors.js
CHANGED
|
@@ -102,6 +102,10 @@ export const InvalidEntrypointError = createError(
|
|
|
102
102
|
`${ERROR_PREFIX}_INVALID_ENTRYPOINT`,
|
|
103
103
|
"Invalid entrypoint: '%s' does not exist"
|
|
104
104
|
)
|
|
105
|
+
export const ApplicationIdCollisionError = createError(
|
|
106
|
+
`${ERROR_PREFIX}_APPLICATION_ID_COLLISION`,
|
|
107
|
+
'The application id "%s" is used by the autoloaded directory "%s" and by a different application defined in the configuration file via %s. Application ids must be unique.'
|
|
108
|
+
)
|
|
105
109
|
export const MissingEntrypointError = createError(
|
|
106
110
|
`${ERROR_PREFIX}_MISSING_ENTRYPOINT`,
|
|
107
111
|
'Missing application entrypoint.'
|
|
@@ -191,6 +195,11 @@ export const DuplicateITCHandlerNameError = createError(
|
|
|
191
195
|
'The ITC command "%s" has already been registered'
|
|
192
196
|
)
|
|
193
197
|
|
|
198
|
+
export const RuntimeExtensionBuildAlreadyCalledError = createError(
|
|
199
|
+
`${ERROR_PREFIX}_EXTENSION_BUILD_ALREADY_CALLED`,
|
|
200
|
+
'The build function can only be called once by each runtime extension.'
|
|
201
|
+
)
|
|
202
|
+
|
|
194
203
|
export const MetricFamilyCollisionError = createError(
|
|
195
204
|
`${ERROR_PREFIX}_METRIC_FAMILY_COLLISION`,
|
|
196
205
|
'Extension "%s" registered metric family "%s" which collides with %s'
|
package/lib/runtime.js
CHANGED
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
MissingPprofCapture,
|
|
55
55
|
ReservedITCHandlerNameError,
|
|
56
56
|
RuntimeAbortedError,
|
|
57
|
+
RuntimeExtensionBuildAlreadyCalledError,
|
|
57
58
|
WorkerInterceptorJoinTimeoutError,
|
|
58
59
|
WorkerNotFoundError
|
|
59
60
|
} from './errors.js'
|
|
@@ -921,19 +922,63 @@ export class Runtime extends EventEmitter {
|
|
|
921
922
|
|
|
922
923
|
async buildApplication (id) {
|
|
923
924
|
const application = await this.#getApplicationById(id)
|
|
925
|
+
const applicationConfig = this.#applications.get(id)
|
|
926
|
+
const context = Object.freeze({
|
|
927
|
+
applicationId: id,
|
|
928
|
+
applicationPath: applicationConfig.path
|
|
929
|
+
})
|
|
924
930
|
|
|
925
931
|
this.emitAndNotify('application:building', id)
|
|
926
|
-
|
|
927
|
-
await
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
932
|
+
for (const extension of this.#extensions) {
|
|
933
|
+
await extension.instance?.preBuild?.(context)
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
let buildHandlerMissing = false
|
|
937
|
+
const build = this.#extensions.reduceRight(
|
|
938
|
+
(next, extension) => {
|
|
939
|
+
if (typeof extension.instance?.onBuild !== 'function') {
|
|
940
|
+
return next
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
return () => {
|
|
944
|
+
let called = false
|
|
945
|
+
const build = () => {
|
|
946
|
+
if (called) {
|
|
947
|
+
throw new RuntimeExtensionBuildAlreadyCalledError()
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
called = true
|
|
951
|
+
return next()
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
return extension.instance.onBuild(context, build)
|
|
955
|
+
}
|
|
956
|
+
},
|
|
957
|
+
async () => {
|
|
958
|
+
try {
|
|
959
|
+
return await sendViaITC(application, 'build')
|
|
960
|
+
} catch (e) {
|
|
961
|
+
// The application exports no meta, return an empty object
|
|
962
|
+
if (e.code === 'PLT_ITC_HANDLER_NOT_FOUND') {
|
|
963
|
+
buildHandlerMissing = true
|
|
964
|
+
return {}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
throw e
|
|
968
|
+
}
|
|
933
969
|
}
|
|
970
|
+
)
|
|
934
971
|
|
|
935
|
-
|
|
972
|
+
const result = await build()
|
|
973
|
+
|
|
974
|
+
for (const extension of [...this.#extensions].reverse()) {
|
|
975
|
+
await extension.instance?.postBuild?.(context, result)
|
|
936
976
|
}
|
|
977
|
+
|
|
978
|
+
if (!buildHandlerMissing) {
|
|
979
|
+
this.emitAndNotify('application:built', id)
|
|
980
|
+
}
|
|
981
|
+
return result
|
|
937
982
|
}
|
|
938
983
|
|
|
939
984
|
async startApplicationProfiling (id, options = {}, ensureStarted = true) {
|
|
@@ -2147,6 +2192,10 @@ export class Runtime extends EventEmitter {
|
|
|
2147
2192
|
return this.#scheduler?.getJobs() ?? []
|
|
2148
2193
|
}
|
|
2149
2194
|
|
|
2195
|
+
getScheduler () {
|
|
2196
|
+
return this.getSchedulerJobs()
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2150
2199
|
pauseSchedulerJob (name) {
|
|
2151
2200
|
return this.#schedulerOrThrow().pauseJob(name)
|
|
2152
2201
|
}
|
|
@@ -4343,8 +4392,7 @@ export class Runtime extends EventEmitter {
|
|
|
4343
4392
|
async #loadExtensions () {
|
|
4344
4393
|
let extensions = this.#config.extensions
|
|
4345
4394
|
|
|
4346
|
-
|
|
4347
|
-
if (!extensions || this.#context.build) {
|
|
4395
|
+
if (!extensions) {
|
|
4348
4396
|
return
|
|
4349
4397
|
}
|
|
4350
4398
|
|
|
@@ -4353,7 +4401,14 @@ export class Runtime extends EventEmitter {
|
|
|
4353
4401
|
}
|
|
4354
4402
|
|
|
4355
4403
|
for (const extension of extensions) {
|
|
4356
|
-
const { path, options } = typeof extension === 'string' ? { path: extension } : extension
|
|
4404
|
+
const { path, options, build } = typeof extension === 'string' ? { path: extension } : extension
|
|
4405
|
+
|
|
4406
|
+
// Runtime extensions historically were not loaded by `wattpm build`.
|
|
4407
|
+
// Preserve that behavior unless an extension explicitly opts into the
|
|
4408
|
+
// build lifecycle.
|
|
4409
|
+
if (this.#context.build && !build) {
|
|
4410
|
+
continue
|
|
4411
|
+
}
|
|
4357
4412
|
|
|
4358
4413
|
let imported
|
|
4359
4414
|
try {
|
package/lib/worker/controller.js
CHANGED
|
@@ -140,10 +140,14 @@ export class Controller extends EventEmitter {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
if (appConfig.config) {
|
|
143
|
-
// Parse the configuration file the first time to obtain the schema
|
|
143
|
+
// Parse the configuration file the first time to obtain the schema. This load is thrown away:
|
|
144
|
+
// the capability loads the configuration again below and that is the configuration the
|
|
145
|
+
// application actually runs on. This one only has to yield $schema and module, so it must not
|
|
146
|
+
// diverge from the second load in any way the user can observe: it deliberately applies no
|
|
147
|
+
// onMissingEnv fallback and emits no strictEnv report, both of which are left to the load the
|
|
148
|
+
// application is built from.
|
|
144
149
|
const unvalidatedConfig = await loadConfiguration(appConfig.config, null, {
|
|
145
|
-
|
|
146
|
-
strictEnv: this.#context.strictEnv
|
|
150
|
+
strictEnv: false
|
|
147
151
|
})
|
|
148
152
|
const pkg = await loadConfigurationModule(appConfig.path, unvalidatedConfig)
|
|
149
153
|
this.capability = await pkg.create(appConfig.path, appConfig.config, this.#context)
|
|
@@ -215,7 +219,7 @@ export class Controller extends EventEmitter {
|
|
|
215
219
|
}
|
|
216
220
|
}
|
|
217
221
|
|
|
218
|
-
const listen = !!this.applicationConfig.useHttp
|
|
222
|
+
const listen = !!(this.applicationConfig.useHttp || this.applicationConfig.websocket)
|
|
219
223
|
|
|
220
224
|
try {
|
|
221
225
|
await this.capability.start({ listen })
|
|
@@ -264,7 +268,12 @@ export class Controller extends EventEmitter {
|
|
|
264
268
|
|
|
265
269
|
async listen () {
|
|
266
270
|
// This server is not an entrypoint or already listened in start. Behave as no-op.
|
|
267
|
-
if (
|
|
271
|
+
if (
|
|
272
|
+
!this.applicationConfig.entrypoint ||
|
|
273
|
+
this.applicationConfig.useHttp ||
|
|
274
|
+
this.applicationConfig.websocket ||
|
|
275
|
+
this.#listening
|
|
276
|
+
) {
|
|
268
277
|
return
|
|
269
278
|
}
|
|
270
279
|
|
package/lib/worker/main.js
CHANGED
|
@@ -274,7 +274,7 @@ async function main () {
|
|
|
274
274
|
let serverConfig = null
|
|
275
275
|
if (runtimeConfig.server && applicationConfig.entrypoint) {
|
|
276
276
|
serverConfig = runtimeConfig.server
|
|
277
|
-
} else if (applicationConfig.useHttp) {
|
|
277
|
+
} else if (applicationConfig.useHttp || applicationConfig.websocket) {
|
|
278
278
|
serverConfig = {
|
|
279
279
|
port: 0,
|
|
280
280
|
hostname: '127.0.0.1',
|
package/lib/worker/management.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.68.0",
|
|
4
4
|
"description": "Platformatic runtime",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -36,17 +36,17 @@
|
|
|
36
36
|
"pino-sentry-transport": "^1.6.0",
|
|
37
37
|
"split2": "^4.2.0",
|
|
38
38
|
"tstyche": "^6.2.0",
|
|
39
|
-
"typescript": "
|
|
39
|
+
"typescript": "~6.0.3",
|
|
40
40
|
"undici-oidc-interceptor": "^0.9.0",
|
|
41
41
|
"why-is-node-running": "^2.2.2",
|
|
42
|
-
"@platformatic/composer": "3.
|
|
43
|
-
"@platformatic/
|
|
44
|
-
"@platformatic/
|
|
45
|
-
"@platformatic/
|
|
46
|
-
"@platformatic/
|
|
47
|
-
"@platformatic/sql-
|
|
48
|
-
"@platformatic/
|
|
49
|
-
"@platformatic/
|
|
42
|
+
"@platformatic/composer": "3.68.0",
|
|
43
|
+
"@platformatic/db": "3.68.0",
|
|
44
|
+
"@platformatic/gateway": "3.68.0",
|
|
45
|
+
"@platformatic/node": "3.68.0",
|
|
46
|
+
"@platformatic/service": "3.68.0",
|
|
47
|
+
"@platformatic/sql-graphql": "3.68.0",
|
|
48
|
+
"@platformatic/sql-mapper": "3.68.0",
|
|
49
|
+
"@platformatic/wattpm-pprof-capture": "3.68.0"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@fastify/accepts": "^5.0.0",
|
|
@@ -77,13 +77,13 @@
|
|
|
77
77
|
"undici": "^7.27.2",
|
|
78
78
|
"undici-thread-interceptor": "^1.3.1",
|
|
79
79
|
"ws": "^8.16.0",
|
|
80
|
-
"@platformatic/
|
|
81
|
-
"@platformatic/
|
|
82
|
-
"@platformatic/itc": "3.
|
|
83
|
-
"@platformatic/metrics": "3.
|
|
84
|
-
"@platformatic/
|
|
85
|
-
"@platformatic/
|
|
86
|
-
"@platformatic/
|
|
80
|
+
"@platformatic/generators": "3.68.0",
|
|
81
|
+
"@platformatic/globals": "3.68.0",
|
|
82
|
+
"@platformatic/itc": "3.68.0",
|
|
83
|
+
"@platformatic/metrics": "3.68.0",
|
|
84
|
+
"@platformatic/telemetry": "3.68.0",
|
|
85
|
+
"@platformatic/basic": "3.68.0",
|
|
86
|
+
"@platformatic/foundation": "3.68.0"
|
|
87
87
|
},
|
|
88
88
|
"engines": {
|
|
89
89
|
"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.68.0.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"title": "Platformatic Runtime Config",
|
|
5
5
|
"type": "object",
|
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
"options": {
|
|
43
43
|
"type": "object",
|
|
44
44
|
"additionalProperties": true
|
|
45
|
+
},
|
|
46
|
+
"build": {
|
|
47
|
+
"type": "boolean",
|
|
48
|
+
"default": false
|
|
45
49
|
}
|
|
46
50
|
}
|
|
47
51
|
},
|
|
@@ -67,6 +71,10 @@
|
|
|
67
71
|
"options": {
|
|
68
72
|
"type": "object",
|
|
69
73
|
"additionalProperties": true
|
|
74
|
+
},
|
|
75
|
+
"build": {
|
|
76
|
+
"type": "boolean",
|
|
77
|
+
"default": false
|
|
70
78
|
}
|
|
71
79
|
}
|
|
72
80
|
}
|
|
@@ -134,6 +142,9 @@
|
|
|
134
142
|
"useHttp": {
|
|
135
143
|
"type": "boolean"
|
|
136
144
|
},
|
|
145
|
+
"websocket": {
|
|
146
|
+
"type": "boolean"
|
|
147
|
+
},
|
|
137
148
|
"reuseTcpPorts": {
|
|
138
149
|
"type": "boolean",
|
|
139
150
|
"default": true
|
|
@@ -566,6 +577,9 @@
|
|
|
566
577
|
"useHttp": {
|
|
567
578
|
"type": "boolean"
|
|
568
579
|
},
|
|
580
|
+
"websocket": {
|
|
581
|
+
"type": "boolean"
|
|
582
|
+
},
|
|
569
583
|
"reuseTcpPorts": {
|
|
570
584
|
"type": "boolean",
|
|
571
585
|
"default": true
|
|
@@ -996,6 +1010,9 @@
|
|
|
996
1010
|
"useHttp": {
|
|
997
1011
|
"type": "boolean"
|
|
998
1012
|
},
|
|
1013
|
+
"websocket": {
|
|
1014
|
+
"type": "boolean"
|
|
1015
|
+
},
|
|
999
1016
|
"reuseTcpPorts": {
|
|
1000
1017
|
"type": "boolean",
|
|
1001
1018
|
"default": true
|
|
@@ -1426,6 +1443,9 @@
|
|
|
1426
1443
|
"useHttp": {
|
|
1427
1444
|
"type": "boolean"
|
|
1428
1445
|
},
|
|
1446
|
+
"websocket": {
|
|
1447
|
+
"type": "boolean"
|
|
1448
|
+
},
|
|
1429
1449
|
"reuseTcpPorts": {
|
|
1430
1450
|
"type": "boolean",
|
|
1431
1451
|
"default": true
|