feat(middleware): add datapoint transform engine with round, scale, and map support

- src/modules/datapoints/transforms.ts: applyTransform() supporting map (priority), scale, and round
- tests/transforms.test.ts: 17 tests covering no-transform passthrough, round precision, scale multiply, map lookup, map priority over numeric transforms, combined transforms, and edge cases

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-06 12:47:41 +02:00
co-authored by Claude Sonnet 4.6
parent f7d16c077e
commit 0df1c523bd
2 changed files with 129 additions and 0 deletions
@@ -0,0 +1,45 @@
export interface TransformConfig {
round?: number;
scale?: number;
map?: Record<string, string>;
}
/**
* Apply a transformation pipeline to a raw string value.
*
* Processing order:
* 1. If `map` is defined and the value exists as a key, return the mapped value immediately.
* 2. Parse the value as a float. If it's not numeric, return as-is.
* 3. Apply `scale` (multiply).
* 4. Apply `round` (toFixed, then remove trailing zeros).
*/
export function applyTransform(value: string, config: TransformConfig | null): string {
if (!config || Object.keys(config).length === 0) {
return value;
}
// Map takes priority — if key matches, return immediately without numeric transforms
if (config.map && Object.prototype.hasOwnProperty.call(config.map, value)) {
return config.map[value];
}
// Only apply numeric transforms if value is actually a number
const num = parseFloat(value);
if (isNaN(num)) {
return value;
}
let result = num;
// Apply scale
if (typeof config.scale === 'number') {
result = result * config.scale;
}
// Apply round
if (typeof config.round === 'number') {
result = parseFloat(result.toFixed(config.round));
}
return String(result);
}