Files
2026-10-19 18:52:49 -04:00

60 lines
2.2 KiB
TypeScript

/** Live, off-chain Wikipedia data. Text fields are plain strings, never HTML. */
export interface PageEnrichment {
pageId: number;
title: string;
description: string | null;
wikidataId: string | null;
revisionId: number;
pageBytes: number;
/** Number of returned interlanguage links (a lower bound when truncated). */
languageCount: number;
languagesTruncated: boolean;
categories: string[];
categoriesTruncated: boolean;
fetchedAt: string;
pageviews: {
status: 'available' | 'unavailable';
/** Six completed UTC months, oldest first, identified as YYYY-MM. */
months: { month: string; views: number }[];
latestMonth: string;
total: number | null;
/** Latest completed month's views divided by its actual UTC day count. */
dailyAverage: number | null;
trendPercent: number | null;
};
stats: { attention: number | null; depth: number; reach: number };
}
/** Immutable numeric Wikipedia metrics committed to a minted block. */
export type MintStatsSnapshot = {
capturedAt: string;
revisionId: number;
pageBytes: number;
languageCount: number;
languagesTruncated: boolean;
pageviews: PageEnrichment['pageviews'];
stats: PageEnrichment['stats'];
};
export function snapshotStats(data: PageEnrichment): MintStatsSnapshot {
return {
capturedAt: new Date().toISOString(),
revisionId: data.revisionId,
pageBytes: data.pageBytes,
languageCount: data.languageCount,
languagesTruncated: data.languagesTruncated,
pageviews: { ...data.pageviews, months: data.pageviews.months.map(item => ({ ...item })) },
stats: { ...data.stats },
};
}
/** Attention uses the latest completed month's views, not the six-month total. */
export function deriveStats(pageBytes: number, languageCount: number, monthlyViews: number | null): PageEnrichment['stats'] {
const score = (value: number, scale: number) => {
if (!Number.isFinite(value) || value < 0) throw new RangeError('Invalid enrichment score input');
return Math.min(100, Math.round(Math.log10(value + 1) / scale * 100));
};
return { attention: monthlyViews === null ? null : score(monthlyViews, 6),
depth: score(pageBytes, 6), reach: score(languageCount, 3) };
}