buildEmptyMeterValue,
buildMeterValue,
buildSampledValue,
+ createPayloadConfigs,
getSampledValueTemplate,
- PayloadValidatorConfig,
PayloadValidatorOptions,
sendAndSetConnectorStatus,
} from '../OCPPServiceUtils.js'
public static createIncomingRequestPayloadConfigs = (): [
OCPP16IncomingRequestCommand,
{ schemaPath: string }
- ][] =>
- OCPP16ServiceUtils.incomingRequestSchemaNames.map(([command, schemaBase]) => [
- command,
- PayloadValidatorConfig(`${schemaBase}.json`),
- ])
+ ][] => createPayloadConfigs(OCPP16ServiceUtils.incomingRequestSchemaNames, '.json')
/**
* OCPP 1.6 Incoming Request Response Service validator configurations
public static createIncomingRequestResponsePayloadConfigs = (): [
OCPP16IncomingRequestCommand,
{ schemaPath: string }
- ][] =>
- OCPP16ServiceUtils.incomingRequestSchemaNames.map(([command, schemaBase]) => [
- command,
- PayloadValidatorConfig(`${schemaBase}Response.json`),
- ])
+ ][] => createPayloadConfigs(OCPP16ServiceUtils.incomingRequestSchemaNames, 'Response.json')
/**
* Factory options for OCPP 1.6 payload validators
public static createRequestPayloadConfigs = (): [
OCPP16RequestCommand,
{ schemaPath: string }
- ][] =>
- OCPP16ServiceUtils.outgoingRequestSchemaNames.map(([command, schemaBase]) => [
- command,
- PayloadValidatorConfig(`${schemaBase}.json`),
- ])
+ ][] => createPayloadConfigs(OCPP16ServiceUtils.outgoingRequestSchemaNames, '.json')
/**
* OCPP 1.6 Response Service validator configurations
public static createResponsePayloadConfigs = (): [
OCPP16RequestCommand,
{ schemaPath: string }
- ][] =>
- OCPP16ServiceUtils.outgoingRequestSchemaNames.map(([command, schemaBase]) => [
- command,
- PayloadValidatorConfig(`${schemaBase}Response.json`),
- ])
+ ][] => createPayloadConfigs(OCPP16ServiceUtils.outgoingRequestSchemaNames, 'Response.json')
/**
* Checks whether a connector or the charging station has a valid reservation for the given idTag.
import { buildConfigKey, getConfigurationKey } from '../../ConfigurationKeyUtils.js'
import {
buildMeterValue,
- PayloadValidatorConfig,
+ createPayloadConfigs,
PayloadValidatorOptions,
sendAndSetConnectorStatus,
} from '../OCPPServiceUtils.js'
public static createIncomingRequestPayloadConfigs = (): [
OCPP20IncomingRequestCommand,
{ schemaPath: string }
- ][] =>
- OCPP20ServiceUtils.incomingRequestSchemaNames.map(([command, schemaBase]) => [
- command,
- PayloadValidatorConfig(`${schemaBase}Request.json`),
- ])
+ ][] => createPayloadConfigs(OCPP20ServiceUtils.incomingRequestSchemaNames, 'Request.json')
/**
* Configuration for OCPP 2.0 Incoming Request Response validators
public static createIncomingRequestResponsePayloadConfigs = (): [
OCPP20IncomingRequestCommand,
{ schemaPath: string }
- ][] =>
- OCPP20ServiceUtils.incomingRequestSchemaNames.map(([command, schemaBase]) => [
- command,
- PayloadValidatorConfig(`${schemaBase}Response.json`),
- ])
+ ][] => createPayloadConfigs(OCPP20ServiceUtils.incomingRequestSchemaNames, 'Response.json')
/**
* Factory options for OCPP 2.0 payload validators
public static createRequestPayloadConfigs = (): [
OCPP20RequestCommand,
{ schemaPath: string }
- ][] =>
- OCPP20ServiceUtils.outgoingRequestSchemaNames.map(([command, schemaBase]) => [
- command,
- PayloadValidatorConfig(`${schemaBase}Request.json`),
- ])
+ ][] => createPayloadConfigs(OCPP20ServiceUtils.outgoingRequestSchemaNames, 'Request.json')
/**
* OCPP 2.0 Response Service validator configurations
public static createResponsePayloadConfigs = (): [
OCPP20RequestCommand,
{ schemaPath: string }
- ][] =>
- OCPP20ServiceUtils.outgoingRequestSchemaNames.map(([command, schemaBase]) => [
- command,
- PayloadValidatorConfig(`${schemaBase}Response.json`),
- ])
+ ][] => createPayloadConfigs(OCPP20ServiceUtils.outgoingRequestSchemaNames, 'Response.json')
/**
* Enforce ItemsPerMessage and BytesPerMessage limits on request data.
-import _Ajv, { type ValidateFunction } from 'ajv'
-import _ajvFormats from 'ajv-formats'
+import type { ValidateFunction } from 'ajv'
+import type _Ajv from 'ajv'
+
import { EventEmitter } from 'node:events'
import { type ChargingStation } from '../../charging-station/index.js'
type OCPPVersion,
} from '../../types/index.js'
import { isAsyncFunction, logger } from '../../utils/index.js'
-import { ajvErrorsToErrorType } from './OCPPServiceUtils.js'
+import { ajvErrorsToErrorType, createAjv } from './OCPPServiceUtils.js'
type Ajv = _Ajv.default
-// eslint-disable-next-line @typescript-eslint/no-redeclare
-const Ajv = _Ajv.default
-const ajvFormats = _ajvFormats.default
const moduleName = 'OCPPIncomingRequestService'
protected constructor (version: OCPPVersion) {
super()
this.version = version
- this.ajv = new Ajv({
- keywords: ['javaType'],
- multipleOfPrecision: 2,
- })
- ajvFormats(this.ajv)
+ this.ajv = createAjv()
this.incomingRequestHandler = this.incomingRequestHandler.bind(this)
this.validateIncomingRequestPayload = this.validateIncomingRequestPayload.bind(this)
}
-import _Ajv, { type ValidateFunction } from 'ajv'
-import _ajvFormats from 'ajv-formats'
+import type { ValidateFunction } from 'ajv'
+import type _Ajv from 'ajv'
import type { ChargingStation } from '../../charging-station/index.js'
import type { OCPPResponseService } from './OCPPResponseService.js'
logger,
} from '../../utils/index.js'
import { OCPPConstants } from './OCPPConstants.js'
-import { ajvErrorsToErrorType, convertDateToISOString } from './OCPPServiceUtils.js'
+import { ajvErrorsToErrorType, convertDateToISOString, createAjv } from './OCPPServiceUtils.js'
type Ajv = _Ajv.default
-// eslint-disable-next-line @typescript-eslint/no-redeclare
-const Ajv = _Ajv.default
-const ajvFormats = _ajvFormats.default
const moduleName = 'OCPPRequestService'
protected constructor (version: OCPPVersion, ocppResponseService: OCPPResponseService) {
this.version = version
- this.ajv = new Ajv({
- keywords: ['javaType'],
- multipleOfPrecision: 2,
- })
- ajvFormats(this.ajv)
+ this.ajv = createAjv()
this.ocppResponseService = ocppResponseService
this.requestHandler = this.requestHandler.bind(this)
this.sendMessage = this.sendMessage.bind(this)
-import _Ajv, { type ValidateFunction } from 'ajv'
-import _ajvFormats from 'ajv-formats'
+import type { ValidateFunction } from 'ajv'
+import type _Ajv from 'ajv'
import type { ChargingStation } from '../../charging-station/index.js'
type ResponseHandler,
} from '../../types/index.js'
import { Constants, isAsyncFunction, logger } from '../../utils/index.js'
-import { ajvErrorsToErrorType } from './OCPPServiceUtils.js'
+import { ajvErrorsToErrorType, createAjv } from './OCPPServiceUtils.js'
type Ajv = _Ajv.default
-// eslint-disable-next-line @typescript-eslint/no-redeclare
-const Ajv = _Ajv.default
-const ajvFormats = _ajvFormats.default
const moduleName = 'OCPPResponseService'
protected constructor (version: OCPPVersion) {
this.version = version
- this.ajv = new Ajv({
- keywords: ['javaType'],
- multipleOfPrecision: 2,
- })
- ajvFormats(this.ajv)
- this.ajvIncomingRequest = new Ajv({
- keywords: ['javaType'],
- multipleOfPrecision: 2,
- })
- ajvFormats(this.ajvIncomingRequest)
+ this.ajv = createAjv()
+ this.ajvIncomingRequest = createAjv()
this.responseHandler = this.responseHandler.bind(this)
this.validateResponsePayload = this.validateResponsePayload.bind(this)
}
import _Ajv, { type ErrorObject, type JSONSchemaType, type ValidateFunction } from 'ajv'
+import _ajvFormats from 'ajv-formats'
import { isDate } from 'date-fns'
import { randomInt } from 'node:crypto'
import { readFileSync } from 'node:fs'
type Ajv = _Ajv.default
// eslint-disable-next-line @typescript-eslint/no-redeclare
const Ajv = _Ajv.default
+const ajvFormats = _ajvFormats.default
+
+export const createAjv = (): Ajv => {
+ const ajv = new Ajv({
+ keywords: ['javaType'],
+ multipleOfPrecision: 2,
+ })
+ ajvFormats(ajv)
+ return ajv
+}
interface MultiPhaseMeasurandData {
perPhaseTemplates: MeasurandPerPhaseSampledValueTemplates
return false
}
-/**
- * Configuration for a single payload validator.
- * @param schemaPath - Path to the JSON schema file
- * @returns Configuration object for payload validator creation
- */
-export const PayloadValidatorConfig = (schemaPath: string) =>
+const PayloadValidatorConfig = (schemaPath: string) =>
({
schemaPath,
}) as const
+/**
+ * Maps schema name tuples to payload validator config tuples with the given suffix.
+ * @param schemaNames - Array of `[command, schemaBase]` tuples
+ * @param schemaSuffix - File suffix appended to each schema base (e.g. `Request.json`)
+ * @returns Array of `[command, config]` tuples for payload validator map construction
+ */
+export function createPayloadConfigs<Command> (
+ schemaNames: readonly [Command, string][],
+ schemaSuffix: string
+): [Command, { schemaPath: string }][] {
+ return schemaNames.map(([command, schemaBase]) => [
+ command,
+ PayloadValidatorConfig(`${schemaBase}${schemaSuffix}`),
+ ])
+}
+
/**
* Options for payload validator creation.
* @param ocppVersion - The OCPP version
} from '../../types/index.js'
import { isEmpty, isNotEmptyString, logger, logPrefix } from '../../utils/index.js'
import { UIServiceFactory } from './ui-services/UIServiceFactory.js'
-import { isValidCredential } from './UIServerSecurity.js'
+import {
+ createRateLimiter,
+ DEFAULT_RATE_LIMIT,
+ DEFAULT_RATE_WINDOW,
+ isValidCredential,
+} from './UIServerSecurity.js'
import { getUsernameAndPasswordFromAuthorizationToken } from './UIServerUtils.js'
const moduleName = 'AbstractUIServer'
export abstract class AbstractUIServer {
protected readonly httpServer: Http2Server | Server
+ protected readonly rateLimiter: ReturnType<typeof createRateLimiter>
protected readonly responseHandlers: Map<UUIDv4, ServerResponse | WebSocket>
protected abstract readonly uiServerType: string
)
}
this.responseHandlers = new Map<UUIDv4, ServerResponse | WebSocket>()
+ this.rateLimiter = createRateLimiter(DEFAULT_RATE_LIMIT, DEFAULT_RATE_WINDOW)
this.uiServices = new Map<ProtocolVersion, AbstractUIService>()
}
import { AbstractUIServer } from './AbstractUIServer.js'
import {
createBodySizeLimiter,
- createRateLimiter,
DEFAULT_COMPRESSION_THRESHOLD,
DEFAULT_MAX_PAYLOAD_SIZE,
- DEFAULT_RATE_LIMIT,
- DEFAULT_RATE_WINDOW,
} from './UIServerSecurity.js'
import { HttpMethod, isProtocolAndVersionSupported } from './UIServerUtils.js'
const moduleName = 'UIHttpServer'
-const rateLimiter = createRateLimiter(DEFAULT_RATE_LIMIT, DEFAULT_RATE_WINDOW)
-
/**
* @deprecated Use UIMCPServer (ApplicationProtocol.MCP) instead. Will be removed in a future major version.
*/
private requestListener (req: IncomingMessage, res: ServerResponse): void {
// Rate limiting check
const clientIp = req.socket.remoteAddress ?? 'unknown'
- if (!rateLimiter(clientIp)) {
+ if (!this.rateLimiter(clientIp)) {
res
.writeHead(StatusCodes.TOO_MANY_REQUESTS, {
'Content-Type': 'text/plain',
registerMCPResources,
registerMCPSchemaResources,
} from './mcp/index.js'
-import {
- createRateLimiter,
- DEFAULT_MAX_PAYLOAD_SIZE,
- DEFAULT_RATE_LIMIT,
- DEFAULT_RATE_WINDOW,
-} from './UIServerSecurity.js'
+import { DEFAULT_MAX_PAYLOAD_SIZE } from './UIServerSecurity.js'
import { HttpMethod } from './UIServerUtils.js'
const moduleName = 'UIMCPServer'
const MCP_TOOL_TIMEOUT_MS = 30_000
-const rateLimiter = createRateLimiter(DEFAULT_RATE_LIMIT, DEFAULT_RATE_WINDOW)
-
export class UIMCPServer extends AbstractUIServer {
protected override readonly uiServerType = 'UI MCP Server'
}
const clientIp = req.socket.remoteAddress ?? 'unknown'
- if (!rateLimiter(clientIp)) {
+ if (!this.rateLimiter(clientIp)) {
res.writeHead(429, { 'Content-Type': 'text/plain' }).end('429 Too Many Requests')
return
}
DEFAULT_WORKER_START_DELAY,
WorkerProcessType,
} from '../worker/index.js'
+import { checkDeprecatedConfigurationKeys } from './ConfigurationMigration.js'
import {
buildPerformanceUriFilePath,
checkWorkerElementsPerWorker,
}
public static getStationTemplateUrls (): StationTemplateUrl[] | undefined {
- const checkDeprecatedConfigurationKeysOnce = once(
- Configuration.checkDeprecatedConfigurationKeys.bind(Configuration)
- )
+ const checkDeprecatedConfigurationKeysOnce = once(() => {
+ checkDeprecatedConfigurationKeys(Configuration.getConfigurationData())
+ })
checkDeprecatedConfigurationKeysOnce()
return Configuration.getConfigurationData()?.stationTemplateUrls
}
}
}
- private static checkDeprecatedConfigurationKeys (): void {
- const deprecatedKeys: [string, ConfigurationSection | undefined, string][] = [
- // connection timeout
- [
- 'autoReconnectTimeout',
- undefined,
- "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
- ],
- [
- 'connectionTimeout',
- undefined,
- "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
- ],
- // connection retries
- ['autoReconnectMaxRetries', undefined, 'Use it in charging station template instead'],
- // station template url(s)
- ['stationTemplateURLs', undefined, "Use 'stationTemplateUrls' instead"],
- // supervision url(s)
- ['supervisionURLs', undefined, "Use 'supervisionUrls' instead"],
- // supervision urls distribution
- ['distributeStationToTenantEqually', undefined, "Use 'supervisionUrlDistribution' instead"],
- ['distributeStationsToTenantsEqually', undefined, "Use 'supervisionUrlDistribution' instead"],
- // worker section
- [
- 'useWorkerPool',
- undefined,
- `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`,
- ],
- [
- 'workerProcess',
- undefined,
- `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`,
- ],
- [
- 'workerStartDelay',
- undefined,
- `Use '${ConfigurationSection.worker}' section to define the worker start delay instead`,
- ],
- [
- 'chargingStationsPerWorker',
- undefined,
- `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead`,
- ],
- [
- 'elementAddDelay',
- undefined,
- `Use '${ConfigurationSection.worker}' section to define the worker's element add delay instead`,
- ],
- [
- 'workerPoolMinSize',
- undefined,
- `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead`,
- ],
- [
- 'workerPoolSize',
- undefined,
- `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`,
- ],
- [
- 'workerPoolMaxSize',
- undefined,
- `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`,
- ],
- [
- 'workerPoolStrategy',
- undefined,
- `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead`,
- ],
- ['poolStrategy', ConfigurationSection.worker, 'Not publicly exposed to end users'],
- ['elementStartDelay', ConfigurationSection.worker, "Use 'elementAddDelay' instead"],
- // log section
- [
- 'logEnabled',
- undefined,
- `Use '${ConfigurationSection.log}' section to define the logging enablement instead`,
- ],
- [
- 'logFile',
- undefined,
- `Use '${ConfigurationSection.log}' section to define the log file instead`,
- ],
- [
- 'logErrorFile',
- undefined,
- `Use '${ConfigurationSection.log}' section to define the log error file instead`,
- ],
- [
- 'logConsole',
- undefined,
- `Use '${ConfigurationSection.log}' section to define the console logging enablement instead`,
- ],
- [
- 'logStatisticsInterval',
- undefined,
- `Use '${ConfigurationSection.log}' section to define the log statistics interval instead`,
- ],
- [
- 'logLevel',
- undefined,
- `Use '${ConfigurationSection.log}' section to define the log level instead`,
- ],
- [
- 'logFormat',
- undefined,
- `Use '${ConfigurationSection.log}' section to define the log format instead`,
- ],
- [
- 'logRotate',
- undefined,
- `Use '${ConfigurationSection.log}' section to define the log rotation enablement instead`,
- ],
- [
- 'logMaxFiles',
- undefined,
- `Use '${ConfigurationSection.log}' section to define the log maximum files instead`,
- ],
- [
- 'logMaxSize',
- undefined,
- `Use '${ConfigurationSection.log}' section to define the log maximum size instead`,
- ],
- // performanceStorage section
- ['URI', ConfigurationSection.performanceStorage, "Use 'uri' instead"],
- ]
- for (const [key, section, msg] of deprecatedKeys) {
- Configuration.warnDeprecatedConfigurationKey(key, section, msg)
- }
- // station template url(s) remapping
- if (
- Configuration.getConfigurationData()?.['stationTemplateURLs' as keyof ConfigurationData] !=
- null
- ) {
- const configurationData = Configuration.getConfigurationData()
- if (configurationData != null) {
- configurationData.stationTemplateUrls = configurationData[
- 'stationTemplateURLs' as keyof ConfigurationData
- ] as StationTemplateUrl[]
- }
- }
- Configuration.getConfigurationData()?.stationTemplateUrls.forEach(
- (stationTemplateUrl: StationTemplateUrl) => {
- if (stationTemplateUrl['numberOfStation' as keyof StationTemplateUrl] != null) {
- console.error(
- `${chalk.green(logPrefix())} ${chalk.red(
- `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`
- )}`
- )
- }
- }
- )
- // worker section: staticPool check
- if (
- Configuration.getConfigurationData()?.worker?.processType ===
- ('staticPool' as WorkerProcessType)
- ) {
- console.error(
- `${chalk.green(logPrefix())} ${chalk.red(
- `Deprecated configuration 'staticPool' value usage in worker section 'processType' field. Use '${WorkerProcessType.fixedPool}' value instead`
- )}`
- )
- }
- // uiServer section
- if (has('uiWebSocketServer', Configuration.getConfigurationData())) {
- console.error(
- `${chalk.green(logPrefix())} ${chalk.red(
- `Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead`
- )}`
- )
- }
- }
-
private static getConfigurationFileWatcher (): FSWatcher | undefined {
if (
Configuration.configurationFile == null ||
private static isConfigurationSectionCached (sectionName: ConfigurationSection): boolean {
return Configuration.configurationSectionCache.has(sectionName)
}
-
- private static warnDeprecatedConfigurationKey (
- key: string,
- configurationSection?: ConfigurationSection,
- logMsgToAppend = ''
- ): void {
- if (
- configurationSection != null &&
- Configuration.getConfigurationData()?.[configurationSection as keyof ConfigurationData] !=
- null &&
- (
- Configuration.getConfigurationData()?.[
- configurationSection as keyof ConfigurationData
- ] as Record<string, unknown>
- )[key] != null
- ) {
- console.error(
- `${chalk.green(logPrefix())} ${chalk.red(
- `Deprecated configuration key '${key}' usage in section '${configurationSection}'${
- logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
- }`
- )}`
- )
- } else if (Configuration.getConfigurationData()?.[key as keyof ConfigurationData] != null) {
- console.error(
- `${chalk.green(logPrefix())} ${chalk.red(
- `Deprecated configuration key '${key}' usage${
- logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
- }`
- )}`
- )
- }
- }
}
--- /dev/null
+import chalk from 'chalk'
+
+import {
+ type ConfigurationData,
+ ConfigurationSection,
+ type StationTemplateUrl,
+} from '../types/index.js'
+import { WorkerProcessType } from '../worker/index.js'
+import { logPrefix } from './ConfigurationUtils.js'
+import { has } from './Utils.js'
+
+/**
+ * Check and warn about deprecated configuration keys
+ * @param configurationData - The configuration data to check
+ */
+export function checkDeprecatedConfigurationKeys (
+ configurationData: ConfigurationData | undefined
+): void {
+ const deprecatedKeys: [string, ConfigurationSection | undefined, string][] = [
+ // connection timeout
+ [
+ 'autoReconnectTimeout',
+ undefined,
+ "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
+ ],
+ [
+ 'connectionTimeout',
+ undefined,
+ "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
+ ],
+ // connection retries
+ ['autoReconnectMaxRetries', undefined, 'Use it in charging station template instead'],
+ // station template url(s)
+ ['stationTemplateURLs', undefined, "Use 'stationTemplateUrls' instead"],
+ // supervision url(s)
+ ['supervisionURLs', undefined, "Use 'supervisionUrls' instead"],
+ // supervision urls distribution
+ ['distributeStationToTenantEqually', undefined, "Use 'supervisionUrlDistribution' instead"],
+ ['distributeStationsToTenantsEqually', undefined, "Use 'supervisionUrlDistribution' instead"],
+ // worker section
+ [
+ 'useWorkerPool',
+ undefined,
+ `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`,
+ ],
+ [
+ 'workerProcess',
+ undefined,
+ `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`,
+ ],
+ [
+ 'workerStartDelay',
+ undefined,
+ `Use '${ConfigurationSection.worker}' section to define the worker start delay instead`,
+ ],
+ [
+ 'chargingStationsPerWorker',
+ undefined,
+ `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead`,
+ ],
+ [
+ 'elementAddDelay',
+ undefined,
+ `Use '${ConfigurationSection.worker}' section to define the worker's element add delay instead`,
+ ],
+ [
+ 'workerPoolMinSize',
+ undefined,
+ `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead`,
+ ],
+ [
+ 'workerPoolSize',
+ undefined,
+ `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`,
+ ],
+ [
+ 'workerPoolMaxSize',
+ undefined,
+ `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`,
+ ],
+ [
+ 'workerPoolStrategy',
+ undefined,
+ `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead`,
+ ],
+ ['poolStrategy', ConfigurationSection.worker, 'Not publicly exposed to end users'],
+ ['elementStartDelay', ConfigurationSection.worker, "Use 'elementAddDelay' instead"],
+ // log section
+ [
+ 'logEnabled',
+ undefined,
+ `Use '${ConfigurationSection.log}' section to define the logging enablement instead`,
+ ],
+ [
+ 'logFile',
+ undefined,
+ `Use '${ConfigurationSection.log}' section to define the log file instead`,
+ ],
+ [
+ 'logErrorFile',
+ undefined,
+ `Use '${ConfigurationSection.log}' section to define the log error file instead`,
+ ],
+ [
+ 'logConsole',
+ undefined,
+ `Use '${ConfigurationSection.log}' section to define the console logging enablement instead`,
+ ],
+ [
+ 'logStatisticsInterval',
+ undefined,
+ `Use '${ConfigurationSection.log}' section to define the log statistics interval instead`,
+ ],
+ [
+ 'logLevel',
+ undefined,
+ `Use '${ConfigurationSection.log}' section to define the log level instead`,
+ ],
+ [
+ 'logFormat',
+ undefined,
+ `Use '${ConfigurationSection.log}' section to define the log format instead`,
+ ],
+ [
+ 'logRotate',
+ undefined,
+ `Use '${ConfigurationSection.log}' section to define the log rotation enablement instead`,
+ ],
+ [
+ 'logMaxFiles',
+ undefined,
+ `Use '${ConfigurationSection.log}' section to define the log maximum files instead`,
+ ],
+ [
+ 'logMaxSize',
+ undefined,
+ `Use '${ConfigurationSection.log}' section to define the log maximum size instead`,
+ ],
+ // performanceStorage section
+ ['URI', ConfigurationSection.performanceStorage, "Use 'uri' instead"],
+ ]
+ for (const [key, section, msg] of deprecatedKeys) {
+ warnDeprecatedConfigurationKey(configurationData, key, section, msg)
+ }
+ // station template url(s) remapping
+ if (configurationData?.['stationTemplateURLs' as keyof ConfigurationData] != null) {
+ configurationData.stationTemplateUrls = configurationData[
+ 'stationTemplateURLs' as keyof ConfigurationData
+ ] as StationTemplateUrl[]
+ }
+ configurationData?.stationTemplateUrls.forEach((stationTemplateUrl: StationTemplateUrl) => {
+ if (stationTemplateUrl['numberOfStation' as keyof StationTemplateUrl] != null) {
+ console.error(
+ `${chalk.green(logPrefix())} ${chalk.red(
+ `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`
+ )}`
+ )
+ }
+ })
+ // worker section: staticPool check
+ if (configurationData?.worker?.processType === ('staticPool' as WorkerProcessType)) {
+ console.error(
+ `${chalk.green(logPrefix())} ${chalk.red(
+ `Deprecated configuration 'staticPool' value usage in worker section 'processType' field. Use '${WorkerProcessType.fixedPool}' value instead`
+ )}`
+ )
+ }
+ // uiServer section
+ if (has('uiWebSocketServer', configurationData)) {
+ console.error(
+ `${chalk.green(logPrefix())} ${chalk.red(
+ `Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead`
+ )}`
+ )
+ }
+}
+
+/**
+ * Warn about a deprecated configuration key
+ * @param configurationData - The configuration data to check
+ * @param key - The deprecated key name
+ * @param configurationSection - The configuration section containing the key
+ * @param logMsgToAppend - Additional message to append to the warning
+ */
+function warnDeprecatedConfigurationKey (
+ configurationData: ConfigurationData | undefined,
+ key: string,
+ configurationSection?: ConfigurationSection,
+ logMsgToAppend = ''
+): void {
+ if (
+ configurationSection != null &&
+ configurationData?.[configurationSection as keyof ConfigurationData] != null &&
+ (configurationData[configurationSection as keyof ConfigurationData] as Record<string, unknown>)[
+ key
+ ] != null
+ ) {
+ console.error(
+ `${chalk.green(logPrefix())} ${chalk.red(
+ `Deprecated configuration key '${key}' usage in section '${configurationSection}'${
+ logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
+ }`
+ )}`
+ )
+ } else if (configurationData?.[key as keyof ConfigurationData] != null) {
+ console.error(
+ `${chalk.green(logPrefix())} ${chalk.red(
+ `Deprecated configuration key '${key}' usage${
+ logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
+ }`
+ )}`
+ )
+ }
+}