"poolifier",
"postject",
"preinstall",
- "rambda",
"recurrency",
"RFID",
"shutdowning",
'logform',
'mnemonist',
'poolifier',
- 'rambda',
'measurand',
'measurands',
'mikro',
"mnemonist": "0.40.3",
"mongodb": "^6.17.0",
"poolifier": "^4.4.5",
- "rambda": "^9.4.2",
"tar": "^7.4.3",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0",
poolifier:
specifier: ^4.4.5
version: 4.4.5
- rambda:
- specifier: ^9.4.2
- version: 9.4.2
tar:
specifier: ^7.4.3
version: 7.4.3
resolution: {integrity: sha512-kKr2uQ2AokadPjvTyKJQad9xELbZwYzWlNfI3Uz2j/ib5u6H9lDP7fUUR//rMycd0gv4Z5P1qXMfXR8YpIxrjQ==}
hasBin: true
- rambda@9.4.2:
- resolution: {integrity: sha512-++euMfxnl7OgaEKwXh9QqThOjMeta2HH001N1v4mYQzBjJBnmXBh2BCK6dZAbICFVXOFUVD3xFG0R3ZPU0mxXw==}
-
randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
minimist: 1.2.8
through2: 2.0.5
- rambda@9.4.2: {}
-
randombytes@2.1.0:
dependencies:
safe-buffer: 5.2.1
'mongodb',
'node:*',
'poolifier',
- 'rambda',
'tar',
'winston',
'winston/*',
import { dirname, join } from 'node:path'
import { URL } from 'node:url'
import { parentPort } from 'node:worker_threads'
-import { mergeDeepRight, once } from 'rambda'
import { type RawData, WebSocket } from 'ws'
import { BaseError, OCPPError } from '../exception/index.js'
isNotEmptyString,
logger,
logPrefix,
+ mergeDeepRight,
min,
+ once,
roundTo,
secureRandom,
sleep,
const stationInfoFromFile = this.getStationInfoFromFile(
stationInfoFromTemplate.stationInfoPersistentConfiguration
)
- let stationInfo: ChargingStationInfo
+ let stationInfo: Partial<ChargingStationInfo>
// Priority:
// 1. charging station info from template
// 2. charging station info from configuration file
import { basename, dirname, isAbsolute, join, parse, relative, resolve } from 'node:path'
import { env } from 'node:process'
import { fileURLToPath } from 'node:url'
-import { isEmpty } from 'rambda'
import type { ChargingStation } from './ChargingStation.js'
convertToInt,
DCElectricUtils,
isArraySorted,
+ isEmpty,
isNotEmptyArray,
isNotEmptyString,
isValidDate,
import { LRUMapWithDelete as LRUCache } from 'mnemonist'
-import { isEmpty } from 'rambda'
import type { ChargingStationConfiguration, ChargingStationTemplate } from '../types/index.js'
-import { isNotEmptyArray, isNotEmptyString } from '../utils/index.js'
+import { isEmpty, isNotEmptyArray, isNotEmptyString } from '../utils/index.js'
import { Bootstrap } from './Bootstrap.js'
enum CacheType {
import { secondsToMilliseconds } from 'date-fns'
-import { isEmpty } from 'rambda'
import type { ChargingStation } from '../ChargingStation.js'
type StopTransactionRequest,
type StopTransactionResponse,
} from '../../types/index.js'
-import { Constants, convertToInt, isAsyncFunction, logger } from '../../utils/index.js'
+import { Constants, convertToInt, isAsyncFunction, isEmpty, logger } from '../../utils/index.js'
import { getConfigurationKey } from '../ConfigurationKeyUtils.js'
import { buildMeterValue } from '../ocpp/index.js'
import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js'
import { createWriteStream, readdirSync } from 'node:fs'
import { dirname, extname, join, resolve } from 'node:path'
import { fileURLToPath, URL } from 'node:url'
-import { isEmpty } from 'rambda'
import { create } from 'tar'
import {
formatDurationMilliSeconds,
handleIncomingRequestError,
isAsyncFunction,
+ isEmpty,
isNotEmptyArray,
isNotEmptyString,
logger,
import { CircularBuffer } from 'mnemonist'
import { performance, type PerformanceEntry, PerformanceObserver } from 'node:perf_hooks'
import { parentPort } from 'node:worker_threads'
-import { is, mean, median } from 'rambda'
import { BaseError } from '../exception/index.js'
import {
logger,
logPrefix,
max,
+ mean,
+ median,
min,
nthPercentile,
stdDeviation,
try {
performance.measure(name, markId)
} catch (error) {
- if (is(Error, error) && error.message.includes('performance mark has not been set')) {
+ if (error instanceof Error && error.message.includes('performance mark has not been set')) {
/* Ignore */
} else {
throw error
import { dirname, join } from 'node:path'
import { env } from 'node:process'
import { fileURLToPath } from 'node:url'
-import { has, mergeDeepRight, once } from 'rambda'
import {
ApplicationProtocol,
logPrefix,
} from './ConfigurationUtils.js'
import { Constants } from './Constants.js'
-import { isCFEnvironment } from './Utils.js'
+import { has, isCFEnvironment, mergeDeepRight, once } from './Utils.js'
type ConfigurationSectionType =
| LogConfiguration
}
public static getSupervisionUrlDistribution (): SupervisionUrlDistribution | undefined {
- return has(Configuration.getConfigurationData(), 'supervisionUrlDistribution')
+ return has('supervisionUrlDistribution', Configuration.getConfigurationData())
? Configuration.getConfigurationData()?.supervisionUrlDistribution
: SupervisionUrlDistribution.ROUND_ROBIN
}
"Use 'uri' instead"
)
// uiServer section
- if (has(Configuration.getConfigurationData(), 'uiWebSocketServer')) {
+ if (has('uiWebSocketServer', Configuration.getConfigurationData())) {
console.error(
`${chalk.green(logPrefix())} ${chalk.red(
`Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead`
static readonly DEFAULT_PERFORMANCE_RECORDS_DB_NAME = 'e-mobility-charging-stations-simulator'
static readonly DEFAULT_PERFORMANCE_RECORDS_FILENAME = 'performanceRecords.json'
- static readonly DEFAULT_STATION_INFO: Partial<ChargingStationInfo> = Object.freeze({
+ static readonly DEFAULT_STATION_INFO: Readonly<Partial<ChargingStationInfo>> = Object.freeze({
automaticTransactionGeneratorPersistentConfiguration: true,
autoReconnectMaxRetries: -1,
autoStart: true,
-import { mean } from 'rambda'
+export const mean = (dataSet: number[]): number => {
+ if (Array.isArray(dataSet) && dataSet.length === 0) {
+ return 0
+ }
+ return dataSet.reduce((accumulator, num) => accumulator + num, 0) / dataSet.length
+}
+
+export const median = (dataSet: number[]): number => {
+ if (Array.isArray(dataSet) && dataSet.length === 0) {
+ return 0
+ }
+ const sortedDataSet = dataSet.slice().sort((a, b) => a - b)
+ const length = sortedDataSet.length
+ if (length % 2 === 0) {
+ return (sortedDataSet[length / 2 - 1] + sortedDataSet[length / 2]) / 2
+ }
+ return sortedDataSet[Math.floor(length / 2)]
+}
export const min = (...args: number[]): number =>
args.reduce((minimum, num) => (minimum < num ? minimum : num), Number.POSITIVE_INFINITY)
} from 'date-fns'
import { getRandomValues, randomBytes, randomUUID } from 'node:crypto'
import { env } from 'node:process'
-import { is, isNotEmpty, type NonEmptyArray, type ReadonlyNonEmptyArray } from 'rambda'
import {
type JsonType,
WebSocketCloseEventStatusString,
} from '../types/index.js'
+type NonEmptyArray<T> = [T, ...T[]]
+type ReadonlyNonEmptyArray<T> = readonly [T, ...(readonly T[])]
+
export const logPrefix = (prefixString = ''): string => {
return `${new Date().toLocaleString()}${prefixString}`
}
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export const once = <T extends (...args: any[]) => any>(fn: T): T => {
+ let hasBeenCalled = false
+ let result: ReturnType<T> | undefined
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ return function (this: any, ...args: Parameters<T>): ReturnType<T> {
+ if (!hasBeenCalled) {
+ hasBeenCalled = true
+ result = fn.apply(this, args) as ReturnType<T>
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return result
+ } as T
+}
+
+export const has = (property: PropertyKey, object: object | undefined): boolean => {
+ if (object == null) {
+ return false
+ }
+ return Object.prototype.hasOwnProperty.call(object, property)
+}
+
+const type = (value: unknown): string => {
+ if (value === null) return 'Null'
+ if (value === undefined) return 'Undefined'
+ if (Number.isNaN(value)) return 'NaN'
+ if (Array.isArray(value)) return 'Array'
+ return Object.prototype.toString.call(value).slice(8, -1)
+}
+
+export const isEmpty = (value: unknown): boolean => {
+ const inputType = type(value)
+ if (['NaN', 'Null', 'Number', 'Undefined'].includes(inputType)) {
+ return false
+ }
+ if (!value) return true
+
+ if (inputType === 'Object') {
+ return Object.keys(value as Record<string, unknown>).length === 0
+ }
+
+ if (inputType === 'Array') {
+ return (value as unknown[]).length === 0
+ }
+
+ return false
+}
+
+const isObject = (value: unknown): value is object => {
+ return type(value) === 'Object'
+}
+
+export const mergeDeepRight = <T extends object>(target: T, source: Partial<T>): T => {
+ const output = { ...target }
+
+ if (isObject(target) && isObject(source)) {
+ Object.keys(source).forEach(key => {
+ if (isObject(source[key])) {
+ if (!(key in target)) {
+ Object.assign(output, { [key]: source[key] })
+ } else {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ output[key] = mergeDeepRight(target[key], source[key])
+ }
+ } else {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ Object.assign(output, { [key]: source[key] })
+ }
+ })
+ }
+
+ return output
+}
+
export const generateUUID = (): `${string}-${string}-${string}-${string}-${string}` => {
return randomUUID()
}
export const isNotEmptyArray = <T>(
value: unknown
): value is NonEmptyArray<T> | ReadonlyNonEmptyArray<T> => {
- return Array.isArray(value) && isNotEmpty<T>(value as T[])
+ return Array.isArray(value) && value.length > 0
}
export const insertAt = (str: string, subStr: string, pos: number): string =>
return JSON.stringify(
object,
(_, value: Record<string, unknown>) => {
- if (is(Map, value)) {
+ if (value instanceof Map) {
switch (mapFormat) {
case MapStringifyFormat.object:
return {
default:
return [...value]
}
- } else if (is(Set, value)) {
- return [...value]
+ } else if (value instanceof Set) {
+ return [...value] as Record<string, unknown>[]
}
return value
},
buildStoppedMessage,
buildUpdatedMessage,
} from './MessageChannelUtils.js'
-export { max, min, nthPercentile, stdDeviation } from './StatisticUtils.js'
+export { max, mean, median, min, nthPercentile, stdDeviation } from './StatisticUtils.js'
export {
clone,
convertToBoolean,
getRandomFloatFluctuatedRounded,
getRandomFloatRounded,
getWebSocketCloseEventStatusString,
+ has,
isArraySorted,
isAsyncFunction,
+ isEmpty,
isNotEmptyArray,
isNotEmptyString,
isValidDate,
JSONStringify,
logPrefix,
+ mergeDeepRight,
+ once,
roundTo,
secureRandom,
sleep,
import { isMainThread } from 'node:worker_threads'
-import { mergeDeepRight } from 'rambda'
import type { WorkerAbstract } from './WorkerAbstract.js'
+import { mergeDeepRight } from '../utils/index.js'
import { DEFAULT_WORKER_OPTIONS } from './WorkerConstants.js'
import { WorkerDynamicPool } from './WorkerDynamicPool.js'
import { WorkerFixedPool } from './WorkerFixedPool.js'
public static getWorkerImplementation<D extends WorkerData, R extends WorkerData>(
workerScript: string,
workerProcessType: WorkerProcessType,
- workerOptions?: WorkerOptions
+ workerOptions?: Partial<WorkerOptions>
): WorkerAbstract<D, R> {
if (!isMainThread) {
throw new Error('Cannot get a worker implementation outside the main thread')