/** * WebSensor taxonomy. Event types are open (strings) so the set can grow without * migrations, but everything we emit ourselves comes from this list and carries an * intrinsic severity used by the importance model. */ export const SENSOR_TYPES = [ "WEBHOOK", "WEBSOCKET", "SSE", "REST_API", "GRAPHQL", "RSS", "ATOM", "JSON", "XML", "SITEMAP", "HTML", "RENDERED_HTML", "PDF_INDEX", "GITHUB_RELEASE", "GITHUB_REPO", "STATUSPAGE", "DNS", "TLS", "HTTP_HEADERS", "FILE", "CUSTOM_CONNECTOR", ] as const; export type SensorType = (typeof SENSOR_TYPES)[number]; export const TIERS = ["S", "A", "B", "C", "D"] as const; export type Tier = (typeof TIERS)[number]; /** Base polling interval per tier (seconds). */ export const TIER_BASE_INTERVAL: Record = { S: 60, A: 180, B: 900, C: 3600, D: 21600, }; export const TIER_MIN_INTERVAL: Record = { S: 15, A: 60, B: 300, C: 1800, D: 21600, }; export const TIER_MAX_INTERVAL: Record = { S: 300, A: 1800, B: 7200, C: 21600, D: 86400, }; export const CATEGORIES = [ "ai", "cloud", "developer", "cyber", "consumer-tech", "semiconductors", "finance", "government", "statistics", "health", "pharma", "science", "space", "automotive", "commerce", "payments", "crypto", "enterprise", "internet", "standards", "technology", "news", "media", "politics", ] as const; export type Category = (typeof CATEGORIES)[number] | string; /** Public feed channels → categories that map to them. */ export const FEED_CHANNELS: Record = { ai: ["ai"], cyber: ["cyber"], finance: ["finance", "payments", "crypto", "commerce"], health: ["health", "pharma"], government: ["government", "statistics"], science: ["science", "space"], products: ["consumer-tech", "automotive", "semiconductors", "enterprise"], infrastructure: ["cloud", "developer", "internet", "standards"], news: ["news", "media"], }; export interface EventTypeSpec { /** Intrinsic severity 0–100 used as the 25 % component of importance. */ severity: number; label: string; /** Does this type usually come with a public announcement? Used for silent-change detection. */ usuallyAnnounced: boolean; } export const EVENT_TYPES: Record = { announcement: { severity: 55, label: "Announcement", usuallyAnnounced: true }, product_launch: { severity: 80, label: "Product launch", usuallyAnnounced: true }, product_update: { severity: 55, label: "Product update", usuallyAnnounced: true }, model_release: { severity: 88, label: "Model release", usuallyAnnounced: true }, software_release: { severity: 55, label: "Software release", usuallyAnnounced: true }, pricing_change: { severity: 82, label: "Pricing change", usuallyAnnounced: false }, availability_change: { severity: 62, label: "Availability change", usuallyAnnounced: false }, policy_change: { severity: 68, label: "Policy change", usuallyAnnounced: false }, terms_change: { severity: 66, label: "Terms change", usuallyAnnounced: false }, documentation_change: { severity: 30, label: "Documentation change", usuallyAnnounced: false }, API_change: { severity: 62, label: "API change", usuallyAnnounced: false }, security_advisory: { severity: 86, label: "Security advisory", usuallyAnnounced: true }, vulnerability: { severity: 90, label: "Vulnerability", usuallyAnnounced: true }, breach: { severity: 95, label: "Breach", usuallyAnnounced: true }, incident: { severity: 78, label: "Incident", usuallyAnnounced: true }, outage: { severity: 90, label: "Outage", usuallyAnnounced: true }, maintenance: { severity: 35, label: "Maintenance", usuallyAnnounced: true }, recall: { severity: 85, label: "Recall", usuallyAnnounced: true }, drug_approval: { severity: 88, label: "Drug approval", usuallyAnnounced: true }, clinical_trial: { severity: 60, label: "Clinical trial", usuallyAnnounced: true }, scientific_publication: { severity: 45, label: "Scientific publication", usuallyAnnounced: true }, regulatory_filing: { severity: 65, label: "Regulatory filing", usuallyAnnounced: true }, financial_filing: { severity: 68, label: "Financial filing", usuallyAnnounced: true }, earnings: { severity: 75, label: "Earnings", usuallyAnnounced: true }, leadership_change: { severity: 72, label: "Leadership change", usuallyAnnounced: true }, acquisition: { severity: 85, label: "Acquisition", usuallyAnnounced: true }, funding: { severity: 70, label: "Funding", usuallyAnnounced: true }, partnership: { severity: 60, label: "Partnership", usuallyAnnounced: true }, job_expansion: { severity: 40, label: "Job expansion", usuallyAnnounced: false }, layoffs: { severity: 78, label: "Layoffs", usuallyAnnounced: true }, new_region: { severity: 62, label: "New region", usuallyAnnounced: true }, new_country: { severity: 65, label: "New country", usuallyAnnounced: true }, infrastructure_change: { severity: 50, label: "Infrastructure change", usuallyAnnounced: false }, api_change: { severity: 62, label: "API change", usuallyAnnounced: false }, dns_change: { severity: 48, label: "DNS change", usuallyAnnounced: false }, domain_registration_change: { severity: 45, label: "Domain registration change", usuallyAnnounced: false }, crawler_policy_change: { severity: 52, label: "Crawler policy change", usuallyAnnounced: false }, DNS_change: { severity: 45, label: "DNS change", usuallyAnnounced: false }, certificate_change: { severity: 30, label: "Certificate change", usuallyAnnounced: false }, repository_release: { severity: 50, label: "Repository release", usuallyAnnounced: true }, dataset_release: { severity: 55, label: "Dataset release", usuallyAnnounced: true }, standard_update: { severity: 58, label: "Standard update", usuallyAnnounced: true }, government_announcement: { severity: 65, label: "Government announcement", usuallyAnnounced: true }, economic_release: { severity: 80, label: "Economic release", usuallyAnnounced: true }, legal_change: { severity: 70, label: "Legal change", usuallyAnnounced: true }, monetary_policy: { severity: 92, label: "Monetary policy", usuallyAnnounced: true }, page_created: { severity: 35, label: "Page created", usuallyAnnounced: false }, page_removed: { severity: 40, label: "Page removed", usuallyAnnounced: false }, content_change: { severity: 25, label: "Content change", usuallyAnnounced: false }, unknown: { severity: 20, label: "Unknown", usuallyAnnounced: false }, // ---- 2026-09-11: cyber event classes ---- zero_day: { severity: 97, label: "Zero-day", usuallyAnnounced: true }, active_exploitation: { severity: 96, label: "Active exploitation", usuallyAnnounced: true }, patch_release: { severity: 70, label: "Patch release", usuallyAnnounced: true }, supply_chain_attack: { severity: 95, label: "Supply-chain attack", usuallyAnnounced: true }, credential_leak: { severity: 88, label: "Credential leak", usuallyAnnounced: true }, malware_campaign: { severity: 80, label: "Malware campaign", usuallyAnnounced: true }, ransomware: { severity: 88, label: "Ransomware", usuallyAnnounced: true }, // ---- finance ---- merger: { severity: 85, label: "Merger", usuallyAnnounced: true }, ipo: { severity: 80, label: "IPO", usuallyAnnounced: true }, guidance: { severity: 78, label: "Guidance", usuallyAnnounced: true }, dividend: { severity: 60, label: "Dividend", usuallyAnnounced: true }, buyback: { severity: 62, label: "Buyback", usuallyAnnounced: true }, bankruptcy: { severity: 92, label: "Bankruptcy", usuallyAnnounced: true }, capital_raise: { severity: 70, label: "Capital raise", usuallyAnnounced: true }, rating_change: { severity: 74, label: "Rating change", usuallyAnnounced: true }, insider_transaction: { severity: 45, label: "Insider transaction", usuallyAnnounced: true }, // ---- government / legal ---- regulatory_action: { severity: 76, label: "Regulatory action", usuallyAnnounced: true }, lawsuit: { severity: 70, label: "Lawsuit", usuallyAnnounced: true }, court_decision: { severity: 74, label: "Court decision", usuallyAnnounced: true }, sanction: { severity: 84, label: "Sanction", usuallyAnnounced: true }, legislation: { severity: 72, label: "Legislation", usuallyAnnounced: true }, budget: { severity: 74, label: "Budget", usuallyAnnounced: true }, procurement: { severity: 45, label: "Procurement", usuallyAnnounced: true }, appointment: { severity: 62, label: "Appointment", usuallyAnnounced: true }, emergency_notice: { severity: 90, label: "Emergency notice", usuallyAnnounced: true }, // ---- health / science ---- outbreak: { severity: 90, label: "Outbreak", usuallyAnnounced: true }, drug_warning: { severity: 82, label: "Drug warning", usuallyAnnounced: true }, device_recall: { severity: 82, label: "Device recall", usuallyAnnounced: true }, paper_release: { severity: 45, label: "Paper", usuallyAnnounced: true }, retraction: { severity: 70, label: "Retraction", usuallyAnnounced: true }, space_mission: { severity: 68, label: "Space mission", usuallyAnnounced: true }, // ---- transport ---- accident: { severity: 88, label: "Accident", usuallyAnnounced: true }, grounding: { severity: 85, label: "Grounding", usuallyAnnounced: true }, safety_bulletin: { severity: 72, label: "Safety bulletin", usuallyAnnounced: true }, route_change: { severity: 50, label: "Route change", usuallyAnnounced: true }, production_delay: { severity: 65, label: "Production delay", usuallyAnnounced: true }, // ---- product / service ---- service_launch: { severity: 70, label: "Service launch", usuallyAnnounced: true }, service_shutdown: { severity: 78, label: "Service shutdown", usuallyAnnounced: false }, feature_removed: { severity: 66, label: "Feature removed", usuallyAnnounced: false }, domain_change: { severity: 50, label: "Domain change", usuallyAnnounced: false }, sports_transaction: { severity: 40, label: "Transaction", usuallyAnnounced: true }, sports_result: { severity: 25, label: "Result", usuallyAnnounced: true }, suspension: { severity: 45, label: "Suspension", usuallyAnnounced: true }, schedule_change: { severity: 35, label: "Schedule change", usuallyAnnounced: true }, }; export function eventTypeSpec(type: string): EventTypeSpec { return EVENT_TYPES[type] ?? EVENT_TYPES.unknown!; } /** Event types whose silent modification is a signal (pricing quietly changed, terms rewritten…). */ export const SILENT_ELIGIBLE_TYPES = new Set([ "pricing_change", "terms_change", "policy_change", "API_change", "api_change", "availability_change", "documentation_change", "service_shutdown", "feature_removed", "leadership_change", "page_removed", "crawler_policy_change", "product_update", "infrastructure_change", ]); /** Groups used by filters, category desks and the explore page. */ export const EVENT_GROUPS: Record = { security: { label: "Security", types: ["zero_day", "active_exploitation", "vulnerability", "security_advisory", "breach", "supply_chain_attack", "credential_leak", "malware_campaign", "ransomware", "patch_release"] }, reliability: { label: "Reliability", types: ["outage", "incident", "maintenance", "infrastructure_change", "dns_change", "DNS_change", "certificate_change"] }, product: { label: "Product & API", types: ["product_launch", "product_update", "model_release", "software_release", "repository_release", "service_launch", "service_shutdown", "feature_removed", "API_change", "api_change", "availability_change", "new_region", "new_country", "documentation_change"] }, commercial: { label: "Pricing & terms", types: ["pricing_change", "terms_change", "policy_change", "crawler_policy_change"] }, corporate: { label: "Corporate", types: ["earnings", "guidance", "financial_filing", "acquisition", "merger", "ipo", "funding", "capital_raise", "dividend", "buyback", "bankruptcy", "rating_change", "insider_transaction", "leadership_change", "appointment", "layoffs", "job_expansion", "partnership"] }, government: { label: "Government & legal", types: ["government_announcement", "regulatory_filing", "regulatory_action", "legislation", "budget", "procurement", "sanction", "lawsuit", "court_decision", "legal_change", "emergency_notice", "monetary_policy", "economic_release"] }, science: { label: "Science & health", types: ["scientific_publication", "paper_release", "retraction", "dataset_release", "clinical_trial", "drug_approval", "drug_warning", "recall", "device_recall", "outbreak", "space_mission", "standard_update"] }, transport: { label: "Transport", types: ["accident", "grounding", "safety_bulletin", "route_change", "production_delay"] }, sports: { label: "Sports", types: ["sports_transaction", "sports_result", "suspension", "schedule_change"] }, web: { label: "Web", types: ["announcement", "page_created", "page_removed", "content_change", "domain_change", "domain_registration_change", "unknown"] }, }; export function eventGroupOf(type: string): string { for (const [g, spec] of Object.entries(EVENT_GROUPS)) if (spec.types.includes(type)) return g; return "web"; } /** Semantic classes assigned to every raw change by the semantic diff stage. */ export const CHANGE_CLASSES = ["meaningful", "pricing", "policy", "product", "personnel", "cosmetic", "navigation", "timestamp", "advertisement", "boilerplate"] as const; export type ChangeClass = (typeof CHANGE_CLASSES)[number]; export const NOISE_CLASSES = new Set(["cosmetic", "navigation", "timestamp", "advertisement", "boilerplate"]); /** Cluster / breaking states. */ export const CLUSTER_STATES = ["breaking", "developing", "confirmed", "watching", "closed"] as const; export type ClusterState = (typeof CLUSTER_STATES)[number]; /** Priority tiers for scheduling (spec §76). Derived from tier + category. */ export const PRIORITY_LABELS: Record = { 0: "TIER 0 · critical", 1: "TIER 1 · major", 2: "TIER 2 · specialized", 3: "TIER 3 · low urgency" }; /** Countries with dedicated desks. ISO 3166-1 alpha-2 (plus EU / INT). */ export const COUNTRIES: Record = { CA: { name: "Canada", slug: "canada", flag: "🇨🇦" }, US: { name: "United States", slug: "united-states", flag: "🇺🇸" }, EU: { name: "European Union", slug: "european-union", flag: "🇪🇺" }, GB: { name: "United Kingdom", slug: "united-kingdom", flag: "🇬🇧" }, FR: { name: "France", slug: "france", flag: "🇫🇷" }, DE: { name: "Germany", slug: "germany", flag: "🇩🇪" }, IT: { name: "Italy", slug: "italy", flag: "🇮🇹" }, ES: { name: "Spain", slug: "spain", flag: "🇪🇸" }, JP: { name: "Japan", slug: "japan", flag: "🇯🇵" }, KR: { name: "South Korea", slug: "south-korea", flag: "🇰🇷" }, AU: { name: "Australia", slug: "australia", flag: "🇦🇺" }, IN: { name: "India", slug: "india", flag: "🇮🇳" }, BR: { name: "Brazil", slug: "brazil", flag: "🇧🇷" }, MX: { name: "Mexico", slug: "mexico", flag: "🇲🇽" }, CH: { name: "Switzerland", slug: "switzerland", flag: "🇨🇭" }, NL: { name: "Netherlands", slug: "netherlands", flag: "🇳🇱" }, SE: { name: "Sweden", slug: "sweden", flag: "🇸🇪" }, NO: { name: "Norway", slug: "norway", flag: "🇳🇴" }, FI: { name: "Finland", slug: "finland", flag: "🇫🇮" }, DK: { name: "Denmark", slug: "denmark", flag: "🇩🇰" }, IE: { name: "Ireland", slug: "ireland", flag: "🇮🇪" }, BE: { name: "Belgium", slug: "belgium", flag: "🇧🇪" }, AT: { name: "Austria", slug: "austria", flag: "🇦🇹" }, PT: { name: "Portugal", slug: "portugal", flag: "🇵🇹" }, PL: { name: "Poland", slug: "poland", flag: "🇵🇱" }, CN: { name: "China", slug: "china", flag: "🇨🇳" }, HK: { name: "Hong Kong", slug: "hong-kong", flag: "🇭🇰" }, TW: { name: "Taiwan", slug: "taiwan", flag: "🇹🇼" }, SG: { name: "Singapore", slug: "singapore", flag: "🇸🇬" }, IL: { name: "Israel", slug: "israel", flag: "🇮🇱" }, AE: { name: "United Arab Emirates", slug: "uae", flag: "🇦🇪" }, SA: { name: "Saudi Arabia", slug: "saudi-arabia", flag: "🇸🇦" }, QA: { name: "Qatar", slug: "qatar", flag: "🇶🇦" }, ZA: { name: "South Africa", slug: "south-africa", flag: "🇿🇦" }, NG: { name: "Nigeria", slug: "nigeria", flag: "🇳🇬" }, KE: { name: "Kenya", slug: "kenya", flag: "🇰🇪" }, AR: { name: "Argentina", slug: "argentina", flag: "🇦🇷" }, CL: { name: "Chile", slug: "chile", flag: "🇨🇱" }, CO: { name: "Colombia", slug: "colombia", flag: "🇨🇴" }, NZ: { name: "New Zealand", slug: "new-zealand", flag: "🇳🇿" }, RU: { name: "Russia", slug: "russia", flag: "🇷🇺" }, TR: { name: "Türkiye", slug: "turkiye", flag: "🇹🇷" }, ID: { name: "Indonesia", slug: "indonesia", flag: "🇮🇩" }, TH: { name: "Thailand", slug: "thailand", flag: "🇹🇭" }, VN: { name: "Vietnam", slug: "vietnam", flag: "🇻🇳" }, MY: { name: "Malaysia", slug: "malaysia", flag: "🇲🇾" }, PH: { name: "Philippines", slug: "philippines", flag: "🇵🇭" }, EG: { name: "Egypt", slug: "egypt", flag: "🇪🇬" }, UA: { name: "Ukraine", slug: "ukraine", flag: "🇺🇦" }, CZ: { name: "Czechia", slug: "czechia", flag: "🇨🇿" }, GR: { name: "Greece", slug: "greece", flag: "🇬🇷" }, LU: { name: "Luxembourg", slug: "luxembourg", flag: "🇱🇺" }, INT: { name: "International", slug: "international", flag: "🌐" }, }; export function countryBySlug(slug: string): { code: string; name: string; flag: string } | null { const s = slug.toLowerCase(); for (const [code, c] of Object.entries(COUNTRIES)) if (c.slug === s || code.toLowerCase() === s) return { code, name: c.name, flag: c.flag }; return null; } export const ENTITY_TYPES = [ "organization", "company", "government", "agency", "product", "software", "AI_model", "API", "drug", "disease", "person", "country", "city", "stock", "crypto_asset", "repository", "standard", "technology", "dataset", "publication", "vulnerability", ] as const; export type EntityType = (typeof ENTITY_TYPES)[number]; export const EVIDENCE_LABELS = ["OBSERVED", "INFERRED", "CONFIRMED", "UNCONFIRMED"] as const; export type EvidenceLabel = (typeof EVIDENCE_LABELS)[number]; export const CONNECTOR_HEALTH = ["UP", "DEGRADED", "ERROR", "DISABLED", "RATE_LIMITED"] as const; export type ConnectorHealth = (typeof CONNECTOR_HEALTH)[number];