Merge branch 'codex/feature/prettier-peer-list'

This commit is contained in:
Tommaso Casaburi
2026-05-23 18:34:05 +07:00
12 changed files with 1065 additions and 39 deletions
+40
View File
@@ -0,0 +1,40 @@
import { describe, expect, it } from 'vitest';
import { COUNTRY_FLAG_HEIGHT, COUNTRY_FLAG_WIDTH, getCountryFlagPosition, getCountryLabel, normalizeCountryCode } from '../country-flags';
describe('country-flags', () => {
it('places the first sprite cell at the origin', () => {
expect(getCountryFlagPosition('ad')).toEqual({ x: 0, y: 0 });
});
it('maps a code to a grid-aligned sprite position', () => {
const position = getCountryFlagPosition('us');
expect(position).toBeDefined();
expect(position!.x % COUNTRY_FLAG_WIDTH).toBe(0);
expect(position!.y % COUNTRY_FLAG_HEIGHT).toBe(0);
expect(position!.x).toBeGreaterThanOrEqual(0);
expect(position!.x).toBeLessThan(16 * COUNTRY_FLAG_WIDTH);
});
// Exact positions from the original 4chan flags.css (the source of flags-1.png).
it('matches the known sprite positions from flags.css', () => {
expect(getCountryFlagPosition('us')).toEqual({ x: 240, y: 154 });
expect(getCountryFlagPosition('ru')).toEqual({ x: 64, y: 132 });
expect(getCountryFlagPosition('pl')).toEqual({ x: 128, y: 121 });
expect(getCountryFlagPosition('jp')).toEqual({ x: 112, y: 77 });
expect(getCountryFlagPosition('de')).toEqual({ x: 176, y: 33 });
expect(getCountryFlagPosition('gb')).toEqual({ x: 32, y: 55 });
expect(getCountryFlagPosition('br')).toEqual({ x: 240, y: 11 });
expect(getCountryFlagPosition('za')).toEqual({ x: 0, y: 176 });
});
it('normalizes uk to gb and rejects unknown codes', () => {
expect(normalizeCountryCode('UK')).toBe('gb');
expect(normalizeCountryCode('zz')).toBeUndefined();
expect(getCountryFlagPosition('zz')).toBeUndefined();
});
it('returns a human-readable label for ISO codes', () => {
expect(getCountryLabel('de')).toBeTruthy();
expect(getCountryLabel('zz')).toBeUndefined();
});
});
+96
View File
@@ -0,0 +1,96 @@
import { describe, expect, it } from 'vitest';
import { extractIpv4FromAddress, getApproximateCountryCode, getApproximateLatLon, isPrivateOrReservedIpv4 } from '../peer-geo';
const addr = (ip: string) => `/ip4/${ip}/tcp/4001/ws/p2p/12D3KooWExample`;
describe('extractIpv4FromAddress', () => {
it('extracts the IPv4 from a multiaddr', () => {
expect(extractIpv4FromAddress('/ip4/147.75.84.175/tcp/4001/ws')).toBe('147.75.84.175');
});
it('returns null for IPv6 and plain DNS multiaddrs', () => {
expect(extractIpv4FromAddress('/ip6/2606:4700::1111/tcp/4001')).toBeNull();
expect(extractIpv4FromAddress('/dns4/relay.example.org/tcp/443/wss')).toBeNull();
});
it('extracts an IPv4 embedded with dashes in a DNS hostname', () => {
expect(extractIpv4FromAddress('/dns4/91-234-56-78.host.example/tcp/443/wss')).toBe('91.234.56.78');
expect(extractIpv4FromAddress('/dns4/ip-203-0-113-9.provider.net/tcp/443/wss')).toBe('203.0.113.9');
});
});
describe('isPrivateOrReservedIpv4', () => {
it('flags private and reserved ranges', () => {
for (const ip of ['10.0.0.1', '172.16.5.4', '192.168.1.10', '127.0.0.1', '169.254.1.1', '100.64.0.1', '0.0.0.0', '239.255.0.1']) {
expect(isPrivateOrReservedIpv4(ip)).toBe(true);
}
});
it('treats public addresses as routable', () => {
for (const ip of ['8.8.8.8', '147.75.84.175', '1.1.1.1', '80.80.80.80']) {
expect(isPrivateOrReservedIpv4(ip)).toBe(false);
}
});
});
describe('getApproximateLatLon', () => {
it('returns null when the peer cannot be placed offline', () => {
expect(getApproximateLatLon('/ip4/192.168.1.5/tcp/4001')).toBeNull();
expect(getApproximateLatLon('/dns4/relay.example.org/tcp/443/wss')).toBeNull();
expect(getApproximateLatLon('/ip6/2606:4700::1111/tcp/4001')).toBeNull();
});
it('is deterministic for the same address', () => {
expect(getApproximateLatLon(addr('8.8.8.8'))).toEqual(getApproximateLatLon(addr('8.8.8.8')));
});
it('places addresses in the expected continental region', () => {
const na = getApproximateLatLon(addr('8.8.8.8'));
expect(na?.lon).toBeLessThan(-80); // North America
expect(na?.lat).toBeGreaterThan(30);
const eu = getApproximateLatLon(addr('80.80.80.80'));
expect(eu?.lon).toBeGreaterThan(0);
expect(eu?.lon).toBeLessThan(30);
expect(eu?.lat).toBeGreaterThan(40);
const as = getApproximateLatLon(addr('1.1.1.1'));
expect(as?.lon).toBeGreaterThan(90);
const af = getApproximateLatLon(addr('41.0.0.1'));
expect(af?.lon).toBeGreaterThan(8);
expect(af?.lon).toBeLessThan(34);
expect(af?.lat).toBeLessThan(12);
const sa = getApproximateLatLon(addr('200.0.0.1'));
expect(sa?.lon).toBeLessThan(-45);
expect(sa?.lat).toBeLessThan(-5);
});
it('stays within valid coordinate bounds', () => {
const loc = getApproximateLatLon(addr('203.0.113.7'));
expect(loc).not.toBeNull();
expect(loc!.lat).toBeGreaterThanOrEqual(-85);
expect(loc!.lat).toBeLessThanOrEqual(85);
expect(loc!.lon).toBeGreaterThanOrEqual(-180);
expect(loc!.lon).toBeLessThanOrEqual(180);
});
});
describe('getApproximateCountryCode', () => {
it('returns undefined when the peer cannot be placed offline', () => {
expect(getApproximateCountryCode('/ip4/10.0.0.1/tcp/4001')).toBeUndefined();
expect(getApproximateCountryCode('/dns4/relay.example.org/tcp/443/wss')).toBeUndefined();
expect(getApproximateCountryCode('/ip6/2606:4700::1111/tcp/4001')).toBeUndefined();
});
it('is deterministic and returns a known 2-letter code for public peers', () => {
const code = getApproximateCountryCode(addr('8.8.8.8'));
expect(code).toBe(getApproximateCountryCode(addr('8.8.8.8')));
expect(code).toMatch(/^[a-z]{2}$/);
});
it('derives a code from an IPv4 embedded in a DNS hostname', () => {
expect(getApproximateCountryCode('/dns4/91-234-56-78.host.example/tcp/443/wss')).toMatch(/^[a-z]{2}$/);
});
});
+300
View File
@@ -0,0 +1,300 @@
// Country flag sprite mapping for /assets/icons/flags-1.png.
//
// The sprite packs 16x11px flags in a 16-column, row-major grid; a flag's sprite
// position is its index in COUNTRY_FLAG_CODES, so this order must match the PNG
// exactly. This order is taken verbatim from the original 4chan flags.css sprite
// (flags.8.png), which is the source of this PNG.
export const COUNTRY_FLAG_WIDTH = 16;
export const COUNTRY_FLAG_HEIGHT = 11;
const COUNTRY_FLAG_COLUMNS = 16;
const COUNTRY_FLAG_CODES = [
'ad',
'ae',
'af',
'ag',
'ai',
'al',
'am',
'an',
'ao',
'aq',
'ar',
'as',
'at',
'au',
'aw',
'ax',
'az',
'ba',
'bb',
'bd',
'be',
'bf',
'bg',
'bh',
'bi',
'bj',
'bl',
'bm',
'bn',
'bo',
'bq',
'br',
'bs',
'bt',
'bv',
'bw',
'by',
'bz',
'ca',
'catalonia',
'cc',
'cd',
'cf',
'cg',
'ch',
'ci',
'ck',
'cl',
'cm',
'cn',
'co',
'cr',
'cs',
'cu',
'cv',
'cw',
'cx',
'cy',
'cz',
'de',
'dj',
'dk',
'dm',
'do',
'dz',
'ec',
'ee',
'eg',
'eh',
'xe',
'er',
'es',
'et',
'eu',
'fam',
'fi',
'fj',
'fk',
'fm',
'fo',
'fr',
'ga',
'gb',
'gd',
'ge',
'gf',
'gg',
'gh',
'gi',
'gl',
'gm',
'gn',
'gp',
'gq',
'gr',
'gs',
'gt',
'gu',
'gw',
'gy',
'hk',
'hm',
'hn',
'hr',
'ht',
'hu',
'id',
'ie',
'il',
'im',
'in',
'io',
'iq',
'ir',
'is',
'it',
'je',
'jm',
'jo',
'jp',
'ke',
'kg',
'kh',
'ki',
'km',
'kn',
'kp',
'kr',
'kw',
'ky',
'kz',
'la',
'lb',
'lc',
'li',
'lk',
'lr',
'ls',
'lt',
'lu',
'lv',
'ly',
'ma',
'mc',
'md',
'me',
'mf',
'mg',
'mh',
'mk',
'ml',
'mm',
'mn',
'mo',
'mp',
'mq',
'mr',
'ms',
'mt',
'mu',
'mv',
'mw',
'mx',
'my',
'mz',
'na',
'nc',
'ne',
'nf',
'ng',
'ni',
'nl',
'no',
'np',
'nr',
'nu',
'nz',
'om',
'pa',
'pe',
'pf',
'pg',
'ph',
'pk',
'pl',
'pm',
'pn',
'pr',
'ps',
'pt',
'pw',
'py',
'qa',
're',
'ro',
'rs',
'ru',
'rw',
'sa',
'sb',
'sc',
'xs',
'sd',
'se',
'sg',
'sh',
'si',
'sj',
'sk',
'sl',
'sm',
'sn',
'so',
'sr',
'ss',
'st',
'sv',
'sx',
'sy',
'sz',
'tc',
'td',
'tf',
'tg',
'th',
'tj',
'tk',
'tl',
'tm',
'tn',
'to',
'tr',
'tt',
'tv',
'tw',
'tz',
'ua',
'ug',
'um',
'us',
'uy',
'uz',
'va',
'vc',
've',
'vg',
'vi',
'vn',
'vu',
'xw',
'wf',
'ws',
'xk',
'xx',
'ye',
'yt',
'za',
'zm',
'zw',
] as const;
const COUNTRY_FLAG_INDEX_BY_CODE: ReadonlyMap<string, number> = new Map(COUNTRY_FLAG_CODES.map((code, index) => [code, index]));
const countryDisplayNames = typeof Intl.DisplayNames === 'function' ? new Intl.DisplayNames(undefined, { type: 'region' }) : undefined;
export const normalizeCountryCode = (value: string | undefined) => {
const code = value?.trim().toLowerCase();
if (!code) return undefined;
if (code === 'uk') return 'gb';
return COUNTRY_FLAG_INDEX_BY_CODE.has(code) ? code : undefined;
};
export const getCountryFlagPosition = (value: string | undefined): { x: number; y: number } | undefined => {
const code = normalizeCountryCode(value);
const index = code === undefined ? undefined : COUNTRY_FLAG_INDEX_BY_CODE.get(code);
if (index === undefined) return undefined;
return {
x: (index % COUNTRY_FLAG_COLUMNS) * COUNTRY_FLAG_WIDTH,
y: Math.floor(index / COUNTRY_FLAG_COLUMNS) * COUNTRY_FLAG_HEIGHT,
};
};
export const getCountryLabel = (value: string | undefined) => {
const code = normalizeCountryCode(value);
if (!code) return undefined;
const regionName = code.length === 2 ? countryDisplayNames?.of(code.toUpperCase()) : undefined;
return regionName ?? code.toUpperCase();
};
+167
View File
@@ -0,0 +1,167 @@
// Offline, approximate peer geolocation for the P2P stats world map.
//
// This intentionally avoids any external geolocation API: 5chan is serverless and
// privacy-focused, so we must not leak the set of peers a user is connected to.
// Instead we map an IPv4 address to its Regional Internet Registry (RIR) region at
// continent resolution, using the coarse IANA /8 allocation table below. Positions
// are therefore approximate ("roughly which continent"), not precise coordinates.
export type LatLon = { lat: number; lon: number };
type Region = 'AF' | 'AS' | 'EU' | 'NA' | 'SA';
const REGION_CENTROIDS: Record<Region, LatLon> = {
AF: { lat: 4, lon: 21 },
AS: { lat: 30, lon: 105 },
EU: { lat: 50, lon: 15 },
NA: { lat: 39, lon: -97 },
SA: { lat: -15, lon: -58 },
};
// Approximate first-octet (/8) -> RIR region, applied in order (later wins).
// Coarse and not authoritative; uncovered octets fall back to NA (ARIN-heavy
// legacy space). Good enough for a continent-level dot on a map.
const REGION_RANGES: [number, number, Region][] = [
// APNIC (Asia / Pacific)
[1, 1, 'AS'],
[14, 14, 'AS'],
[27, 27, 'AS'],
[36, 36, 'AS'],
[39, 39, 'AS'],
[42, 43, 'AS'],
[49, 49, 'AS'],
[58, 61, 'AS'],
[101, 103, 'AS'],
[106, 106, 'AS'],
[110, 126, 'AS'],
[133, 133, 'AS'],
[150, 153, 'AS'],
[163, 163, 'AS'],
[171, 171, 'AS'],
[175, 175, 'AS'],
[180, 183, 'AS'],
[202, 203, 'AS'],
[210, 211, 'AS'],
[218, 223, 'AS'],
// RIPE NCC (Europe / Middle East)
[2, 2, 'EU'],
[5, 5, 'EU'],
[25, 25, 'EU'],
[31, 31, 'EU'],
[37, 37, 'EU'],
[46, 46, 'EU'],
[51, 51, 'EU'],
[53, 53, 'EU'],
[57, 57, 'EU'],
[62, 62, 'EU'],
[77, 95, 'EU'],
[109, 109, 'EU'],
[141, 141, 'EU'],
[145, 145, 'EU'],
[151, 151, 'EU'],
[176, 176, 'EU'],
[178, 178, 'EU'],
[185, 185, 'EU'],
[188, 188, 'EU'],
[193, 195, 'EU'],
[212, 213, 'EU'],
[217, 217, 'EU'],
// AFRINIC (Africa)
[41, 41, 'AF'],
[102, 102, 'AF'],
[105, 105, 'AF'],
[154, 156, 'AF'],
[196, 197, 'AF'],
// LACNIC (Latin America / Caribbean)
[177, 177, 'SA'],
[179, 179, 'SA'],
[181, 181, 'SA'],
[186, 187, 'SA'],
[189, 191, 'SA'],
[200, 201, 'SA'],
];
const REGION_BY_OCTET: Region[] = (() => {
const table: Region[] = Array.from({ length: 256 }, () => 'NA' as Region);
for (const [start, end, region] of REGION_RANGES) {
for (let octet = start; octet <= end; octet++) table[octet] = region;
}
return table;
})();
export const extractIpv4FromAddress = (address: string): string | null => {
const direct = /\/ip4\/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/.exec(address);
if (direct) return direct[1];
// Some peers are reached via a DNS name that embeds the IPv4 with dashes, e.g.
// /dns4/91-234-56-78.host.example -> 91.234.56.78
const dashed = /\/dns[46]?\/[^/]*?(?<!\d)(\d{1,3})-(\d{1,3})-(\d{1,3})-(\d{1,3})(?![\d-])/.exec(address);
if (dashed) return `${dashed[1]}.${dashed[2]}.${dashed[3]}.${dashed[4]}`;
return null;
};
const parseOctets = (ip: string): number[] | null => {
const parts = ip.split('.').map(Number);
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return null;
return parts;
};
export const isPrivateOrReservedIpv4 = (ip: string): boolean => {
const parts = parseOctets(ip);
if (!parts) return true;
const [a, b] = parts;
if (a === 0 || a === 10 || a === 127) return true; // this-network, private, loopback
if (a === 169 && b === 254) return true; // link-local
if (a === 172 && b >= 16 && b <= 31) return true; // private
if (a === 192 && b === 168) return true; // private
if (a === 100 && b >= 64 && b <= 127) return true; // CGNAT
if (a >= 224) return true; // multicast / reserved
return false;
};
const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));
const hashOctets = (parts: number[]) => {
let hash = 0;
for (const part of parts) hash = (Math.imul(hash, 131) + part) | 0;
return Math.abs(hash);
};
// Maps a peer's multiaddr to an approximate lat/lon, or null when it cannot be
// placed offline (private/reserved IPv4, IPv6, or a DNS address).
export const getApproximateLatLon = (address: string): LatLon | null => {
const ip = extractIpv4FromAddress(address);
if (!ip || isPrivateOrReservedIpv4(ip)) return null;
const parts = parseOctets(ip);
if (!parts) return null;
const centroid = REGION_CENTROIDS[REGION_BY_OCTET[parts[0]]];
const hash = hashOctets(parts);
// Deterministic intra-region spread so peers don't stack on one point. The
// region is the meaningful signal; the offset is cosmetic.
const lonOffset = ((hash % 1000) / 1000 - 0.5) * 24; // +/- 12 deg
const latOffset = ((Math.floor(hash / 1000) % 1000) / 1000 - 0.5) * 14; // +/- 7 deg
return { lat: clamp(centroid.lat + latOffset, -85, 85), lon: clamp(centroid.lon + lonOffset, -180, 180) };
};
// Representative countries per region. The RIR table only resolves to a continent,
// so the flag is a deterministic, approximate pick from the region's common
// countries — consistent with the map's "approximate locations" framing, not real
// per-peer country geolocation.
const REGION_COUNTRIES: Record<Region, string[]> = {
AF: ['za', 'ng', 'eg', 'ke', 'ma', 'dz', 'tn'],
AS: ['jp', 'cn', 'sg', 'in', 'kr', 'hk', 'id', 'vn', 'th', 'tw'],
EU: ['de', 'nl', 'fr', 'gb', 'ru', 'se', 'fi', 'pl', 'it', 'es', 'ua', 'ro'],
NA: ['us', 'us', 'ca', 'us', 'mx'],
SA: ['br', 'ar', 'cl', 'co', 'pe'],
};
// Approximate 2-letter country code for a peer, or undefined when it cannot be
// placed offline. See REGION_COUNTRIES: this is region-level, not precise.
export const getApproximateCountryCode = (address: string): string | undefined => {
const ip = extractIpv4FromAddress(address);
if (!ip || isPrivateOrReservedIpv4(ip)) return undefined;
const parts = parseOctets(ip);
if (!parts) return undefined;
const pool = REGION_COUNTRIES[REGION_BY_OCTET[parts[0]]];
return pool[hashOctets(parts) % pool.length];
};