All files / lib/awareness/collective/analysis insight.ts

0% Statements 0/42
100% Branches 1/1
100% Functions 1/1
0% Lines 0/42

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67                                                                                                                                     
import { ChartDataPoint } from '../components/charts/types';
import { predictNextTrend } from './predict-next';
import { detectStabilityZones } from './stability-detector';
import { detectConfidenceSurges } from './confidence-surge';
 
export type InsightBadge = 'STABILITY' | 'SURGE' | 'PREDICTION' | 'BASELINE';
 
export interface Insight {
  point: ChartDataPoint | (ChartDataPoint & { isPrediction?: boolean });
  prev?: ChartDataPoint;
  next?: ChartDataPoint;
  rolling: {
    window: number;
    avgChangeRate: number;
    avgConfidence: number;
  };
  badges: InsightBadge[];
  stabilityWindow?: { start: number; end: number };
  surgeDelta?: number;
  predictedNext?: { timestamp: number; trendValue: number; confidence: number } | null;
}
 
export function computeInsight(series: ChartDataPoint[], idx: number, windowSize = 5): Insight {
  const clamp = (n: number, a = 0, b = 1) => Math.max(a, Math.min(b, n));
  const point = series[idx];
  const prev = series[idx - 1];
  const next = series[idx + 1];
 
  // rolling stats
  const from = Math.max(0, idx - (windowSize - 1));
  const slice = series.slice(from, idx + 1);
  const avgChangeRate = slice.reduce((s, d) => s + (d.changeRate ?? 0), 0) / slice.length;
  const avgConfidence = slice.reduce((s, d) => s + clamp(d.confidence ?? 0.5), 0) / slice.length;
 
  // zones & surges
  const zones = detectStabilityZones(series, windowSize, 5);
  const inZone = zones.find((z) => point.timestamp >= z.start && point.timestamp <= z.end);
  const surges = detectConfidenceSurges(series, 5, 2);
  const surge = surges.find((s) => s.timestamp === point.timestamp);
 
  // prediction (from full series)
  const predicted = predictNextTrend(series);
 
  const badges: InsightBadge[] = [];
  if (inZone) badges.push('STABILITY');
  if (surge) badges.push('SURGE');
  if ((point as any).isPrediction) badges.push('PREDICTION');
  if (!badges.length) badges.push('BASELINE');
 
  return {
    point,
    prev,
    next,
    rolling: { window: windowSize, avgChangeRate, avgConfidence },
    badges,
    stabilityWindow: inZone ? { start: inZone.start, end: inZone.end } : undefined,
    surgeDelta: surge?.delta,
    predictedNext: predicted
      ? {
          timestamp: predicted.timestamp,
          trendValue: predicted.trendValue,
          confidence: predicted.confidence,
        }
      : null,
  };
}