27 KiB
generalFunctions
Reflects code as of
f21e2aa· regenerated2026-05-11(hand-written) Nonpm run wiki:allscript exists for this library. The API surface block (section 5) is hand-maintained between the AUTOGEN markers. If the banner is stale, treat this page as informative, not authoritative.
1. What this library is
generalFunctions is the shared infrastructure every EVOLV node depends on. It provides the base classes all nodes extend (BaseDomain, BaseNodeAdapter), the command dispatch engine, the measurement store, unit-policy system, child-registration machinery, InfluxDB output formatting, and a set of domain utilities (PID, curve interpolation, prediction, statistics, coolprop). Nodes hold zero duplicated scaffolding — they only write the logic that differs.
2. Position in the platform
flowchart LR
gf["generalFunctions\n(shared library)"]:::lib
rm["rotatingMachine\nEquipment"]:::equip
mgc["machineGroupControl\nUnit"]:::unit
ps["pumpingStation\nProcess Cell"]:::proc
meas["measurement\nControl Module"]:::ctrl
valve["valve\nEquipment"]:::equip
vgc["valveGroupControl\nUnit"]:::unit
reactor["reactor\nUnit"]:::unit
settler["settler\nUnit"]:::unit
monster["monster\nUnit"]:::unit
diffuser["diffuser\nEquipment"]:::equip
dashAPI["dashboardAPI\nutility"]:::util
gf --> rm
gf --> mgc
gf --> ps
gf --> meas
gf --> valve
gf --> vgc
gf --> reactor
gf --> settler
gf --> monster
gf --> diffuser
gf --> dashAPI
classDef lib fill:#222,color:#fff,stroke:#444
classDef proc fill:#0c99d9,color:#fff
classDef unit fill:#50a8d9,color:#000
classDef equip fill:#86bbdd,color:#000
classDef ctrl fill:#a9daee,color:#000
classDef util fill:#dddddd,color:#000
Every EVOLV node declares generalFunctions as a dependencies entry and imports from the package root only (require('generalFunctions')). Cross-node coupling happens exclusively through this library's API surface and Node-RED messages — never through direct imports between node packages.
3. Capability matrix
| Capability | Status | Notes |
|---|---|---|
Base domain scaffolding (BaseDomain) |
✅ | Constructor, emitter, logger, measurements, child registry wired automatically |
Base Node-RED adapter (BaseNodeAdapter) |
✅ | Tick/event loop, status badge, input dispatch, Port 0/1/2 output |
Declarative command dispatch (CommandRegistry) |
✅ | Alias deprecation warnings, unit normalisation, query.units auto-topic |
Declarative child-registration routing (ChildRouter) |
✅ | Replaces per-node registerChild switch blocks |
Unit policy + conversion (UnitPolicy, convert) |
✅ | Canonical ↔ output ↔ curve unit sets; dual method/property access |
Measurement store (MeasurementContainer) |
✅ | Chainable, windowed, auto-convert, 4-segment key output |
InfluxDB + process output formatting (outputUtils) |
✅ | Delta-compressed; consumers must cache and merge |
Status badge helpers (statusBadge, StatusUpdater) |
✅ | Converged look-and-feel across all nodes |
Latest-wins async gate (LatestWinsGate) |
✅ | Extracted from MGC; shared by PS, VGC, MGC |
Prediction quality / drift tracking (HealthStatus) |
✅ | Frozen plain-object shape; composable |
Config schema registry (configManager) |
✅ | One JSON schema per node in src/configs/ |
PID control (PIDController, CascadePIDController) |
✅ | Full-featured discrete PID with bumpless transfer |
Curve interpolation (interpolation, predict) |
✅ | Multidimensional characteristic-curve predictor |
Statistical helpers (stats, nrmse, outliers) |
✅ | Mean, stddev, median, NRMSE, dynamic-cluster outlier detection |
Thermodynamic properties (coolprop) |
✅ | CoolProp bindings for fluid/gas property lookup |
FSM for valve/machine states (state) |
✅ | StateManager + MovementManager |
Gravity calculations (gravity) |
✅ | WGS-84 model |
Physical constants (Fysics) |
✅ | Air density, viscosity, etc. |
Browser-side editor dropdowns (MenuManager, menuUtils) |
✅ | Node-RED editor form population |
4. Module map
flowchart TB
subgraph domain["src/domain/ — base classes"]
BD["BaseDomain.js"]
CR["ChildRouter.js"]
UP["UnitPolicy.js"]
LWG["LatestWinsGate.js"]
HS["HealthStatus.js"]
end
subgraph nodered["src/nodered/ — Node-RED adapter layer"]
BNA["BaseNodeAdapter.js"]
CMR["commandRegistry.js"]
SB["statusBadge.js"]
SU["statusUpdater.js"]
end
subgraph measurements["src/measurements/ — measurement store"]
MC["MeasurementContainer.js"]
MB["MeasurementBuilder.js"]
Meas["Measurement.js"]
end
subgraph helper["src/helper/ — shared utilities"]
LOG["logger.js"]
OU["outputUtils.js"]
CRU["childRegistrationUtils.js"]
CFG["configUtils.js"]
VAL["validationUtils.js"]
MU["menuUtils.js"]
GR["gravity.js"]
end
subgraph predict_grp["src/predict/ — curve prediction"]
PRED["predict_class.js"]
INTERP["interpolation.js"]
end
subgraph configs["src/configs/ — schema registry"]
CFGM["index.js (ConfigManager)"]
JSON["*.json — per-node schemas"]
end
subgraph math["numeric & domain utilities"]
PID["src/pid/ — PIDController"]
NRMSE["src/nrmse/ — ErrorMetrics"]
STATS["src/stats/ — mean/stddev/median"]
OUT["src/outliers/ — DynamicClusterDeviation"]
STATE["src/state/ — state FSM"]
CONV["src/convert/ — unit conversion"]
COOL["src/coolprop-node/ — thermodynamics"]
FYS["src/convert/fysics.js — physical constants"]
end
subgraph menu_grp["src/menu/ — editor menus"]
MM["MenuManager"]
end
subgraph constants["src/constants/"]
POS["positions.js"]
end
BD --> CR
BD --> UP
BD --> MC
BD --> CRU
BD --> LOG
BNA --> BD
BNA --> CMR
BNA --> OU
BNA --> SU
| Directory | Primary export | Read first if you're changing… |
|---|---|---|
src/domain/ |
BaseDomain, ChildRouter, UnitPolicy, LatestWinsGate, HealthStatus |
Base class contracts, child routing, unit system |
src/nodered/ |
BaseNodeAdapter, CommandRegistry, statusBadge, StatusUpdater |
Input dispatch, output loops, editor status |
src/measurements/ |
MeasurementContainer |
Measurement storage, statistics, 4-segment key output |
src/helper/ |
logger, outputUtils, childRegistrationUtils, configUtils, validationUtils, menuUtils, gravity |
Logging, InfluxDB formatting, child registration |
src/configs/ |
ConfigManager + per-node JSON schemas |
Schema loading, config validation, default values |
src/predict/ |
predict, interpolation |
Characteristic curve fitting and flow/power prediction |
src/pid/ |
PIDController, CascadePIDController |
Closed-loop control |
src/nrmse/ |
ErrorMetrics (NRMSE) |
Prediction quality scoring |
src/stats/ |
stats (mean, stddev, median) |
Statistical reducers |
src/outliers/ |
DynamicClusterDeviation |
Online outlier detection |
src/state/ |
state, StateManager, MovementManager |
FSM for valve/machine state machines |
src/convert/ |
convert, Fysics |
Unit conversion, physical constants |
src/coolprop-node/ |
coolprop |
Thermodynamic property lookup |
src/menu/ |
MenuManager |
Editor-form dropdown population |
src/constants/ |
POSITIONS, POSITION_VALUES, isValidPosition |
Canonical spatial position constants |
5. API surface
All imports use the package root: const { X } = require('generalFunctions');
| Export | Import name | Source file | Contract |
|---|---|---|---|
BaseDomain |
BaseDomain |
src/domain/BaseDomain.js |
Abstract base class for every specificClass.js. Provides emitter, config, logger, measurements, childRegistrationUtils, router. Subclass must declare static name (maps to schema JSON) and implement configure(). See CONTRACTS.md §3. |
BaseNodeAdapter |
BaseNodeAdapter |
src/nodered/BaseNodeAdapter.js |
Abstract base for every nodeClass.js. Wires config build → domain instantiation → registration delay → output strategy → status loop → input dispatch → close handler. Subclass declares static DomainClass, static commands, static tickInterval, static statusInterval, and overrides buildDomainConfig(uiConfig, nodeId). See CONTRACTS.md §2. |
ChildRouter |
ChildRouter |
src/domain/ChildRouter.js |
Declarative parent-side child registration. Replaces per-node registerChild switch. Chain .onRegister(softwareType, cb), .onMeasurement(softwareType, filter, cb), .onPrediction(softwareType, filter, cb). See CONTRACTS.md §5. |
CommandRegistry |
CommandRegistry |
src/nodered/commandRegistry.js |
Class form of the command registry. Accepts array of descriptors (topic, aliases, payloadSchema, units, description, handler). Dispatches by O(1) lookup, normalises units before handler runs, warns on alias use. |
createRegistry |
createRegistry |
src/nodered/commandRegistry.js |
Factory: createRegistry(descriptors, options) → CommandRegistry. Used by BaseNodeAdapter; rarely needed directly. |
UnitPolicy |
UnitPolicy |
src/domain/UnitPolicy.js |
Declare unit sets: UnitPolicy.declare({ canonical, output, curve?, requireUnitForTypes? }). Returns policy with dual method/property access (policy.canonical('flow') and policy.canonical.flow). Methods: canonical, output, curve, resolve, convert, containerOptions, setLogger. See CONTRACTS.md §6. |
LatestWinsGate |
LatestWinsGate |
src/domain/LatestWinsGate.js |
Serialises async dispatches so only the latest value wins. fire(value) — non-blocking. fireAndWait(value) → Promise that resolves with dispatch result or LatestWinsGate.SUPERSEDED. drain() — await idle. See CONTRACTS.md §8. |
HealthStatus |
HealthStatus |
src/domain/HealthStatus.js |
Factory functions for frozen health objects: HealthStatus.ok(msg, src), HealthStatus.degraded(level, flags, msg, src), HealthStatus.compose(statuses). Shape: { level: 0–3, flags: string[], message, source }. See CONTRACTS.md §9. |
MeasurementContainer |
MeasurementContainer |
src/measurements/MeasurementContainer.js |
Chainable measurement store: .type().variant().position().value(v, ts, srcUnit). Query: getCurrentValue(unit), getAverage(unit), difference({ from, to, unit }). Introspect: getFlattenedOutput() returns 4-segment keyed object (type.variant.position.childId). |
outputUtils |
outputUtils |
src/helper/outputUtils.js |
Singleton-per-node delta-compression engine. formatMsg(output, config, format) returns msg only when fields changed, or undefined. format is 'process' or 'influxdb'. Consumers must cache and merge. |
logger |
logger |
src/helper/logger.js |
new logger(enabled, logLevel, moduleName). Methods: debug, info, warn, error, setLogLevel, toggleLogging. Never use console.log directly. |
configManager |
configManager |
src/configs/index.js |
new configManager(). Methods: getConfig(name), buildConfig(name, uiConfig, nodeId, domainSlice?), getAvailableConfigs(), hasConfig(name). Config files live in src/configs/*.json. |
configUtils |
configUtils |
src/helper/configUtils.js |
new configUtils(defaultConfig). initConfig(userConfig) validates and merges user values over defaults via validationUtils. |
validation |
validation |
src/helper/validationUtils.js |
new validation(logEnabled, logLevel). validateSchema(config, schema, name) walks schema, clamps numbers, coerces types, strips unknown keys. |
childRegistrationUtils |
childRegistrationUtils |
src/helper/childRegistrationUtils.js |
new childRegistrationUtils(parentDomain). registerChild(child, positionVsParent, distance?) stores child by softwareType/category with alias normalisation. getChildrenOfType(softwareType, category?), getChildById(id), getAllChildren(). Normally used via ChildRouter — direct use is for advanced cases. |
statusBadge |
statusBadge |
src/nodered/statusBadge.js |
Pure-function badge builder. statusBadge.compose(parts, opts?) → { fill, shape, text }. statusBadge.error(msg), statusBadge.idle(label). Text clipped to 60 chars. See CONTRACTS.md §7. |
StatusUpdater |
StatusUpdater |
src/nodered/statusUpdater.js |
new StatusUpdater({ node, source, intervalMs, logger }). start(), stop(). Calls source.getStatusBadge() on interval; catches errors and shows a red badge. Owned by BaseNodeAdapter — rarely needed directly. |
convert |
convert |
src/convert/index.js |
unit-converter factory. convert(value).from(unit).to(unit). convert.possibilities(measure) lists accepted units. Measures: volumeFlowRate, pressure, power, temperature, volume, length, mass, energy, reactivePower, apparentPower, reactiveEnergy, and more. |
Fysics |
Fysics |
src/convert/fysics.js |
new Fysics(). Physical constants: air_density, g0; methods for gravity and viscosity calculations. |
gravity |
gravity |
src/helper/gravity.js |
Singleton-style Gravity instance. getStandardGravity() → 9.80665 m/s². WGS-84 latitude/altitude corrections available. |
predict |
predict |
src/predict/predict_class.js |
new predict(config, logger). Multidimensional characteristic-curve predictor; emits results via internal EventEmitter. |
interpolation |
interpolation |
src/predict/interpolation.js |
Class for 1-D and 2-D curve interpolation (linear, cubic-spline). Used internally by predict. |
PIDController |
PIDController |
src/pid/PIDController.js |
Discrete PID with bumpless auto/manual transfer, anti-windup, derivative filtering, rate limiting, gain scheduling, feedforward. |
CascadePIDController |
CascadePIDController |
src/pid/PIDController.js |
Outer-inner PID cascade built on PIDController. |
createPidController |
createPidController |
src/pid/index.js |
Factory shorthand: createPidController(options) → PIDController. |
createCascadePidController |
createCascadePidController |
src/pid/index.js |
Factory shorthand for cascade PID. |
nrmse |
nrmse |
src/nrmse/index.js |
ErrorMetrics class for normalised-root-mean-squared-error tracking. Multi-metric via registerMetric(id), update(id, predicted, measured). |
stats |
stats |
src/stats/index.js |
Pure functions: mean(arr), stdDev(arr), median(arr). No state; safe to call on any numeric array. |
state |
state |
src/state/index.js |
new state(config, logger). FSM for valve/machine: StateManager (transitions) + MovementManager (timed moves). Emits state-change events. |
MenuManager |
MenuManager |
src/menu/index.js |
new MenuManager(). Manages editor dropdown menus (asset, logger, position, aquon). registerMenu(type, factory). Used in node entry files to power Node-RED editor forms. |
menuUtils / MenuUtils |
via menuUtils in helper |
src/helper/menuUtils.js |
Browser-side editor helper. Toggles, data fetching, URL construction, dropdown population, HTML generation. Served to browser via endpointUtils. |
POSITIONS |
POSITIONS |
src/constants/positions.js |
Frozen enum: { UPSTREAM, DOWNSTREAM, AT_EQUIPMENT, DELTA }. |
POSITION_VALUES |
POSITION_VALUES |
src/constants/positions.js |
string[] of all four position strings. |
isValidPosition |
isValidPosition |
src/constants/positions.js |
(pos: string) => boolean. |
coolprop |
coolprop |
src/coolprop-node/src/index.js |
CoolProp fluid/gas thermodynamic property lookup. Used by nodes that model heat transfer or gas compression. |
loadModel |
loadModel |
datasets/assetData/modelData/index.js |
Load a JSON model-data asset by dataset type and asset ID (with LRU cache). Preferred over deprecated loadCurve. |
loadCurve |
loadCurve |
datasets/assetData/curves/index.js |
Deprecated — load a pump-curve JSON. Replaced by loadModel. |
6. Config schema registry
One JSON file per node in src/configs/. ConfigManager.buildConfig merges the schema defaults with the Node-RED editor values before the domain sees them.
| File | Node | What it defines |
|---|---|---|
baseConfig.json |
all nodes | Shared general, asset, functionality, logging sections |
rotatingMachine.json |
rotatingMachine | Curve selection, startup/shutdown ramps, safety thresholds, unit config |
machineGroupControl.json |
machineGroupControl | Demand targets, strategy selection, dispatcher settings |
pumpingStation.json |
pumpingStation | Basin geometry, hydraulics, control strategies, safety levels |
measurement.json |
measurement | Scaling, smoothing, stability threshold, digital/MQTT mode |
valve.json |
valve | Actuator travel time, position limits, FSM config |
valveGroupControl.json |
valveGroupControl | Group strategy, demand distribution |
reactor.json |
reactor | ASM kinetics, reactor type (CSTR/PFR), volume, influent |
settler.json |
settler | Sludge settling parameters, effluent quality |
monster.json |
monster | Multi-parameter monitoring, flow bounds, sample intervals |
diffuser.json |
diffuser | Aeration model, oxygen transfer parameters |
To add a new node: create src/configs/<nodeName>.json extending baseConfig.json, declare static name = '<nodeName>' in the domain class. configManager.buildConfig finds it automatically.
7. Lifecycle — how a node tick or event reaches the output port
The sequence below uses rotatingMachine as the example. Every stateful EVOLV node follows the same path. See the rotatingMachine wiki for node-specific detail.
sequenceDiagram
participant RED as Node-RED runtime
participant BNA as BaseNodeAdapter
participant CMD as CommandRegistry
participant DOM as Domain (specificClass)
participant CR as ChildRouter
participant MC as MeasurementContainer
participant OU as outputUtils
participant PORT as Port 0 / 1 / 2
RED->>BNA: constructor(uiConfig, RED, node, name)
BNA->>BNA: configManager.buildConfig()
BNA->>DOM: new DomainClass(config)
DOM->>MC: new MeasurementContainer(unitPolicy.containerOptions())
DOM->>DOM: configure() — wire ChildRouter, concern modules
BNA-->>PORT: Port 2 registration msg (after 100 ms delay)
BNA->>BNA: start status loop (1000 ms)
Note over RED,PORT: Event-driven path (default)
RED->>BNA: input msg {topic: 'data.pressure', payload: 3.4}
BNA->>CMD: dispatch(msg)
CMD->>CMD: unit normalisation (Pa → mbar)
CMD->>DOM: handler(source, msg, ctx)
DOM->>MC: .type('pressure').variant('measured').position('upstream').value(3.4)
DOM->>DOM: emitter.emit('output-changed')
BNA->>DOM: getOutput()
DOM-->>BNA: flat snapshot object
BNA->>OU: formatMsg(snapshot, config, 'process')
OU-->>BNA: delta msg (only changed fields)
BNA-->>PORT: Port 0 process msg, Port 1 influx msg
Note over RED,PORT: Tick-driven path (opt-in — tickInterval set)
RED->>BNA: timer fires every tickInterval ms
BNA->>DOM: tick()
DOM->>DOM: time-based math; emitter.emit('output-changed')
BNA->>DOM: getOutput()
BNA->>OU: formatMsg(...)
BNA-->>PORT: Port 0 / 1 msgs (delta only)
8. Stability + versioning
Source of truth: .claude/rules/general-functions.md.
| Category | Rule |
|---|---|
| Safe to add | New named exports. New optional methods on existing classes. New config keys with defaults in the schema. |
| Requires decision-gate interview | Removing or renaming any export. Changing a method signature. Changing the output key format of MeasurementContainer.getFlattenedOutput(). Changing the formatMsg delta-compression behaviour. |
| Forbidden without migration | Breaking the 4-segment key shape (type.variant.position.childId). Changing Port 0/1/2 payload envelope. Changing the CONTRACTS.md §1–§9 shapes. |
generalFunctions is a git submodule shared by all 12 node repos. A breaking change here requires updating every consumer in a single coordinated commit. Before modifying any module, run grep -r "require('generalFunctions')" nodes/*/ to identify all call sites.
9. No editor form — consumers' config forms map to config slices
generalFunctions has no Node-RED editor form of its own. The library is never placed directly in a flow.
Consumer nodes expose their own editor forms. Each form field writes into a config key that configManager.buildConfig validates against the node's schema (in src/configs/<nodeName>.json). The resulting merged config is passed to the domain constructor.
For the form-to-config mapping of a specific node, see section 9 of that node's wiki page.
10. Examples — usage snippets from a real node
10.1 Extending BaseDomain (from pumpingStation/specificClass.js pattern)
const { BaseDomain, UnitPolicy, ChildRouter } = require('generalFunctions');
class PumpingStation extends BaseDomain {
static name = 'pumpingStation';
static unitPolicy = UnitPolicy.declare({
canonical: { flow: 'm3/s', pressure: 'Pa', power: 'W', temperature: 'K' },
output: { flow: 'm3/h', pressure: 'mbar', power: 'kW', temperature: 'C' },
});
configure() {
// Declare named child getters — readable in code, registry is source of truth
this.declareChildGetter('machines', 'machine');
this.declareChildGetter('machineGroups', 'machinegroup');
// Declarative child routing — no per-node registerChild switch
this.router
.onRegister('machinegroup', (child) => this._onMachineGroupRegistered(child))
.onMeasurement('measurement', { type: 'level' }, (data, child) => {
this._onLevel(data.value, data);
});
}
getOutput() {
return {
...this.measurements.getFlattenedOutput(),
...this.basin.snapshot(),
};
}
getStatusBadge() {
const { statusBadge } = require('generalFunctions');
return statusBadge.compose(['filling', 'V=12.4/50.0 m³']);
}
}
module.exports = PumpingStation;
10.2 Extending BaseNodeAdapter (from pumpingStation/nodeClass.js pattern)
const { BaseNodeAdapter } = require('generalFunctions');
const Domain = require('./specificClass');
const commands = require('./commands');
class nodeClass extends BaseNodeAdapter {
static DomainClass = Domain;
static commands = commands;
static tickInterval = 1000; // ms — only for time-driven math
static statusInterval = 1000;
buildDomainConfig(uiConfig, nodeId) {
return {
basin: {
volume: Number(uiConfig.basinVolume),
height: Number(uiConfig.basinHeight),
surfaceArea: Number(uiConfig.basinSurface),
},
hydraulics: {
inflowPipeArea: Number(uiConfig.inflowArea),
},
};
}
}
module.exports = nodeClass;
10.3 Command descriptor with unit normalisation
// src/commands/index.js
module.exports = [
{
topic: 'set.demand',
aliases: ['Qd'], // legacy name — logs one-time deprecation
units: { measure: 'volumeFlowRate', default: 'm3/h' },
payloadSchema: { type: 'number' },
description: 'Operator demand setpoint. Unit-normalised before handler runs.',
handler: (source, msg) => { source.setDemand(msg.payload); },
},
{
topic: 'cmd.startup',
payloadSchema: { type: 'none' },
description: 'Trigger startup sequence.',
handler: (source, msg) => { source.startup(msg.payload?.source); },
},
];
11. Debug recipes
| Symptom | First check | Where to look |
|---|---|---|
Child never registers (no registerChild log) |
Is the child's softwareType in the SOFTWARE_TYPE_ALIASES map? |
src/helper/childRegistrationUtils.js line 1–12 and src/domain/ChildRouter.js |
| Port 0 sends nothing after an input | outputUtils only emits on changes. Is the field actually different from the last call? |
Add a debug tap after formatMsg; check outputUtils._output[format] state |
| Unit mismatch — handler receives wrong value | Did the command descriptor declare units: { measure, default }? Is msg.unit set by the sender? |
commandRegistry.js → _normaliseUnit(); check the warn log |
query.units returns empty object |
The commands array has no descriptors with a units field. |
BaseNodeAdapter._buildImplicitUnitsCommand() |
MeasurementContainer.getFlattenedOutput() returns unexpected key shape |
Key is type.variant.position.childId — position is always lowercase. Check setChildId() was called. |
src/measurements/MeasurementContainer.js → getFlattenedOutput() |
LatestWinsGate promise never resolves |
A superseded fire resolves with { superseded: true }, not undefined. Branch on r && r.superseded. |
src/domain/LatestWinsGate.js |
| Status badge stuck at grey | getStatusBadge() threw and StatusUpdater caught it. Look for statusBadge.error(...) in the container log. |
src/nodered/statusUpdater.js |
Never ship
enableLog: 'debug'in a demo or production config — it fills the container log within seconds and obscures real errors.
12. When NOT to depend on this library
- Passive HTTP gateway nodes (e.g.
dashboardAPI) may skipBaseDomainandBaseNodeAdapterentirely if they hold no domain state. A plain Node-RED node with HTTP endpoints needs onlylogger,outputUtils, andconfigManager. See thedashboardAPIwiki for the rationale. - External scripts or standalone tools that need only unit conversion can import just
const { convert } = require('generalFunctions')without pulling in the full domain stack. - Nodes at a different S88 level that inherit from a third-party base class must not import from
src/domain/orsrc/nodered/internal paths — they may only use root-level exports.
13. Known limitations
| # | Issue | Tracked in |
|---|---|---|
| 1 | loadCurve is deprecated; replacement loadModel exists but not all nodes have migrated |
OPEN_QUESTIONS.md — Phase 8.5 cleanup |
| 2 | outlierDetection (DynamicClusterDeviation) prints to console.log internally — not routed through logger |
Code review backlog |
| 3 | configUtils.initConfig strips unknown keys silently; schema must include every key the domain reads or defaults are lost |
OPEN_QUESTIONS.md — e.g. monster schema fix 2026-05-11 |
| 4 | state (FSM) and predict are not yet integrated with BaseDomain lifecycle — nodes wire them manually in configure() |
Architecture backlog |
| 5 | menuUtils / MenuManager are served as browser JavaScript and bypass the normal Node.js import path — deep changes require testing in both environments |
endpointUtils.js |
| 6 | CascadePIDController has no dedicated test suite |
Test backlog |
| 7 | Wiki autogen script (wiki:all) not yet wired for this library; API surface block is hand-maintained |
Phase 9 follow-up |