spb/company-atlas
Public
Python 66.3%
TypeScript 22.7%
JavaScript 8.6%
HTML 1.4%
CSS 0.7%
1#!/usr/bin/env node2/**3 * Company Atlas — mock API for development and QA (NOT used in production).4 * Dependency-free Node HTTP server on :8371 serving contract-shaped sample data for every endpoint in docs/API.md:5 * ~40 companies across countries/industries, events of every type with careful wording, metrics, rankings, industries,6 * countries, signals, trends, map buckets, sensors/snapshots/changes/diff, in-memory watchlists/alerts, SSE live stream7 * (one event every ~4 s) and admin payloads. Data is generated from a fixed seed so SSR and client agree.8 *9 * Run: node apps/web/qa/mock-api.mjs [port]10 */11import { createServer } from 'node:http';1213const PORT = Number(process.argv[2] ?? process.env.PORT ?? 8371);14const NOW = Date.now();15const DAY = 86_400_000;1617// ------------------------------------------------------------------------------------------------ deterministic random18let seed = 20260912;19function rnd() {20 seed |= 0;21 seed = (seed + 0x6d2b79f5) | 0;22 let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);23 t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;24 return ((t ^ (t >>> 14)) >>> 0) / 4294967296;25}26const ri = (a, b) => a + Math.floor(rnd() * (b - a + 1));27const pick = (arr) => arr[Math.floor(rnd() * arr.length)];28const chance = (p) => rnd() < p;29const B32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';30let idc = 1000;31function id(prefix) {32 idc += 7;33 let n = idc * 9973 + 12345;34 let s = '';35 for (let i = 0; i < 16; i++) {36 s = B32[(n + i * 7) % 32] + s;37 n = Math.floor(n / 3) + i * 131;38 }39 return `${prefix}_${s}`;40}41const iso = (t) => new Date(t).toISOString();42const day = (t) => new Date(t).toISOString().slice(0, 10);43const slugify = (s) =>44 s45 .toLowerCase()46 .replace(/&/g, ' and ')47 .replace(/[^a-z0-9]+/g, '-')48 .replace(/^-|-$/g, '');49const r1 = (x) => Math.round(x * 10) / 10;50const clamp = (x, a, b) => Math.max(a, Math.min(b, x));5152// ------------------------------------------------------------------------------------------------ reference data53const INDUSTRIES = [54 ['fintech', 'Fintech', 'financial-services'],55 ['financial-services', 'Financial services', null],56 ['banking', 'Banking', 'financial-services'],57 ['software', 'Software', 'technology'],58 ['technology', 'Technology', null],59 ['artificial-intelligence', 'Artificial intelligence', 'technology'],60 ['semiconductors', 'Semiconductors', 'technology'],61 ['cloud-infrastructure', 'Cloud infrastructure', 'technology'],62 ['e-commerce', 'E-commerce', 'retail'],63 ['retail', 'Retail', null],64 ['automotive', 'Automotive', 'manufacturing'],65 ['manufacturing', 'Manufacturing', null],66 ['aerospace', 'Aerospace', 'manufacturing'],67 ['energy', 'Energy', null],68 ['renewable-energy', 'Renewable energy', 'energy'],69 ['healthcare', 'Healthcare', null],70 ['biotech', 'Biotech', 'healthcare'],71 ['pharmaceuticals', 'Pharmaceuticals', 'healthcare'],72 ['telecom', 'Telecom', null],73 ['media', 'Media', null],74 ['logistics', 'Logistics', null],75 ['insurance', 'Insurance', 'financial-services'],76 ['real-estate', 'Real estate', null],77 ['consumer', 'Consumer goods', null],78 ['cybersecurity', 'Cybersecurity', 'technology'],79];80const IND = Object.fromEntries(INDUSTRIES.map(([slug, name, parent]) => [slug, { slug, name, parent_slug: parent }]));8182const COUNTRY_META = {83 US: ['United States', 'North America', 39.8, -98.6],84 CA: ['Canada', 'North America', 56.1, -106.3],85 GB: ['United Kingdom', 'Europe', 55.4, -3.4],86 DE: ['Germany', 'Europe', 51.2, 10.4],87 FR: ['France', 'Europe', 46.2, 2.2],88 NL: ['Netherlands', 'Europe', 52.1, 5.3],89 SE: ['Sweden', 'Europe', 60.1, 18.6],90 CH: ['Switzerland', 'Europe', 46.8, 8.2],91 IE: ['Ireland', 'Europe', 53.4, -8.2],92 ES: ['Spain', 'Europe', 40.5, -3.7],93 JP: ['Japan', 'Asia', 36.2, 138.3],94 KR: ['South Korea', 'Asia', 35.9, 127.8],95 IN: ['India', 'Asia', 20.6, 79.0],96 SG: ['Singapore', 'Asia', 1.35, 103.8],97 AU: ['Australia', 'Oceania', -25.3, 133.8],98 BR: ['Brazil', 'South America', -14.2, -51.9],99 MX: ['Mexico', 'North America', 23.6, -102.6],100 AE: ['United Arab Emirates', 'Middle East', 23.4, 53.8],101 IL: ['Israel', 'Middle East', 31.0, 34.9],102 ZA: ['South Africa', 'Africa', -30.6, 22.9],103 NG: ['Nigeria', 'Africa', 9.1, 8.7],104 TW: ['Taiwan', 'Asia', 23.7, 121.0],105 CN: ['China', 'Asia', 35.9, 104.2],106 FI: ['Finland', 'Europe', 61.9, 25.7],107};108109// [name, domain, country, city, lat, lon, industries, public, ticker, exchange, founded, employees_band, importance, description]110const SEED = [111 ['Stripe', 'stripe.com', 'US', 'South San Francisco', 37.65, -122.4, ['fintech', 'software'], false, null, null, 2010, '5,001–10,000', 92, 'Payments infrastructure for the internet.'],112 ['Adyen', 'adyen.com', 'NL', 'Amsterdam', 52.37, 4.9, ['fintech'], true, 'ADYEN', 'Euronext', 2006, '1,001–5,000', 84, 'Global payments platform for enterprises.'],113 ['Block', 'block.xyz', 'US', 'Oakland', 37.8, -122.27, ['fintech'], true, 'XYZ', 'NYSE', 2009, '10,001+', 83, 'Economic empowerment tools: Square, Cash App, TIDAL.'],114 ['Shopify', 'shopify.com', 'CA', 'Ottawa', 45.42, -75.7, ['e-commerce', 'software'], true, 'SHOP', 'TSX', 2006, '5,001–10,000', 90, 'Commerce platform for merchants of every size.'],115 ['Lightspeed', 'lightspeedhq.com', 'CA', 'Montréal', 45.5, -73.57, ['software', 'retail'], true, 'LSPD', 'TSX', 2005, '1,001–5,000', 62, 'Point-of-sale and commerce platform.'],116 ['Wealthsimple', 'wealthsimple.com', 'CA', 'Toronto', 43.65, -79.38, ['fintech'], false, null, null, 2014, '1,001–5,000', 58, 'Investing, spending and saving app.'],117 ['Cohere', 'cohere.com', 'CA', 'Toronto', 43.65, -79.38, ['artificial-intelligence'], false, null, null, 2019, '201–500', 74, 'Enterprise AI models and retrieval.'],118 ['Anthropic', 'anthropic.com', 'US', 'San Francisco', 37.77, -122.42, ['artificial-intelligence'], false, null, null, 2021, '1,001–5,000', 91, 'AI safety and research company building Claude.'],119 ['NVIDIA', 'nvidia.com', 'US', 'Santa Clara', 37.35, -121.95, ['semiconductors', 'artificial-intelligence'], true, 'NVDA', 'NASDAQ', 1993, '10,001+', 98, 'Accelerated computing and AI platforms.'],120 ['Apple', 'apple.com', 'US', 'Cupertino', 37.32, -122.03, ['technology', 'consumer'], true, 'AAPL', 'NASDAQ', 1976, '10,001+', 99, 'Consumer hardware, software and services.'],121 ['Snowflake', 'snowflake.com', 'US', 'Bozeman', 45.68, -111.04, ['cloud-infrastructure', 'software'], true, 'SNOW', 'NYSE', 2012, '5,001–10,000', 80, 'AI Data Cloud.'],122 ['Datadog', 'datadoghq.com', 'US', 'New York', 40.71, -74.0, ['software', 'cloud-infrastructure'], true, 'DDOG', 'NASDAQ', 2010, '5,001–10,000', 79, 'Observability and security platform.'],123 ['Cloudflare', 'cloudflare.com', 'US', 'San Francisco', 37.77, -122.42, ['cloud-infrastructure', 'cybersecurity'], true, 'NET', 'NYSE', 2009, '1,001–5,000', 85, 'Connectivity cloud.'],124 ['Revolut', 'revolut.com', 'GB', 'London', 51.5, -0.12, ['fintech', 'banking'], false, null, null, 2015, '5,001–10,000', 78, 'Global financial super-app.'],125 ['Monzo', 'monzo.com', 'GB', 'London', 51.5, -0.12, ['banking', 'fintech'], false, null, null, 2015, '1,001–5,000', 60, 'Digital bank.'],126 ['Arm', 'arm.com', 'GB', 'Cambridge', 52.2, 0.12, ['semiconductors'], true, 'ARM', 'NASDAQ', 1990, '5,001–10,000', 82, 'CPU architecture and IP.'],127 ['SAP', 'sap.com', 'DE', 'Walldorf', 49.3, 8.64, ['software'], true, 'SAP', 'XETRA', 1972, '10,001+', 88, 'Enterprise application software.'],128 ['Siemens', 'siemens.com', 'DE', 'Munich', 48.14, 11.58, ['manufacturing', 'technology'], true, 'SIE', 'XETRA', 1847, '10,001+', 90, 'Industrial technology.'],129 ['Zalando', 'zalando.com', 'DE', 'Berlin', 52.52, 13.4, ['e-commerce', 'retail'], true, 'ZAL', 'XETRA', 2008, '10,001+', 70, 'Online fashion platform.'],130 ['Mistral AI', 'mistral.ai', 'FR', 'Paris', 48.86, 2.35, ['artificial-intelligence'], false, null, null, 2023, '201–500', 76, 'Open and portable generative AI.'],131 ['Dassault Systèmes', '3ds.com', 'FR', 'Vélizy-Villacoublay', 48.78, 2.19, ['software', 'manufacturing'], true, 'DSY', 'Euronext', 1981, '10,001+', 77, '3D design and PLM software.'],132 ['Spotify', 'spotify.com', 'SE', 'Stockholm', 59.33, 18.07, ['media', 'technology'], true, 'SPOT', 'NYSE', 2006, '5,001–10,000', 83, 'Audio streaming.'],133 ['Klarna', 'klarna.com', 'SE', 'Stockholm', 59.33, 18.07, ['fintech'], false, null, null, 2005, '1,001–5,000', 72, 'Payments and shopping.'],134 ['Roche', 'roche.com', 'CH', 'Basel', 47.56, 7.59, ['pharmaceuticals', 'healthcare'], true, 'ROG', 'SIX', 1896, '10,001+', 89, 'Pharmaceuticals and diagnostics.'],135 ['Intercom', 'intercom.com', 'IE', 'Dublin', 53.35, -6.26, ['software'], false, null, null, 2011, '1,001–5,000', 61, 'AI-first customer service.'],136 ['Cabify', 'cabify.com', 'ES', 'Madrid', 40.42, -3.7, ['logistics', 'technology'], false, null, null, 2011, '1,001–5,000', 48, 'Mobility platform.'],137 ['Toyota', 'toyota-global.com', 'JP', 'Toyota City', 35.08, 137.16, ['automotive', 'manufacturing'], true, '7203', 'TSE', 1937, '10,001+', 95, 'Automobiles and mobility.'],138 ['Sony', 'sony.com', 'JP', 'Tokyo', 35.68, 139.69, ['consumer', 'media', 'technology'], true, '6758', 'TSE', 1946, '10,001+', 91, 'Electronics, entertainment and financial services.'],139 ['Samsung Electronics', 'samsung.com', 'KR', 'Suwon', 37.26, 127.03, ['semiconductors', 'consumer'], true, '005930', 'KRX', 1969, '10,001+', 96, 'Consumer electronics and semiconductors.'],140 ['Infosys', 'infosys.com', 'IN', 'Bengaluru', 12.97, 77.59, ['software', 'technology'], true, 'INFY', 'NSE', 1981, '10,001+', 80, 'Digital services and consulting.'],141 ['Zerodha', 'zerodha.com', 'IN', 'Bengaluru', 12.97, 77.59, ['fintech'], false, null, null, 2010, '1,001–5,000', 55, 'Discount brokerage.'],142 ['Grab', 'grab.com', 'SG', 'Singapore', 1.29, 103.85, ['logistics', 'fintech'], true, 'GRAB', 'NASDAQ', 2012, '5,001–10,000', 71, 'Superapp for deliveries, mobility and finance.'],143 ['Atlassian', 'atlassian.com', 'AU', 'Sydney', -33.87, 151.21, ['software'], true, 'TEAM', 'NASDAQ', 2002, '10,001+', 84, 'Team collaboration software.'],144 ['Canva', 'canva.com', 'AU', 'Sydney', -33.87, 151.21, ['software', 'media'], false, null, null, 2013, '1,001–5,000', 73, 'Visual communication platform.'],145 ['Nubank', 'nubank.com.br', 'BR', 'São Paulo', -23.55, -46.63, ['banking', 'fintech'], true, 'NU', 'NYSE', 2013, '5,001–10,000', 81, 'Digital banking platform.'],146 ['Bitso', 'bitso.com', 'MX', 'Mexico City', 19.43, -99.13, ['fintech'], false, null, null, 2014, '501–1,000', 45, 'Crypto financial services.'],147 ['Careem', 'careem.com', 'AE', 'Dubai', 25.2, 55.27, ['logistics', 'technology'], false, null, null, 2012, '1,001–5,000', 52, 'Everything app for the Middle East.'],148 ['Wiz', 'wiz.io', 'IL', 'Tel Aviv', 32.08, 34.78, ['cybersecurity', 'cloud-infrastructure'], false, null, null, 2020, '1,001–5,000', 69, 'Cloud security platform.'],149 ['Discovery', 'discovery.co.za', 'ZA', 'Sandton', -26.1, 28.05, ['insurance', 'financial-services'], true, 'DSY', 'JSE', 1992, '10,001+', 57, 'Shared-value insurance.'],150 ['Flutterwave', 'flutterwave.com', 'NG', 'Lagos', 6.52, 3.38, ['fintech'], false, null, null, 2016, '501–1,000', 50, 'Payments technology for Africa.'],151 ['TSMC', 'tsmc.com', 'TW', 'Hsinchu', 24.8, 120.97, ['semiconductors', 'manufacturing'], true, '2330', 'TWSE', 1987, '10,001+', 97, 'Dedicated semiconductor foundry.'],152 ['Ørsted', 'orsted.com', 'DE', 'Hamburg', 53.55, 9.99, ['renewable-energy', 'energy'], true, 'ORSTED', 'Nasdaq Copenhagen', 1972, '5,001–10,000', 66, 'Offshore wind developer.'],153 ['Nokia', 'nokia.com', 'FI', 'Espoo', 60.2, 24.66, ['telecom', 'technology'], true, 'NOKIA', 'Nasdaq Helsinki', 1865, '10,001+', 78, 'Network infrastructure and technology.'],154 ['Moderna', 'modernatx.com', 'US', 'Cambridge', 42.37, -71.11, ['biotech', 'pharmaceuticals'], true, 'MRNA', 'NASDAQ', 2010, '5,001–10,000', 75, 'mRNA medicines.'],155 ['Rocket Lab', 'rocketlabusa.com', 'US', 'Long Beach', 33.77, -118.19, ['aerospace'], true, 'RKLB', 'NASDAQ', 2006, '1,001–5,000', 64, 'Launch and space systems.'],156 ['Compass', 'compass.com', 'US', 'New York', 40.71, -74.0, ['real-estate', 'technology'], true, 'COMP', 'NYSE', 2012, '1,001–5,000', 47, 'Real estate technology and brokerage.'],157];158159const SURFACES = ['homepage', 'about', 'careers', 'newsroom', 'blog', 'products', 'pricing', 'leadership', 'locations', 'investor_relations', 'documentation', 'changelog', 'legal', 'security', 'developer', 'partners', 'customers', 'sitemap', 'feed'];160const SURFACE_PATH = { homepage: '/', about: '/about', careers: '/careers', newsroom: '/newsroom', blog: '/blog', products: '/products', pricing: '/pricing', leadership: '/about/leadership', locations: '/about/locations', investor_relations: '/investors', documentation: '/docs', changelog: '/changelog', legal: '/legal/terms', security: '/security', developer: '/developers', partners: '/partners', customers: '/customers', sitemap: '/sitemap.xml', feed: '/blog/rss.xml' };161const CONNECTOR_FOR = { careers: 'generic_careers', newsroom: 'generic_news', blog: 'rss_connector', pricing: 'generic_pricing', leadership: 'generic_leadership', locations: 'generic_locations', products: 'generic_products', documentation: 'generic_docs', changelog: 'generic_changelog', legal: 'generic_legal', sitemap: 'sitemap_connector', feed: 'rss_connector', investor_relations: 'generic_ir' };162const CONNECTORS = [163 ['generic_html', 'Generic HTML page', '1.4.0', 'core'],164 ['generic_careers', 'Generic careers page', '2.1.0', 'jobs'],165 ['greenhouse_connector', 'Greenhouse board', '1.2.0', 'jobs'],166 ['lever_connector', 'Lever board', '1.1.0', 'jobs'],167 ['generic_news', 'Generic newsroom', '1.3.0', 'news'],168 ['rss_connector', 'RSS / Atom feed', '1.0.2', 'news'],169 ['generic_pricing', 'Generic pricing page', '2.0.0', 'pricing'],170 ['generic_leadership', 'Generic leadership page', '1.1.0', 'people'],171 ['generic_locations', 'Generic locations page', '1.0.0', 'locations'],172 ['generic_products', 'Generic product catalogue', '1.2.0', 'products'],173 ['generic_docs', 'Documentation', '1.0.1', 'developer'],174 ['generic_changelog', 'Changelog', '1.0.0', 'developer'],175 ['generic_legal', 'Legal / terms', '1.0.0', 'legal'],176 ['generic_ir', 'Investor relations', '1.0.0', 'ir'],177 ['sitemap_connector', 'Sitemap discovery', '1.5.0', 'discovery'],178];179const TIERS = ['A', 'B', 'C', 'D', 'E'];180const TIER_INTERVAL = { A: 900, B: 3600, C: 21600, D: 86400, E: 432000 };181const FAILURE_CLASSES = ['TIMEOUT', 'HTTP_4XX', 'HTTP_5XX', 'BOT_CHALLENGE', 'PARSING', 'REDIRECT', 'PAGE_REMOVED', 'RATE_LIMIT', 'DNS'];182183const DEPARTMENTS = ['Engineering', 'Product', 'Sales', 'Marketing', 'Customer Success', 'Finance', 'Legal', 'Operations', 'Data', 'Design', 'Security', 'People'];184const JOB_TITLES = ['Software Engineer', 'Senior Software Engineer', 'Staff Engineer', 'Machine Learning Engineer', 'Applied AI Engineer', 'Data Scientist', 'Product Manager', 'Account Executive', 'Solutions Engineer', 'Security Engineer', 'Site Reliability Engineer', 'Technical Writer', 'Designer', 'Recruiter', 'Finance Analyst', 'Legal Counsel', 'Research Scientist, LLMs', 'Developer Advocate', 'Platform Engineer', 'Support Specialist'];185const PRODUCT_NAMES = ['Terminal', 'Connect', 'Radar', 'Atlas', 'Issuing', 'Billing', 'Sigma', 'Vault', 'Insights', 'Studio', 'Workflows', 'Assistant', 'Guard', 'Ledger', 'Pulse', 'Edge', 'Core API', 'Marketplace', 'Analytics', 'Identity'];186const PEOPLE = ['Ava Martin', 'Noah Chen', 'Léa Dubois', 'Mateo Rossi', 'Priya Nair', 'Kenji Watanabe', 'Sofia Alvarez', 'Liam O’Connor', 'Amara Okafor', 'Hugo Lindqvist', 'Yuna Park', 'Daniel Cohen', 'Fatima Al-Sayed', 'Elena Petrova', 'Tomás Silva', 'Grace Kim', 'Arjun Mehta', 'Mia Fischer', 'Oliver Brown', 'Chloé Bernard'];187const TITLES = ['Chief Executive Officer', 'Chief Financial Officer', 'Chief Technology Officer', 'Chief Operating Officer', 'Chief Product Officer', 'Chief Revenue Officer', 'General Counsel', 'Chief People Officer', 'VP Engineering', 'VP Sales', 'Head of AI', 'Chief Information Security Officer'];188const CITIES = [['New York', 'US', 40.71, -74.0], ['London', 'GB', 51.5, -0.12], ['Berlin', 'DE', 52.52, 13.4], ['Paris', 'FR', 48.86, 2.35], ['Toronto', 'CA', 43.65, -79.38], ['Singapore', 'SG', 1.29, 103.85], ['Sydney', 'AU', -33.87, 151.21], ['Tokyo', 'JP', 35.68, 139.69], ['Dublin', 'IE', 53.35, -6.26], ['Bengaluru', 'IN', 12.97, 77.59], ['São Paulo', 'BR', -23.55, -46.63], ['Austin', 'US', 30.27, -97.74], ['Amsterdam', 'NL', 52.37, 4.9], ['Dubai', 'AE', 25.2, 55.27], ['Seoul', 'KR', 37.57, 126.98], ['Mexico City', 'MX', 19.43, -99.13], ['Warsaw', 'PL', 52.23, 21.01], ['Lisbon', 'PT', 38.72, -9.14]];189const TREND_TERMS = ['agentic AI', 'AI engineer', 'usage-based pricing', 'data residency', 'FedRAMP', 'EU AI Act', 'sovereign cloud', 'stablecoin', 'on-device inference', 'enterprise tier', 'SOC 2', 'MCP server', 'embedded finance', 'carbon accounting', 'returns policy', 'API deprecation', 'self-serve', 'hybrid work', 'Bengaluru hub', 'German expansion'];190191// ------------------------------------------------------------------------------------------------ event templates192// [event_type, event_subtype, importanceRange, surfaces, titleFn, summaryFn, old/new fn]193const EVENT_TEMPLATES = [194 ['PRODUCT', 'PRODUCT_LAUNCH', [0.55, 0.92], ['products', 'homepage', 'newsroom', 'blog'], (c, p) => `${c.display_name} lists a new product: ${p.name}`, (c, p) => `A product page for ${p.name} was detected on the monitored catalog. It did not appear in the previous snapshot.`, (c, p) => [null, p.name]],195 ['PRODUCT', 'PRODUCT_REMOVAL', [0.5, 0.8], ['products'], (c, p) => `${p.name} no longer listed on ${c.display_name}'s product catalog`, (c, p) => `The product entry was present in the previous version of the page and is no longer visible. This does not by itself confirm discontinuation.`, (c, p) => [p.name, null]],196 ['PRODUCT', 'PRODUCT_RENAME', [0.35, 0.6], ['products'], (c, p) => `${c.display_name} appears to have renamed ${p.name}`, (c, p) => `The product card kept its position and description but its name changed between snapshots.`, (c, p) => [p.name, `${p.name} Pro`]],197 ['PRICING', 'PRICE_INCREASE', [0.6, 0.95], ['pricing'], (c, p) => `${c.display_name} ${p.plan} plan price increased`, (c, p) => `The listed monthly price of the ${p.plan} plan changed on the public pricing page.`, (c, p) => [`$${p.oldPrice}/mo`, `$${p.newPrice}/mo`]],198 ['PRICING', 'PRICE_DECREASE', [0.5, 0.85], ['pricing'], (c, p) => `${c.display_name} ${p.plan} plan price decreased`, (c, p) => `The listed monthly price of the ${p.plan} plan is lower than in the previous snapshot.`, (c, p) => [`$${p.newPrice}/mo`, `$${p.oldPrice}/mo`]],199 ['PRICING', 'NEW_PRICING_TIER', [0.55, 0.85], ['pricing'], (c, p) => `${c.display_name} adds a new pricing tier: ${p.plan}`, (c) => `A pricing column that did not exist in the previous snapshot is now listed.`, (c, p) => [null, p.plan]],200 ['HIRING', 'JOB_COUNT_INCREASE', [0.3, 0.75], ['careers'], (c, p) => `${c.display_name} open listings up ${p.pct} % over 7 days`, (c, p) => `${p.n} listings were added on the monitored careers surface, ${p.ai} of them mentioning AI or machine learning.`, (c, p) => [`${p.from} open listings`, `${p.to} open listings`]],201 ['HIRING', 'JOB_COUNT_DECREASE', [0.4, 0.85], ['careers'], (c, p) => `${p.n} ${c.display_name} listings are no longer visible`, (c, p) => `${p.n} monitored job listings are no longer visible on the careers page (${p.pct} % of the previous count). This reflects public listings only and is not evidence of layoffs.`, (c, p) => [`${p.from} open listings`, `${p.to} open listings`]],202 ['LEADERSHIP', 'NEW_EXECUTIVE', [0.6, 0.9], ['leadership', 'newsroom'], (c, p) => `${p.name} listed as ${p.title} at ${c.display_name}`, (c, p) => `A new profile appeared on the monitored leadership page.`, (c, p) => [null, `${p.name} — ${p.title}`]],203 ['LEADERSHIP', 'EXECUTIVE_REMOVED', [0.6, 0.9], ['leadership'], (c, p) => `${p.name} no longer listed on ${c.display_name}'s leadership page`, (c, p) => `The profile for ${p.name} (${p.title}) is no longer visible on the monitored leadership page. The reason is not stated on the page.`, (c, p) => [`${p.name} — ${p.title}`, null]],204 ['LOCATION', 'NEW_OFFICE', [0.5, 0.8], ['locations', 'careers'], (c, p) => `${c.display_name} lists a new office in ${p.city}`, (c, p) => `A ${p.city}, ${p.country} location was added to the monitored locations page.`, (c, p) => [null, `${p.city}, ${p.country}`]],205 ['LOCATION', 'COUNTRY_EXPANSION', [0.65, 0.92], ['locations', 'careers', 'newsroom'], (c, p) => `${c.display_name} appears to expand into ${p.country}`, (c, p) => `First monitored presence in ${p.country}: a location entry and ${p.jobs} job listings referencing ${p.city}.`, (c, p) => [null, p.country]],206 ['LOCATION', 'OFFICE_REMOVED', [0.45, 0.75], ['locations'], (c, p) => `${c.display_name}'s ${p.city} office no longer listed`, (c, p) => `The ${p.city} entry was present in the previous snapshot of the locations page and is no longer visible.`, (c, p) => [`${p.city}, ${p.country}`, null]],207 ['DEVELOPER', 'API_LAUNCH', [0.55, 0.85], ['developer', 'documentation', 'changelog'], (c, p) => `${c.display_name} publishes a new API: ${p.name} API`, (c, p) => `A new reference section for the ${p.name} API appeared in the developer documentation.`, (c, p) => [null, `${p.name} API v1`]],208 ['DEVELOPER', 'DOCUMENTATION_CHANGE', [0.25, 0.55], ['documentation', 'changelog'], (c, p) => `${c.display_name} documentation updated: ${p.section}`, (c, p) => `${p.blocks} blocks changed in the ${p.section} section of the monitored documentation.`, () => [null, null]],209 ['LEGAL', 'TERMS_CHANGE', [0.45, 0.8], ['legal'], (c, p) => `${c.display_name} terms of service updated (section ${p.section})`, (c, p) => `Section ${p.section} of the terms was materially updated; ${p.pct} % of the text changed. The previous version is preserved.`, (c, p) => [`Terms v${p.v}`, `Terms v${p.v + 1}`]],210 ['COMMUNICATION', 'NEWS_RELEASE', [0.3, 0.7], ['newsroom', 'blog', 'feed'], (c, p) => `${c.display_name}: ${p.headline}`, (c, p) => `A first-party newsroom item was published.`, () => [null, null]],211 ['PARTNERSHIP', 'NEW_PARTNERSHIP', [0.5, 0.8], ['newsroom', 'partners'], (c, p) => `${c.display_name} announces a partnership with ${p.partner}`, (c, p) => `A partnership was described in a first-party release and ${p.partner} now appears on the partners page.`, (c, p) => [null, p.partner]],212 ['M&A', 'ACQUISITION', [0.75, 0.98], ['newsroom', 'investor_relations'], (c, p) => `${c.display_name} announces the acquisition of ${p.target}`, (c, p) => `Stated in a first-party release on the newsroom; corroborated by an investor relations notice ${p.delay} minutes later.`, (c, p) => [null, p.target]],213 ['FINANCING', 'FUNDING_ROUND', [0.7, 0.95], ['newsroom', 'blog'], (c, p) => `${c.display_name} states a ${p.amount} funding round`, (c, p) => `Amount as stated by the company in its own release; not independently verified.`, (c, p) => [null, p.amount]],214 ['SECURITY', 'SECURITY_NOTICE', [0.6, 0.9], ['security', 'blog'], (c, p) => `${c.display_name} publishes a security notice`, (c, p) => `A new advisory appeared on the monitored security page (${p.kind}).`, () => [null, null]],215 ['STRATEGY', 'BRAND_REPOSITIONING', [0.45, 0.75], ['homepage', 'about'], (c, p) => `${c.display_name} homepage headline changed`, (c, p) => `The hero headline changed from an SMB-oriented message to enterprise language. Signal, not a confirmed strategy change.`, (c, p) => [p.oldHeadline, p.newHeadline]],216 ['TECHNOLOGY', 'TECHNOLOGY_SIGNAL', [0.3, 0.6], ['developer', 'blog', 'careers'], (c, p) => `${c.display_name} references ${p.tech} across ${p.n} surfaces`, (c, p) => `${p.tech} appeared in new documentation and ${p.jobs} job listings within the same week.`, () => [null, null]],217 ['INVESTOR_RELATIONS', 'IR_UPDATE', [0.4, 0.7], ['investor_relations'], (c, p) => `${c.display_name} posts ${p.item} on investor relations`, () => `Published on the company's investor relations page; not a substitute for regulated filings.`, () => [null, null]],218 ['SUSTAINABILITY', 'SUSTAINABILITY_UPDATE', [0.3, 0.6], ['about', 'newsroom'], (c, p) => `${c.display_name} publishes its ${p.year} sustainability report`, () => `A new report link appeared on the monitored page.`, () => [null, null]],219 ['MARKETING', 'CAMPAIGN_CHANGE', [0.2, 0.45], ['homepage'], (c) => `${c.display_name} homepage hero campaign changed`, () => `Hero imagery and call-to-action blocks were replaced; classified as marketing noise above the significance floor.`, () => [null, null]],220 ['OPERATIONS', 'STATUS_INCIDENT', [0.4, 0.7], ['status', 'security'], (c, p) => `${c.display_name} status page reports ${p.kind}`, () => `Observed on the public status page.`, () => [null, null]],221];222const HEADLINES = ['expands enterprise offering to the EU', 'reports record quarter for developer sign-ups', 'opens applications for its startup program', 'introduces new sustainability commitments', 'launches regional data residency', 'names new advisory board members', 'publishes annual developer survey', 'partners with universities on AI research'];223const PARTNERS = ['Microsoft', 'AWS', 'Google Cloud', 'Accenture', 'Deloitte', 'Salesforce', 'Visa', 'Mastercard', 'Snowflake', 'NVIDIA'];224const TECHS = ['Rust', 'Kubernetes', 'MCP', 'vector search', 'WebAssembly', 'PostgreSQL', 'Kafka', 'Terraform', 'Claude', 'LLM evaluation'];225226// ------------------------------------------------------------------------------------------------ enrichment profiles227// Sample `CompanyCard.profile` payloads (mock only — figures are approximate public values used to exercise the UI, not228// a dataset). Mix: complete (nvidia, apple, toyota, sony, roche, sap, spotify, adyen, arm, tsmc, siemens, samsung),229// partial (stripe, cohere, anthropic, mistral-ai, shopify, nubank, revolut, klarna), null-filled (cabify, careem,230// flutterwave, zerodha, discovery), absent key (compass, bitso, wiz), and an auto-derived minimal profile for the rest.231const MOCK_ASSET_BASE = `http://127.0.0.1:${PORT}/api/v1/_mock`;232const WD = (q) => `https://www.wikidata.org/wiki/${q}`;233const WP = (t) => `https://en.wikipedia.org/wiki/${t}`;234const money = (value, currency, year) => ({ value, currency, year });235const RETRIEVED = iso(NOW - 3 * DAY);236const NO_PROFILE_KEY = new Set(['compass', 'bitso', 'wiz']);237const NULL_PROFILE = new Set(['cabify', 'careem', 'flutterwave', 'zerodha', 'discovery']);238const PROFILES = {239 nvidia: {240 description: 'Nvidia Corporation is an American technology company headquartered in Santa Clara, California. It designs graphics processing units, system-on-a-chip units and application programming interfaces for data science, high-performance computing and mobile and automotive markets.',241 description_source: 'wikipedia', description_url: WP('Nvidia'), description_license: 'CC BY-SA 4.0',242 logo: true, icon: true, founded_year: 1993, legal_form: 'Public company (Delaware corporation)', employees: 36000, employees_year: 2025,243 revenue: money(130497000000, 'USD', 2025), net_income: money(72880000000, 'USD', 2025), total_assets: money(111601000000, 'USD', 2025),244 hq: { city: 'Santa Clara', region: 'California', country: 'US', address: '2788 San Tomas Expressway', lat: 37.3706, lon: -121.9636 },245 ticker: 'NVDA', exchange: 'NASDAQ', isin: 'US67066G1040', lei: '549300S4KLFTLO7GSQ80', sec_cik: '0001045810', public_company: true,246 wikipedia_url: WP('Nvidia'), wikidata_url: WD('Q182477'), official_website: 'https://www.nvidia.com', phone: '+1 408-486-2000',247 products: ['GeForce', 'Quadro', 'CUDA', 'DGX', 'Jetson', 'Tegra', 'NVIDIA DRIVE', 'Omniverse', 'Nvidia RTX'], industry_labels: ['semiconductor industry', 'artificial intelligence', 'computer hardware'],248 socials: { linkedin: 'https://www.linkedin.com/company/nvidia', x: 'https://x.com/nvidia', youtube: 'https://www.youtube.com/@NVIDIA', facebook: 'https://www.facebook.com/NVIDIA', instagram: 'https://www.instagram.com/nvidia', github: 'https://github.com/NVIDIA' },249 extra_facts: [{ key: 'index_membership', label: 'Index membership', value: 'S&P 500 · Nasdaq-100 · Dow Jones Industrial Average', source: 'wikidata' }],250 financial_source: 'sec_edgar',251 },252 apple: {253 description: 'Apple Inc. is an American multinational technology company headquartered in Cupertino, California. It designs, develops and sells consumer electronics, software and online services, including the iPhone, Mac, iPad, Apple Watch and the App Store.',254 description_source: 'wikipedia', description_url: WP('Apple_Inc.'), description_license: 'CC BY-SA 4.0',255 logo: true, icon: true, founded_year: 1976, legal_form: 'Public company (California corporation)', employees: 164000, employees_year: 2024,256 revenue: money(391035000000, 'USD', 2024), net_income: money(93736000000, 'USD', 2024), total_assets: money(364980000000, 'USD', 2024),257 hq: { city: 'Cupertino', region: 'California', country: 'US', address: 'One Apple Park Way', lat: 37.3349, lon: -122.009 },258 ticker: 'AAPL', exchange: 'NASDAQ', isin: 'US0378331005', lei: 'HWUPKR0MPOU8FGXBT394', sec_cik: '0000320193', public_company: true,259 wikipedia_url: WP('Apple_Inc.'), wikidata_url: WD('Q312'), official_website: 'https://www.apple.com', phone: '+1 408-996-1010',260 products: ['iPhone', 'iPad', 'Mac', 'Apple Watch', 'AirPods', 'Apple TV', 'Apple Vision Pro', 'iOS', 'macOS', 'App Store', 'Apple Music', 'iCloud'], industry_labels: ['consumer electronics', 'software industry', 'online services'],261 socials: { linkedin: 'https://www.linkedin.com/company/apple', x: 'https://x.com/Apple', youtube: 'https://www.youtube.com/@Apple', instagram: 'https://www.instagram.com/apple', facebook: 'https://www.facebook.com/apple' },262 extra_facts: [{ key: 'index_membership', label: 'Index membership', value: 'S&P 500 · Nasdaq-100 · Dow Jones Industrial Average', source: 'wikidata' }, { key: 'auditor', label: 'Auditor', value: 'Ernst & Young', source: 'sec_edgar' }],263 financial_source: 'sec_edgar',264 },265 toyota: {266 description: 'Toyota Motor Corporation is a Japanese multinational automotive manufacturer headquartered in Toyota City, Aichi. It is one of the largest automobile manufacturers in the world by production volume.',267 description_source: 'wikipedia', description_url: WP('Toyota'), description_license: 'CC BY-SA 4.0',268 logo: true, icon: true, founded_year: 1937, legal_form: 'Kabushiki gaisha (public)', employees: 380793, employees_year: 2024,269 revenue: money(48036704000000, 'JPY', 2025), net_income: money(4765086000000, 'JPY', 2025), total_assets: money(93601350000000, 'JPY', 2025),270 hq: { city: 'Toyota City', region: 'Aichi', country: 'JP', address: '1 Toyota-cho', lat: 35.0826, lon: 137.1562 },271 ticker: '7203', exchange: 'TSE', isin: 'JP3633400001', lei: '5493006W3QUS5LMH6R84', sec_cik: '0001094517', public_company: true,272 wikipedia_url: WP('Toyota'), wikidata_url: WD('Q53268'), official_website: 'https://global.toyota', phone: null,273 products: ['Toyota Corolla', 'Toyota Camry', 'Toyota RAV4', 'Toyota Prius', 'Toyota Hilux', 'Toyota Land Cruiser', 'Lexus'], industry_labels: ['automotive industry'],274 socials: { linkedin: 'https://www.linkedin.com/company/toyota', x: 'https://x.com/Toyota', youtube: 'https://www.youtube.com/@toyotaglobal' },275 },276 sony: {277 description: 'Sony Group Corporation is a Japanese multinational conglomerate headquartered in Minato, Tokyo, active in electronics, gaming, entertainment (pictures and music), imaging sensors and financial services.',278 description_source: 'homepage', description_url: 'https://www.sony.com/en/SonyInfo/CorporateInfo/', description_license: null,279 logo: true, icon: false, founded_year: 1946, legal_form: 'Kabushiki gaisha (public)', employees: 113000, employees_year: 2024,280 revenue: money(12957000000000, 'JPY', 2025), net_income: money(1141600000000, 'JPY', 2025), total_assets: money(35300000000000, 'JPY', 2025),281 hq: { city: 'Minato', region: 'Tokyo', country: 'JP', address: '1-7-1 Konan', lat: 35.6299, lon: 139.7402 },282 ticker: '6758', exchange: 'TSE', isin: 'JP3435000009', lei: '353800A2DP3ZMC4LR436', sec_cik: '0000313838', public_company: true,283 wikipedia_url: WP('Sony'), wikidata_url: WD('Q41187'), official_website: 'https://www.sony.com', phone: null,284 products: ['PlayStation 5', 'Sony Alpha', 'Bravia', 'WH-1000XM5', 'Xperia', 'Sony Pictures', 'Sony Music'], industry_labels: ['conglomerate', 'consumer electronics', 'entertainment industry'],285 socials: { linkedin: 'https://www.linkedin.com/company/sony', x: 'https://x.com/Sony', youtube: 'https://www.youtube.com/@Sony', instagram: 'https://www.instagram.com/sony', facebook: 'https://www.facebook.com/Sony', tiktok: 'https://www.tiktok.com/@sony' },286 },287 roche: {288 description: 'F. Hoffmann-La Roche AG is a Swiss multinational healthcare company operating worldwide under two divisions: Pharmaceuticals and Diagnostics. It is headquartered in Basel.',289 description_source: 'wikipedia', description_url: WP('Hoffmann-La_Roche'), description_license: 'CC BY-SA 4.0',290 logo: true, icon: true, founded_year: 1896, legal_form: 'Aktiengesellschaft (public)', employees: 103613, employees_year: 2024,291 revenue: money(60500000000, 'CHF', 2024), net_income: money(9186000000, 'CHF', 2024), total_assets: money(93700000000, 'CHF', 2024),292 hq: { city: 'Basel', region: 'Basel-Stadt', country: 'CH', address: 'Grenzacherstrasse 124', lat: 47.5615, lon: 7.6086 },293 ticker: 'ROG', exchange: 'SIX', isin: 'CH0012032048', lei: '549300U41AUUVOAZRV96', sec_cik: null, public_company: true,294 wikipedia_url: WP('Hoffmann-La_Roche'), wikidata_url: WD('Q212646'), official_website: 'https://www.roche.com', phone: '+41 61 688 11 11',295 products: ['Ocrevus', 'Hemlibra', 'Tecentriq', 'Vabysmo', 'Perjeta', 'cobas', 'Accu-Chek'], industry_labels: ['pharmaceutical industry', 'in vitro diagnostics'],296 socials: { linkedin: 'https://www.linkedin.com/company/roche', x: 'https://x.com/Roche', youtube: 'https://www.youtube.com/@roche' },297 },298 sap: {299 description: 'SAP SE is a German multinational software company based in Walldorf, Baden-Württemberg, that develops enterprise software to manage business operations and customer relations.',300 description_source: 'wikipedia', description_url: WP('SAP'), description_license: 'CC BY-SA 4.0',301 logo: true, icon: true, founded_year: 1972, legal_form: 'Societas Europaea (SE)', employees: 109121, employees_year: 2024,302 revenue: money(34176000000, 'EUR', 2024), net_income: money(3096000000, 'EUR', 2024), total_assets: money(69700000000, 'EUR', 2024),303 hq: { city: 'Walldorf', region: 'Baden-Württemberg', country: 'DE', address: 'Dietmar-Hopp-Allee 16', lat: 49.2933, lon: 8.6414 },304 ticker: 'SAP', exchange: 'XETRA', isin: 'DE0007164600', lei: '529900D6BF99LW9R2E68', sec_cik: '0001000184', public_company: true,305 wikipedia_url: WP('SAP'), wikidata_url: WD('Q166262'), official_website: 'https://www.sap.com', phone: '+49 6227 7-47474',306 products: ['SAP S/4HANA', 'SAP HANA', 'SAP Business Technology Platform', 'SAP SuccessFactors', 'SAP Ariba', 'SAP Concur', 'Joule'], industry_labels: ['software industry', 'enterprise software'],307 socials: { linkedin: 'https://www.linkedin.com/company/sap', x: 'https://x.com/SAP', youtube: 'https://www.youtube.com/@SAP', github: 'https://github.com/SAP' },308 },309 spotify: {310 description: 'Spotify Technology S.A. is a Swedish audio streaming and media services provider founded in 2006 and headquartered in Stockholm, with its legal seat in Luxembourg.',311 description_source: 'wikipedia', description_url: WP('Spotify'), description_license: 'CC BY-SA 4.0',312 logo: true, icon: true, founded_year: 2006, legal_form: 'Société anonyme (Luxembourg)', employees: 7359, employees_year: 2024,313 revenue: money(15673000000, 'EUR', 2024), net_income: money(1138000000, 'EUR', 2024), total_assets: money(11100000000, 'EUR', 2024),314 hq: { city: 'Stockholm', region: null, country: 'SE', address: 'Regeringsgatan 19', lat: 59.3326, lon: 18.0649 },315 ticker: 'SPOT', exchange: 'NYSE', isin: 'LU1778762911', lei: '549300I8UDPDOMRCNP86', sec_cik: '0001639920', public_company: true,316 wikipedia_url: WP('Spotify'), wikidata_url: WD('Q689141'), official_website: 'https://www.spotify.com', phone: null,317 products: ['Spotify', 'Spotify Premium', 'Spotify for Artists', 'Spotify for Podcasters', 'Anchor'], industry_labels: ['music streaming', 'podcasting'],318 socials: { linkedin: 'https://www.linkedin.com/company/spotify', x: 'https://x.com/Spotify', youtube: 'https://www.youtube.com/@Spotify', instagram: 'https://www.instagram.com/spotify', github: 'https://github.com/spotify' },319 },320 adyen: {321 description: 'Adyen N.V. is a Dutch payment company headquartered in Amsterdam that provides a single platform for accepting payments across online, mobile and in-store channels.',322 description_source: 'wikipedia', description_url: WP('Adyen'), description_license: 'CC BY-SA 4.0',323 logo: true, icon: true, founded_year: 2006, legal_form: 'Naamloze vennootschap (public)', employees: 4322, employees_year: 2024,324 revenue: money(1996000000, 'EUR', 2024), net_income: money(925000000, 'EUR', 2024), total_assets: null,325 hq: { city: 'Amsterdam', region: 'North Holland', country: 'NL', address: 'Simon Carmiggeltstraat 6-50', lat: 52.3765, lon: 4.9016 },326 ticker: 'ADYEN', exchange: 'Euronext Amsterdam', isin: 'NL0012969182', lei: '724500PSWKAY73WSLD26', sec_cik: null, public_company: true,327 wikipedia_url: WP('Adyen'), wikidata_url: WD('Q19833716'), official_website: 'https://www.adyen.com', phone: null,328 products: ['Adyen Platform', 'Adyen for Platforms', 'Adyen Issuing', 'Adyen Terminal'], industry_labels: ['payment service provider', 'financial technology'],329 socials: { linkedin: 'https://www.linkedin.com/company/adyen', x: 'https://x.com/Adyen', youtube: 'https://www.youtube.com/@adyen', github: 'https://github.com/Adyen' },330 },331 arm: {332 description: 'Arm Holdings plc is a British semiconductor and software design company based in Cambridge that licenses processor architectures and IP cores; majority-owned by SoftBank Group.',333 description_source: 'wikipedia', description_url: WP('Arm_Holdings'), description_license: 'CC BY-SA 4.0',334 logo: true, icon: true, founded_year: 1990, legal_form: 'Public limited company', employees: 8300, employees_year: 2025,335 revenue: money(4007000000, 'USD', 2025), net_income: money(792000000, 'USD', 2025), total_assets: money(8700000000, 'USD', 2025),336 hq: { city: 'Cambridge', region: 'Cambridgeshire', country: 'GB', address: '110 Fulbourn Road', lat: 52.1839, lon: 0.1791 },337 ticker: 'ARM', exchange: 'NASDAQ', isin: 'US0420682058', lei: '213800ND9OV4ZZKK7O47', sec_cik: '0001973239', public_company: true,338 wikipedia_url: WP('Arm_Holdings'), wikidata_url: WD('Q1063165'), official_website: 'https://www.arm.com', phone: null,339 products: ['Cortex-A', 'Cortex-M', 'Neoverse', 'Mali', 'Armv9', 'Arm Compute Subsystems'], industry_labels: ['semiconductor industry', 'intellectual property licensing'],340 socials: { linkedin: 'https://www.linkedin.com/company/arm', x: 'https://x.com/Arm', youtube: 'https://www.youtube.com/@Arm', github: 'https://github.com/ARM-software' },341 financial_source: 'sec_edgar',342 },343 tsmc: {344 description: 'Taiwan Semiconductor Manufacturing Company Limited is a Taiwanese multinational semiconductor contract manufacturing and design company headquartered in Hsinchu Science Park; it is the world’s largest dedicated independent semiconductor foundry.',345 description_source: 'wikipedia', description_url: WP('TSMC'), description_license: 'CC BY-SA 4.0',346 logo: true, icon: true, founded_year: 1987, legal_form: 'Public company', employees: 83825, employees_year: 2024,347 revenue: money(2894307000000, 'TWD', 2024), net_income: money(1173268000000, 'TWD', 2024), total_assets: money(6691000000000, 'TWD', 2024),348 hq: { city: 'Hsinchu', region: null, country: 'TW', address: '8 Li-Hsin Road 6, Hsinchu Science Park', lat: 24.7739, lon: 121.0107 },349 ticker: '2330', exchange: 'TWSE', isin: 'TW0002330008', lei: '549300YSKHMGWXOR9E51', sec_cik: '0001046179', public_company: true,350 wikipedia_url: WP('TSMC'), wikidata_url: WD('Q713418'), official_website: 'https://www.tsmc.com', phone: null,351 products: ['3 nm process', '5 nm process', 'CoWoS', 'InFO'], industry_labels: ['semiconductor industry', 'semiconductor fabrication'],352 socials: { linkedin: 'https://www.linkedin.com/company/tsmc', youtube: 'https://www.youtube.com/@tsmc' },353 },354 siemens: {355 description: 'Siemens AG is a German multinational technology conglomerate headquartered in Munich, focused on industrial automation, smart infrastructure, rail transport and, through Siemens Healthineers, medical technology.',356 description_source: 'wikipedia', description_url: WP('Siemens'), description_license: 'CC BY-SA 4.0',357 logo: true, icon: true, founded_year: 1847, legal_form: 'Aktiengesellschaft (public)', employees: 327000, employees_year: 2024,358 revenue: money(75930000000, 'EUR', 2024), net_income: money(9000000000, 'EUR', 2024), total_assets: money(146000000000, 'EUR', 2024),359 hq: { city: 'Munich', region: 'Bavaria', country: 'DE', address: 'Werner-von-Siemens-Straße 1', lat: 48.1396, lon: 11.5744 },360 ticker: 'SIE', exchange: 'XETRA', isin: 'DE0007236101', lei: 'W38RGI023J3WT1HWRP32', sec_cik: null, public_company: true,361 wikipedia_url: WP('Siemens'), wikidata_url: WD('Q81230'), official_website: 'https://www.siemens.com', phone: '+49 89 636-00',362 products: ['SIMATIC', 'Siemens Xcelerator', 'TIA Portal', 'Desigo', 'Velaro', 'Mobility Vectron'], industry_labels: ['industrial automation', 'electrical engineering', 'rail transport'],363 socials: { linkedin: 'https://www.linkedin.com/company/siemens', x: 'https://x.com/Siemens', youtube: 'https://www.youtube.com/@Siemens', instagram: 'https://www.instagram.com/siemens' },364 },365 'samsung-electronics': {366 description: 'Samsung Electronics Co., Ltd. is a South Korean multinational electronics company headquartered in Suwon, and the flagship affiliate of the Samsung Group, producing memory chips, displays, smartphones and home appliances.',367 description_source: 'wikipedia', description_url: WP('Samsung_Electronics'), description_license: 'CC BY-SA 4.0',368 logo: true, icon: true, founded_year: 1969, legal_form: 'Chusik hoesa (public)', employees: 267860, employees_year: 2023,369 revenue: money(300870900000000, 'KRW', 2024), net_income: money(34451000000000, 'KRW', 2024), total_assets: money(514531900000000, 'KRW', 2024),370 hq: { city: 'Suwon', region: 'Gyeonggi', country: 'KR', address: '129 Samsung-ro, Yeongtong-gu', lat: 37.2599, lon: 127.0303 },371 ticker: '005930', exchange: 'KRX', isin: 'KR7005930003', lei: '988400E5HRVX81AYLM04', sec_cik: null, public_company: true,372 wikipedia_url: WP('Samsung_Electronics'), wikidata_url: WD('Q20718'), official_website: 'https://www.samsung.com', phone: null,373 products: ['Galaxy S', 'Galaxy Z', 'Galaxy Tab', 'Neo QLED', 'Bespoke', 'Exynos', 'HBM3E'], industry_labels: ['consumer electronics', 'semiconductor industry', 'display technology'],374 socials: { linkedin: 'https://www.linkedin.com/company/samsung-electronics', x: 'https://x.com/Samsung', youtube: 'https://www.youtube.com/@Samsung', instagram: 'https://www.instagram.com/samsung', facebook: 'https://www.facebook.com/SamsungGlobal', tiktok: 'https://www.tiktok.com/@samsung' },375 },376 // ---- partial profiles377 stripe: {378 description: 'Stripe, Inc. is an Irish-American multinational financial services and software-as-a-service company dual-headquartered in South San Francisco, California, and Dublin, Ireland. It offers payment-processing software and APIs for e-commerce websites and mobile applications.',379 description_source: 'wikipedia', description_url: WP('Stripe,_Inc.'), description_license: 'CC BY-SA 4.0',380 logo: true, icon: true, founded_year: 2010, legal_form: 'Private company', employees: 8550, employees_year: 2024,381 hq: { city: 'South San Francisco', region: 'California', country: 'US', address: '354 Oyster Point Blvd', lat: 37.6547, lon: -122.3894 },382 public_company: false, wikipedia_url: WP('Stripe,_Inc.'), wikidata_url: WD('Q10318979'), official_website: 'https://stripe.com',383 products: ['Stripe Payments', 'Stripe Connect', 'Stripe Billing', 'Stripe Terminal', 'Stripe Radar', 'Stripe Atlas', 'Stripe Issuing', 'Stripe Treasury'], industry_labels: ['payment service provider', 'financial technology'],384 socials: { linkedin: 'https://www.linkedin.com/company/stripe', x: 'https://x.com/stripe', youtube: 'https://www.youtube.com/@StripeDevelopers', github: 'https://github.com/stripe', crunchbase: 'https://www.crunchbase.com/organization/stripe' },385 },386 shopify: {387 description: 'Shopify Inc. is a Canadian multinational e-commerce company headquartered in Ottawa, Ontario, that provides a proprietary platform for online stores and retail point-of-sale systems.',388 description_source: 'wikipedia', description_url: WP('Shopify'), description_license: 'CC BY-SA 4.0',389 logo: true, icon: true, founded_year: 2006, legal_form: 'Public company (Canada Business Corporations Act)', employees: 8100, employees_year: 2024,390 revenue: money(8880000000, 'USD', 2024), net_income: money(2020000000, 'USD', 2024),391 hq: { city: 'Ottawa', region: 'Ontario', country: 'CA', address: '151 O’Connor Street', lat: 45.4215, lon: -75.6972 },392 ticker: 'SHOP', exchange: 'TSX · NASDAQ', isin: 'CA82509L1076', lei: '549300HPKKP5AVXRM893', sec_cik: '0001594805', public_company: true,393 wikipedia_url: WP('Shopify'), wikidata_url: WD('Q3963870'), official_website: 'https://www.shopify.com',394 products: ['Shopify', 'Shopify Plus', 'Shopify POS', 'Shopify Payments', 'Shop Pay', 'Shop app', 'Shopify Magic'], industry_labels: ['e-commerce', 'software as a service'],395 socials: { linkedin: 'https://www.linkedin.com/company/shopify', x: 'https://x.com/Shopify', youtube: 'https://www.youtube.com/@Shopify', github: 'https://github.com/Shopify' },396 financial_source: 'sec_edgar',397 },398 cohere: {399 description: 'Cohere is a Toronto-based AI company that builds large language models and retrieval systems for enterprises, offered through an API and deployed in private clouds. Its public pages emphasise data privacy and multilingual models.',400 description_source: 'llm', description_url: null, description_license: null,401 logo: false, icon: true, founded_year: 2019, legal_form: 'Private company', employees: null, employees_year: null,402 hq: { city: 'Toronto', region: 'Ontario', country: 'CA', address: null, lat: 43.6532, lon: -79.3832 },403 public_company: false, wikipedia_url: WP('Cohere'), wikidata_url: WD('Q108024780'), official_website: 'https://cohere.com',404 products: ['Command', 'Embed', 'Rerank', 'Aya', 'North'], industry_labels: ['artificial intelligence'],405 socials: { linkedin: 'https://www.linkedin.com/company/cohere-ai', x: 'https://x.com/cohere', github: 'https://github.com/cohere-ai', youtube: 'https://www.youtube.com/@cohere-ai' },406 },407 anthropic: {408 description: 'American artificial intelligence company founded in 2021, developer of the Claude family of large language models.',409 description_source: 'wikidata', description_url: WD('Q109832790'), description_license: 'CC0',410 logo: false, icon: true, founded_year: 2021, legal_form: 'Public-benefit corporation', employees: null, employees_year: null,411 hq: { city: 'San Francisco', region: 'California', country: 'US', address: null, lat: 37.7749, lon: -122.4194 },412 public_company: false, wikipedia_url: WP('Anthropic'), wikidata_url: WD('Q109832790'), official_website: 'https://www.anthropic.com',413 products: ['Claude'], industry_labels: ['artificial intelligence'],414 socials: { linkedin: 'https://www.linkedin.com/company/anthropicresearch', x: 'https://x.com/AnthropicAI', youtube: 'https://www.youtube.com/@anthropic-ai', github: 'https://github.com/anthropics' },415 },416 'mistral-ai': {417 description: 'Mistral AI is a French artificial intelligence company headquartered in Paris that develops open-weight and commercial large language models.',418 description_source: 'wikipedia', description_url: WP('Mistral_AI'), description_license: 'CC BY-SA 4.0',419 logo: true, icon: false, founded_year: 2023, legal_form: 'Société par actions simplifiée', employees: null, employees_year: null,420 hq: { city: 'Paris', region: 'Île-de-France', country: 'FR', address: null, lat: 48.8566, lon: 2.3522 },421 public_company: false, wikipedia_url: WP('Mistral_AI'), wikidata_url: WD('Q119711183'), official_website: 'https://mistral.ai',422 products: ['Mistral Large', 'Mistral Small', 'Codestral', 'Le Chat', 'Pixtral'], industry_labels: ['artificial intelligence'],423 socials: { linkedin: 'https://www.linkedin.com/company/mistralai', x: 'https://x.com/MistralAI', github: 'https://github.com/mistralai' },424 },425 nubank: {426 description: 'Nu Holdings Ltd. is a Brazilian neobank headquartered in São Paulo, operating in Brazil, Mexico and Colombia; it is one of the largest digital banking platforms in the world by customer count.',427 description_source: 'wikipedia', description_url: WP('Nubank'), description_license: 'CC BY-SA 4.0',428 logo: true, icon: true, founded_year: 2013, legal_form: 'Cayman Islands holding company', employees: null, employees_year: null,429 revenue: money(11500000000, 'USD', 2024), net_income: money(1970000000, 'USD', 2024),430 hq: { city: 'São Paulo', region: 'São Paulo', country: 'BR', address: null, lat: -23.5505, lon: -46.6333 },431 ticker: 'NU', exchange: 'NYSE', isin: 'KYG6683N1034', sec_cik: '0001691493', public_company: true,432 wikipedia_url: WP('Nubank'), wikidata_url: WD('Q28129905'), official_website: 'https://nubank.com.br',433 products: ['Nu conta', 'Nu cartão', 'NuInvest', 'Nu Pagamentos'], industry_labels: ['neobank', 'financial technology'],434 socials: { linkedin: 'https://www.linkedin.com/company/nubank', x: 'https://x.com/nubank', instagram: 'https://www.instagram.com/nubank', youtube: 'https://www.youtube.com/@nubank' },435 financial_source: 'sec_edgar',436 },437 revolut: {438 description: 'Revolut Group Holdings Ltd is a British fintech company headquartered in London offering banking services, including multi-currency accounts, cards, trading and crypto, to retail and business customers.',439 description_source: 'wikipedia', description_url: WP('Revolut'), description_license: 'CC BY-SA 4.0',440 logo: true, icon: true, founded_year: 2015, legal_form: 'Private limited company', employees: 10000, employees_year: 2024,441 hq: { city: 'London', region: 'England', country: 'GB', address: null, lat: 51.5074, lon: -0.1278 },442 public_company: false, wikipedia_url: WP('Revolut'), wikidata_url: WD('Q21179207'), official_website: 'https://www.revolut.com',443 products: ['Revolut', 'Revolut Business', 'Revolut X', 'Revolut <18'], industry_labels: ['neobank', 'financial technology'],444 socials: { linkedin: 'https://www.linkedin.com/company/revolut', x: 'https://x.com/RevolutApp', instagram: 'https://www.instagram.com/revolutapp', youtube: 'https://www.youtube.com/@Revolut' },445 },446 klarna: {447 description: 'Klarna Group plc is a Swedish fintech company that provides online financial services such as payments for online storefronts, direct payments and post-purchase payments.',448 description_source: 'wikipedia', description_url: WP('Klarna'), description_license: 'CC BY-SA 4.0',449 logo: true, icon: false, founded_year: 2005, legal_form: 'Public limited company', employees: 3422, employees_year: 2024,450 revenue: money(2810000000, 'USD', 2024),451 hq: { city: 'Stockholm', region: null, country: 'SE', address: 'Sveavägen 46', lat: 59.3376, lon: 18.0603 },452 ticker: 'KLAR', exchange: 'NYSE', public_company: true, wikipedia_url: WP('Klarna'), wikidata_url: WD('Q1747210'), official_website: 'https://www.klarna.com',453 products: ['Klarna', 'Pay in 4', 'Klarna Card'], industry_labels: ['financial technology', 'buy now, pay later'],454 socials: { linkedin: 'https://www.linkedin.com/company/klarna', x: 'https://x.com/Klarna', instagram: 'https://www.instagram.com/klarna' },455 },456};457458/** Build a full-shape profile from a sample; `null` fills everything the sample omits (the UI must hide those). */459function makeProfile(c, sample) {460 const base = {461 description: null, description_source: null, description_url: null, description_license: null,462 logo_url: null, icon_url: null, founded_year: null, legal_form: null, employees: null, employees_year: null, revenue: null, net_income: null, total_assets: null,463 hq: { city: null, region: null, country: null, address: null, lat: null, lon: null },464 ticker: null, exchange: null, isin: null, lei: null, sec_cik: null, public_company: false,465 wikipedia_url: null, wikidata_url: null, official_website: null, phone: null, products: [], industries: [], industry_labels: [], socials: {}, enriched_at: null, sources: [],466 };467 if (!sample) return base;468 const { logo, icon, extra_facts, financial_source, ...rest } = sample;469 const p = { ...base, ...rest, hq: { ...base.hq, ...(rest.hq ?? {}) } };470 if (logo) p.logo_url = `${MOCK_ASSET_BASE}/logo/${c.slug}.svg`;471 if (icon) p.icon_url = `${MOCK_ASSET_BASE}/icon/${c.slug}.svg`;472 p.enriched_at = RETRIEVED;473 const src = (field, source, url) => p.sources.push({ field, source, url: url ?? null, retrieved_at: RETRIEVED });474 if (p.description) src('description', p.description_source ?? 'wikidata', p.description_url);475 if (p.logo_url || p.icon_url) src('logo_url', 'wikidata', p.wikidata_url);476 if (p.founded_year) src('founded_year', 'wikidata', p.wikidata_url);477 if (p.legal_form) src('legal_form', 'wikidata', p.wikidata_url);478 if (p.hq.city || p.hq.country) src('hq', 'wikidata', p.wikidata_url);479 if (typeof p.employees === 'number') src('employees', financial_source === 'sec_edgar' ? 'sec_edgar' : 'wikidata', financial_source === 'sec_edgar' && p.sec_cik ? `https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=${p.sec_cik}` : p.wikidata_url);480 for (const k of ['revenue', 'net_income', 'total_assets']) if (p[k]) src(k, financial_source === 'sec_edgar' ? 'sec_edgar' : 'wikidata', financial_source === 'sec_edgar' && p.sec_cik ? `https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=${p.sec_cik}` : p.wikidata_url);481 if (p.ticker) src('ticker', 'wikidata', p.wikidata_url);482 if (p.isin) src('isin', 'wikidata', p.wikidata_url);483 if (p.lei) src('lei', 'gleif', `https://search.gleif.org/#/record/${p.lei}`);484 if (p.sec_cik) src('sec_cik', 'sec_edgar', `https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=${p.sec_cik}`);485 if (p.products.length) src('products', 'wikidata', p.wikidata_url);486 if (p.industry_labels.length) src('industry_labels', 'wikidata', p.wikidata_url);487 if (p.phone) src('phone', 'homepage', p.official_website);488 if (Object.keys(p.socials).length) src('socials', 'homepage', p.official_website);489 if (p.wikipedia_url) src('wikipedia_url', 'wikidata', p.wikidata_url);490 return p;491}492/** Minimal auto profile for companies without a hand-written sample: registry description, HQ, listing; no logo, no numbers. */493function autoProfile(c) {494 return makeProfile(c, { description: c.description, description_source: 'homepage', description_url: c.website, founded_year: c.founded_year, hq: { city: c.hq_city, country: c.country, lat: c._lat, lon: c._lon }, ticker: c.ticker, exchange: c.exchange, public_company: c.public_company, official_website: c.website, logo: false, icon: chance(0.6) });495}496/** Flattened, pre-formatted facts (`CompanyDetail.facts`) — the structured profile + registry extras. */497function factsOf(c) {498 const p = c.profile;499 if (!p) return [];500 const out = [];501 const bySrc = Object.fromEntries(p.sources.map((s) => [s.field, s]));502 const add = (key, label, value, field = key) => {503 const s = bySrc[field];504 if (value === null || value === undefined || value === '') return;505 out.push({ key, label, value: String(value), source: s?.source ?? 'wikidata', url: s?.url ?? p.wikidata_url, retrieved_at: s?.retrieved_at ?? RETRIEVED });506 };507 add('founded_year', 'Founded', p.founded_year);508 add('hq', 'Headquarters', [p.hq.city, p.hq.region, p.hq.country].filter(Boolean).join(', ') || null);509 add('employees', 'Employees', typeof p.employees === 'number' ? `${p.employees}${p.employees_year ? ` (${p.employees_year})` : ''}` : null);510 for (const [k, label] of [['revenue', 'Revenue'], ['net_income', 'Net income'], ['total_assets', 'Total assets']]) if (p[k]) add(k, label, `${p[k].value} ${p[k].currency} (${p[k].year})`);511 add('legal_form', 'Legal form', p.legal_form);512 add('isin', 'ISIN', p.isin);513 add('lei', 'LEI', p.lei);514 add('sec_cik', 'SEC CIK', p.sec_cik);515 for (const f of PROFILES[c.slug]?.extra_facts ?? []) out.push({ key: f.key, label: f.label, value: f.value, source: f.source, url: f.source === 'wikidata' ? p.wikidata_url : null, retrieved_at: RETRIEVED });516 return out;517}518519// relationships in the v1.1 shape: [kind, counterpart (atlas slug or plain name), valid_from, valid_to, confidence, source, property]520const WDP = (prop) => ({ source: 'wikidata', property: prop });521const RELATIONS = {522 nvidia: [['PARENT_OF', 'Mellanox Technologies', '2020-04-27', null, 0.97, WDP('P355')], ['PARENT_OF', 'Cumulus Networks', '2020-05-04', null, 0.9, WDP('P355')], ['PARENT_OF', 'NVIDIA GmbH', null, null, 0.85, WDP('P355')], ['ACQUIRED', 'Run:ai', '2024-12-30', null, 0.9, WDP('P1830')], ['ACQUIRED', 'OctoAI', '2024-09-30', null, 0.8, WDP('P1830')], ['PARTNER', 'tsmc', null, null, 0.75, { source: 'registry' }], ['COMPETITOR', 'arm', null, null, 0.6, { source: 'registry' }]],523 apple: [['PARENT_OF', 'Beats Electronics', '2014-08-01', null, 0.98, WDP('P355')], ['PARENT_OF', 'Shazam', '2018-09-24', null, 0.95, WDP('P355')], ['PARENT_OF', 'Claris International', '1998-01-01', null, 0.9, WDP('P355')], ['PARENT_OF', 'Apple Sales International', null, null, 0.85, WDP('P355')], ['PARENT_OF', 'Braeburn Capital', '2005-01-01', null, 0.85, WDP('P355')], ['ACQUIRED', 'NeXT', '1997-02-07', null, 0.99, WDP('P1830')], ['PARTNER', 'tsmc', null, null, 0.7, { source: 'registry' }], ['COMPETITOR', 'samsung-electronics', null, null, 0.7, { source: 'registry' }]],524 sony: [['PARENT_OF', 'Sony Interactive Entertainment', '2016-04-01', null, 0.98, WDP('P355')], ['PARENT_OF', 'Sony Pictures Entertainment', '1991-08-07', null, 0.98, WDP('P355')], ['PARENT_OF', 'Sony Music Entertainment', '1991-01-01', null, 0.98, WDP('P355')], ['PARENT_OF', 'Sony Semiconductor Solutions', '2016-04-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'Sony Music Publishing', '2012-06-29', null, 0.9, WDP('P355')], ['PARENT_OF', 'Crunchyroll', '2021-08-09', null, 0.95, WDP('P355')], ['PARENT_OF', 'Bungie', '2022-07-15', null, 0.95, WDP('P355')], ['PARENT_OF', 'Insomniac Games', '2019-08-19', null, 0.95, WDP('P355')], ['PARENT_OF', 'Aniplex', '1995-09-01', null, 0.9, WDP('P355')], ['PARENT_OF', 'Sony Honda Mobility', '2022-09-28', null, 0.8, WDP('P355')], ['PARENT_OF', 'Sony Financial Group', '2004-04-01', '2025-10-01', 0.85, WDP('P355')], ['OWNER_OF', 'Olympus Corporation', '2012-09-28', '2019-08-30', 0.8, WDP('P1830')], ['COMPETITOR', 'samsung-electronics', null, null, 0.6, { source: 'registry' }]],525 toyota: [['PARENT_OF', 'Daihatsu', '2016-08-01', null, 0.98, WDP('P355')], ['PARENT_OF', 'Hino Motors', '2001-01-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'Toyota Financial Services', '2000-07-01', null, 0.9, WDP('P355')], ['PARENT_OF', 'Woven by Toyota', '2021-01-01', null, 0.85, WDP('P355')], ['PARENT_OF', 'Toyota Motor Europe', null, null, 0.85, WDP('P355')], ['OWNER_OF', 'Subaru Corporation', '2019-12-27', null, 0.8, WDP('P1830')], ['OWNER_OF', 'Mazda Motor Corporation', '2017-08-04', null, 0.7, WDP('P1830')]],526 roche: [['PARENT_OF', 'Genentech', '2009-03-26', null, 0.99, WDP('P355')], ['PARENT_OF', 'Chugai Pharmaceutical', '2002-10-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'Foundation Medicine', '2018-07-31', null, 0.95, WDP('P355')], ['PARENT_OF', 'Spark Therapeutics', '2019-12-17', null, 0.95, WDP('P355')], ['PARENT_OF', 'Flatiron Health', '2018-04-06', null, 0.9, WDP('P355')], ['PARENT_OF', 'Ventana Medical Systems', '2008-02-01', null, 0.9, WDP('P355')]],527 sap: [['PARENT_OF', 'Qualtrics', '2019-01-23', '2023-06-28', 0.95, WDP('P355')], ['PARENT_OF', 'SAP Concur', '2014-12-04', null, 0.95, WDP('P355')], ['PARENT_OF', 'SAP Ariba', '2012-10-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'SAP SuccessFactors', '2012-02-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'SAP Fieldglass', '2014-05-01', null, 0.9, WDP('P355')], ['PARENT_OF', 'Signavio', '2021-03-05', null, 0.9, WDP('P355')], ['PARENT_OF', 'LeanIX', '2023-11-08', null, 0.85, WDP('P355')], ['PARENT_OF', 'WalkMe', '2024-09-12', null, 0.85, WDP('P355')], ['PARTNER', 'siemens', null, null, 0.5, { source: 'registry' }]],528 spotify: [['PARENT_OF', 'Anchor', '2019-02-06', null, 0.9, WDP('P355')], ['PARENT_OF', 'Megaphone', '2020-12-15', null, 0.9, WDP('P355')], ['PARENT_OF', 'Podsights', '2022-02-16', null, 0.8, WDP('P355')], ['PARENT_OF', 'Findaway', '2022-06-01', null, 0.8, WDP('P355')]],529 adyen: [['PARENT_OF', 'Adyen Bank N.V.', null, null, 0.8, WDP('P355')], ['COMPETITOR', 'stripe', null, null, 0.7, { source: 'registry' }]],530 arm: [['SUBSIDIARY_OF', 'SoftBank Group', '2016-09-05', null, 0.98, WDP('P749')], ['OWNED_BY', 'SoftBank Group', '2016-09-05', null, 0.95, WDP('P127')], ['PARENT_OF', 'Arm China', '2018-04-01', null, 0.7, WDP('P355')], ['COMPETITOR', 'nvidia', null, null, 0.5, { source: 'registry' }]],531 tsmc: [['PARENT_OF', 'TSMC Arizona', '2020-05-15', null, 0.95, WDP('P355')], ['PARENT_OF', 'WaferTech', '1996-06-01', null, 0.9, WDP('P355')], ['OWNER_OF', 'ESMC (European Semiconductor Manufacturing Company)', '2023-08-08', null, 0.85, WDP('P1830')], ['OWNER_OF', 'Vanguard International Semiconductor', '1994-12-01', null, 0.8, WDP('P1830')], ['PARTNER', 'nvidia', null, null, 0.7, { source: 'registry' }], ['PARTNER', 'apple', null, null, 0.7, { source: 'registry' }]],532 siemens: [['PARENT_OF', 'Siemens Healthineers', '2018-03-16', null, 0.95, WDP('P355')], ['PARENT_OF', 'Siemens Mobility', '2018-08-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'Siemens Financial Services', null, null, 0.85, WDP('P355')], ['PARENT_OF', 'Siemens Energy', '2020-04-01', '2020-09-28', 0.9, WDP('P355')], ['OWNER_OF', 'Siemens Energy', '2020-09-28', null, 0.8, WDP('P1830')], ['ACQUIRED', 'Altair Engineering', '2025-03-26', null, 0.9, WDP('P1830')], ['ACQUIRED', 'Brightly Software', '2022-06-27', null, 0.85, WDP('P1830')]],533 'samsung-electronics': [['PARENT_OF', 'Harman International', '2017-03-10', null, 0.98, WDP('P355')], ['PARENT_OF', 'Samsung Display', '2012-04-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'Samsung Electronics America', null, null, 0.9, WDP('P355')], ['PARENT_OF', 'Samsung Medison', '2011-04-01', null, 0.85, WDP('P355')], ['OWNED_BY', 'Samsung Life Insurance', null, null, 0.7, WDP('P127')], ['COMPETITOR', 'apple', null, null, 0.7, { source: 'registry' }]],534 stripe: [['PARENT_OF', 'Stripe Payments Europe, Ltd.', null, null, 0.85, WDP('P355')], ['PARENT_OF', 'Stripe Payments UK, Ltd.', null, null, 0.8, WDP('P355')], ['ACQUIRED', 'Paystack', '2020-10-15', null, 0.95, WDP('P1830')], ['ACQUIRED', 'Lemon Squeezy', '2024-07-26', null, 0.85, WDP('P1830')], ['ACQUIRED', 'Bridge', '2025-02-04', null, 0.85, WDP('P1830')], ['COMPETITOR', 'adyen', null, null, 0.7, { source: 'registry' }], ['COMPETITOR', 'block', null, null, 0.6, { source: 'registry' }]],535 shopify: [['ACQUIRED', 'Deliverr', '2022-07-08', '2023-05-04', 0.9, WDP('P1830')], ['ACQUIRED', 'Vantage Discovery', '2025-03-10', null, 0.7, WDP('P1830')], ['PARTNER', 'stripe', null, null, 0.6, { source: 'registry' }]],536 cohere: [['OWNED_BY', 'Nvidia (minority investor)', '2023-06-08', null, 0.5, { source: 'registry' }]],537 anthropic: [['OWNED_BY', 'Amazon (minority investor)', '2023-09-25', null, 0.6, { source: 'registry' }], ['OWNED_BY', 'Alphabet (minority investor)', '2023-02-03', null, 0.6, { source: 'registry' }]],538 nubank: [['PARENT_OF', 'Nu México', '2019-05-01', null, 0.9, WDP('P355')], ['PARENT_OF', 'Nu Colombia', '2020-09-01', null, 0.9, WDP('P355')], ['ACQUIRED', 'Easynvest', '2020-09-01', null, 0.85, WDP('P1830')]],539};540function relationsFor(c) {541 const rows = RELATIONS[c.slug];542 if (rows) return rows.map(([kind, who, from, to, confidence, provenance]) => {543 const other = companiesBySlug.get(who);544 return { kind, company: other ? { slug: other.slug, display_name: other.display_name, logo_url: other.profile?.logo_url ?? other.profile?.icon_url ?? null } : null, to_name: other ? null : who, valid_from: from, valid_to: to, confidence, provenance };545 });546 const peer = companies.find((o) => o !== c && o.industries[0] === c.industries[0]);547 return [{ kind: 'COMPETITOR', company: peer ? { slug: peer.slug, display_name: peer.display_name, logo_url: peer.profile?.logo_url ?? peer.profile?.icon_url ?? null } : null, to_name: peer ? null : 'Unnamed peer', valid_from: null, valid_to: null, confidence: 0.6, provenance: { source: 'registry' } }, { kind: 'PARTNER', company: null, to_name: pick(PARTNERS), valid_from: iso(NOW - 200 * DAY), valid_to: null, confidence: 0.8, provenance: { source: 'registry' } }];548}549550// Wikidata-sourced executives (`source: 'wikidata'`), some overlapping the page-observed rows to exercise the merge.551// [name, title, status, valid_from]552const WD_PEOPLE = {553 nvidia: [['Jensen Huang', 'Chief Executive Officer, President and co-founder', 'listed', '1993-04-05'], ['Colette Kress', 'Chief Financial Officer', 'listed', '2013-09-01'], ['Chris Malachowsky', 'Co-founder', 'listed', '1993-04-05']],554 apple: [['Tim Cook', 'Chief Executive Officer', 'listed', '2011-08-24'], ['Arthur D. Levinson', 'Chairman of the board', 'listed', '2011-11-15'], ['Kevan Parekh', 'Chief Financial Officer', 'listed', '2025-01-01'], ['Luca Maestri', 'Chief Financial Officer', 'no_longer_listed', '2014-05-29'], ['Steve Jobs', 'Co-founder', 'no_longer_listed', '1976-04-01']],555 sony: [['Hiroki Totoki', 'President and Chief Executive Officer', 'listed', '2025-04-01'], ['Kenichiro Yoshida', 'Chairman', 'listed', '2025-04-01'], ['Lin Tao', 'Chief Financial Officer', 'listed', '2025-04-01'], ['Kazuo Hirai', 'Chief Executive Officer', 'no_longer_listed', '2012-04-01']],556 toyota: [['Koji Sato', 'President and Chief Executive Officer', 'listed', '2023-04-01'], ['Akio Toyoda', 'Chairman of the board', 'listed', '2023-04-01']],557 roche: [['Thomas Schinecker', 'Chief Executive Officer', 'listed', '2023-03-15'], ['Severin Schwan', 'Chairman of the board', 'listed', '2023-03-15']],558 sap: [['Christian Klein', 'Chief Executive Officer', 'listed', '2019-10-11'], ['Dominik Asam', 'Chief Financial Officer', 'listed', '2023-03-07'], ['Hasso Plattner', 'Co-founder and Chairman of the supervisory board', 'no_longer_listed', '2003-05-01']],559 spotify: [['Daniel Ek', 'Chief Executive Officer and co-founder', 'listed', '2006-04-23'], ['Martin Lorentzon', 'Co-founder and Chairman', 'listed', '2006-04-23']],560 adyen: [['Pieter van der Does', 'Co-founder and co-CEO', 'listed', '2006-01-01'], ['Ingo Uytdehaage', 'Co-CEO', 'listed', '2023-05-01']],561 arm: [['Rene Haas', 'Chief Executive Officer', 'listed', '2022-02-08'], ['Masayoshi Son', 'Chairman of the board', 'listed', '2016-09-05']],562 tsmc: [['C. C. Wei', 'Chairman and Chief Executive Officer', 'listed', '2024-06-04'], ['Morris Chang', 'Founder', 'no_longer_listed', '1987-02-21']],563 siemens: [['Roland Busch', 'President and Chief Executive Officer', 'listed', '2021-02-03'], ['Ralf P. Thomas', 'Chief Financial Officer', 'listed', '2013-09-18']],564 'samsung-electronics': [['Jun Young-hyun', 'Vice Chairman and co-CEO', 'listed', '2024-11-27'], ['Roh Tae-moon', 'Acting head, Device eXperience division', 'listed', '2025-03-01']],565 stripe: [['Patrick Collison', 'Chief Executive Officer and co-founder', 'listed', '2010-01-01'], ['John Collison', 'President and co-founder', 'listed', '2010-01-01']],566 shopify: [['Tobias Lütke', 'Chief Executive Officer and founder', 'listed', '2008-01-01'], ['Harley Finkelstein', 'President', 'listed', '2020-09-01']],567 cohere: [['Aidan Gomez', 'Chief Executive Officer and co-founder', 'listed', '2019-01-01'], ['Nick Frosst', 'Co-founder', 'listed', '2019-01-01'], ['Ivan Zhang', 'Co-founder', 'listed', '2019-01-01']],568 anthropic: [['Dario Amodei', 'Chief Executive Officer and co-founder', 'listed', '2021-01-01'], ['Daniela Amodei', 'President and co-founder', 'listed', '2021-01-01']],569 'mistral-ai': [['Arthur Mensch', 'Chief Executive Officer and co-founder', 'listed', '2023-04-28'], ['Guillaume Lample', 'Chief Scientist and co-founder', 'listed', '2023-04-28'], ['Timothée Lacroix', 'Chief Technology Officer and co-founder', 'listed', '2023-04-28']],570 nubank: [['David Vélez', 'Chief Executive Officer and founder', 'listed', '2013-05-06'], ['Cristina Junqueira', 'Co-founder', 'listed', '2013-05-06']],571};572function wikidataPeople(c) {573 const rows = WD_PEOPLE[c.slug] ?? [];574 const url = c.profile?.wikidata_url ?? `https://www.wikidata.org/wiki/Special:Search?search=${encodeURIComponent(c.display_name)}`;575 return rows.map(([name, title, status, from]) => ({ id: id('per'), name, title, role_category: /chair/i.test(title) ? 'board' : 'c_suite', is_executive: true, first_seen_at: iso(new Date(from).getTime()), last_seen_at: RETRIEVED, removed_at: status === 'listed' ? null : iso(NOW - ri(200, 2000) * DAY), status, source_url: url, source: 'wikidata' }));576}577578/** Self-hosted sample logos (plate + monogram) so QA never depends on the network; `icon` is the round variant. */579function logoSvg(slug, round) {580 const name = companiesBySlug.get(slug)?.display_name ?? slug;581 const words = name.split(/\s+/).filter(Boolean);582 const mono = (words.length > 1 ? words[0][0] + words[1][0] : name.slice(0, 1)).toUpperCase();583 let h = 0;584 for (const ch of slug) h = (h * 31 + ch.charCodeAt(0)) >>> 0;585 const hue = h % 360;586 return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="${round ? 32 : 10}" fill="hsl(${hue} 55% 42%)"/><text x="32" y="40" font-family="ui-monospace, Menlo, monospace" font-size="${mono.length > 1 ? 24 : 30}" font-weight="700" fill="#fff" text-anchor="middle">${mono}</text></svg>`;587}588589// ------------------------------------------------------------------------------------------------ build dataset590const companies = [];591const companiesBySlug = new Map();592const sensors = new Map();593const snapshots = new Map();594const changes = new Map();595const events = [];596const eventsById = new Map();597const perCompany = new Map();598599function series(days, base, vol, drift = 0) {600 const out = [];601 let v = base;602 for (let i = days - 1; i >= 0; i--) {603 v = clamp(v + (rnd() - 0.5) * vol + drift, 0, 100);604 out.push({ day: day(NOW - i * DAY), value: r1(v), confidence: r1(0.6 + rnd() * 0.35) });605 }606 return out;607}608609const blockKinds = ['heading', 'paragraph', 'list', 'card', 'table', 'nav'];610function makeBlocks(surface, n) {611 const blocks = [];612 for (let i = 0; i < n; i++) {613 const kind = pick(blockKinds);614 blocks.push({ key: `${surface}:${kind}:${i}`, kind, path: `main > section:nth-of-type(${1 + Math.floor(i / 3)}) > ${kind === 'heading' ? 'h2' : kind === 'list' ? 'ul' : 'div'}:nth-child(${1 + (i % 3)})`, text: sampleText(surface, i) });615 }616 return blocks;617}618function sampleText(surface, i) {619 const map = {620 pricing: ['Starter — $29 per month. Up to 3 seats, community support.', 'Growth — $99 per month. Unlimited seats, SSO, priority support.', 'Enterprise — Contact sales. Custom SLAs, dedicated support, audit logs.', 'All plans include 14-day trial. Prices in USD, billed annually.'],621 careers: ['Senior Software Engineer — Toronto, Canada · Engineering', 'Machine Learning Engineer — Remote (EU) · AI Platform', 'Account Executive — London, UK · Sales', 'We are hiring across 12 offices. See all 148 open roles.'],622 leadership: ['Ava Martin — Chief Executive Officer', 'Noah Chen — Chief Technology Officer', 'Léa Dubois — Chief Financial Officer', 'Our leadership team brings decades of experience across payments and infrastructure.'],623 locations: ['Headquarters — 354 Oyster Point Blvd, South San Francisco', 'Dublin — Grand Canal Dock', 'Singapore — Raffles Place', 'Bengaluru — Indiranagar'],624 legal: ['7. Limitation of liability. To the maximum extent permitted by law…', '8. Governing law. These terms are governed by the laws of Ireland.', '12. Changes to these terms. We may update these terms; material changes will be notified 30 days in advance.'],625 products: ['Terminal — In-person payments hardware and SDKs.', 'Radar — Fraud prevention powered by network data.', 'Atlas — Company incorporation in days.', 'Billing — Subscriptions, invoices and revenue recovery.'],626 };627 const arr = map[surface] ?? ['Welcome to our company. We build tools that help teams move faster.', 'Trusted by thousands of businesses worldwide.', 'Read our latest news and product updates.', 'Contact us to learn more about enterprise plans.'];628 return arr[i % arr.length];629}630631function buildCompany(row, idx) {632 const [name, domain, country, city, lat, lon, inds, pub, ticker, exchange, founded, band, importance, desc] = row;633 const slug = slugify(name);634 const cid = id('co');635 const tier = importance >= 90 ? 1 : importance >= 75 ? 2 : importance >= 55 ? 3 : 4;636 const created = NOW - ri(120, 420) * DAY;637 // sensors638 const nSensors = tier === 1 ? ri(28, 63) : tier === 2 ? ri(16, 32) : tier === 3 ? ri(8, 18) : ri(4, 10);639 const compSensors = [];640 const surfacesUsed = [];641 for (let i = 0; i < nSensors; i++) {642 const surface = i < SURFACES.length ? SURFACES[i] : pick(SURFACES);643 surfacesUsed.push(surface);644 const sid = id('sen');645 const stier = surface === 'homepage' || surface === 'newsroom' || surface === 'careers' ? pick(['A', 'B']) : surface === 'legal' || surface === 'sitemap' ? pick(['D', 'E']) : pick(['B', 'C', 'C', 'D']);646 const statusRoll = rnd();647 const status = statusRoll < 0.86 ? 'active' : statusRoll < 0.93 ? 'failing' : statusRoll < 0.97 ? 'paused' : 'retired';648 const obs = ri(40, 2400);649 const snaps = ri(3, 12);650 const chg = Math.floor(snaps * (0.4 + rnd()));651 const path = SURFACE_PATH[surface] ?? `/${surface}`;652 const url = `https://${domain}${path}${i >= SURFACES.length ? `?p=${i}` : ''}`;653 const lastRun = NOW - ri(1, 300) * 60_000;654 const failing = status === 'failing';655 const sensor = {656 id: sid,657 company_id: cid,658 surface,659 connector_id: CONNECTOR_FOR[surface] ?? 'generic_html',660 url,661 canonical_url: url,662 domain,663 status,664 tier: stier,665 quality_score: r1(45 + rnd() * 55),666 discovery_confidence: r1(0.55 + rnd() * 0.45),667 discovery_method: pick(['navigation', 'sitemap', 'url_pattern', 'json_ld', 'manual']),668 current_interval_s: TIER_INTERVAL[stier] * (failing ? 4 : 1),669 next_run_at: iso(lastRun + TIER_INTERVAL[stier] * 1000),670 last_run_at: iso(lastRun),671 last_success_at: failing ? iso(NOW - ri(2, 6) * DAY) : iso(lastRun),672 last_change_at: iso(NOW - ri(1, 30) * DAY),673 last_status: failing ? pick([403, 429, 503, 0]) : 200,674 last_failure_class: failing ? pick(FAILURE_CLASSES) : null,675 consecutive_failures: failing ? ri(3, 40) : 0,676 observation_count: obs,677 snapshot_count: snaps,678 change_count: chg,679 meaningful_change_count: Math.floor(chg * 0.35),680 event_count: 0,681 created_at: iso(created + ri(0, 10) * DAY),682 _versions: [],683 _changes: [],684 };685 // snapshots (versions) and changes686 let prev = null;687 let t = created + ri(1, 20) * DAY;688 for (let v = 1; v <= snaps; v++) {689 const snapId = id('snap');690 t += ri(2, 20) * DAY;691 if (t > NOW) t = NOW - ri(0, 3) * 3600_000;692 const blocks = makeBlocks(surface, ri(8, 20));693 const snap = {694 id: snapId,695 sensor_id: sid,696 version_no: v,697 fetched_at: iso(t),698 title: `${name} — ${surface.replace('_', ' ')}`,699 language: pick(['en', 'en', 'en', 'fr', 'de', 'ja']),700 text_length: blocks.reduce((a, b) => a + b.text.length, 0) * ri(8, 20),701 block_count: blocks.length,702 extracted_summary: surface === 'careers' ? { jobs: ri(5, 200) } : surface === 'pricing' ? { plans: ri(2, 5) } : surface === 'leadership' ? { people: ri(4, 14) } : surface === 'locations' ? { locations: ri(1, 12) } : { blocks: blocks.length },703 content_hash: `sha256:${Array.from({ length: 16 }, () => B32[Math.floor(rnd() * 32)].toLowerCase()).join('')}`,704 previous_snapshot_id: prev ? prev.id : null,705 _blocks: blocks,706 _text: blocks.map((b) => b.text).join('\n\n'),707 };708 snapshots.set(snapId, snap);709 sensor._versions.push(snap);710 if (prev) {711 const chgId = id('chg');712 const added = blocks.slice(0, ri(0, 3)).map((b) => ({ key: b.key, kind: b.kind, path: b.path, before: null, after: b.text, weight: r1(0.3 + rnd() * 0.7), similarity: null }));713 const removed = prev._blocks.slice(-ri(0, 2)).map((b) => ({ key: b.key, kind: b.kind, path: b.path, before: b.text, after: null, weight: r1(0.3 + rnd() * 0.7), similarity: null }));714 const modified = prev._blocks.slice(1, 1 + ri(0, 3)).map((b, k) => ({ key: b.key, kind: b.kind, path: b.path, before: b.text, after: (blocks[k + 1] ?? blocks[0]).text, weight: r1(0.3 + rnd() * 0.7), similarity: r1(0.4 + rnd() * 0.5) }));715 const sig = clamp(r1(0.1 + (added.length + removed.length + modified.length) * 0.12 + rnd() * 0.25), 0.02, 0.98);716 const reasons = [];717 if (added.length) reasons.push(`${added.length} new block${added.length > 1 ? 's' : ''} (${added.map((a) => a.kind).join(', ')})`);718 if (removed.length) reasons.push(`${removed.length} block${removed.length > 1 ? 's' : ''} no longer present`);719 if (modified.length) reasons.push(`${modified.length} block${modified.length > 1 ? 's' : ''} modified with low similarity`);720 if (surface === 'pricing') reasons.push('structured pricing fields changed');721 if (surface === 'careers') reasons.push('job count changed');722 if (!reasons.length) reasons.push('text delta below noise threshold');723 const change = {724 id: chgId,725 sensor_id: sid,726 surface,727 company_id: cid,728 detected_at: snap.fetched_at,729 significance: sig,730 kind: sig < 0.2 ? 'noise' : sig < 0.4 ? 'minor' : sig < 0.65 ? 'meaningful' : sig < 0.85 ? 'major' : 'critical',731 blocks_added: added.length,732 blocks_removed: removed.length,733 blocks_modified: modified.length,734 text_delta_ratio: r1(sig * 0.6),735 similarity: r1(1 - sig * 0.7),736 snapshot_before: prev.id,737 snapshot_after: snapId,738 diff: { added, removed, modified, moved: [], counts: { added: added.length, removed: removed.length, modified: modified.length, unchanged: Math.max(0, blocks.length - added.length - modified.length) }, text_delta_ratio: r1(sig * 0.6), similarity: r1(1 - sig * 0.7), reasons },739 structured_delta: surface === 'careers' ? { jobs_added: added.length + ri(0, 4), jobs_removed: removed.length } : surface === 'pricing' ? { plans_changed: modified.length } : {},740 _events: [],741 };742 changes.set(chgId, change);743 sensor._changes.push(change);744 }745 prev = snap;746 }747 sensors.set(sid, sensor);748 compSensors.push(sensor);749 }750751 // entities752 const jobs = [];753 const nJobs = tier === 1 ? ri(60, 240) : tier === 2 ? ri(20, 90) : ri(3, 30);754 for (let i = 0; i < nJobs; i++) {755 const title = pick(JOB_TITLES);756 const cityRow = chance(0.5) ? [city, country] : pick(CITIES);757 const first = NOW - ri(1, 120) * DAY;758 const removed = chance(0.22);759 const removedAt = removed ? first + ri(5, 60) * DAY : null;760 jobs.push({761 id: id('job'),762 title,763 department: pick(DEPARTMENTS),764 location_text: `${cityRow[0]}, ${cityRow[1]}`,765 city: cityRow[0],766 country: cityRow[1],767 remote: chance(0.35),768 employment_type: pick(['full_time', 'full_time', 'contract', 'internship']),769 seniority: pick(['junior', 'mid', 'senior', 'staff', 'lead']),770 url: `https://${domain}/careers/${slugify(title)}-${i}`,771 posted_at: iso(first - ri(0, 3) * DAY),772 first_seen_at: iso(first),773 last_seen_at: iso(removed ? Math.min(NOW, removedAt) : NOW - ri(0, 3) * 3600_000),774 removed_at: removed && removedAt < NOW ? iso(removedAt) : null,775 status: removed && removedAt < NOW ? 'no_longer_listed' : 'open',776 is_ai: /AI|Machine Learning|LLM|Research/.test(title) || chance(0.1),777 });778 }779 const people = [];780 for (let i = 0; i < ri(5, 12); i++) {781 const removed = chance(0.2);782 const first = NOW - ri(30, 400) * DAY;783 people.push({ id: id('per'), name: PEOPLE[(idx * 3 + i) % PEOPLE.length], title: TITLES[i % TITLES.length], role_category: i < 6 ? 'c_suite' : 'vp', is_executive: i < 8, first_seen_at: iso(first), last_seen_at: iso(removed ? first + ri(10, 200) * DAY : NOW - ri(0, 2) * DAY), removed_at: removed ? iso(first + ri(10, 200) * DAY) : null, status: removed ? 'no_longer_listed' : 'listed', source_url: `https://${domain}/about/leadership`, source: 'page' });784 }785 // a couple of Wikidata executives also appear on the monitored page (exercise the name merge → both chips)786 for (const [pname, ptitle] of (WD_PEOPLE[slug] ?? []).slice(0, 1)) people.unshift({ id: id('per'), name: pname, title: ptitle.split(' and ')[0], role_category: 'c_suite', is_executive: true, first_seen_at: iso(created + 5 * DAY), last_seen_at: iso(NOW - DAY), removed_at: null, status: 'listed', source_url: `https://${domain}/about/leadership`, source: 'page' });787 const products = [];788 for (let i = 0; i < ri(3, 10); i++) {789 const removed = chance(0.15);790 const first = NOW - ri(30, 400) * DAY;791 const pname = PRODUCT_NAMES[(idx * 5 + i) % PRODUCT_NAMES.length];792 products.push({ id: id('prd'), name: pname, category: pick(['platform', 'api', 'app', 'hardware', 'service']), description: `${pname} by ${name}.`, url: `https://${domain}/products/${slugify(pname)}`, first_seen_at: iso(first), last_seen_at: iso(removed ? first + ri(10, 200) * DAY : NOW - ri(0, 2) * DAY), removed_at: removed ? iso(first + ri(10, 200) * DAY) : null, status: removed ? 'removed' : 'listed' });793 }794 const plans = [];795 const planNames = ['Starter', 'Growth', 'Scale', 'Enterprise'];796 let pv = 1;797 for (let i = 0; i < ri(2, 4); i++) {798 const pname = planNames[i];799 const enterprise = pname === 'Enterprise';800 let price = enterprise ? null : [29, 99, 299][i] ?? 49;801 const versions = ri(1, 3);802 let from = NOW - ri(200, 400) * DAY;803 for (let v = 1; v <= versions; v++) {804 const last = v === versions;805 const to = last ? null : from + ri(40, 120) * DAY;806 plans.push({ id: id('pln'), plan_name: pname, price, price_text: enterprise ? 'Contact sales' : `$${price}/mo`, currency: enterprise ? null : 'USD', billing_period: enterprise ? null : 'month', unit: enterprise ? null : 'seat', features: enterprise ? ['Custom SLAs', 'Dedicated support', 'Audit logs', 'SSO'] : [`${[3, 10, 50][i] ?? 5} seats`, 'API access', v > 1 ? 'Priority support' : 'Community support'], contact_sales: enterprise, version_no: pv++, valid_from: iso(from), valid_to: to ? iso(to) : null, status: last ? 'current' : 'superseded', source_url: `https://${domain}/pricing` });807 if (to) from = to;808 if (price !== null) price = Math.round(price * (1 + (rnd() * 0.3 - 0.05)));809 }810 }811 const locations = [{ id: id('loc'), kind: 'headquarters', name: `${name} HQ`, city, region: null, country, lat, lon, first_seen_at: iso(created), last_seen_at: iso(NOW - DAY), removed_at: null, status: 'listed', source_url: `https://${domain}/about/locations` }];812 for (let i = 0; i < (tier <= 2 ? ri(3, 9) : ri(0, 3)); i++) {813 const c = pick(CITIES);814 const removed = chance(0.15);815 const first = NOW - ri(30, 400) * DAY;816 locations.push({ id: id('loc'), kind: pick(['office', 'office', 'office', 'lab', 'warehouse', 'store']), name: `${c[0]} office`, city: c[0], region: null, country: c[1], lat: c[2], lon: c[3], first_seen_at: iso(first), last_seen_at: iso(removed ? first + ri(10, 200) * DAY : NOW - DAY), removed_at: removed ? iso(first + ri(10, 200) * DAY) : null, status: removed ? 'no_longer_listed' : 'listed', source_url: `https://${domain}/about/locations` });817 }818 const news = [];819 for (let i = 0; i < ri(5, 18); i++) {820 const t = NOW - ri(0, 120) * DAY;821 news.push({ id: id('nws'), title: `${name} ${pick(HEADLINES)}`, url: `https://${domain}/newsroom/${t}`, summary: 'First-party newsroom item.', category: pick(['press', 'product', 'company', 'research']), published_at: iso(t), first_seen_at: iso(t + ri(5, 240) * 60_000), language: 'en' });822 }823824 // metrics825 const activity = clamp(importance * 0.6 + rnd() * 40 - 10, 5, 99);826 const hiring30 = r1((rnd() - 0.42) * 60);827 const metrics = {828 activity_score: r1(activity),829 hiring_momentum_7d: r1(hiring30 / 3 + (rnd() - 0.5) * 8),830 hiring_momentum_30d: hiring30,831 hiring_momentum_90d: r1(hiring30 * 1.6 + (rnd() - 0.5) * 12),832 open_jobs: jobs.filter((j) => j.status === 'open').length,833 ai_adoption: r1(clamp(inds.includes('artificial-intelligence') ? 70 + rnd() * 30 : rnd() * 70, 0, 100)),834 product_velocity: r1(clamp(rnd() * 90, 0, 100)),835 geo_expansion: r1(clamp(rnd() * 80, 0, 100)),836 developer_momentum: r1(clamp((inds.includes('software') || inds.includes('cloud-infrastructure') ? 40 : 5) + rnd() * 60, 0, 100)),837 communication_activity: r1(rnd() * 100),838 pricing_activity: r1(rnd() * 60),839 leadership_activity: r1(rnd() * 50),840 corporate_change_index: 0,841 anomaly_score: r1(rnd() * 100),842 historical_coverage: r1(60 + rnd() * 40),843 };844 metrics.corporate_change_index = r1(0.25 * clamp(50 + hiring30, 0, 100) + 0.2 * metrics.product_velocity + 0.15 * metrics.geo_expansion + 0.15 * metrics.leadership_activity + 0.1 * metrics.developer_momentum + 0.1 * metrics.communication_activity + 0.05 * metrics.pricing_activity);845 if (chance(0.12)) delete metrics.ai_adoption; // omit when no inputs (never fabricate)846 if (chance(0.1)) delete metrics.developer_momentum;847 const act30 = series(30, metrics.activity_score, 12);848 const hir90 = series(90, 50 + hiring30 / 2, 8);849 const seriesAll = { activity_score: series(90, metrics.activity_score, 10), hiring_momentum_30d: hir90.map((p) => ({ ...p, value: r1((p.value - 50) * 2) })), product_velocity: series(90, metrics.product_velocity, 9), ai_adoption: series(90, metrics.ai_adoption ?? 30, 6, 0.1), corporate_change_index: series(90, metrics.corporate_change_index, 7), open_jobs: series(90, 50, 4).map((p) => ({ ...p, value: Math.round(metrics.open_jobs * (0.7 + p.value / 160)) })) };850 seriesAll.activity_score.splice(-30, 30, ...act30);851852 const company = {853 id: cid,854 slug,855 display_name: name,856 legal_name: `${name}${pub ? ' Inc.' : ', Inc.'}`,857 canonical_domain: domain,858 website: `https://${domain}`,859 description: desc,860 industries: inds,861 industry_primary: inds[0],862 country,863 hq_city: city,864 hq_region: null,865 public_company: pub,866 ticker,867 exchange,868 founded_year: founded,869 employees_band: band,870 logo_url: null,871 profile: null, // filled after all companies exist (relationships reference peers)872 status: chance(0.94) ? 'ACTIVE' : 'POSSIBLY_INACTIVE',873 onboarding_status: 'active',874 importance,875 tier,876 metrics,877 counts: { sensors: compSensors.filter((s) => s.status === 'active').length, observations: compSensors.reduce((a, s) => a + s.observation_count, 0), changes: compSensors.reduce((a, s) => a + s.change_count, 0), events: 0, jobs_open: metrics.open_jobs },878 last_event_at: null,879 last_observed_at: iso(NOW - ri(1, 90) * 60_000),880 sparkline: act30.map((p) => p.value),881 _lat: lat,882 _lon: lon,883 _created: created,884 _sensors: compSensors,885 _jobs: jobs,886 _people: people,887 _products: products,888 _plans: plans,889 _locations: locations,890 _news: news,891 _series: seriesAll,892 _hir90: hir90.map((p) => r1((p.value - 50) * 2)),893 _signals: [],894 _aliases: [name.toUpperCase(), `${name} Inc`, domain.split('.')[0]],895 };896 companies.push(company);897 companiesBySlug.set(slug, company);898 companiesBySlug.set(cid, company);899 return company;900}901902SEED.forEach((row, i) => buildCompany(row, i));903for (const c of companies) c.profile = NULL_PROFILE.has(c.slug) || NO_PROFILE_KEY.has(c.slug) ? makeProfile(c, null) : PROFILES[c.slug] ? makeProfile(c, PROFILES[c.slug]) : autoProfile(c);904const NO_CATALOGUE = new Set(['anthropic', 'mistral-ai']); // no monitored catalogue surface → Products tab falls back to Wikidata products905for (const c of companies) {906 c._relationships = relationsFor(c);907 c._facts = factsOf(c);908 c._people.push(...wikidataPeople(c));909 if (NO_CATALOGUE.has(c.slug)) c._products = [];910}911912// events913function makeEvent(c, template, t, opts = {}) {914 const [type, subtype, [imLo, imHi], surfaces, titleFn, summaryFn, ovFn] = template;915 const p = {916 name: pick(PRODUCT_NAMES),917 plan: pick(['Starter', 'Growth', 'Scale', 'Team']),918 oldPrice: ri(19, 199),919 city: pick(CITIES)[0],920 country: COUNTRY_META[pick(Object.keys(COUNTRY_META))][0],921 jobs: ri(2, 14),922 n: ri(3, 72),923 pct: ri(5, 45),924 ai: ri(0, 9),925 from: ri(40, 300),926 section: pick(['7', '8.2', '12', '3.1', 'Data processing addendum', 'Authentication', 'Webhooks', 'Rate limits']),927 blocks: ri(2, 30),928 v: ri(3, 14),929 headline: pick(HEADLINES),930 partner: pick(PARTNERS),931 target: `${pick(['Nimbus', 'Lattice', 'Parcel', 'Quill', 'Beacon'])} ${pick(['Labs', 'Systems', 'AI', 'Technologies'])}`,932 delay: ri(3, 90),933 amount: `$${pick([40, 75, 120, 250, 500])}M`,934 kind: pick(['advisory', 'partial outage', 'degraded performance', 'disclosure']),935 oldHeadline: 'Payments for small businesses',936 newHeadline: 'The financial infrastructure platform for enterprises',937 tech: pick(TECHS),938 item: pick(['Q3 results', 'annual report', 'investor day materials', 'a shareholder letter']),939 year: 2025,940 name2: pick(PEOPLE),941 title: pick(TITLES),942 };943 p.newPrice = subtype === 'PRICE_INCREASE' ? Math.round(p.oldPrice * (1.05 + rnd() * 0.3)) : Math.round(p.oldPrice * (0.7 + rnd() * 0.25));944 p.to = subtype === 'JOB_COUNT_INCREASE' ? p.from + p.n : Math.max(0, p.from - p.n);945 if (type === 'LEADERSHIP') {946 const person = pick(c._people);947 p.name = person.name;948 p.title = person.title;949 }950 const [oldV, newV] = ovFn(c, p);951 const surface = pick(surfaces);952 const sensor = c._sensors.find((s) => s.surface === surface) ?? c._sensors[0];953 const change = sensor._changes.length ? pick(sensor._changes) : null;954 const origin = pick(['deterministic', 'deterministic', 'deterministic', 'llm', 'hybrid', 'backfill']);955 const confidence = r1(clamp(0.45 + rnd() * 0.55 - (origin === 'llm' ? 0.12 : 0), 0.3, 0.99));956 const label = confidence >= 0.95 ? 'VERIFIED' : confidence >= 0.85 ? 'HIGH_CONFIDENCE' : confidence >= 0.7 ? 'LIKELY' : confidence >= 0.55 ? 'INFERRED' : 'LOW_CONFIDENCE';957 const importance = r1(imLo + rnd() * (imHi - imLo));958 const statusRoll = rnd();959 const eid = id('evt');960 const ev = {961 id: eid,962 company: { id: c.id, slug: c.slug, display_name: c.display_name, canonical_domain: c.canonical_domain, country: c.country, logo_url: null },963 event_type: type,964 event_subtype: subtype,965 importance,966 confidence,967 confidence_label: label,968 title: titleFn(c, p),969 summary: summaryFn(c, p),970 old_value: oldV,971 new_value: newV,972 payload: { template: subtype, ...(type === 'HIRING' ? { count_before: p.from, count_after: p.to } : {}), ...(type === 'PRICING' ? { plan: p.plan, price_before: p.oldPrice, price_after: p.newPrice, currency: 'USD' } : {}) },973 entities: type === 'LEADERSHIP' ? { person: p.name, title: p.title } : type === 'LOCATION' ? { city: p.city, country: p.country } : type === 'PRODUCT' ? { product: p.name } : {},974 tags: [...new Set([type.toLowerCase(), surface, ...(p.ai > 4 && type === 'HIRING' ? ['ai'] : [])])],975 detected_at: iso(t),976 effective_at: chance(0.5) ? iso(t - ri(0, 3) * DAY) : null,977 published_at: type === 'COMMUNICATION' || type === 'M&A' ? iso(t - ri(5, 240) * 60_000) : null,978 source_url: sensor.url,979 surface,980 sensor_id: sensor.id,981 change_id: change ? change.id : null,982 cluster_id: chance(0.3) ? id('cls') : null,983 origin,984 model_name: origin === 'llm' || origin === 'hybrid' ? 'qwen3.6-35b-a3b-4bit' : null,985 prompt_version: origin === 'llm' || origin === 'hybrid' ? 'event-classifier/v3' : null,986 status: opts.live ? 'active' : statusRoll < 0.94 ? 'active' : statusRoll < 0.97 ? 'retracted' : statusRoll < 0.99 ? 'review' : 'duplicate',987 sources: [{ source_url: sensor.url, surface, detected_at: iso(t), kind: 'primary', sensor_id: sensor.id }],988 };989 if (chance(0.4)) {990 const other = pick(c._sensors);991 ev.sources.push({ source_url: other.url, surface: other.surface, detected_at: iso(t + ri(2, 180) * 60_000), kind: 'corroboration', sensor_id: other.id });992 }993 if (change) change._events.push(ev.id);994 sensor.event_count += 1;995 return ev;996}997998for (const c of companies) {999 const n = c.tier === 1 ? ri(90, 260) : c.tier === 2 ? ri(40, 120) : c.tier === 3 ? ri(12, 50) : ri(3, 15);1000 for (let i = 0; i < n; i++) {1001 const t = NOW - Math.floor(Math.pow(rnd(), 1.6) * 365 * DAY) - ri(0, 3600_000);1002 events.push(makeEvent(c, pick(EVENT_TEMPLATES), t));1003 }1004}1005// make sure the most recent hours are populated for the live feed1006for (let i = 0; i < 60; i++) {1007 const c = pick(companies);1008 events.push(makeEvent(c, pick(EVENT_TEMPLATES), NOW - ri(1, 360) * 60_000));1009}1010events.sort((a, b) => (a.detected_at < b.detected_at ? 1 : -1));1011for (const e of events) {1012 eventsById.set(e.id, e);1013 const c = companiesBySlug.get(e.company.slug);1014 if (!perCompany.has(c.slug)) perCompany.set(c.slug, []);1015 perCompany.get(c.slug).push(e);1016}1017for (const c of companies) {1018 const list = perCompany.get(c.slug) ?? [];1019 c.counts.events = list.filter((e) => e.status === 'active').length;1020 c.last_event_at = list[0]?.detected_at ?? null;1021}10221023// signals1024const SIGNAL_KINDS = [['hiring_surge', 'Hiring surge signal'], ['hiring_freeze', 'Hiring slowdown signal'], ['launch_buildup', 'Possible launch preparation signal'], ['international_expansion', 'International expansion signal'], ['pricing_migration', 'Pricing migration signal'], ['developer_push', 'Developer ecosystem push'], ['enterprise_repositioning', 'Enterprise repositioning signal'], ['ai_acceleration', 'AI acceleration signal']];1025const signals = [];1026for (const c of companies) {1027 for (let i = 0; i < ri(0, 3); i++) {1028 const [kind, label] = pick(SIGNAL_KINDS);1029 const s = { id: id('sig'), company_id: c.id, scope: 'company', scope_key: c.slug, kind, strength: r1(0.3 + rnd() * 0.7), confidence: r1(0.4 + rnd() * 0.5), title: `${label} — ${c.display_name}`, explanation: kind === 'launch_buildup' ? 'Documentation, careers and changelog surfaces changed together within 9 days; historically this pattern preceded a product listing in 4 of 7 monitored cases for this company. Presented as a signal, not a fact.' : kind === 'hiring_surge' ? `Open listings are ${ri(20, 80)} % above the 90-day baseline, concentrated in ${pick(DEPARTMENTS)}.` : 'Derived from per-company baselines of monitored surfaces.', evidence: { events: ri(2, 12), surfaces: ri(2, 5), window_days: 30 }, window_days: 30, detected_at: iso(NOW - ri(1, 20) * DAY), status: 'active' };1030 signals.push(s);1031 c._signals.push(s);1032 }1033}1034for (const [slug, ind] of Object.entries(IND).slice(0, 8)) signals.push({ id: id('sig'), company_id: null, scope: 'industry', scope_key: slug, kind: 'ai_acceleration', strength: r1(0.3 + rnd() * 0.6), confidence: r1(0.5 + rnd() * 0.4), title: `AI hiring acceleration in ${ind.name}`, explanation: 'Share of AI-tagged listings rose across monitored companies in this industry.', evidence: { companies: ri(3, 12) }, window_days: 30, detected_at: iso(NOW - ri(1, 10) * DAY), status: 'active' });1035signals.push({ id: id('sig'), company_id: null, scope: 'global', scope_key: null, kind: 'pricing_migration', strength: 0.61, confidence: 0.72, title: 'SaaS pricing pages shifting to usage-based tiers', explanation: `${ri(8, 20)} monitored pricing pages added usage units in 30 days.`, evidence: { companies: 14 }, window_days: 30, detected_at: iso(NOW - 2 * DAY), status: 'active' });10361037// trends1038const trends = TREND_TERMS.map((term) => ({ term, mentions: ri(12, 480), companies: ri(3, 30), momentum: r1((rnd() - 0.3) * 120), series: Array.from({ length: 14 }, () => ri(0, 40)) })).sort((a, b) => b.momentum - a.momentum);10391040// industries and countries1041function industryRow(slug) {1042 const ind = IND[slug];1043 const cs = companies.filter((c) => c.industries.includes(slug));1044 const evs = cs.flatMap((c) => perCompany.get(c.slug) ?? []).filter((e) => e.status === 'active');1045 const avg = (k) => {1046 const v = cs.map((c) => c.metrics[k]).filter((x) => typeof x === 'number');1047 return v.length ? r1(v.reduce((a, b) => a + b, 0) / v.length) : null;1048 };1049 const byType = {};1050 for (const e of evs.filter((e) => e.detected_at > iso(NOW - 30 * DAY))) byType[e.event_type] = (byType[e.event_type] ?? 0) + 1;1051 return { slug, name: ind.name, parent_slug: ind.parent_slug, companies: cs.length, events_7d: evs.filter((e) => e.detected_at > iso(NOW - 7 * DAY)).length, events_30d: evs.filter((e) => e.detected_at > iso(NOW - 30 * DAY)).length, hiring_momentum_30d: avg('hiring_momentum_30d'), activity_score: avg('activity_score'), ai_adoption: avg('ai_adoption'), top_event_types: Object.entries(byType).sort((a, b) => b[1] - a[1]).slice(0, 3).map((x) => x[0]), _companies: cs, _events: evs };1052}1053function countryRow(code) {1054 const [name, region, lat, lon] = COUNTRY_META[code];1055 const cs = companies.filter((c) => c.country === code);1056 const evs = cs.flatMap((c) => perCompany.get(c.slug) ?? []).filter((e) => e.status === 'active');1057 const avg = (k) => {1058 const v = cs.map((c) => c.metrics[k]).filter((x) => typeof x === 'number');1059 return v.length ? r1(v.reduce((a, b) => a + b, 0) / v.length) : null;1060 };1061 const mix = {};1062 for (const c of cs) for (const i of c.industries) mix[i] = (mix[i] ?? 0) + 1;1063 return { code, name, region, companies: cs.length, events_7d: evs.filter((e) => e.detected_at > iso(NOW - 7 * DAY)).length, events_30d: evs.filter((e) => e.detected_at > iso(NOW - 30 * DAY)).length, hiring_momentum_30d: avg('hiring_momentum_30d'), activity_score: avg('activity_score'), industry_mix: Object.entries(mix).map(([industry, companies]) => ({ industry, companies })).sort((a, b) => b.companies - a.companies), lat, lon, _companies: cs, _events: evs };1064}1065const industryRows = () => Object.keys(IND).map(industryRow).filter((r) => r.companies > 0).sort((a, b) => b.events_30d - a.events_30d);1066const countryRows = () => Object.keys(COUNTRY_META).map(countryRow).filter((r) => r.companies > 0).sort((a, b) => b.events_30d - a.events_30d);10671068// global daily history + index1069const history = [];1070{1071 let idx = 100;1072 for (let i = 364; i >= 0; i--) {1073 const t = NOW - i * DAY;1074 idx = clamp(idx + (rnd() - 0.48) * 3, 70, 150);1075 const dayEvents = events.filter((e) => day(new Date(e.detected_at).getTime()) === day(t));1076 const byType = {};1077 for (const e of dayEvents) byType[e.event_type] = (byType[e.event_type] ?? 0) + 1;1078 const growth = 1 - i / 600;1079 history.push({ day: day(t), companies_active: Math.round(companies.length * growth), sensors_active: Math.round(sensors.size * growth), observations: Math.round((18000 + rnd() * 6000) * growth), changes: Math.round((900 + rnd() * 400) * growth), meaningful_changes: Math.round((260 + rnd() * 120) * growth), events: dayEvents.length, events_by_type: byType, jobs_open: Math.round(12000 * growth + rnd() * 800), jobs_new: ri(80, 400), jobs_removed: ri(60, 380), activity_index: r1(idx) });1080 }1081}10821083function stats() {1084 const obs = [...sensors.values()].reduce((a, s) => a + s.observation_count, 0);1085 const first = Math.min(...companies.map((c) => c._created));1086 return { companies: companies.length, companies_active: companies.filter((c) => c.status === 'ACTIVE').length, sensors: sensors.size, sensors_active: [...sensors.values()].filter((s) => s.status === 'active').length, observations: obs + liveTicks * 37, snapshots: [...sensors.values()].reduce((a, s) => a + s.snapshot_count, 0), changes: changes.size + liveTicks * 3, meaningful_changes: [...changes.values()].filter((c) => c.significance >= 0.4).length + liveTicks, events: events.filter((e) => e.status === 'active').length, jobs_open: companies.reduce((a, c) => a + c.counts.jobs_open, 0), countries: new Set(companies.map((c) => c.country)).size, industries: new Set(companies.flatMap((c) => c.industries)).size, observations_today: 21560 + liveTicks * 37, changes_today: 1130 + liveTicks * 3, events_today: events.filter((e) => e.detected_at.slice(0, 10) === day(NOW) && e.status === 'active').length, dataset_started_at: iso(first), dataset_age_days: Math.round((NOW - first) / DAY), oldest_history_days: Math.round((NOW - first) / DAY), last_observation_at: iso(Date.now() - ri(3, 40) * 1000), archive: { objects: 1_284_310 + liveTicks * 30, bytes: 412_000_000_000 + liveTicks * 800_000 } };1087}10881089function mapBuckets(metric) {1090 const buckets = new Map();1091 for (const c of companies) {1092 const key = `${c.country}:${c.hq_city}`;1093 if (!buckets.has(key)) buckets.set(key, { lat: c._lat, lon: c._lon, country: c.country, city: c.hq_city, companies: 0, events_30d: 0, jobs_open: 0, top: [] });1094 const b = buckets.get(key);1095 b.companies += 1;1096 b.events_30d += (perCompany.get(c.slug) ?? []).filter((e) => e.detected_at > iso(NOW - 30 * DAY)).length;1097 b.jobs_open += c.counts.jobs_open;1098 b.top.push({ slug: c.slug, display_name: c.display_name, importance: c.importance });1099 }1100 for (const b of buckets.values()) b.top = b.top.sort((a, z) => z.importance - a.importance).slice(0, 3).map(({ slug, display_name }) => ({ slug, display_name }));1101 return [...buckets.values()].sort((a, b) => b[metric] - a[metric]);1102}11031104function rankings(kind, window, country, industry, limit) {1105 let cs = companies.filter((c) => (!country || c.country === country.toUpperCase()) && (!industry || c.industries.includes(industry)));1106 const wf = { '24h': 0.25, '7d': 0.6, '30d': 1, '90d': 1.3, '1y': 1.6 }[window] ?? 1;1107 const val = (c) => {1108 switch (kind) {1109 case 'hiring_growth':1110 return c.metrics.hiring_momentum_30d * wf;1111 case 'hiring_decline':1112 return c.metrics.hiring_momentum_30d * wf;1113 case 'product_velocity':1114 return c.metrics.product_velocity;1115 case 'ai_active':1116 return c.metrics.ai_adoption ?? -1;1117 case 'geo_expansion':1118 return c.metrics.geo_expansion;1119 case 'developer_momentum':1120 return c.metrics.developer_momentum ?? -1;1121 case 'pricing_changes':1122 return (perCompany.get(c.slug) ?? []).filter((e) => e.event_type === 'PRICING').length * wf;1123 case 'unusual_activity':1124 return c.metrics.anomaly_score;1125 default:1126 return c.metrics.activity_score;1127 }1128 };1129 cs = cs.filter((c) => val(c) >= 0);1130 cs.sort((a, b) => (kind === 'hiring_decline' ? val(a) - val(b) : val(b) - val(a)));1131 return cs.slice(0, limit).map((c, i) => ({ ...pub(c), rank: i + 1, value: r1(val(c)), delta: chance(0.8) ? r1((rnd() - 0.5) * 20) : null }));1132}11331134const pub = (c) => {1135 const { _lat, _lon, _created, _sensors, _jobs, _people, _products, _plans, _locations, _news, _series, _hir90, _signals, _aliases, _relationships, _facts, ...rest } = c;1136 if (NO_PROFILE_KEY.has(c.slug)) delete rest.profile; // API builds that predate the profile field1137 return rest;1138};1139const pubSensor = (s) => {1140 const { _versions, _changes, ...rest } = s;1141 return rest;1142};1143const pubChange = (c) => {1144 const { _events, ...rest } = c;1145 return rest;1146};1147const pubSnap = (s) => {1148 const { _blocks, _text, ...rest } = s;1149 return rest;1150};1151const ref = (c) => ({ id: c.id, slug: c.slug, display_name: c.display_name, canonical_domain: c.canonical_domain, country: c.country, logo_url: null });11521153// ------------------------------------------------------------------------------------------------ live stream1154let liveTicks = 0;1155const sseClients = new Set();1156setInterval(() => {1157 const c = pick(companies);1158 const ev = makeEvent(c, pick(EVENT_TEMPLATES), Date.now() - ri(0, 2000), { live: true });1159 events.unshift(ev);1160 eventsById.set(ev.id, ev);1161 (perCompany.get(c.slug) ?? perCompany.set(c.slug, []).get(c.slug)).unshift(ev);1162 c.counts.events += 1;1163 c.last_event_at = ev.detected_at;1164 liveTicks += 1;1165 const frame = `event: event\nid: ${ev.id}\ndata: ${JSON.stringify(ev)}\n\n`;1166 for (const res of sseClients) res.write(frame);1167}, 4000);1168setInterval(() => {1169 for (const res of sseClients) res.write(`event: heartbeat\ndata: ${JSON.stringify({ at: new Date().toISOString(), clients: sseClients.size })}\n\n`);1170}, 20000);11711172// ------------------------------------------------------------------------------------------------ owner / admin state1173const watchlists = new Map(); // token -> Set(slug)1174const alerts = new Map(); // token -> Alert[]1175const adminToken = process.env.CA_ADMIN_TOKEN ?? 'dev-admin-token';11761177// ------------------------------------------------------------------------------------------------ helpers1178function paginate(items, q, defaultPer = 25) {1179 const page = Math.max(1, Number(q.get('page') ?? 1));1180 const per = clamp(Number(q.get('per_page') ?? defaultPer), 1, 200);1181 const total = items.length;1182 return { items: items.slice((page - 1) * per, page * per), page, per_page: per, total, pages: Math.max(1, Math.ceil(total / per)) };1183}1184function filterEvents(list, q) {1185 let out = list;1186 const g = (k) => q.get(k);1187 if (g('event_type')) out = out.filter((e) => e.event_type === g('event_type').toUpperCase());1188 if (g('event_subtype')) out = out.filter((e) => e.event_subtype === g('event_subtype').toUpperCase());1189 if (g('country')) out = out.filter((e) => (e.company.country ?? '').toUpperCase() === g('country').toUpperCase());1190 if (g('industry')) out = out.filter((e) => companiesBySlug.get(e.company.slug)?.industries.includes(g('industry')));1191 if (g('since')) out = out.filter((e) => e.detected_at > g('since'));1192 if (g('until')) out = out.filter((e) => e.detected_at < g('until'));1193 if (g('min_importance')) out = out.filter((e) => e.importance >= Number(g('min_importance')));1194 if (g('min_confidence')) out = out.filter((e) => e.confidence >= Number(g('min_confidence')));1195 if (g('surface')) out = out.filter((e) => e.surface === g('surface'));1196 if (g('origin')) out = out.filter((e) => e.origin === g('origin'));1197 if (g('company')) out = out.filter((e) => e.company.slug === g('company'));1198 if (g('status')) out = out.filter((e) => e.status === g('status'));1199 if (g('q')) {1200 const s = g('q').toLowerCase();1201 out = out.filter((e) => e.title.toLowerCase().includes(s) || (e.summary ?? '').toLowerCase().includes(s) || e.company.display_name.toLowerCase().includes(s));1202 }1203 if (g('sort') === 'importance') out = [...out].sort((a, b) => b.importance - a.importance);1204 return out;1205}1206const TIMELINE_MAP = { products: ['PRODUCT'], jobs: ['HIRING'], pricing: ['PRICING'], leadership: ['LEADERSHIP'], locations: ['LOCATION'], legal: ['LEGAL'], news: ['COMMUNICATION', 'PARTNERSHIP', 'M&A', 'FINANCING', 'INVESTOR_RELATIONS'], developer: ['DEVELOPER', 'TECHNOLOGY'] };12071208function json(res, status, body, extra = {}) {1209 const data = JSON.stringify(body);1210 res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'access-control-allow-origin': '*', 'access-control-allow-headers': 'content-type, x-ca-owner-token, x-ca-admin-token', 'access-control-allow-methods': 'GET,POST,DELETE,OPTIONS', 'x-api-version': 'mock-1', ...extra });1211 res.end(data);1212}1213const notFound = (res, what = 'not found') => json(res, 404, { detail: what });1214function readBody(req) {1215 return new Promise((resolve) => {1216 let b = '';1217 req.on('data', (c) => (b += c));1218 req.on('end', () => {1219 try {1220 resolve(b ? JSON.parse(b) : {});1221 } catch {1222 resolve({});1223 }1224 });1225 });1226}1227function csv(rows) {1228 if (!rows.length) return '';1229 const keys = Object.keys(rows[0]).filter((k) => typeof rows[0][k] !== 'object' || rows[0][k] === null);1230 const esc = (v) => (v === null || v === undefined ? '' : /[",\n]/.test(String(v)) ? `"${String(v).replace(/"/g, '""')}"` : String(v));1231 return [keys.join(','), ...rows.map((r) => keys.map((k) => esc(r[k])).join(','))].join('\n');1232}12331234// ------------------------------------------------------------------------------------------------ router1235const server = createServer(async (req, res) => {1236 const url = new URL(req.url, `http://${req.headers.host ?? 'localhost'}`);1237 const q = url.searchParams;1238 let path = url.pathname.replace(/\/$/, '') || '/';1239 if (req.method === 'OPTIONS') return json(res, 204, {});1240 if (path === '/health' || path === '/ready' || path === '/api/v1/health' || path === '/api/v1/ready') return json(res, 200, { status: 'ok', service: 'company-atlas-mock', time: new Date().toISOString() });1241 if (!path.startsWith('/api/v1')) return notFound(res);1242 path = path.slice('/api/v1'.length) || '/';1243 const seg = path.split('/').filter(Boolean);1244 const owner = req.headers['x-ca-owner-token'];1245 const admin = req.headers['x-ca-admin-token'];12461247 try {1248 // ---- mock assets (sample logos; not part of the API contract)1249 if (seg[0] === '_mock' && (seg[1] === 'logo' || seg[1] === 'icon') && seg[2]) {1250 const slug = seg[2].replace(/\.svg$/, '');1251 if (!companiesBySlug.has(slug)) return notFound(res, 'logo not found');1252 res.writeHead(200, { 'content-type': 'image/svg+xml', 'cache-control': 'public, max-age=3600', 'access-control-allow-origin': '*' });1253 return res.end(logoSvg(slug, seg[1] === 'icon'));1254 }12551256 // ---- platform1257 if (path === '/stats') return json(res, 200, stats(), { 'cache-control': 'public, max-age=60' });1258 if (path === '/stats/history') return json(res, 200, { items: history.slice(-clamp(Number(q.get('days') ?? 90), 1, 365)) });1259 if (path === '/system') return json(res, 200, { sensors_online: [...sensors.values()].filter((s) => s.status === 'active').length, sensors_failing: [...sensors.values()].filter((s) => s.status === 'failing').length, observations_today: 21560 + liveTicks * 37, events_today: stats().events_today, countries_covered: new Set(companies.map((c) => c.country)).size, queue_lag_s: ri(2, 40), scheduler_last_tick_at: iso(Date.now() - ri(1, 20) * 1000), fetch_per_min: ri(280, 460), success_rate_24h: r1(96 + rnd() * 3.5) });1260 if (path === '/pulse') {1261 const active = events.filter((e) => e.status === 'active');1262 const idxSeries = history.slice(-30).map((h) => ({ day: h.day, value: h.activity_index, confidence: 0.9 }));1263 const last = idxSeries[idxSeries.length - 1].value;1264 const wk = idxSeries[idxSeries.length - 8].value;1265 return json(res, 200, { stats: stats(), live: active.slice(0, 12), movers: [...companies].sort((a, b) => b.metrics.corporate_change_index - a.metrics.corporate_change_index).slice(0, 10).map(pub), hiring: [...companies].sort((a, b) => b.metrics.hiring_momentum_30d - a.metrics.hiring_momentum_30d).slice(0, 8).map(pub), launches: active.filter((e) => e.event_subtype === 'PRODUCT_LAUNCH').slice(0, 8), pricing: active.filter((e) => e.event_type === 'PRICING').slice(0, 8), ai: [...companies].filter((c) => typeof c.metrics.ai_adoption === 'number').sort((a, b) => b.metrics.ai_adoption - a.metrics.ai_adoption).slice(0, 8).map(pub), industries: industryRows().slice(0, 12).map(({ _companies, _events, ...r }) => r), countries: countryRows().slice(0, 12).map(({ _companies, _events, ...r }) => r), trending: trends.slice(0, 10), activity_index: { value: last, delta_7d: r1(((last - wk) / wk) * 100), series: idxSeries }, map: mapBuckets('events_30d') }, { 'cache-control': 'public, max-age=60' });1266 }1267 if (path === '/live') {1268 const limit = clamp(Number(q.get('limit') ?? 50), 1, 500);1269 let list = events.filter((e) => e.status === 'active');1270 list = filterEvents(list, q);1271 return json(res, 200, { items: list.slice(0, limit) }, { 'cache-control': 'no-store' });1272 }1273 if (path === '/live/stream') {1274 res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-store, no-transform', connection: 'keep-alive', 'access-control-allow-origin': '*', 'x-accel-buffering': 'no' });1275 res.write(`: connected\n\n`);1276 const since = q.get('since');1277 if (since) for (const e of filterEvents(events.filter((e) => e.status === 'active' && e.detected_at > since), q).slice(0, 20).reverse()) res.write(`event: event\nid: ${e.id}\ndata: ${JSON.stringify(e)}\n\n`);1278 sseClients.add(res);1279 req.on('close', () => sseClients.delete(res));1280 return;1281 }12821283 // ---- companies1284 if (path === '/companies/compare') {1285 const slugs = (q.get('companies') ?? '').split(',').map((s) => s.trim()).filter(Boolean).slice(0, 6);1286 const cs = slugs.map((s) => companiesBySlug.get(s)).filter(Boolean);1287 if (cs.length < 2) return json(res, 400, { detail: 'compare needs 2–6 known companies' });1288 const metrics = {};1289 for (const k of ['activity_score', 'hiring_momentum_30d', 'product_velocity', 'ai_adoption', 'geo_expansion', 'developer_momentum', 'corporate_change_index', 'open_jobs']) {1290 metrics[k] = {};1291 for (const c of cs) if (typeof c.metrics[k] === 'number') metrics[k][c.slug] = c.metrics[k];1292 }1293 const seriesOut = {};1294 const events_30d = {};1295 const jobs = {};1296 const locations = {};1297 for (const c of cs) {1298 seriesOut[c.slug] = c._series.activity_score;1299 const by = {};1300 for (const e of (perCompany.get(c.slug) ?? []).filter((e) => e.detected_at > iso(NOW - 30 * DAY) && e.status === 'active')) by[e.event_type] = (by[e.event_type] ?? 0) + 1;1301 events_30d[c.slug] = by;1302 jobs[c.slug] = { open: c.counts.jobs_open, ai_open: c._jobs.filter((j) => j.status === 'open' && j.is_ai).length, new_30d: c._jobs.filter((j) => j.first_seen_at > iso(NOW - 30 * DAY)).length };1303 locations[c.slug] = c._locations.filter((l) => l.status === 'listed').length;1304 }1305 return json(res, 200, { companies: cs.map(pub), metrics, series: seriesOut, events_30d, jobs, locations });1306 }1307 if (path === '/companies') {1308 let list = [...companies];1309 const g = (k) => q.get(k);1310 if (g('q')) {1311 const s = g('q').toLowerCase();1312 list = list.filter((c) => c.display_name.toLowerCase().includes(s) || c.canonical_domain.includes(s) || c._aliases.some((a) => a.toLowerCase().includes(s)));1313 }1314 if (g('country')) list = list.filter((c) => c.country === g('country').toUpperCase());1315 if (g('industry')) list = list.filter((c) => c.industries.includes(g('industry')));1316 if (g('tier')) list = list.filter((c) => String(c.tier) === g('tier'));1317 if (g('public')) list = list.filter((c) => c.public_company === (g('public') === '1' || g('public') === 'true'));1318 if (g('status')) list = list.filter((c) => c.status.toLowerCase() === g('status').toLowerCase());1319 if (g('has_events')) list = list.filter((c) => (c.counts.events > 0) === (g('has_events') === '1' || g('has_events') === 'true'));1320 const sort = g('sort') ?? 'activity';1321 const cmp = { activity: (a, b) => b.metrics.activity_score - a.metrics.activity_score, events: (a, b) => b.counts.events - a.counts.events, hiring: (a, b) => b.metrics.hiring_momentum_30d - a.metrics.hiring_momentum_30d, name: (a, b) => a.display_name.localeCompare(b.display_name), importance: (a, b) => b.importance - a.importance, recent: (a, b) => (b.last_event_at ?? '').localeCompare(a.last_event_at ?? '') }[sort];1322 if (cmp) list.sort(cmp);1323 const page = paginate(list, q);1324 const spark = g('sparkline') === '1';1325 page.items = page.items.map((c) => {1326 const p = pub(c);1327 if (!spark) delete p.sparkline;1328 return p;1329 });1330 return json(res, 200, page);1331 }1332 if (seg[0] === 'companies' && seg[1]) {1333 const c = companiesBySlug.get(decodeURIComponent(seg[1]));1334 if (!c) return notFound(res, 'company not found');1335 const sub = seg[2];1336 const list = perCompany.get(c.slug) ?? [];1337 if (!sub) {1338 return json(res, 200, { ...pub(c), aliases: c._aliases, domains: [{ domain: c.canonical_domain, kind: 'canonical' }, { domain: `docs.${c.canonical_domain}`, kind: 'subdomain' }, { domain: `status.${c.canonical_domain}`, kind: 'subdomain' }], relationships: c._relationships, facts: c._facts, metrics_detail: Object.entries(c.metrics).map(([metric, value]) => ({ metric, value, confidence: r1(0.6 + rnd() * 0.35), computed_at: iso(NOW - ri(5, 120) * 60_000), formula_version: 'v1.0', inputs: { sensors: c.counts.sensors, events_30d: list.filter((e) => e.detected_at > iso(NOW - 30 * DAY)).length } })), sensors_by_surface: c._sensors.reduce((a, s) => ((a[s.surface] = (a[s.surface] ?? 0) + 1), a), {}), coverage: { historical_coverage: c.metrics.historical_coverage, first_observed_at: iso(c._created), days_observed: Math.round((NOW - c._created) / DAY), sensor_uptime: r1(90 + rnd() * 9.5) }, signals: c._signals, sparklines: { activity_30d: c.sparkline, hiring_90d: c._hir90 } });1339 }1340 if (sub === 'events') return json(res, 200, paginate(filterEvents(list, q), q));1341 if (sub === 'timeline') {1342 const f = q.get('filter') ?? 'all';1343 const limit = clamp(Number(q.get('limit') ?? 200), 1, 500);1344 const types = TIMELINE_MAP[f];1345 const items = (types ? list.filter((e) => types.includes(e.event_type)) : list).slice(0, limit).map((e) => ({ ...e, day: e.detected_at.slice(0, 10) }));1346 const days = {};1347 for (const e of items) days[e.day] = (days[e.day] ?? 0) + 1;1348 return json(res, 200, { items, days: Object.entries(days).map(([day, count]) => ({ day, count })) });1349 }1350 if (sub === 'metrics') {1351 const days = clamp(Number(q.get('days') ?? 90), 7, 365);1352 const s = {};1353 for (const [k, v] of Object.entries(c._series)) s[k] = v.slice(-days);1354 return json(res, 200, { current: Object.entries(c.metrics).map(([metric, value]) => ({ metric, value, confidence: r1(0.6 + rnd() * 0.35), computed_at: iso(NOW - ri(5, 120) * 60_000), formula_version: 'v1.0', inputs: {} })), series: s });1355 }1356 if (sub === 'jobs') {1357 let jobs = [...c._jobs];1358 const st = q.get('status') ?? 'open';1359 if (st === 'open') jobs = jobs.filter((j) => j.status === 'open');1360 if (st === 'removed') jobs = jobs.filter((j) => j.status === 'no_longer_listed');1361 if (q.get('q')) jobs = jobs.filter((j) => j.title.toLowerCase().includes(q.get('q').toLowerCase()));1362 if (q.get('country')) jobs = jobs.filter((j) => j.country === q.get('country').toUpperCase());1363 if (q.get('ai') === '1') jobs = jobs.filter((j) => j.is_ai);1364 jobs.sort((a, b) => b.first_seen_at.localeCompare(a.first_seen_at));1365 const open = c._jobs.filter((j) => j.status === 'open');1366 const byC = {};1367 const byD = {};1368 for (const j of open) {1369 byC[j.country] = (byC[j.country] ?? 0) + 1;1370 byD[j.department] = (byD[j.department] ?? 0) + 1;1371 }1372 const page = paginate(jobs, q, 50);1373 page.meta = { summary: { open: open.length, new_7d: c._jobs.filter((j) => j.first_seen_at > iso(NOW - 7 * DAY)).length, removed_7d: c._jobs.filter((j) => j.removed_at && j.removed_at > iso(NOW - 7 * DAY)).length, ai_open: open.filter((j) => j.is_ai).length, by_country: Object.entries(byC).map(([country, n]) => ({ country, n })).sort((a, b) => b.n - a.n), by_department: Object.entries(byD).map(([department, n]) => ({ department, n })).sort((a, b) => b.n - a.n), remote_ratio: open.length ? r1(open.filter((j) => j.remote).length / open.length) : null } };1374 return json(res, 200, page);1375 }1376 if (sub === 'people') return json(res, 200, { listed: c._people.filter((p) => p.status === 'listed'), no_longer_listed: c._people.filter((p) => p.status !== 'listed') });1377 if (sub === 'products') return json(res, 200, { listed: c._products.filter((p) => p.status === 'listed'), removed: c._products.filter((p) => p.status !== 'listed') });1378 if (sub === 'pricing') return json(res, 200, { current: c._plans.filter((p) => p.status === 'current'), history: c._plans.filter((p) => p.status !== 'current') });1379 if (sub === 'locations') return json(res, 200, { items: c._locations, countries: [...new Set(c._locations.filter((l) => l.status === 'listed').map((l) => l.country))] });1380 if (sub === 'news') return json(res, 200, { items: [...c._news].sort((a, b) => b.published_at.localeCompare(a.published_at)).slice(0, clamp(Number(q.get('limit') ?? 50), 1, 200)) });1381 if (sub === 'sensors') return json(res, 200, { items: c._sensors.map(pubSensor) });1382 if (sub === 'history') return json(res, 200, { sensors: c._sensors.filter((s) => s._versions.length).map((s) => ({ ...pubSensor(s), versions: [...s._versions].reverse().slice(0, 20).map(pubSnap) })) });1383 if (sub === 'similar') return json(res, 200, { items: companies.filter((o) => o !== c && (o.industries.some((i) => c.industries.includes(i)) || o.country === c.country)).sort((a, b) => Math.abs(a.importance - c.importance) - Math.abs(b.importance - c.importance)).slice(0, clamp(Number(q.get('limit') ?? 8), 1, 24)).map(pub) });1384 return notFound(res);1385 }13861387 // ---- provenance1388 if (seg[0] === 'sensors' && seg[1]) {1389 const s = sensors.get(seg[1]);1390 if (!s) return notFound(res, 'sensor not found');1391 const c = companies.find((x) => x.id === s.company_id);1392 if (!seg[2]) return json(res, 200, { ...pubSensor(s), company: ref(c), latest_snapshot: s._versions.length ? pubSnap(s._versions[s._versions.length - 1]) : null });1393 if (seg[2] === 'snapshots') return json(res, 200, { items: [...s._versions].reverse().slice(0, clamp(Number(q.get('limit') ?? 50), 1, 200)).map(pubSnap) });1394 if (seg[2] === 'changes') return json(res, 200, { items: [...s._changes].reverse().slice(0, clamp(Number(q.get('limit') ?? 50), 1, 200)).map(pubChange) });1395 }1396 if (seg[0] === 'snapshots' && seg[1]) {1397 const s = snapshots.get(seg[1]);1398 if (!s) return notFound(res, 'snapshot not found');1399 if (seg[2] === 'diff' && seg[3]) {1400 const o = snapshots.get(seg[3]);1401 if (!o) return notFound(res, 'snapshot not found');1402 const before = s.fetched_at < o.fetched_at ? s : o;1403 const after = before === s ? o : s;1404 const existing = [...changes.values()].find((c) => c.snapshot_before === before.id && c.snapshot_after === after.id);1405 const diff = existing ? existing.diff : { added: after._blocks.slice(0, 2).map((b) => ({ key: b.key, kind: b.kind, path: b.path, before: null, after: b.text, weight: 0.5, similarity: null })), removed: before._blocks.slice(-1).map((b) => ({ key: b.key, kind: b.kind, path: b.path, before: b.text, after: null, weight: 0.5, similarity: null })), modified: before._blocks.slice(1, 3).map((b, i) => ({ key: b.key, kind: b.kind, path: b.path, before: b.text, after: after._blocks[i + 1]?.text ?? '', weight: 0.5, similarity: 0.6 })), moved: [], counts: { added: 2, removed: 1, modified: 2 }, text_delta_ratio: 0.18, similarity: 0.81, reasons: ['computed on demand between non-adjacent versions'] };1406 return json(res, 200, { before: pubSnap(before), after: pubSnap(after), diff });1407 }1408 return json(res, 200, { ...pubSnap(s), text: s._text, blocks: s._blocks, extracted: s.extracted_summary });1409 }1410 if (seg[0] === 'changes' && seg[1]) {1411 const ch = changes.get(seg[1]);1412 if (!ch) return notFound(res, 'change not found');1413 const c = companies.find((x) => x.id === ch.company_id);1414 return json(res, 200, { ...pubChange(ch), events: ch._events.map((id) => eventsById.get(id)).filter(Boolean), company: ref(c) });1415 }14161417 // ---- events1418 if (path === '/events/types') {1419 const cutoff = iso(NOW - 30 * DAY);1420 const byType = {};1421 for (const e of events.filter((e) => e.detected_at > cutoff && e.status === 'active')) {1422 byType[e.event_type] ??= { event_type: e.event_type, count_30d: 0, subtypes: {} };1423 byType[e.event_type].count_30d += 1;1424 byType[e.event_type].subtypes[e.event_subtype] = (byType[e.event_type].subtypes[e.event_subtype] ?? 0) + 1;1425 }1426 return json(res, 200, { types: Object.values(byType).map((t) => ({ ...t, subtypes: Object.entries(t.subtypes).map(([event_subtype, count_30d]) => ({ event_subtype, count_30d })) })).sort((a, b) => b.count_30d - a.count_30d) });1427 }1428 if (path === '/events/summary') {1429 const days = clamp(Number(q.get('days') ?? 7), 1, 365);1430 const group = q.get('group') ?? 'type';1431 const cur = events.filter((e) => e.detected_at > iso(NOW - days * DAY) && e.status === 'active');1432 const prev = events.filter((e) => e.detected_at > iso(NOW - 2 * days * DAY) && e.detected_at <= iso(NOW - days * DAY) && e.status === 'active');1433 const keyOf = (e) => (group === 'country' ? e.company.country : group === 'industry' ? companiesBySlug.get(e.company.slug)?.industry_primary : e.event_type);1434 const count = (list) => list.reduce((a, e) => ((a[keyOf(e)] = (a[keyOf(e)] ?? 0) + 1), a), {});1435 const a = count(cur);1436 const b = count(prev);1437 return json(res, 200, { items: Object.entries(a).map(([key, count]) => ({ key, count, delta_pct: b[key] ? r1(((count - b[key]) / b[key]) * 100) : null })).sort((x, y) => y.count - x.count) });1438 }1439 if (path === '/events') return json(res, 200, paginate(filterEvents(events.filter((e) => q.get('status') ? true : e.status !== 'duplicate'), q), q));1440 if (seg[0] === 'events' && seg[1]) {1441 const e = eventsById.get(seg[1]);1442 if (!e) return notFound(res, 'event not found');1443 const ch = e.change_id ? changes.get(e.change_id) : null;1444 return json(res, 200, { ...e, change: ch ? pubChange({ ...ch, diff: undefined, structured_delta: undefined }) : null });1445 }14461447 // ---- rankings, atlases1448 if (path === '/rankings') return json(res, 200, { kind: q.get('kind') ?? 'most_active', window: q.get('window') ?? '30d', items: rankings(q.get('kind') ?? 'most_active', q.get('window') ?? '30d', q.get('country'), q.get('industry'), clamp(Number(q.get('limit') ?? 50), 1, 200)) }, { 'cache-control': 'public, max-age=60' });1449 if (path === '/industries') return json(res, 200, { items: industryRows().map(({ _companies, _events, ...r }) => r) });1450 if (seg[0] === 'industries' && seg[1]) {1451 if (!IND[seg[1]]) return notFound(res, 'industry not found');1452 const r = industryRow(seg[1]);1453 const cs = r._companies;1454 const open = cs.reduce((a, c) => a + c.counts.jobs_open, 0);1455 const { _companies, _events, ...row } = r;1456 const cc = {};1457 for (const c of cs) cc[c.country] = (cc[c.country] ?? 0) + 1;1458 return json(res, 200, { ...row, description: `Monitored companies classified under ${row.name}.`, companies: [...cs].sort((a, b) => b.metrics.activity_score - a.metrics.activity_score).slice(0, 24).map(pub), events: _events.slice(0, 20), hiring: { open, new_30d: ri(20, 400), removed_30d: ri(10, 300), momentum_30d: row.hiring_momentum_30d }, series: series(90, row.activity_score ?? 50, 6), countries: Object.entries(cc).map(([country, companies]) => ({ country, companies })).sort((a, b) => b.companies - a.companies), trending: trends.slice(0, 8) });1459 }1460 if (path === '/countries') return json(res, 200, { items: countryRows().map(({ _companies, _events, ...r }) => r) });1461 if (seg[0] === 'countries' && seg[1]) {1462 const code = seg[1].toUpperCase();1463 if (!COUNTRY_META[code]) return notFound(res, 'country not found');1464 const r = countryRow(code);1465 const { _companies, _events, ...row } = r;1466 const cs = r._companies;1467 return json(res, 200, { ...row, companies: [...cs].sort((a, b) => b.metrics.activity_score - a.metrics.activity_score).slice(0, 24).map(pub), events: _events.slice(0, 20), movers: [...cs].sort((a, b) => b.metrics.corporate_change_index - a.metrics.corporate_change_index).slice(0, 10).map(pub), new_entrants: [...cs].sort((a, b) => b._created - a._created).slice(0, 10).map(pub), series: series(90, row.activity_score ?? 50, 6), industries: industryRows().filter((i) => cs.some((c) => c.industries.includes(i.slug))).map(({ _companies, _events, ...x }) => x) });1468 }1469 if (path === '/signals') {1470 let list = signals;1471 if (q.get('kind')) list = list.filter((s) => s.kind === q.get('kind'));1472 if (q.get('scope')) list = list.filter((s) => s.scope === q.get('scope'));1473 return json(res, 200, { items: list.slice(0, clamp(Number(q.get('limit') ?? 50), 1, 200)) });1474 }1475 if (path === '/trends') return json(res, 200, { items: trends.slice(0, clamp(Number(q.get('limit') ?? 30), 1, 100)) });1476 if (path === '/map') return json(res, 200, { buckets: mapBuckets(q.get('metric') === 'companies' ? 'companies' : q.get('metric') === 'hiring' ? 'jobs_open' : 'events_30d') });1477 if (path === '/index') {1478 const s = history.map((h) => ({ day: h.day, value: h.activity_index, confidence: 0.9 }));1479 const last = s[s.length - 1].value;1480 const byType = {};1481 for (const e of events.filter((e) => e.detected_at > iso(NOW - 30 * DAY))) byType[e.event_type] = (byType[e.event_type] ?? 0) + 1;1482 return json(res, 200, { value: last, baseline: 100, delta_7d: r1(((last - s[s.length - 8].value) / s[s.length - 8].value) * 100), delta_30d: r1(((last - s[s.length - 31].value) / s[s.length - 31].value) * 100), series: s, by_type: byType, by_country: countryRows().slice(0, 12).map((c) => ({ key: c.code, value: r1(80 + rnd() * 60) })), by_industry: industryRows().slice(0, 12).map((i) => ({ key: i.slug, value: r1(80 + rnd() * 60) })), formula_version: 'gcai-v1.0' });1483 }14841485 // ---- search1486 if (path === '/search/suggest') {1487 const s = (q.get('q') ?? '').toLowerCase().trim();1488 const items = [];1489 if (s) {1490 for (const c of companies) if (c.display_name.toLowerCase().includes(s) || c.canonical_domain.includes(s)) items.push({ kind: 'company', label: c.display_name, sublabel: `${c.canonical_domain} · ${c.country}`, href: `/company/${c.slug}` });1491 for (const i of Object.values(IND)) if (i.name.toLowerCase().includes(s)) items.push({ kind: 'industry', label: i.name, sublabel: 'Industry', href: `/industry/${i.slug}` });1492 for (const [code, [name]] of Object.entries(COUNTRY_META)) if (name.toLowerCase().includes(s) || code.toLowerCase() === s) items.push({ kind: 'country', label: name, sublabel: code, href: `/country/${code.toLowerCase()}` });1493 for (const t of [...new Set(EVENT_TEMPLATES.map((t) => t[0]))]) if (t.toLowerCase().includes(s)) items.push({ kind: 'event_type', label: t, sublabel: 'Event type', href: `/events?event_type=${encodeURIComponent(t)}` });1494 }1495 return json(res, 200, { items: items.slice(0, 10) }, { 'cache-control': 'no-store' });1496 }1497 if (path === '/search') {1498 const s = (q.get('q') ?? '').toLowerCase().trim();1499 const t0 = performance.now();1500 const limit = clamp(Number(q.get('limit') ?? 10), 1, 50);1501 const words = s.split(/\s+/).filter(Boolean);1502 const hit = (txt) => words.some((w) => txt.toLowerCase().includes(w));1503 const cs = companies.filter((c) => hit(c.display_name) || hit(c.canonical_domain) || c.industries.some((i) => hit(IND[i].name)) || hit(COUNTRY_META[c.country][0]));1504 return json(res, 200, { query: q.get('q') ?? '', companies: cs.slice(0, limit).map(pub), events: events.filter((e) => e.status === 'active' && (hit(e.title) || hit(e.summary ?? ''))).slice(0, limit), industries: Object.keys(IND).filter((k) => hit(IND[k].name)).map(industryRow).map(({ _companies, _events, ...r }) => r), countries: Object.keys(COUNTRY_META).filter((k) => hit(COUNTRY_META[k][0])).map(countryRow).map(({ _companies, _events, ...r }) => r), people: companies.flatMap((c) => c._people.filter((p) => hit(p.name) || hit(p.title ?? '')).map((p) => ({ ...p, company: ref(c) }))).slice(0, limit), products: companies.flatMap((c) => c._products.filter((p) => hit(p.name)).map((p) => ({ ...p, company: ref(c) }))).slice(0, limit), took_ms: Math.round(performance.now() - t0) }, { 'cache-control': 'no-store' });1505 }1506 if (path === '/ask') {1507 const s = (q.get('q') ?? '').toLowerCase();1508 const filters = {};1509 for (const [code, [name]] of Object.entries(COUNTRY_META)) if (s.includes(name.toLowerCase())) filters.country = code;1510 for (const i of Object.values(IND)) if (s.includes(i.name.toLowerCase())) filters.industry = i.slug;1511 if (/\bai\b|machine learning|llm/.test(s)) filters.ai = true;1512 if (/hiring|jobs|engineer/.test(s)) filters.event_type = 'HIRING';1513 if (/pric/.test(s)) filters.event_type = 'PRICING';1514 if (/office|expan|countr/.test(s)) filters.event_type = 'LOCATION';1515 if (/launch|product/.test(s)) filters.event_type = 'PRODUCT';1516 if (/leader|exec|ceo|cto/.test(s)) filters.event_type = 'LEADERSHIP';1517 let cs = companies.filter((c) => (!filters.country || c.country === filters.country) && (!filters.industry || c.industries.includes(filters.industry)));1518 if (filters.ai) cs = cs.filter((c) => (c.metrics.ai_adoption ?? 0) > 40 || c._jobs.some((j) => j.is_ai && j.status === 'open'));1519 cs.sort((a, b) => b.metrics.activity_score - a.metrics.activity_score);1520 let evs = events.filter((e) => e.status === 'active' && (!filters.event_type || e.event_type === filters.event_type) && (!filters.country || e.company.country === filters.country) && (!filters.industry || companiesBySlug.get(e.company.slug)?.industries.includes(filters.industry)));1521 const answer = cs.length ? `${cs.length} monitored ${cs.length === 1 ? 'company matches' : 'companies match'} this question${filters.country ? ` in ${COUNTRY_META[filters.country][0]}` : ''}${filters.industry ? ` (${IND[filters.industry].name})` : ''}. ${evs.length ? `${Math.min(evs.length, 200)} related structured events were detected in the monitored record; the most recent are listed below with their sources.` : 'No related structured events were detected yet.'} This is a routed structured query over observed public pages, not an opinion.` : 'No monitored company matches this question yet. Try a broader industry or country, or search company names directly.';1522 return json(res, 200, { interpretation: filters, answer, companies: cs.slice(0, 10).map(pub), events: evs.slice(0, 10), sources: evs.slice(0, 10).map((e) => e.source_url).filter(Boolean) }, { 'cache-control': 'no-store' });1523 }15241525 // ---- watchlist & alerts1526 if (path.startsWith('/watchlist') || path.startsWith('/alerts')) {1527 if (!owner || String(owner).length < 24) return json(res, 401, { detail: 'X-CA-Owner-Token required (≥ 24 chars)' });1528 const key = String(owner);1529 if (!watchlists.has(key)) watchlists.set(key, new Set());1530 if (!alerts.has(key)) alerts.set(key, []);1531 const wl = watchlists.get(key);1532 if (path === '/watchlist' && req.method === 'GET') {1533 const cs = [...wl].map((s) => companiesBySlug.get(s)).filter(Boolean);1534 const evs = events.filter((e) => e.status === 'active' && wl.has(e.company.slug)).slice(0, 30);1535 return json(res, 200, { items: cs.map(pub), events: evs }, { 'cache-control': 'no-store' });1536 }1537 if (path === '/watchlist' && req.method === 'POST') {1538 const body = await readBody(req);1539 const c = companiesBySlug.get(body.company);1540 if (!c) return notFound(res, 'company not found');1541 wl.add(c.slug);1542 return json(res, 201, { ok: true, company: c.slug, items: wl.size });1543 }1544 if (seg[0] === 'watchlist' && seg[1] && req.method === 'DELETE') {1545 wl.delete(decodeURIComponent(seg[1]));1546 return json(res, 200, { ok: true, items: wl.size });1547 }1548 const al = alerts.get(key);1549 if (path === '/alerts' && req.method === 'GET') return json(res, 200, { items: al }, { 'cache-control': 'no-store' });1550 if (path === '/alerts' && req.method === 'POST') {1551 const body = await readBody(req);1552 if (!body.name) return json(res, 422, { detail: 'name required' });1553 const a = { id: id('alr'), name: body.name, company: body.company ?? null, condition: body.condition ?? {}, channel: body.channel === 'webhook' ? 'webhook' : 'web', target: body.target ?? null, created_at: new Date().toISOString(), status: 'active' };1554 al.push(a);1555 return json(res, 201, a);1556 }1557 if (path === '/alerts/deliveries') {1558 const items = al.slice(0, 5).flatMap((a) => events.filter((e) => e.status === 'active' && (!a.company || e.company.slug === a.company) && (!a.condition.event_types?.length || a.condition.event_types.includes(e.event_type))).slice(0, 4).map((e) => ({ id: id('dlv'), alert_id: a.id, alert_name: a.name, event_id: e.id, event: e, channel: a.channel, status: 'delivered', delivered_at: e.detected_at })));1559 return json(res, 200, { items: items.slice(0, clamp(Number(q.get('limit') ?? 50), 1, 200)) }, { 'cache-control': 'no-store' });1560 }1561 if (seg[0] === 'alerts' && seg[1] && req.method === 'DELETE') {1562 alerts.set(key, al.filter((a) => a.id !== seg[1]));1563 return json(res, 200, { ok: true });1564 }1565 }15661567 // ---- exports & docs1568 if (seg[0] === 'export') {1569 const [name, fmt] = (seg[1] ?? '').split('.');1570 let rows = [];1571 if (name === 'events') rows = filterEvents(events.filter((e) => e.status === 'active'), q).slice(0, clamp(Number(q.get('limit') ?? 10000), 1, 10000)).map(({ sources, payload, entities, company, ...e }) => ({ ...e, company_slug: company.slug, company: company.display_name, tags: e.tags.join('|') }));1572 else if (name === 'companies') rows = companies.filter((c) => (!q.get('country') || c.country === q.get('country').toUpperCase()) && (!q.get('industry') || c.industries.includes(q.get('industry')))).map((c) => ({ ...pub(c), industries: c.industries.join('|'), metrics: undefined, counts: undefined, sparkline: undefined, activity_score: c.metrics.activity_score, sensors: c.counts.sensors, events: c.counts.events }));1573 else if (name === 'jobs') rows = companies.filter((c) => !q.get('company') || c.slug === q.get('company')).flatMap((c) => c._jobs.map((j) => ({ ...j, company: c.slug })));1574 else return notFound(res);1575 if (fmt === 'csv') {1576 res.writeHead(200, { 'content-type': 'text/csv; charset=utf-8', 'content-disposition': `attachment; filename="${name}.csv"` });1577 return res.end(csv(rows));1578 }1579 if (fmt === 'ndjson') {1580 res.writeHead(200, { 'content-type': 'application/x-ndjson' });1581 return res.end(rows.map((r) => JSON.stringify(r)).join('\n'));1582 }1583 return json(res, 200, { items: rows });1584 }1585 if (path === '/sitemap') {1586 const kind = q.get('kind') ?? 'companies';1587 const page = Number(q.get('page') ?? 0);1588 const items = kind === 'companies' ? companies.filter((c) => c.counts.events > 2).map((c) => ({ slug: c.slug, updated_at: c.last_event_at })) : kind === 'industries' ? industryRows().map((i) => ({ slug: i.slug, updated_at: null })) : countryRows().map((c) => ({ slug: c.code.toLowerCase(), updated_at: null }));1589 const per = 5000;1590 return json(res, 200, { items: items.slice(page * per, (page + 1) * per), pages: Math.max(1, Math.ceil(items.length / per)) });1591 }1592 if (path === '/methodology') {1593 return json(res, 200, {1594 metrics: [1595 { metric: 'activity_score', formula_version: 'v1.0', description: 'Coverage-normalised rate of meaningful changes and structured events across a company’s monitored surfaces over 30 days, weighted by surface importance, scaled 0–100 against the population baseline.', inputs: ['meaningful_changes_30d', 'events_30d', 'surface_weights', 'sensors_active', 'population_baseline'] },1596 { metric: 'hiring_momentum_30d', formula_version: 'v1.0', description: 'Percentage change in publicly listed open roles over 30 days, adjusted for listings that disappeared and re-appeared within 48 h.', inputs: ['jobs_open_t0', 'jobs_open_t1', 'jobs_new', 'jobs_removed'] },1597 { metric: 'product_velocity', formula_version: 'v1.0', description: 'Rate of product, documentation, changelog and API events per 90 days, normalised by the number of product-related sensors.', inputs: ['product_events_90d', 'developer_events_90d', 'sensors_product'] },1598 { metric: 'ai_adoption', formula_version: 'v1.1', description: 'Observable public AI signals only: AI-tagged listings share, AI product pages, AI mentions in documentation and first-party communications. Never claims internal usage.', inputs: ['ai_jobs_share', 'ai_products', 'ai_docs_mentions', 'ai_news_mentions'] },1599 { metric: 'geo_expansion', formula_version: 'v1.0', description: 'New countries and cities appearing on locations and careers surfaces over 90 days.', inputs: ['new_countries_90d', 'new_cities_90d', 'jobs_new_countries'] },1600 { metric: 'developer_momentum', formula_version: 'v1.0', description: 'Documentation, changelog and API surface change rate; public repository activity where available.', inputs: ['docs_changes_90d', 'changelog_entries_90d', 'api_events_90d'] },1601 { metric: 'corporate_change_index', formula_version: 'v1.0', description: '0.25 hiring + 0.20 product + 0.15 geographic + 0.15 leadership + 0.10 developer + 0.10 communication + 0.05 pricing; weights are provisional.', inputs: ['hiring_momentum_30d', 'product_velocity', 'geo_expansion', 'leadership_activity', 'developer_momentum', 'communication_activity', 'pricing_activity'] },1602 { metric: 'anomaly_score', formula_version: 'v1.0', description: 'Deviation of the last 7 days from the company’s own 90-day baseline of changes and events (z-score mapped to 0–100).', inputs: ['baseline_changes_per_week', 'changes_7d', 'events_7d'] },1603 { metric: 'historical_coverage', formula_version: 'v1.0', description: 'Historical Completeness Score: share of days since onboarding with at least one successful observation per active surface, penalised for failed periods.', inputs: ['days_observed', 'days_since_onboarding', 'sensor_uptime', 'failed_periods'] },1604 ],1605 significance_bands: [1606 { label: 'noise', min: 0, max: 0.2 },1607 { label: 'minor', min: 0.2, max: 0.4 },1608 { label: 'meaningful', min: 0.4, max: 0.65 },1609 { label: 'major', min: 0.65, max: 0.85 },1610 { label: 'critical', min: 0.85, max: 1 },1611 ],1612 event_types: [...new Set(EVENT_TEMPLATES.map((t) => t[0]))],1613 confidence_labels: { VERIFIED: 'Confirmed by two independent first-party surfaces or a structured feed.', HIGH_CONFIDENCE: 'Deterministic extraction from a structured surface, single source.', LIKELY: 'Deterministic extraction from unstructured HTML with a stable block identity.', INFERRED: 'Interpretation produced by an enrichment model from a deterministic change.', LOW_CONFIDENCE: 'Weak or partially corroborated signal; kept for transparency.' },1614 });1615 }16161617 // ---- admin1618 if (seg[0] === 'admin') {1619 if (!admin || String(admin) !== adminToken) return json(res, 401, { detail: 'invalid admin token' });1620 const sub = seg[1];1621 if (sub === 'overview') {1622 const byS = {};1623 const byT = {};1624 for (const s of sensors.values()) {1625 byS[s.status] = (byS[s.status] ?? 0) + 1;1626 byT[s.tier] = (byT[s.tier] ?? 0) + 1;1627 }1628 const failures = {};1629 for (const f of FAILURE_CLASSES) failures[f] = ri(0, 60);1630 return json(res, 200, { companies_by_status: { ACTIVE: companies.filter((c) => c.status === 'ACTIVE').length, POSSIBLY_INACTIVE: companies.filter((c) => c.status !== 'ACTIVE').length }, sensors_by_status: byS, sensors_by_tier: byT, queue: { pending: ri(120, 900), running: ri(8, 32), dead: ri(0, 14), oldest_pending_s: ri(5, 600) }, llm: { pending: ri(0, 40), done_today: ri(80, 400), failed_today: ri(0, 6), budget_left: r1(rnd() * 100) }, failures_24h_by_class: failures, fetch_rate_1h: ri(15000, 26000), change_rate_1h: ri(600, 1400), meaningful_rate_1h: ri(120, 400), storage: stats().archive, workers: ['M2U64-w1', 'M2U64-w2', 'M2U64-w3', 'M2U64-browser'].map((name) => ({ name, last_seen_at: iso(Date.now() - ri(1, 60) * 1000), inflight: ri(0, 8) })), cost_today: { fetch: r1(rnd() * 3), browser: r1(rnd() * 2), llm: r1(rnd() * 6) } });1631 }1632 if (sub === 'connectors') return json(res, 200, { items: CONNECTORS.map(([cid, name, version, category]) => ({ id: cid, name, version, category, enabled: cid !== 'lever_connector', sensors_active: [...sensors.values()].filter((s) => s.connector_id === cid && s.status === 'active').length, sensors_failing: [...sensors.values()].filter((s) => s.connector_id === cid && s.status === 'failing').length, success_rate_24h: r1(90 + rnd() * 10), avg_latency_ms: ri(200, 2400), change_rate_24h: r1(rnd() * 12), errors_24h: ri(0, 40), last_run_at: iso(Date.now() - ri(1, 400) * 1000) })) });1633 if (sub === 'sensors' && !seg[2]) {1634 let list = [...sensors.values()];1635 const g = (k) => q.get(k);1636 if (g('status')) list = list.filter((s) => s.status === g('status'));1637 if (g('domain')) list = list.filter((s) => s.domain.includes(g('domain')));1638 if (g('connector')) list = list.filter((s) => s.connector_id === g('connector'));1639 if (g('company')) list = list.filter((s) => companies.find((c) => c.id === s.company_id)?.slug === g('company'));1640 const f = g('filter');1641 if (f === 'healthy') list = list.filter((s) => s.status === 'active' && s.consecutive_failures === 0);1642 if (f === 'failing') list = list.filter((s) => s.status === 'failing');1643 if (f === 'stale') list = list.filter((s) => new Date(s.last_success_at).getTime() < NOW - 2 * DAY);1644 if (f === 'blocked') list = list.filter((s) => s.last_failure_class === 'BOT_CHALLENGE' || s.last_status === 403);1645 if (f === 'redirected') list = list.filter((s) => s.last_failure_class === 'REDIRECT');1646 if (f === 'low_quality') list = list.filter((s) => s.quality_score < 55);1647 if (f === 'high_activity') list = list.filter((s) => s.change_count > 8);1648 const page = paginate(list, q, 50);1649 page.items = page.items.map((s) => ({ ...pubSensor(s), company: ref(companies.find((c) => c.id === s.company_id)) }));1650 return json(res, 200, page);1651 }1652 if (sub === 'sensors' && seg[2] && seg[3] && req.method === 'POST') {1653 const s = sensors.get(seg[2]);1654 if (!s) return notFound(res, 'sensor not found');1655 const body = await readBody(req);1656 const action = seg[3];1657 if (action === 'pause') s.status = 'paused';1658 if (action === 'resume') s.status = 'active';1659 if (action === 'retire') s.status = 'retired';1660 if (action === 'retry' || action === 'run_now') {1661 s.status = 'active';1662 s.consecutive_failures = 0;1663 s.next_run_at = new Date().toISOString();1664 }1665 if (action === 'set_interval' && body.interval_s) s.current_interval_s = Number(body.interval_s);1666 if (action === 'set_connector' && body.connector_id) s.connector_id = body.connector_id;1667 return json(res, 200, { ok: true, sensor: pubSensor(s), action });1668 }1669 if (sub === 'companies' && !seg[2]) {1670 if (req.method === 'POST') {1671 const body = await readBody(req);1672 if (!body.website) return json(res, 422, { detail: 'website required' });1673 const domain = String(body.website).replace(/^https?:\/\//, '').replace(/\/.*$/, '');1674 const c = buildCompany([body.display_name || domain.split('.')[0], domain, (body.country || 'US').toUpperCase(), 'Unknown', 0, 0, body.industries?.length ? body.industries : ['technology'], false, null, null, null, null, 30, null], companies.length);1675 c.onboarding_status = 'pending';1676 return json(res, 201, pub(c));1677 }1678 let list = [...companies];1679 if (q.get('onboarding_status')) list = list.filter((c) => c.onboarding_status === q.get('onboarding_status'));1680 const page = paginate(list, q, 50);1681 page.items = page.items.map(pub);1682 return json(res, 200, page);1683 }1684 if (sub === 'companies' && seg[2] && seg[3] === 'rediscover') return json(res, 200, { ok: true, queued: true });1685 if (sub === 'failures') {1686 const items = Array.from({ length: 120 }, () => {1687 const s = pick([...sensors.values()]);1688 return { id: id('fail'), sensor_id: s.id, company: ref(companies.find((c) => c.id === s.company_id)), domain: s.domain, failure_class: pick(FAILURE_CLASSES), status_code: pick([0, 403, 404, 429, 500, 503]), message: pick(['connect timeout after 20 s', 'challenge page detected (cf-mitigated)', 'schema validation failed: jobs[3].title missing', 'redirected to /careers-new (301)', 'rate limited by domain governor', 'dns: NXDOMAIN']), occurred_at: iso(Date.now() - ri(1, 1440) * 60_000), retry_at: iso(Date.now() + ri(5, 600) * 60_000) };1689 }).sort((a, b) => b.occurred_at.localeCompare(a.occurred_at));1690 return json(res, 200, paginate(q.get('class') ? items.filter((i) => i.failure_class === q.get('class')) : items, q, 50));1691 }1692 if (sub === 'queue' && !seg[2]) {1693 const items = Array.from({ length: 80 }, () => ({ id: id('job'), kind: pick(['fetch', 'fetch', 'fetch', 'discover', 'enrich', 'metrics', 'daily']), status: pick(['pending', 'pending', 'running', 'done', 'dead']), priority: ri(1, 100), attempts: ri(0, 4), scheduled_at: iso(Date.now() - ri(0, 3600) * 1000), started_at: chance(0.5) ? iso(Date.now() - ri(0, 600) * 1000) : null, finished_at: null, worker: chance(0.5) ? pick(['M2U64-w1', 'M2U64-w2', 'M2U64-w3']) : null, ref: pick([...sensors.keys()]), error: chance(0.1) ? 'TIMEOUT' : null }));1694 const filtered = items.filter((i) => (!q.get('kind') || i.kind === q.get('kind')) && (!q.get('status') || i.status === q.get('status')));1695 const counts = {};1696 for (const i of items) counts[i.status] = (counts[i.status] ?? 0) + 1;1697 return json(res, 200, { items: filtered, counts });1698 }1699 if (sub === 'queue' && seg[2] === 'requeue-dead') return json(res, 200, { ok: true, requeued: ri(0, 14) });1700 if (sub === 'llm') {1701 const items = Array.from({ length: 60 }, () => ({ id: id('llm'), kind: pick(['classify', 'summarize', 'extract_event', 'industry_tag']), status: pick(['done', 'done', 'done', 'pending', 'failed']), model: pick(['qwen3-4b-instruct-2507-4bit', 'qwen3.6-35b-a3b-4bit']), prompt_version: pick(['event-classifier/v3', 'event-summary/v2']), change_id: pick([...changes.keys()]), event_id: chance(0.7) ? pick(events).id : null, tokens_in: ri(400, 6000), tokens_out: ri(50, 600), cost_estimate: r1(rnd() * 0.02 * 100) / 100, created_at: iso(Date.now() - ri(0, 1440) * 60_000), finished_at: chance(0.8) ? iso(Date.now() - ri(0, 1000) * 60_000) : null, error: chance(0.1) ? 'schema validation failed' : null }));1702 return json(res, 200, paginate(q.get('status') ? items.filter((i) => i.status === q.get('status')) : items, q, 50));1703 }1704 if (sub === 'reviews' && !seg[2]) {1705 const items = Array.from({ length: 24 }, () => {1706 const e = pick(events);1707 return { id: id('rev'), kind: pick(['major_event', 'low_confidence_extraction', 'company_merge', 'sensor_migration', 'legal_sensitive']), status: chance(0.8) ? 'open' : 'resolved', subject: e.title, ref_id: e.id, company: e.company, reason: pick(['importance ≥ 0.9', 'confidence < 0.55', 'two companies share a domain', 'URL moved; content identity uncertain', 'mentions litigation']), created_at: iso(Date.now() - ri(1, 5000) * 60_000), resolved_at: null, resolution: null };1708 });1709 return json(res, 200, { items: q.get('status') ? items.filter((i) => i.status === q.get('status')) : items });1710 }1711 if (sub === 'reviews' && seg[2] && req.method === 'POST') return json(res, 200, { ok: true, id: seg[2], ...(await readBody(req)) });1712 if (sub === 'events' && seg[2] && seg[3] && req.method === 'POST') {1713 const e = eventsById.get(seg[2]);1714 if (!e) return notFound(res, 'event not found');1715 e.status = seg[3] === 'retract' ? 'retracted' : 'active';1716 return json(res, 200, { ok: true, status: e.status });1717 }1718 if (sub === 'quality') return json(res, 200, { coverage: { companies_active_pct: r1(92 + rnd() * 6), sensors_active_pct: r1(84 + rnd() * 8) }, freshness: { sensors_checked_24h_pct: r1(88 + rnd() * 10), stale: ri(20, 140) }, duplicate_rate: r1(rnd() * 4), event_confidence_avg: r1(0.7 + rnd() * 0.2), unknown_surfaces: ri(30, 200), failed_sensors: [...sensors.values()].filter((s) => s.status === 'failing').length, calibration: { correct: ri(300, 600), duplicate: ri(5, 40), noise: ri(10, 60), misclassified: ri(3, 30) } });1719 if (sub === 'costs') {1720 const days = clamp(Number(q.get('days') ?? 30), 1, 90);1721 const items = [];1722 for (let i = days - 1; i >= 0; i--) for (const [dimension, key] of [['fetch', 'http'], ['fetch', 'browser'], ['llm', 'qwen3.6-35b'], ['storage', 'objects']]) items.push({ day: day(NOW - i * DAY), dimension, key, units: ri(1000, 30000), cost_estimate: r1(rnd() * 8 * 100) / 100 });1723 return json(res, 200, { items, per_1000_companies: r1(rnd() * 40 + 10), per_million_observations: r1(rnd() * 6 + 1), per_meaningful_event: r1(rnd() * 0.05 * 1000) / 1000 });1724 }1725 if (sub === 'cache' && seg[2] === 'clear') return json(res, 200, { ok: true, cleared: ri(4, 40) });1726 }1727 return notFound(res);1728 } catch (e) {1729 console.error(e);1730 return json(res, 500, { detail: `mock error: ${e.message}` });1731 }1732});17331734server.listen(PORT, '127.0.0.1', () => {1735 console.log(`Company Atlas mock API on http://127.0.0.1:${PORT}/api/v1 — ${companies.length} companies, ${sensors.size} sensors, ${events.length} events, ${changes.size} changes. Admin token: ${adminToken}`);1736});1737