feat(ui): add CLI client and shared UI common library (#1789)
* feat(ui): scaffold ui/common and ui/cli workspaces
- Add ui/common workspace with shared protocol types, SRPC WebSocket client,
UUID utilities, config types, and Zod validation schemas (21 tests, 0 errors)
- Add ui/cli workspace scaffolding with all configuration files
- Register both workspaces in pnpm-workspace.yaml
- Update .prettierignore, release-please config and manifest
- Add build-common (library, single env) and build-cli (3x3 matrix) CI jobs
- Add formatting steps for new workspaces in autofix.yml
- Add sonar-project.properties for both workspaces
* feat(cli): implement CLI core infrastructure
- Commander.js entry point with 10 subcommand groups (simulator, station,
template, connection, connector, atg, transaction, ocpp, performance,
supervision)
- Config loading with lilconfig + Zod validation + merge precedence
(defaults < config file < CLI flags)
- Output formatters: JSON (--json flag) and table (human-readable)
- WS client lifecycle: executeCommand() connects, sends, receives,
disconnects; registerSignalHandlers() for SIGINT/SIGTERM
- Typed error classes: ConnectionError, AuthenticationError,
TimeoutError, ServerError
- esbuild bundle with version injection and shebang
- 23 unit tests passing, zero lint warnings
* feat(cli): implement all 35 UI protocol command groups
Shared runAction() helper DRYs up all command action handlers.
* test(cli): add integration tests
- 8 integration tests covering --help, --version, subcommand help,
connection error handling, JSON mode, and missing required options
- Separate test:integration script targeting tests/integration/
- Unit tests narrowed to tests/*.test.ts (no integration overlap)
* docs(cli): add README for ui/cli and ui/common
- ui/cli/README.md: installation, configuration, all command groups
with examples, global options, exit codes, environment variables,
available scripts
- ui/common/README.md: exported API reference (types, WebSocketClient,
config validation, UUID utilities)
* [autofix.ci] apply automated fixes
* fix(cli): improve error handling for config and connection failures
- Catch config file ENOENT in loader.ts and throw clean error message
- Move loadConfig inside try/catch in runAction to prevent stack traces
- Use event.error in WebSocketClient onerror for better error propagation
- Separate connect errors from request errors in lifecycle.ts
- Include cause message in ConnectionError for descriptive output
* fix(cli): address PR review feedback
- Fix onerror stale after connect: replace with persistent error handler
that fails all pending sendRequest promises immediately on WS error
- Fix dead code: call registerSignalHandlers() in cli.ts for SIGINT/SIGTERM
- Fix JSON error output: write to stdout (not stderr) in --json mode to
match documented contract and enable scripting with 2>/dev/null
- Fix process.exit() in action.ts: use process.exitCode for proper async
cleanup and testability
- Fix Map iteration: use snapshot+clear pattern in clearHandlers/failAllPending
- Fix empty array edge case in table formatter: check .length === 0
- Fix README: merge exit codes 1+2 into single row (Commander uses 1)
- Fix CI: add needs: build-common to build-cli job
* fix(cli): address second round of PR review feedback
- Fix ServerFailureError: WebSocketClient.handleMessage now rejects
with a typed Error (carrying the ResponsePayload) instead of a raw
object, preventing [object Object] in CLI output
- Fix table formatter: FAILURE responses now display hashIdsFailed/
hashIdsSucceeded tables instead of early-returning with minimal info
- Fix auth schema: Zod refinement requires username+password when
authentication enabled with protocol-basic-auth
- Fix AuthenticationConfig.type: use AuthenticationType enum instead
of plain string for compile-time safety
- Fix signal handlers: use process.exitCode instead of process.exit()
so finally blocks in executeCommand can run cleanup
* fix(cli): restore process.exit in signal handlers, fix type mismatch, remove dead code
- Signal handlers: restore process.exit() — setting only process.exitCode
keeps the process alive since registering SIGINT listener removes Node's
default termination behavior
- BroadcastChannelResponsePayload: align field name with server wire format
(hashId: string | undefined, not id: string)
- Remove unused DEFAULT_TIMEOUT_MS export from defaults.ts
* refactor(ui-common): extract WS timeout to shared constant
Move UI_WEBSOCKET_REQUEST_TIMEOUT_MS from a private constant in
WebSocketClient.ts to ui/common/src/constants.ts as a single source
of truth, exported via barrel for consumers.
* refactor(cli): merge duplicate ui-common and commander imports
Consolidate split type/value imports from the same module into single
import statements using inline type syntax (`import { type X, Y }`).
Resolves SonarCloud 'imported multiple times' warning.
* refactor(cli): use Number.parseInt instead of parseInt
Resolves SonarCloud 'Prefer Number.parseInt over parseInt' warning.
* refactor(cli): remove unnecessary Command alias from commander import
Use import { Command } from 'commander' directly instead of aliasing
to Cmd. Single import serves both type and value usage.
- parseCommaSeparatedInts now rejects NaN values with a clear error
message instead of silently sending garbage to the server
- lilconfig search() path now wrapped in try/catch like the explicit
config path, giving consistent error messages for malformed configs
- Bundle all dependencies into single 504KB dist/cli.js (no node_modules
needed at runtime). Only ws native addons (bufferutil, utf-8-validate)
are external — ws falls back to pure JS automatically.
- Replace lilconfig with direct XDG config file reading:
${XDG_CONFIG_HOME:-~/.config}/evse-cli/config.json
- Remove lilconfig dependency
- Add install.sh: builds CLI, copies to ~/.local/bin/evse-cli, creates
default XDG config, warns if ~/.local/bin not in PATH
- Isolate ALL config tests from host env via XDG_CONFIG_HOME in
beforeEach/afterEach + add XDG auto-discovery happy path test
- Guard against JSON array in config file
- Update README: standalone install instructions + XDG config location
* [autofix.ci] apply automated fixes
* fix(cli): address review feedback round 3
WebSocketClient:
- Validate responsePayload shape before casting (guard against [uuid, null])
- Reject connect() Promise if socket closes before onopen fires
- Add tests for both edge cases
CLI:
- Validate ws:/wss: URL scheme in parseServerUrl
- Output ServerFailureError.payload via formatter (show hashIdsFailed details)
- Extract shared parseInteger() validator — reject NaN with clear error
- Remove dead error types (AuthenticationError, ServerError, TimeoutError)
- Chain build in test:integration script
- Remove unreachable FAILURE branch in outputTable
* ci: remove redundant lint:fix from autofix workflow
pnpm format already runs eslint --cache --fix, making the separate
pnpm lint:fix step redundant in all three ui workspaces.
* refactor(cli): remove unnecessary comment in outputTable
* fix(cli): don't override template defaults in station add, reject array config
- station add: only include autoStart, persistentConfiguration,
ocppStrictCompliance, deleteConfiguration in payload when explicitly
passed — lets server use template defaults otherwise
- config loader: reject uiServer array with clear error instead of
silently spreading array keys into object
* refactor(cli): remove unused terminal-link dep and dead exports
- Remove terminal-link from dependencies (never imported)
- Remove unused exports: printSuccess, printWarning, printInfo (human.ts),
outputTableList (table.ts)
- Remove corresponding test for printSuccess
* [autofix.ci] apply automated fixes
* fix(ui-common): reject malformed payloads, replace ReadyState with enum
- handleMessage: reject pending handler immediately when server sends
response with matching UUID but missing status field, instead of
silently dropping and waiting for 60s timeout
- Replace ReadyState type alias with WebSocketReadyState const enum
- Remove redundant ReadyState type (duplicated enum semantics)
* fix(cli): add connection timeout, remove dead defaultUIServerConfig
- Wrap client.connect() with Promise.race timeout to prevent infinite
hang when server accepts TCP but never completes WS handshake
- Remove unused defaultUIServerConfig export from defaults.ts
* fix(cli): clear connection timeout timer on success
- WebSocketClient: make buildUrl() a public url getter so consumers
use the canonical URL instead of reconstructing it
- lifecycle.ts: use client.url instead of building URL independently
- Remove ConfigurationType.ts: UIServerConfigurationSection is now
a type alias for the Zod-inferred UIServerConfig (single source)
* fix(cli): display failure status instead of misleading Success
displayGenericPayload now checks payload.status before printing — a
failure response without hashIds shows the red status line instead of
a green checkmark.
* fix: align ui/web ResponsePayload with server, reject non-object config
- ui/web ResponsePayload: replace incorrect hashIds with
hashIdsFailed/hashIdsSucceeded/responsesFailed matching server schema
- cli config loader: reject non-object uiServer values with clear error
instead of silently falling back to defaults
- Config loader: throw on primitive JSON config values (42, "hello")
instead of silently falling back to defaults
- Merge duplicate UIProtocol.ts import in ui/common/src/client/types.ts
* fix: suppress dangling connect rejection, remove dead Zod defaults
- Attach .catch() to connect() promise to prevent unhandled rejection
when the timeout wins Promise.race and disconnect triggers onclose
- Remove .default() calls from Zod schema — the CLI loader always
provides all fields via its canonical defaults map, making Zod
defaults dead code paths
* fix(cli): preserve error cause in config loader for debuggability
refactor(tests): replace re-export hub with direct imports
Remove ChargingStationTestUtils.ts re-export hub and update all 73
test files to import directly from their defining modules
(StationHelpers, TestLifecycleHelpers, MockCaches, MockWebSocket).
Fix __testable__ barrel bypass in OCPP20RequestService-CallChain test.
Harmonize Web UI test imports to use composables/types barrels
consistently and export validateUUID from composables barrel.
Add 'Direct Imports' convention to TEST_STYLE_GUIDE.md §6.
fix(ocpp): use Volatile instead of invented Ephemeral for CertificatePrivateKey persistence
Remove non-spec PersistenceEnumType.Ephemeral value. Use existing
Volatile which correctly prevents disk persistence and is already
used for ~25 other runtime-only variables in the registry.
refactor(crypto): modernize crypto APIs and harden certificate handling
- Migrate createHash() to crypto.hash() one-shot API (Node.js 22+)
- Migrate createSign() to crypto.sign() one-shot API
- Add derLength() bounds check for lengths > 0xFFFF
- Change CertificatePrivateKey persistence to Ephemeral
- Add logger.debug in deriveSigningMethodFromPublicKeyHex catch block
- Harden validateCertificatePath with async realpath for symlink
protection (falls back to resolve for not-yet-created paths)
- Add X.509 chain validation in validateCertificateX509: verify issuer
linkage and signatures for all certificates, validate validity
period for entire chain (not just leaf), document leaf-first ordering
- Implement RFC 6960 §4.1.1 compliant issuerNameHash via DER issuer
extraction from certificate raw bytes (extractDerIssuer)
- Move DER parsing functions (readDerLength, skipDerElement,
extractDerIssuer) to Asn1DerUtils with proper JSDoc
- Rename readDerTagLength to readDerLength for accuracy
- Add unit tests for DER parsing functions and chain validation
- Add real CA + leaf certificate test fixtures
refactor(ocpp): use Node.js crypto for EC curve derivation
Replace manual ASN.1 OID hex substring matching with
crypto.createPublicKey which parses the DER structure via OpenSSL,
validates the EC point, and returns the named curve directly. Map
Node.js curve names (e.g. prime256v1) to SigningMethodEnumType.
feat(ocpp): add signing prerequisites validation and EC curve auto-derivation
- Add deriveSigningMethodFromPublicKeyHex: extracts EC curve OID from
ASN.1 DER public key and maps to SigningMethodEnumType
- Add validateSigningPrerequisites: checks public key presence, curve
detection, and config/key curve consistency
- Integrate validation in OCPP 2.0 (OCPPServiceUtils.buildMeterValue)
and OCPP 1.6 (readSigningConfigForConnector) signing paths
- On validation failure: log warning and gracefully fallback to unsigned
meter values instead of producing invalid signed data
- Add unit tests for deriveSigningMethodFromPublicKeyHex and
validateSigningPrerequisites covering all paths
feat(ocpp): auto-derive signing method from EC public key curve
- Add deriveSigningMethodFromPublicKeyHex that extracts EC curve OID
from ASN.1 DER public key and maps to SigningMethodEnumType
- Add validateSigningPrerequisites that validates public key presence,
curve detection, and config consistency — returns enabled/disabled
with reason
- Integrate validation in OCPP 2.0 (OCPPServiceUtils.buildMeterValue)
and OCPP 1.6 (readSigningConfigForConnector) signing paths
- On validation failure: log error and gracefully fallback to unsigned
meter values instead of producing invalid signed data
- Move TEST_PUBLIC_KEY_HEX to ChargingStationTestConstants for reuse
- Update test fixtures to use valid secp256k1 ASN.1 EC public key
fix(ocpp): conform signingMethod to OCA spec and fix template curve mismatch
- Set signingMethod to empty string when using OCMF encoding per OCA
Application Note Table 11: SA field already in signedMeterData
- Add FiscalMetering.SigningMethod=ECDSA-secp256k1-SHA256 to
keba-ocpp2-signed template to match the secp256k1 public key from
OCA spec §5.3 example
- Update tests to expect empty signingMethod for OCMF payloads
- Remove unused SigningMethodEnumType imports from test files
feat(ocpp): add EncodingMethodEnumType enum for signed meter value encoding methods
- Add EncodingMethodEnumType enum with OCMF and EDL values based on
OCA Application Note and OCPP 2.0.1/2.1 specs
- Use enum in SignedMeterData interface and wire types (with string
union for extensibility)
- Replace hardcoded 'OCMF' string constant with enum value
- Update all tests to use EncodingMethodEnumType consistently
fix(ocpp): populate signingMethod field and add SigningMethodEnumType enum
- Fix signingMethod being empty string in SignedMeterValueType payload
- Add SigningMethodEnumType enum per OCA Application Note Table 12 and
OCPP 2.1 Appendix 7.4 with all 7 standardized values
- Propagate FiscalMetering.SigningMethod config variable to the signed
meter data generator with fallback to ECDSA-secp256r1-SHA256
- Add OCPP 1.6 vendor key SigningMethod and read it in
readSigningConfigForConnector
- Add MeterPublicKey and SigningMethod mappings to OCPP2_PARAMETER_KEY_MAP
- Use SigningMethodEnumType in all interfaces and type definitions
- Update README vendor-specific keys documentation
feat(ocpp): add signed meter values support for OCPP 1.6 and 2.0.x (#1775)
* feat(types): export OCPP16MeterValueFormat and add OCPP16SignedMeterValue
* feat(ocpp1.6): add vendor configuration keys for signed meter values
* feat(ocpp2.0): add variable registry entries for signed meter readings
* feat(mock-server): handle signed meter value payloads
* feat(ocpp): add simulated signed meter data generator
* feat(ocpp): add PublicKeyWithSignedMeterValue enum and state tracking
* feat(ocpp2.0): populate signedMeterValue in sampled value building
* feat(ocpp1.6): add signed meter value support to sampled value building
* feat(templates): add station templates with signed meter value config
* docs: document signed meter values configuration
* fix(lint): apply formatter fixes to signed meter value files
* fix(ocpp): fix publicKey state tracking and reset in signed meter values
* fix(ocpp): fix OCMF payload, hash, and public key encoding
* fix(ocpp): add defensive guards and improve signing code quality
* fix(ocpp2.0): respect SignStartedReadings and SignUpdatedReadings sub-switches
* [autofix.ci] apply automated fixes
* fix(ocpp): consistent publicKey state update, version-gated vendor keys, use enum constants
* [autofix.ci] apply automated fixes
* refactor(ocpp): use union types in common code and correct enum classification per OCPP specs
- Use VendorParametersKey (union) in ChargingStation.ts, not OCPP16VendorParametersKey
- Remove VERSION_16 guard: OCPP2_PARAMETER_KEY_MAP resolves keys per version
- Add 6 mapping entries to OCPP2_PARAMETER_KEY_MAP for signed MV config keys
- Classify enums per OCPP 2.0.1 base spec: SignReadings and
PublicKeyWithSignedMeterValue in OCPP20OptionalVariableName (Required: no)
- Classify Application Note-only variables (SignStartedReadings,
SignUpdatedReadings, PublicKey, SigningMethod) in OCPP20VendorVariableName
- Replace all string literals with enum references in variable registry
* [autofix.ci] apply automated fixes
* fix(ocpp): fix corrupted Measurands entry, Wh/kWh unit handling, context mapping, and enum validation
- Fix AlignedDataCtrlr.Measurands variable corrupted by lint auto-sort (was SignUpdatedReadings)
- Add meterValueUnit to SignedMeterDataParams to handle kWh input without double-division
- Replace unsafe context cast with explicit OCPP20-to-generator context mapping
- Extract parsePublicKeyWithSignedMeterValue helper (hoisted Set, shared across OCPP versions)
- Use MeterValueUnit enum constant instead of string literal for kWh comparison
* [autofix.ci] apply automated fixes
* refactor(ocpp): use MeterValueContext/MeterValueUnit enums and barrel imports instead of string literals
* style: apply prettier formatting to SignedMeterDataGenerator test
* fix(ocpp): add vendorSpecific flag, guard publicKey inclusion on key availability, add kWh test
* refactor(ocpp): use PublicKeyWithSignedMeterValueEnumType.Never instead of string literal
* test(ocpp): add JSDoc header, TX=P/Clock tests, non-energy measurand and transactionData force tests
* test(ocpp1.6): add periodic signing tests with mock timers for startUpdatedMeterValues
* refactor(test): use beforeEach for station creation in OCPP16SignedMeterValues per style guide
* fix(ocpp): treat undefined context as periodic for SignUpdatedReadings, conditional try/catch for signing-forced transactionData, use node:assert/strict import
* refactor(ocpp2.0): add defaultValue and enumeration to PublicKeyWithSignedMeterValue registry entry
* refactor(ocpp1.6): remove redundant nullish coalescing on publicKeyHex
* refactor(ocpp): use BaseError, return tuple in OCPP 2.0 builder, extract config reading in OCPP 1.6
* refactor(ocpp): harmonize naming, signatures, and data structures across signing paths
- SignedMeterData extends JsonObject: eliminates as-cast to OCPP16SignedMeterValue
- Rename publicKeyConfig to publicKeyWithSignedMeterValue: consistent with OCPP 2.0 interface
- Rename meterValueWh to meterValue: unit-agnostic (generator handles via meterValueUnit)
- Remove unused OCPP16SignedMeterValue import
* fix(mock-server): log signed meter values in TransactionEvent.Ended and add test
* fix(ocpp): include Transaction.Begin MV in TransactionEvent(Ended) per spec §4.1.2, set signingMethod to empty string per spec §3.2.1
* refactor(ocpp): rename SignedMeter* files to follow OCPP* naming convention
* refactor(ocpp): extract generic SignedSampledValueResult<T> to shared utils
* refactor(ocpp): extract shared SigningConfig interface, OCPP20SampledValueSigningConfig extends it
* refactor(ocpp): use roundTo helper instead of Number/toFixed for kWh conversion
* style(mock-server): reorder signed MV tests to follow Started/Updated/Ended lifecycle
* refactor(ocpp): move SampledValueSigningConfig to shared utils, remove version-specific type import from common code
* fix(ocpp): map StartTxnSampledData to SampledDataCtrlr.TxStartedMeasurands, remove VERSION_16 guard
* [autofix.ci] apply automated fixes
* style: remove unnecessary parenthetical from eslint cspell comment
* style: rename Configuration.test.ts to OCPP16VendorParametersKey.test.ts to reflect content
* refactor(ocpp): rename ocpp20SigningConfig/State to signingConfig/State (version-agnostic)
* style: harmonize blank lines in OCPP20VariableRegistry between component sections
* [autofix.ci] apply automated fixes
* style: remove unjustified blank lines before inline comments within same component section
* fix(ocpp2.0): add missing DeviceDataCtrlr section comment in variable registry
test: harmonize RemoteStartTransaction tests across OCPP stacks
OCPP 1.6:
- Remove fabricated TC_0XX_CS codes (no official 1.6 test case spec)
- Reorder tests by category: happy path, input validation, availability,
transaction state, event listeners
OCPP 2.0:
- Add 2 EVSE availability tests (skip inoperative, all inoperative)
- Add official TC refs where tests match Part 6 spec (TC_F_02_CS,
TC_F_03_CS, TC_K_37_CS)
- Harmonize FR comment format on new tests
fix(ocpp): check connector availability in RemoteStartTransaction auto-selection (#1774)
* fix(ocpp): check connector availability in RemoteStartTransaction auto-selection
The OCPP 1.6 RemoteStartTransaction handler's connector auto-selection
(when connectorId is omitted) only checked transactionStarted and
reservation status but not administrative availability. This meant an
Inoperative connector could be selected, causing the request to be
rejected even when other Operative connectors were free.
Added isConnectorAvailable() check to the auto-selection loop, matching
the behavior already present in the OCPP 2.0 selectAvailableEvse().
- code_style_conventions: reorder sections (general TS → domain → process
→ other → pitfalls), deduplicate BaseError constraint and payload
validation, move Utility Usage Rules next to other TS patterns
- task_completion_checklist: add inline comments to all 3 sections
refactor(ui): use EMPTY_VALUE_PLACEHOLDER in Utils.ts and test assertions
Replace remaining hardcoded 'Ø' in getWebSocketStateName() and 4 test
assertions with the centralized EMPTY_VALUE_PLACEHOLDER constant.
Consolidate duplicate Constants import in Utils.ts.
fix(ui): enable authorize for all OCPP versions in StartTransaction
The OCPP 2.0.x authorize restriction was a UI-only limitation — the
simulator stack already handles Authorize for both 1.6 and 2.0.x.
Remove the isOCPP20x guard so the checkbox is available for all versions.
refactor: add unit suffixes to time-related constants and fix heartbeat unit mismatch
- Rename 19 constants to include _MS or _SECONDS suffix (Constants, OCPPConstants, WorkerConstants)
- Remove redundant // Ms and // Seconds comments from definitions
- Fix pre-existing bug: resetLimits set HeartbeatInterval to 60000 seconds instead of 60
- Remove 97 redundant heartbeatInterval overrides in tests (mock default already correct)
refactor: improve helper/utility usage consistency across codebase
- Make worker module standalone by inlining mergeDeepRight in WorkerUtils
- Replace console/chalk with structured logger calls in Bootstrap
- Extract magic numbers to named constants (auth cache, WS reconnect)
- Use setupConnectorWithTransaction helper in transaction tests
- Remove dead test helper OCPPAuthIntegrationTest
- Fix 'shutdowning' typo in graceful shutdown log message
refactor(tests): extract hardcoded values to constants
- extract TEST_TIMEOUT_MS constant for 9 setTimeout(30000) calls in
UIMCPServer.test.ts
- use Constants.DEFAULT_ATG_CONFIGURATION instead of inline ATG config
objects in SharedLRUCache, ChargingStationConfigurationUtils, and
AutomaticTransactionGenerator tests
- add TEST_RESERVATION_EXPIRY_MS constant and replace 6 hardcoded 3600000 values in reservation and transaction tests
refactor: use utility functions consistently across codebase
Replace manual patterns with existing utility functions to improve DRY
compliance:
- Use isEmpty/isNotEmptyArray/isNotEmptyString instead of .length checks
- Use convertToInt/convertToFloat instead of Number.parseInt/Number
- Use roundTo instead of Math.round(x * N) / N
- Use ensureError in catch blocks for proper error type narrowing
- Add UNIT_DIVIDER_KILO, MILLISECONDS_PER_HOUR and
DEFAULT_EXPONENTIAL_BACKOFF_JITTER_MS to Constants
Note: isEmpty/isNotEmptyString apply whitespace trimming, which
tightens validation for identifiers and configuration values.
refactor: use getErrorMessage helper and extract auth cache constant
Replace 7 instanceof Error ternary patterns with getErrorMessage()
for consistent error message extraction. Extract hardcoded
maxEntries 1000 to Constants.DEFAULT_AUTH_CACHE_MAX_ENTRIES
across InMemoryAuthCache, AuthComponentFactory, and
OCPPAuthServiceImpl.
refactor: replace string literals with OCPP enum constants
Add AllowReset and MasterPassGroupId to OCPP20OptionalVariableName
enum. Replace all hardcoded OCPP variable name strings with enum
references in source and test files. Move EVSE Component section
comment to correct position.
refactor: use string/array helper functions consistently
Replace 9 manual .trim().length checks with isEmpty() and
isNotEmptyString() helpers. Replace Array.isArray+length
guards with isNotEmptyArray() in StatisticUtils. Simplify
isValidIdentifierValue to delegate to isNotEmptyString.
Replace hardcoded SecurityCtrlr.OrganizationName string
with OCPP20ComponentName/RequiredVariableName enums.
refactor: use utility helpers consistently across codebase
Replace manual * 1000 with secondsToMilliseconds() in auth
cache, remote strategy, and OCPP20 incoming request. Replace
manual / 1000 with millisecondsToSeconds() in auth helpers,
auth service, and cert signing retry. Replace 'in' operator
with has() in auth service. Replace configurationKey find
with getConfigurationKey() in OCPP20RequestService. Use
formatDurationMilliSeconds for reconnect delay log. Keep
ms precision in auth service logs (sub-second durations).
refactor(webui): remove redundant CSTable re-mount on toggle
The @clicked handler on the Add Charging Stations toggle
forced a CSTable re-mount on every click. This is unnecessary
since watch($chargingStations) already handles re-renders
when data changes via WS server notifications.
feat(webui): remove reload button (auto-refresh via WebSocket)
The WS server pushes refresh notifications on state changes,
making the manual reload button redundant. Remove ReloadButton
component, its styles, tests, and stub. Simplify need-refresh
handler to only reset add-stations toggle (data refresh and
CSTable re-render handled by server notification + watcher).
Remove dead --spacing-xl CSS variable from all themes.
refactor(webui): migrate globalProperties to provide/inject
Replace Vue 2-style app.config.globalProperties with Vue 3
provide/inject using typed InjectionKeys. Create useConfiguration
and useTemplates composables. Remove dead globalProperties
fallback paths and refreshChargingStations no-op. Update test
mounts to use provide. Clean ComponentCustomProperties from
shims-vue.d.ts.
refactor(webui): align $ prefix convention on composable locals
Rename uiClient to $uiClient in 4 components (convention:
composable-returned Vue service instances use $ prefix).
Rename ref to $chargingStations in Utils.ts (was shadowing
Vue ref import). Keep chargingStationsRef unchanged in
ChargingStationsView (would shadow globalProperty in template).
fix: restrict test coverage to src/ runtime code only
Add --test-coverage-include glob to exclude tests, scripts,
package.json and other non-runtime files from coverage report.
Aligns with web UI vitest coverage config pattern.
refactor(tests): migrate hardcoded test tags to shared constants
Replace 41 hardcoded tag/token strings across 13 test files
with imports from ChargingStationTestConstants. Includes
test utility factories. Normalize import paths to shortest
relative form and merge duplicate imports.
refactor(tests): use setupConnectorWithTransaction shared helper
Replace 19 inline transaction setup patterns across 3 test
files with the shared setupConnectorWithTransaction helper.
Only migrates sites where both transactionStarted and
transactionId are set together.
refactor(tests): migrate inline mock stations to shared factory
Replace inline as-unknown-as ChargingStation casts in
CertificateAuthStrategy and OCPP20AuthAdapter tests with
createMockAuthServiceTestStation factory. Add inAcceptedState
method to the shared auth test station factory.
refactor(tests): consolidate duplicate test helpers
Extract createStationWithRequestHandler to shared
TestLifecycleHelpers, eliminating duplication between
OCPPConnectorStatusOperations and OCPPServiceOperations tests.
Extend setupConnectorWithTransaction to support string
transactionId and pending mode, replacing local
setupTransaction/setupPendingTransaction helpers.
Add DisplayMessages, SupportedFormats, SupportedPriorities to
OCPP20VariableRegistry per OCPP 2.0.1 spec §3.1.8. Use
OCPP20MessageFormatEnumType for SupportedFormats enumeration.
Connector.ConnectorType deferred (requires isComponentValid
design change per OpenSpec process).
refactor(ocpp): consolidate payload validation into shared utility
Extract 4 near-identical validation methods into 1 shared
validatePayload function in OCPPServiceUtils. Each service
method becomes a thin wrapper delegating to the shared logic.
Preserves clone/date-conversion behavior per caller and
error message capitalization for OCPP protocol compliance.
refactor(ocpp): consolidate MeterValues validators and helpers
Replace 5 near-identical validators with 1 generic
validateMeasurandValue using options pattern for phase
and interval. Merge addLineToLineVoltageToMeterValue into
addPhaseVoltageToMeterValue with nominalVoltage and optional
noTemplateFallback params preserving voltage simulation
semantics. Inline dead getMeasurandDefaultContext and
getMeasurandDefault. Extract magic numbers as constants.
refactor(ocpp): consolidate variable access in auth adapter
Remove getVariableValue/getDefaultVariableValue wrappers from
OCPP20AuthAdapter in favor of OCPP20ServiceUtils.readVariableAs*
helpers. Fixes LocalAuthListCtrlr.Enabled default discrepancy
between adapter (true) and registry (false). Add readVariableAsString
helper for consistent string variable access.
Remove 19 unused re-exports from ocpp/index.ts (12) and
auth/index.ts (7). All consumers import directly from source
files. Convert OCPPIncomingRequestService and OCPPRequestService
to type-only exports matching their sole consumer.
refactor(ocpp): remove unnecessary exports from internal helpers
De-export 14 MeterValue helper functions in OCPPServiceUtils
that are now only used internally after builder consolidation.
Make readVariableAsIntervalMs private in OCPP20ServiceUtils.
refactor(ocpp): consolidate variable reading through shared helpers
Promote readVariableAs* helpers to public on OCPP20ServiceUtils.
Replace 10 inline getVariables + manual parsing patterns with
helper calls. Remove duplicated getVariableValue from
CertSigningRetryManager and parseBooleanVariable from
OCPP20AuthAdapter. Replace last parseInt with convertToIntOrNaN.
fix(ocpp): align OCPP 2.x auth adapter version to VERSION_201
OCPP20AuthAdapter was the only 2.x component identifying as
VERSION_20 while all services use VERSION_201. Align adapter
and CertificateAuthStrategy to match the established convention.
refactor(ocpp): consolidate MeterValue builders into shared core
Merge buildOCPP16MeterValue and buildOCPP20MeterValue into
buildMeterValue, eliminating ~250 lines of duplicated logic.
Version differences resolved via switch: evseId + sampled
value builder callback.
Harmonize MeterValues test structure between versions:
extract 1.6 tests to dedicated file, add cross-version
parameterized builder output tests, add missing 2.0
edge case and interval restart test coverage.
fix(ocpp): wire context param through OCPP 1.6 meter value builder
The context parameter was silently dropped during extraction
to OCPP16RequestBuilders, unlike the OCPP 2.0 version which
correctly passes it to all internal calls.
refactor: remove unnecessary type casts and improve type safety
- Remove redundant as any cast in __testable__/index.ts
- Rewrite isEmpty() with native type guards (typeof/instanceof)
- Replace parameter mutation with const in WorkerFactory
- Remove unnecessary cast in MessageChannelUtils
refactor(ocpp): complete version-separation in OCPPServiceUtils
Replace buildSampledValue version-switch with resolveSampledValueFields
helper + version-specific buildOCPP16SampledValue/buildOCPP20SampledValue.
Move mapStopReasonToOCPP20 to OCPP20RequestBuilders where it belongs.
OCPPServiceUtils.ts is now 100% version-agnostic — zero OCPP 1.6/2.0
types, zero inline version switches. All version-specific logic lives
in version-specific leaf modules (RequestBuilders, ServiceUtils).
test(ocpp): harmonize test constants and assertion messages
Replace hardcoded station IDs, tokens, and transaction IDs with
shared constants from ChargingStationTestConstants.ts across 8 test
files. Add TEST_TRANSACTION_UUID constant. Add descriptive messages
to 55 assert.ok() numeric comparison calls across 20+ test files
per TEST_STYLE_GUIDE.md requirements.
refactor(ocpp): align builder naming and harmonize test structure
Rename buildMeterValueForOCPP16/20 and buildSampledValueForOCPP16/20
to buildOCPP16/20MeterValue and buildOCPP16/20SampledValue matching
the established buildOCPP{version}{Thing} convention.
Merge IdTagAuthorization.test.ts into OCPPServiceOperations.test.ts
(source file was deleted). Move buildBootNotificationRequest tests
from OCPPServiceOperations.test.ts to OCPPServiceUtils-pure.test.ts
(function was moved to Utils).
refactor(ocpp): consolidate meter value builders into RequestBuilders
Merge OCPP16MeterValueBuilders.ts and OCPP20MeterValueBuilders.ts
into their respective OCPP16RequestBuilders.ts and OCPP20RequestBuilders.ts
files. Version-specific pure builders belong in a single leaf module
per stack, not separate files per builder type.
refactor(ocpp): extract version-specific builders and transaction operations
Extract meter value builders into OCPP16/20MeterValueBuilders.ts leaf
modules. Extract boot notification builders into OCPP16/20RequestBuilders.ts
leaf modules. Move buildBootNotificationRequest dispatcher from Operations
to Utils. Extract OCPP 2.0 startTransactionOnConnector and
stopTransactionOnConnector into OCPP20ServiceUtils, removing inline
OCPP 2.0 enums and logic from the shared Operations module.
refactor(ocpp): extract connector status operations into dedicated module
Move sendAndSetConnectorStatus, restoreConnectorStatus, and
checkConnectorStatusTransition into OCPPConnectorStatusOperations.ts
to enforce the operations/utils semantic separation without creating
circular dependencies. The new module depends only on Constants
(leaf modules), not on version-specific ServiceUtils.
refactor(ocpp): move isIdTagAuthorized into OCPPServiceOperations
The circular dependency that motivated the separate IdTagAuthorization
file no longer exists after auth module refactoring. Move the function
to OCPPServiceOperations alongside other cross-stack transaction
helpers and delete the standalone file.
Jérôme Benoit [Tue, 31 Mar 2026 23:32:29 +0000 (01:32 +0200)]
refactor(ocpp): consolidate Ajv type alias in OCPPServiceUtils
Export the Ajv type alias from OCPPServiceUtils and import it in
OCPPIncomingRequestService, OCPPRequestService, and OCPPResponseService
instead of each file duplicating the _Ajv.default type extraction.