spb/market-atlas
Public
TypeScript 96.7%
SQL 1.6%
CSS 0.8%
JavaScript 0.5%
1import { createHash } from "node:crypto";23export function sha256(input: string | Buffer): string {4 return createHash("sha256").update(input).digest("hex");5}67export function shortHash(input: string, len = 16): string {8 return sha256(input).slice(0, len);9}1011/**12 * Structural fingerprint of a payload: the sorted set of key paths with primitive types,13 * ignoring values and array indexes. Used to detect schema drift on public endpoints.14 */15export function schemaFingerprint(payload: unknown, maxDepth = 6): string {16 const paths = new Set<string>();17 const walk = (v: unknown, path: string, depth: number) => {18 if (depth > maxDepth) return;19 if (Array.isArray(v)) {20 if (v.length === 0) paths.add(`${path}[]`);21 // Sample first 3 elements — enough to capture the shape.22 for (const item of v.slice(0, 3)) walk(item, `${path}[]`, depth + 1);23 return;24 }25 if (v && typeof v === "object") {26 for (const [k, val] of Object.entries(v as Record<string, unknown>)) walk(val, path ? `${path}.${k}` : k, depth + 1);27 return;28 }29 paths.add(`${path}:${v === null ? "null" : typeof v}`);30 };31 walk(payload, "", 0);32 return shortHash([...paths].sort().join("|"), 20);33}3435/** Deterministic fingerprint for deduplicating observations replayed after a reconnect. */36export function observationFingerprint(parts: {37 sourceId: string;38 instrumentId: string;39 field: string;40 value: number;41 sourceTimestamp: number | null;42 sequence?: number | string | null;43}): string {44 return shortHash(45 `${parts.sourceId}|${parts.instrumentId}|${parts.field}|${parts.value}|${parts.sourceTimestamp ?? ""}|${parts.sequence ?? ""}`,46 24,47 );48}49