/** * patterns-helpers — Pure logic for the Cross-Project Intelligence UI * (patterns/+page.svelte + PatternTransferHeatmap.svelte). * * Extracted so the behaviour can be unit-tested in the vitest `node` * environment without jsdom or Svelte component harnessing. Every helper * in this module is a pure function of its inputs. * * Contracts * --------- * - `cellIntensity`: returns opacity in [0,1] from count / max. count=0 → 0, * count>=max → 1. `max<=0` collapses to 0 (avoids div-by-zero — the * component uses `max || 1` for the same reason). * - `filterByCategory`: 'All' passes every pattern through. An unknown * category string (not one of the 6 + 'All') returns an empty array — * there is no hidden alias fallback. * - `buildTransferMatrix`: directional. `matrix[origin][dest]` counts how * many patterns originated in `origin` and were transferred to `dest`. * `origin === dest` captures self-transfer (a project reusing its own * pattern — rare but real per the component's doc comment). */ export const PATTERN_CATEGORIES = [ 'ErrorHandling', 'AsyncConcurrency', 'Testing', 'Architecture', 'Performance', 'Security', ] as const; export type PatternCategory = (typeof PATTERN_CATEGORIES)[number]; export type CategoryFilter = 'All' | PatternCategory; export interface TransferPatternLike { name: string; category: PatternCategory; origin_project: string; transferred_to: string[]; transfer_count: number; } /** * Normalise a raw transfer count to a 0..1 opacity/intensity value against a * known max. Used by the heatmap cell colour ramp. * * count <= 0 → 0 (dead cell) * count >= max > 0 → 1 (hottest cell) * otherwise → count / max * * Non-finite / negative inputs collapse to 0. When `max <= 0` the result is * always 0 — the component's own guard (`maxCount || 1`) means this branch * is unreachable in practice, but defensive anyway. */ export function cellIntensity(count: number, max: number): number { if (!Number.isFinite(count) || count <= 0) return 0; if (!Number.isFinite(max) || max <= 0) return 0; if (count >= max) return 1; return count / max; } /** * Filter a pattern list by the active category tab. * 'All' → full pass-through (same reference-equal array is * NOT guaranteed; callers must not rely on identity) * one of the 6 enums → strict equality on `category` * unknown string → empty array (no silent alias; caller bug) */ export function filterByCategory
(
patterns: readonly P[],
category: CategoryFilter | string,
): P[] {
if (category === 'All') return patterns.slice();
if (!(PATTERN_CATEGORIES as readonly string[]).includes(category)) {
return [];
}
return patterns.filter((p) => p.category === category);
}
/** Cell in the directional N×N transfer matrix. */
export interface TransferCell {
count: number;
topNames: string[];
}
/** Dense row-major directional matrix: matrix[origin][destination]. */
export type TransferMatrix = Record