[{"data":1,"prerenderedAt":9073},["ShallowReactive",2],{"navLinks":3,"sidebar_docs_navigation_\u002Fdocs\u002Fbot-detection":64,"navigation":217,"navLinks_footer":842,"\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fcustom_page":855,"\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fcustom_surround":5357,"\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fcustom":5360},{"id":4,"extension":5,"links":6,"meta":61,"stem":62,"__hash__":63},"navigationMenu\u002Fnavigation.json","json",[7,52,57],{"nested":8,"label":9,"icon":10,"to":11,"children":12},true,"Docs","i-lucide-book-open","\u002Fdocs\u002Fgetting-started",[13,19,26,32,39,45],{"label":14,"icon":15,"to":11,"description":16,"github":17,"badge":18},"Getting Started","i-lucide-rocket","An introduction to help you understand the core components.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fdocshub","Start Here",{"label":20,"icon":21,"to":22,"description":23,"github":24,"badge":25},"Auth H3 Client","i-lucide-key-round","\u002Fdocs\u002Fauth-h3client","Seamlessly enforce OAuth 2.0 authentication and session management integrated directly as the client of the IAM module.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fauth-h3client","Core",{"label":27,"icon":28,"to":29,"description":30,"github":31,"badge":25},"IAM","i-lucide-shield-check","\u002Fdocs\u002Fiam","Identity and Access Management featuring granular roles, permissions, and security policies.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fauth",{"label":33,"icon":34,"to":35,"description":36,"github":37,"badge":38},"Bot Detection","i-lucide-cpu","\u002Fdocs\u002Fbot-detection","Advanced behavioral analysis and request fingerprinting to stop malicious automated traffic.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fbot-detector","Security",{"label":40,"icon":41,"to":42,"description":43,"github":44,"badge":38},"Shield Base","i-lucide-database-zap","\u002Fdocs\u002Fshield-base","CLI and programmatic toolkit for compiling offline-ready IP intelligence databases from BGP, GeoIP, Tor, FireHOL, and other public threat feeds.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fshield-base-cli",{"label":46,"icon":47,"to":48,"description":49,"github":50,"badge":51},"Utils","i-lucide-wrench","\u002Fdocs\u002Futils","A standard library of highly optimized helpers for formatting, validation, and core logic.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Futils","Library",{"nested":53,"label":54,"icon":55,"to":56},false,"Blog","i-lucide-pen-line","\u002Fblog",{"nested":53,"label":58,"icon":59,"to":60},"Website","lucide:app-window-mac","https:\u002F\u002Friavzon.com",{},"navigation","gkaQ0xRGxSLrLyM3kttLe0oBwkrR1EBjlepF8LSbwF8",[65],{"title":9,"path":66,"stem":67,"children":68,"page":53},"\u002Fdocs","docs",[69],{"title":70,"path":35,"stem":71,"children":72,"icon":74},"Bot Detector","docs\u002Fbot-detection\u002Findex",[73,75,78,83,88,112,203,207,212],{"title":70,"path":35,"stem":71,"icon":74},"i-lucide-shield-half",{"title":14,"path":76,"stem":77,"icon":15},"\u002Fdocs\u002Fbot-detection\u002Fgetting-started","docs\u002Fbot-detection\u002F00.getting-started",{"title":79,"path":80,"stem":81,"icon":82},"CLI","\u002Fdocs\u002Fbot-detection\u002Fcli","docs\u002Fbot-detection\u002F01.cli","i-lucide-terminal",{"title":84,"path":85,"stem":86,"icon":87},"Data Sources","\u002Fdocs\u002Fbot-detection\u002Fdata-sources","docs\u002Fbot-detection\u002F02.data-sources","i-lucide-database",{"title":89,"icon":10,"path":90,"stem":91,"children":92,"page":53},"Guides","\u002Fdocs\u002Fbot-detection\u002Fguides","docs\u002Fbot-detection\u002F03.guides",[93,98,102,107],{"title":94,"path":95,"stem":96,"icon":97},"Custom Checkers","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fcustom","docs\u002Fbot-detection\u002F03.guides\u002FCUSTOM","i-lucide-puzzle",{"title":99,"path":100,"stem":101,"icon":41},"Scheduling Database Generation","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fgenerate","docs\u002Fbot-detection\u002F03.guides\u002FGENERATE",{"title":103,"path":104,"stem":105,"icon":106},"Logging","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Flogging","docs\u002Fbot-detection\u002F03.guides\u002FLOGGING","i-lucide-scroll-text",{"title":108,"path":109,"stem":110,"icon":111},"Score Modes and Reputation Healing","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fscore","docs\u002Fbot-detection\u002F03.guides\u002FSCORE","i-lucide-chart-line",{"title":113,"path":114,"stem":115,"children":116,"icon":28},"Checkers","\u002Fdocs\u002Fbot-detection\u002Fcheckers","docs\u002Fbot-detection\u002F04.checkers\u002Findex",[117,118,123,128,133,138,143,148,153,158,163,168,173,178,183,188,193,198],{"title":113,"path":114,"stem":115,"icon":28},{"title":119,"path":120,"stem":121,"icon":122},"IP Validation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fip-validation","docs\u002Fbot-detection\u002F04.checkers\u002F01.ip-validation","i-lucide-network",{"title":124,"path":125,"stem":126,"icon":127},"Good \u002F Bad Bot Verification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgood-bots","docs\u002Fbot-detection\u002F04.checkers\u002F02.good-bots","i-lucide-bot",{"title":129,"path":130,"stem":131,"icon":132},"Browser & Device Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbrowser-device","docs\u002Fbot-detection\u002F04.checkers\u002F03.browser-device","i-lucide-monitor-smartphone",{"title":134,"path":135,"stem":136,"icon":137},"Locale Map","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Flocale-map","docs\u002Fbot-detection\u002F04.checkers\u002F04.locale-map","i-lucide-languages",{"title":139,"path":140,"stem":141,"icon":142},"Known Threats","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-threats","docs\u002Fbot-detection\u002F04.checkers\u002F05.known-threats","i-lucide-shield-alert",{"title":144,"path":145,"stem":146,"icon":147},"ASN Classification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fasn-classification","docs\u002Fbot-detection\u002F04.checkers\u002F06.asn-classification","i-lucide-server",{"title":149,"path":150,"stem":151,"icon":152},"Tor Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftor-analysis","docs\u002Fbot-detection\u002F04.checkers\u002F07.tor-analysis","i-lucide-globe-lock",{"title":154,"path":155,"stem":156,"icon":157},"Timezone Consistency","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftimezone-consistency","docs\u002Fbot-detection\u002F04.checkers\u002F08.timezone-consistency","i-lucide-clock",{"title":159,"path":160,"stem":161,"icon":162},"Honeypot","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fhoneypot","docs\u002Fbot-detection\u002F04.checkers\u002F09.honeypot","i-lucide-bug",{"title":164,"path":165,"stem":166,"icon":167},"Known Bad IPs","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ips","docs\u002Fbot-detection\u002F04.checkers\u002F10.known-bad-ips","i-lucide-ban",{"title":169,"path":170,"stem":171,"icon":172},"Behavior Rate","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbehavior-rate","docs\u002Fbot-detection\u002F04.checkers\u002F11.behavior-rate","i-lucide-activity",{"title":174,"path":175,"stem":176,"icon":177},"Proxy \u002F ISP \u002F Cookie","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fproxy-isp-cookies","docs\u002Fbot-detection\u002F04.checkers\u002F12.proxy-isp-cookies","i-lucide-waypoints",{"title":179,"path":180,"stem":181,"icon":182},"Session Coherence","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fsession-coherence","docs\u002Fbot-detection\u002F04.checkers\u002F13.session-coherence","i-lucide-navigation",{"title":184,"path":185,"stem":186,"icon":187},"Velocity Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fvelocity-fingerprint","docs\u002Fbot-detection\u002F04.checkers\u002F14.velocity-fingerprint","i-lucide-timer",{"title":189,"path":190,"stem":191,"icon":192},"UA & Header Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fua-header","docs\u002Fbot-detection\u002F04.checkers\u002F15.ua-header","i-lucide-scan-search",{"title":194,"path":195,"stem":196,"icon":197},"Geolocation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgeolocation","docs\u002Fbot-detection\u002F04.checkers\u002F16.geolocation","i-lucide-map-pin",{"title":199,"path":200,"stem":201,"icon":202},"Known Bad User-Agents","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ua","docs\u002Fbot-detection\u002F04.checkers\u002F17.known-bad-ua","i-lucide-search-x",{"title":38,"path":204,"stem":205,"icon":206},"\u002Fdocs\u002Fbot-detection\u002Fsecurity","docs\u002Fbot-detection\u002F04.security","i-lucide-lock",{"title":208,"path":209,"stem":210,"icon":211},"API Reference","\u002Fdocs\u002Fbot-detection\u002Fapi","docs\u002Fbot-detection\u002F05.api","i-lucide-code",{"title":213,"path":214,"stem":215,"icon":216},"Configuration","\u002Fdocs\u002Fbot-detection\u002Fconfiguration","docs\u002Fbot-detection\u002F06.configuration","i-lucide-settings",[218],{"title":9,"path":66,"stem":67,"children":219,"page":53},[220,364,399,404,582,649],{"title":20,"path":22,"stem":221,"children":222},"docs\u002Fauth-h3client\u002Findex",[223,224,233,269,295,317,320,340,343],{"title":20,"path":22,"stem":221},{"title":14,"path":225,"stem":226,"children":227},"\u002Fdocs\u002Fauth-h3client\u002Fgetting-started","docs\u002Fauth-h3client\u002F00.getting-started\u002Findex",[228,229],{"title":14,"path":225,"stem":226},{"title":230,"path":231,"stem":232},"Nuxt Module","\u002Fdocs\u002Fauth-h3client\u002Fgetting-started\u002Fnuxt","docs\u002Fauth-h3client\u002F00.getting-started\u002F00.nuxt",{"title":234,"path":235,"stem":236,"children":237},"Essentials","\u002Fdocs\u002Fauth-h3client\u002Fessentials","docs\u002Fauth-h3client\u002F01.essentials\u002Findex",[238,239,243,247,251,255,259,262,266],{"title":234,"path":235,"stem":236},{"title":240,"path":241,"stem":242},"Session Management","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fsession","docs\u002Fauth-h3client\u002F01.essentials\u002F00.session",{"title":244,"path":245,"stem":246},"Route Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Froute-protection","docs\u002Fauth-h3client\u002F01.essentials\u002F01.route-protection",{"title":248,"path":249,"stem":250},"CSRF Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcsrf","docs\u002Fauth-h3client\u002F01.essentials\u002F02.csrf",{"title":252,"path":253,"stem":254},"Auth Flows","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fauth-flows","docs\u002Fauth-h3client\u002F01.essentials\u002F03.auth-flows",{"title":256,"path":257,"stem":258},"OAuth and OIDC","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Foauth","docs\u002Fauth-h3client\u002F01.essentials\u002F04.oauth",{"title":33,"path":260,"stem":261},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fbot-detection","docs\u002Fauth-h3client\u002F01.essentials\u002F05.bot-detection",{"title":263,"path":264,"stem":265},"Cookies","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcookies","docs\u002Fauth-h3client\u002F01.essentials\u002F06.cookies",{"title":103,"path":267,"stem":268},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Flogging","docs\u002Fauth-h3client\u002F01.essentials\u002F07.logging",{"title":270,"path":271,"stem":272,"children":273},"MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa","docs\u002Fauth-h3client\u002F02.mfa\u002Findex",[274,275,279,283,287,291],{"title":270,"path":271,"stem":272},{"title":276,"path":277,"stem":278},"Built-in MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fbuilt-in-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F01.built-in-flow",{"title":280,"path":281,"stem":282},"Password Reset","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fpassword-reset","docs\u002Fauth-h3client\u002F02.mfa\u002F02.password-reset",{"title":284,"path":285,"stem":286},"Email Change","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Femail-change","docs\u002Fauth-h3client\u002F02.mfa\u002F03.email-change",{"title":288,"path":289,"stem":290},"Custom MFA Flow","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fcustom-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F04.custom-flow",{"title":292,"path":293,"stem":294},"Client-Side MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fclient-side","docs\u002Fauth-h3client\u002F02.mfa\u002F05.client-side",{"title":296,"path":297,"stem":298,"children":299},"Client-side","\u002Fdocs\u002Fauth-h3client\u002Fclient","docs\u002Fauth-h3client\u002F03.client\u002Findex",[300,301,305,309,313],{"title":296,"path":297,"stem":298},{"title":302,"path":303,"stem":304},"useAuthData","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-auth-data","docs\u002Fauth-h3client\u002F03.client\u002F00.use-auth-data",{"title":306,"path":307,"stem":308},"useMagicLink","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-magic-link","docs\u002Fauth-h3client\u002F03.client\u002F01.use-magic-link",{"title":310,"path":311,"stem":312},"executeRequest","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fexecute-request","docs\u002Fauth-h3client\u002F03.client\u002F02.execute-request",{"title":314,"path":315,"stem":316},"getCsrfToken","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fget-csrf-token","docs\u002Fauth-h3client\u002F03.client\u002F03.get-csrf-token",{"title":38,"path":318,"stem":319},"\u002Fdocs\u002Fauth-h3client\u002Fsecurity","docs\u002Fauth-h3client\u002F04.security",{"title":89,"path":321,"stem":322,"children":323,"page":53},"\u002Fdocs\u002Fauth-h3client\u002Fguides","docs\u002Fauth-h3client\u002F05.guides",[324,328,332,336],{"title":325,"path":326,"stem":327},"H3 and Nitro Setup","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fh3-nitro","docs\u002Fauth-h3client\u002F05.guides\u002F00.h3-nitro",{"title":329,"path":330,"stem":331},"HMAC Inter-service Auth","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fhmac","docs\u002Fauth-h3client\u002F05.guides\u002Fhmac",{"title":333,"path":334,"stem":335},"Image Upload","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fimage-upload","docs\u002Fauth-h3client\u002F05.guides\u002Fimage-upload",{"title":337,"path":338,"stem":339},"mTLS Configuration","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fmtls","docs\u002Fauth-h3client\u002F05.guides\u002Fmtls",{"title":213,"path":341,"stem":342},"\u002Fdocs\u002Fauth-h3client\u002Fconfiguration","docs\u002Fauth-h3client\u002F06.configuration",{"title":208,"path":344,"stem":345,"children":346},"\u002Fdocs\u002Fauth-h3client\u002Fapi","docs\u002Fauth-h3client\u002F07.api\u002Findex",[347,348,352,356,360],{"title":208,"path":344,"stem":345},{"title":349,"path":350,"stem":351},"Routes Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcontrollers","docs\u002Fauth-h3client\u002F07.api\u002F00.controllers",{"title":353,"path":354,"stem":355},"Middleware Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fmiddleware","docs\u002Fauth-h3client\u002F07.api\u002F01.middleware",{"title":357,"path":358,"stem":359},"Client-side Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcomposables","docs\u002Fauth-h3client\u002F07.api\u002F02.composables",{"title":361,"path":362,"stem":363},"Utilities","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Futilities","docs\u002Fauth-h3client\u002F07.api\u002F03.utilities",{"title":70,"path":35,"stem":71,"children":365},[366,367,368,369,370,376,396,397,398],{"title":70,"path":35,"stem":71},{"title":14,"path":76,"stem":77},{"title":79,"path":80,"stem":81},{"title":84,"path":85,"stem":86},{"title":89,"path":90,"stem":91,"children":371,"page":53},[372,373,374,375],{"title":94,"path":95,"stem":96},{"title":99,"path":100,"stem":101},{"title":103,"path":104,"stem":105},{"title":108,"path":109,"stem":110},{"title":113,"path":114,"stem":115,"children":377},[378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395],{"title":113,"path":114,"stem":115},{"title":119,"path":120,"stem":121},{"title":124,"path":125,"stem":126},{"title":129,"path":130,"stem":131},{"title":134,"path":135,"stem":136},{"title":139,"path":140,"stem":141},{"title":144,"path":145,"stem":146},{"title":149,"path":150,"stem":151},{"title":154,"path":155,"stem":156},{"title":159,"path":160,"stem":161},{"title":164,"path":165,"stem":166},{"title":169,"path":170,"stem":171},{"title":174,"path":175,"stem":176},{"title":179,"path":180,"stem":181},{"title":184,"path":185,"stem":186},{"title":189,"path":190,"stem":191},{"title":194,"path":195,"stem":196},{"title":199,"path":200,"stem":201},{"title":38,"path":204,"stem":205},{"title":208,"path":209,"stem":210},{"title":213,"path":214,"stem":215},{"title":400,"path":11,"stem":401,"children":402},"Introduction","docs\u002Fgetting-started\u002Findex",[403],{"title":400,"path":11,"stem":401},{"title":27,"path":29,"stem":405,"children":406},"docs\u002Fiam\u002Findex",[407,408,411,546,549,565,568],{"title":27,"path":29,"stem":405},{"title":14,"path":409,"stem":410},"\u002Fdocs\u002Fiam\u002Fgetting-started","docs\u002Fiam\u002F00.getting-started",{"title":234,"path":412,"stem":413,"children":414},"\u002Fdocs\u002Fiam\u002Fessentials","docs\u002Fiam\u002F01.essentials\u002Findex",[415,416,420,424,428,432,436,440,444,448,452,456,459,463,467,471,475,478,482,486,489,493,496],{"title":234,"path":412,"stem":413},{"title":417,"path":418,"stem":419},"Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Ftokens","docs\u002Fiam\u002F01.essentials\u002F00.tokens",{"title":421,"path":422,"stem":423},"Access Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Faccess-tokens","docs\u002Fiam\u002F01.essentials\u002F01.access-tokens",{"title":425,"path":426,"stem":427},"Refresh Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens","docs\u002Fiam\u002F01.essentials\u002F02.refresh-tokens",{"title":429,"path":430,"stem":431},"Anomaly Detection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies","docs\u002Fiam\u002F01.essentials\u002F03.anomalies",{"title":433,"path":434,"stem":435},"Signup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fsignup","docs\u002Fiam\u002F01.essentials\u002F04.signup",{"title":437,"path":438,"stem":439},"Login","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogin","docs\u002Fiam\u002F01.essentials\u002F05.login",{"title":441,"path":442,"stem":443},"Logout","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogout","docs\u002Fiam\u002F01.essentials\u002F06.logout",{"title":445,"path":446,"stem":447},"OAuth","\u002Fdocs\u002Fiam\u002Fessentials\u002Foauth","docs\u002Fiam\u002F01.essentials\u002F07.oauth",{"title":449,"path":450,"stem":451},"Magic Links","\u002Fdocs\u002Fiam\u002Fessentials\u002Fmagic-links","docs\u002Fiam\u002F01.essentials\u002F08.magic-links",{"title":453,"path":454,"stem":455},"Emails","\u002Fdocs\u002Fiam\u002Fessentials\u002Femails","docs\u002Fiam\u002F01.essentials\u002F09.emails",{"title":270,"path":457,"stem":458},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa","docs\u002Fiam\u002F01.essentials\u002F10.mfa",{"title":460,"path":461,"stem":462},"Fingerprinting","\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting","docs\u002Fiam\u002F01.essentials\u002F11.fingerprinting",{"title":464,"path":465,"stem":466},"Backend for Frontend","\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff","docs\u002Fiam\u002F01.essentials\u002F12.bff",{"title":468,"path":469,"stem":470},"HMAC Authentication","\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac","docs\u002Fiam\u002F01.essentials\u002F13.hmac",{"title":472,"path":473,"stem":474},"XSS Protection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fxss","docs\u002Fiam\u002F01.essentials\u002F14.xss",{"title":103,"path":476,"stem":477},"\u002Fdocs\u002Fiam\u002Fessentials\u002Flogging","docs\u002Fiam\u002F01.essentials\u002F15.logging",{"title":479,"path":480,"stem":481},"Rate Limiting","\u002Fdocs\u002Fiam\u002Fessentials\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F16.rate-limiting",{"title":483,"path":484,"stem":485},"Database","\u002Fdocs\u002Fiam\u002Fessentials\u002Fdatabase","docs\u002Fiam\u002F01.essentials\u002F17.database",{"title":263,"path":487,"stem":488},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fcookies","docs\u002Fiam\u002F01.essentials\u002F18.cookies",{"title":490,"path":491,"stem":492},"Service Startup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fservice","docs\u002Fiam\u002F01.essentials\u002F19.service",{"title":280,"path":494,"stem":495},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset","docs\u002Fiam\u002F01.essentials\u002F20.password-reset",{"title":497,"path":498,"stem":499,"children":500},"API Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi","docs\u002Fiam\u002F01.essentials\u002F21.api\u002Findex",[501,502,506,510,540,543],{"title":497,"path":498,"stem":499},{"title":503,"path":504,"stem":505},"Creating Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fcreation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F00.creation",{"title":507,"path":508,"stem":509},"Verifying Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fverification","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F01.verification",{"title":511,"path":512,"stem":513,"children":514},"Management","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002Findex",[515,516,520,524,528,532,536],{"title":511,"path":512,"stem":513},{"title":517,"path":518,"stem":519},"Privilege and Scopes","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fprivilege","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F00.privilege",{"title":521,"path":522,"stem":523},"Revocation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frevocation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F01.revocation",{"title":525,"path":526,"stem":527},"Rotation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frotation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F02.rotation",{"title":529,"path":530,"stem":531},"IP Restriction","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fip-updates","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F03.ip-updates",{"title":533,"path":534,"stem":535},"Metadata","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fmetadata","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F04.metadata",{"title":537,"path":538,"stem":539},"Token Listing","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Flist","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F05.list",{"title":479,"path":541,"stem":542},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F03.rate-limiting",{"title":38,"path":544,"stem":545},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fsecurity","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F04.security",{"title":38,"path":547,"stem":548},"\u002Fdocs\u002Fiam\u002Fsecurity","docs\u002Fiam\u002F02.security",{"title":89,"path":550,"stem":551,"children":552,"page":53},"\u002Fdocs\u002Fiam\u002Fguides","docs\u002Fiam\u002F03.guides",[553,557,561],{"title":554,"path":555,"stem":556},"Deployment","\u002Fdocs\u002Fiam\u002Fguides\u002Fdeployment","docs\u002Fiam\u002F03.guides\u002Fdeployment",{"title":558,"path":559,"stem":560},"Operation Scripts","\u002Fdocs\u002Fiam\u002Fguides\u002Foperation-scripts","docs\u002Fiam\u002F03.guides\u002Foperation-scripts",{"title":562,"path":563,"stem":564},"Role-Based Access Control","\u002Fdocs\u002Fiam\u002Fguides\u002Frbac","docs\u002Fiam\u002F03.guides\u002Frbac",{"title":213,"path":566,"stem":567},"\u002Fdocs\u002Fiam\u002Fconfiguration","docs\u002Fiam\u002F04.configuration",{"title":569,"path":570,"stem":571,"children":572,"page":53},"Api","\u002Fdocs\u002Fiam\u002Fapi","docs\u002Fiam\u002F05.API",[573,576,579],{"title":208,"path":574,"stem":575},"\u002Fdocs\u002Fiam\u002Fapi\u002Fapi","docs\u002Fiam\u002F05.API\u002F00.api",{"title":353,"path":577,"stem":578},"\u002Fdocs\u002Fiam\u002Fapi\u002Fmiddlewares","docs\u002Fiam\u002F05.API\u002F02.middlewares",{"title":349,"path":580,"stem":581},"\u002Fdocs\u002Fiam\u002Fapi\u002Froutes","docs\u002Fiam\u002F05.API\u002F03.routes",{"title":40,"path":42,"stem":583,"children":584},"docs\u002Fshield-base\u002Findex",[585,586,589,593,634,638,642,646],{"title":40,"path":42,"stem":583},{"title":14,"path":587,"stem":588},"\u002Fdocs\u002Fshield-base\u002Fgetting-started","docs\u002Fshield-base\u002F00.getting-started",{"title":590,"path":591,"stem":592},"CLI Reference","\u002Fdocs\u002Fshield-base\u002Fcli","docs\u002Fshield-base\u002F01.cli",{"title":84,"path":594,"stem":595,"children":596},"\u002Fdocs\u002Fshield-base\u002Fdata-sources","docs\u002Fshield-base\u002F02.data-sources\u002Findex",[597,598,602,606,610,614,618,622,626,630],{"title":84,"path":594,"stem":595},{"title":599,"path":600,"stem":601},"BGP \u002F ASN","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fbgp","docs\u002Fshield-base\u002F02.data-sources\u002Fbgp",{"title":603,"path":604,"stem":605},"City Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcity","docs\u002Fshield-base\u002F02.data-sources\u002Fcity",{"title":607,"path":608,"stem":609},"Country Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcountry","docs\u002Fshield-base\u002F02.data-sources\u002Fcountry",{"title":611,"path":612,"stem":613},"Verified Crawlers","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcrawlers","docs\u002Fshield-base\u002F02.data-sources\u002Fcrawlers",{"title":615,"path":616,"stem":617},"Disposable Emails","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Femail","docs\u002Fshield-base\u002F02.data-sources\u002Femail",{"title":619,"path":620,"stem":621},"FireHOL Threat Intelligence","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ffirehol","docs\u002Fshield-base\u002F02.data-sources\u002Ffirehol",{"title":623,"path":624,"stem":625},"Proxy Detection","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fproxy","docs\u002Fshield-base\u002F02.data-sources\u002Fproxy",{"title":627,"path":628,"stem":629},"Tor Nodes","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ftor","docs\u002Fshield-base\u002F02.data-sources\u002Ftor",{"title":631,"path":632,"stem":633},"Suspicious User-Agents","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fuseragent","docs\u002Fshield-base\u002F02.data-sources\u002Fuseragent",{"title":635,"path":636,"stem":637},"Programmatic Usage","\u002Fdocs\u002Fshield-base\u002Fusage","docs\u002Fshield-base\u002F03.usage",{"title":639,"path":640,"stem":641},"Custom Data Sources","\u002Fdocs\u002Fshield-base\u002Fcustom-data-sources","docs\u002Fshield-base\u002F04.custom-data-sources",{"title":643,"path":644,"stem":645},"TypeScript Types","\u002Fdocs\u002Fshield-base\u002Ftypes","docs\u002Fshield-base\u002F05.types",{"title":208,"path":647,"stem":648},"\u002Fdocs\u002Fshield-base\u002Fapi","docs\u002Fshield-base\u002F06.api",{"title":361,"path":48,"stem":650,"children":651},"docs\u002Futils\u002Findex",[652,653,670,703,800],{"title":361,"path":48,"stem":650},{"title":654,"path":655,"stem":656,"children":657,"page":53},"Eslint","\u002Fdocs\u002Futils\u002Feslint","docs\u002Futils\u002Feslint",[658,662,666],{"title":659,"path":660,"stem":661},"React Config","\u002Fdocs\u002Futils\u002Feslint\u002Freact","docs\u002Futils\u002Feslint\u002Freact",{"title":663,"path":664,"stem":665},"TypeScript Config","\u002Fdocs\u002Futils\u002Feslint\u002Ftypescript","docs\u002Futils\u002Feslint\u002Ftypescript",{"title":667,"path":668,"stem":669},"Vue Config","\u002Fdocs\u002Futils\u002Feslint\u002Fvue","docs\u002Futils\u002Feslint\u002Fvue",{"title":671,"path":672,"stem":673,"children":674,"page":53},"Server","\u002Fdocs\u002Futils\u002Fserver","docs\u002Futils\u002Fserver",[675,679,683,687,691,695,699],{"title":676,"path":677,"stem":678},"Encryption","\u002Fdocs\u002Futils\u002Fserver\u002Fencryption","docs\u002Futils\u002Fserver\u002Fencryption",{"title":680,"path":681,"stem":682},"Path Resolver","\u002Fdocs\u002Futils\u002Fserver\u002Fpathresolver","docs\u002Futils\u002Fserver\u002FpathResolver",{"title":684,"path":685,"stem":686},"File Replacements","\u002Fdocs\u002Futils\u002Fserver\u002Freplace","docs\u002Futils\u002Fserver\u002Freplace",{"title":688,"path":689,"stem":690},"run","\u002Fdocs\u002Futils\u002Fserver\u002Frun","docs\u002Futils\u002Fserver\u002Frun",{"title":692,"path":693,"stem":694},"scheduleTask","\u002Fdocs\u002Futils\u002Fserver\u002Fscheduletask","docs\u002Futils\u002Fserver\u002FscheduleTask",{"title":696,"path":697,"stem":698},"spawnRun","\u002Fdocs\u002Futils\u002Fserver\u002Fspawnrun","docs\u002Futils\u002Fserver\u002FspawnRun",{"title":700,"path":701,"stem":702},"uploadCsv","\u002Fdocs\u002Futils\u002Fserver\u002Fuploadcsv","docs\u002Futils\u002Fserver\u002FuploadCsv",{"title":704,"path":705,"stem":706,"children":707,"page":53},"Shared","\u002Fdocs\u002Futils\u002Fshared","docs\u002Futils\u002Fshared",[708,712,716,720,724,728,732,736,740,744,748,752,756,760,764,768,772,776,780,784,788,792,796],{"title":709,"path":710,"stem":711},"BatchQueue","\u002Fdocs\u002Futils\u002Fshared\u002Fbatchqueue","docs\u002Futils\u002Fshared\u002FbatchQueue",{"title":713,"path":714,"stem":715},"capitalize","\u002Fdocs\u002Futils\u002Fshared\u002Fcapitalize","docs\u002Futils\u002Fshared\u002Fcapitalize",{"title":717,"path":718,"stem":719},"chunkProcess","\u002Fdocs\u002Futils\u002Fshared\u002Fchunkprocess","docs\u002Futils\u002Fshared\u002FchunkProcess",{"title":721,"path":722,"stem":723},"cleanObject","\u002Fdocs\u002Futils\u002Fshared\u002Fcleanobject","docs\u002Futils\u002Fshared\u002FcleanObject",{"title":725,"path":726,"stem":727},"createConfigManager","\u002Fdocs\u002Futils\u002Fshared\u002Fconfigurationdefiner","docs\u002Futils\u002Fshared\u002FconfigurationDefiner",{"title":729,"path":730,"stem":731},"debounce","\u002Fdocs\u002Futils\u002Fshared\u002Fdebounce","docs\u002Futils\u002Fshared\u002Fdebounce",{"title":733,"path":734,"stem":735},"ensureArray","\u002Fdocs\u002Futils\u002Fshared\u002Fensurearray","docs\u002Futils\u002Fshared\u002FensureArray",{"title":737,"path":738,"stem":739},"fetchWithRetry","\u002Fdocs\u002Futils\u002Fshared\u002Ffetchwithretry","docs\u002Futils\u002Fshared\u002FfetchWithRetry",{"title":741,"path":742,"stem":743},"filterEmptyValues","\u002Fdocs\u002Futils\u002Fshared\u002Ffilteremptyvalues","docs\u002Futils\u002Fshared\u002FfilterEmptyValues",{"title":745,"path":746,"stem":747},"findStringsInObject","\u002Fdocs\u002Futils\u002Fshared\u002Ffindobjectvalues","docs\u002Futils\u002Fshared\u002FfindObjectValues",{"title":749,"path":750,"stem":751},"fisherYatesShuffle","\u002Fdocs\u002Futils\u002Fshared\u002Ffisheryatesshuffle","docs\u002Futils\u002Fshared\u002FfisherYatesShuffle",{"title":753,"path":754,"stem":755},"getRandomImage","\u002Fdocs\u002Futils\u002Fshared\u002Fgetrandomimage","docs\u002Futils\u002Fshared\u002FgetRandomImage",{"title":757,"path":758,"stem":759},"isObjectHasValues","\u002Fdocs\u002Futils\u002Fshared\u002Fisobjecthasvalues","docs\u002Futils\u002Fshared\u002FisObjectHasValues",{"title":761,"path":762,"stem":763},"isAsyncOrPromise","\u002Fdocs\u002Futils\u002Fshared\u002Fispromise","docs\u002Futils\u002Fshared\u002FisPromise",{"title":765,"path":766,"stem":767},"MiniCache","\u002Fdocs\u002Futils\u002Fshared\u002Fminicache","docs\u002Futils\u002Fshared\u002FminiCache",{"title":769,"path":770,"stem":771},"parseCookies","\u002Fdocs\u002Futils\u002Fshared\u002Fparserawcookies","docs\u002Futils\u002Fshared\u002FparseRawCookies",{"title":773,"path":774,"stem":775},"safeAction","\u002Fdocs\u002Futils\u002Fshared\u002Fpromiselocker","docs\u002Futils\u002Fshared\u002FpromiseLocker",{"title":777,"path":778,"stem":779},"Random","\u002Fdocs\u002Futils\u002Fshared\u002Frandom","docs\u002Futils\u002Fshared\u002Frandom",{"title":781,"path":782,"stem":783},"range","\u002Fdocs\u002Futils\u002Fshared\u002Frange","docs\u002Futils\u002Fshared\u002Frange",{"title":785,"path":786,"stem":787},"rateLimiters","\u002Fdocs\u002Futils\u002Fshared\u002Fratelimiters","docs\u002Futils\u002Fshared\u002FrateLimiters",{"title":789,"path":790,"stem":791},"safeObjectMerge","\u002Fdocs\u002Futils\u002Fshared\u002Fsafemerge","docs\u002Futils\u002Fshared\u002FsafeMerge",{"title":793,"path":794,"stem":795},"textTruncation","\u002Fdocs\u002Futils\u002Fshared\u002Ftexttruncation","docs\u002Futils\u002Fshared\u002FtextTruncation",{"title":797,"path":798,"stem":799},"validateZodSchema","\u002Fdocs\u002Futils\u002Fshared\u002Fvalidatezodschema","docs\u002Futils\u002Fshared\u002FvalidateZodSchema",{"title":801,"path":802,"stem":803,"children":804},"Utility Types","\u002Fdocs\u002Futils\u002Ftypes","docs\u002Futils\u002Ftypes\u002Findex",[805,806,810,814,818,822,826,830,834,838],{"title":801,"path":802,"stem":803},{"title":807,"path":808,"stem":809},"Brand","\u002Fdocs\u002Futils\u002Ftypes\u002Fbrand","docs\u002Futils\u002Ftypes\u002FBrand",{"title":811,"path":812,"stem":813},"DeepPartial","\u002Fdocs\u002Futils\u002Ftypes\u002Fdeeppartial","docs\u002Futils\u002Ftypes\u002FDeepPartial",{"title":815,"path":816,"stem":817},"Merge","\u002Fdocs\u002Futils\u002Ftypes\u002Fmerge","docs\u002Futils\u002Ftypes\u002FMerge",{"title":819,"path":820,"stem":821},"NonNullable","\u002Fdocs\u002Futils\u002Ftypes\u002Fnonnullable","docs\u002Futils\u002Ftypes\u002FNonNullable",{"title":823,"path":824,"stem":825},"Prettify","\u002Fdocs\u002Futils\u002Ftypes\u002Fprettify","docs\u002Futils\u002Ftypes\u002FPrettify",{"title":827,"path":828,"stem":829},"PromiseType","\u002Fdocs\u002Futils\u002Ftypes\u002Fpromisetype","docs\u002Futils\u002Ftypes\u002FPromiseType",{"title":831,"path":832,"stem":833},"RequireKeys","\u002Fdocs\u002Futils\u002Ftypes\u002Frequirekeys","docs\u002Futils\u002Ftypes\u002FRequireKeys",{"title":835,"path":836,"stem":837},"StandardResponse","\u002Fdocs\u002Futils\u002Ftypes\u002Fstandardresponse","docs\u002Futils\u002Ftypes\u002FStandardResponse",{"title":839,"path":840,"stem":841},"ValueOf","\u002Fdocs\u002Futils\u002Ftypes\u002Fvalueof","docs\u002Futils\u002Ftypes\u002FValueOf",{"id":4,"extension":5,"links":843,"meta":854,"stem":62,"__hash__":63},[844,852,853],{"nested":8,"label":9,"icon":10,"to":11,"children":845},[846,847,848,849,850,851],{"label":14,"icon":15,"to":11,"description":16,"github":17,"badge":18},{"label":20,"icon":21,"to":22,"description":23,"github":24,"badge":25},{"label":27,"icon":28,"to":29,"description":30,"github":31,"badge":25},{"label":33,"icon":34,"to":35,"description":36,"github":37,"badge":38},{"label":40,"icon":41,"to":42,"description":43,"github":44,"badge":38},{"label":46,"icon":47,"to":48,"description":49,"github":50,"badge":51},{"nested":53,"label":54,"icon":55,"to":56},{"nested":53,"label":58,"icon":59,"to":60},{},{"id":856,"title":94,"body":857,"description":5350,"extension":5351,"icon":97,"meta":5352,"module":5353,"navigation":8,"path":95,"rawbody":5354,"seo":5355,"stem":96,"__hash__":5356},"docs\u002Fdocs\u002Fbot-detection\u002F03.guides\u002FCUSTOM.md",{"type":858,"value":859,"toc":5319},"minimark",[860,878,883,905,908,931,941,944,950,956,989,1260,1269,1271,1275,1327,1336,1338,1344,1349,1390,1395,1414,1742,1781,1784,1798,2031,2034,2043,2097,2114,2118,2167,2186,2190,2200,2314,2336,2340,2357,2366,2368,2372,2382,2794,2807,2941,2943,2947,2969,2972,3674,3676,3680,3690,3699,4289,4299,4301,4305,4314,4486,4489,4491,4495,4500,4548,4645,4647,4651,4657,4719,4727,4834,4852,4856,4859,4874,4877,5000,5002,5009,5012,5148,5158,5160,5164,5315],[861,862,863,864,868,869,873,874,877],"p",{},"The ",[865,866,867],"code",{},"bot detector"," is built around a ",[870,871,872],"strong",{},"checker registry",", every check is a class that implements ",[865,875,876],{},"IBotChecker",", self registers on import, and gets picked up automatically when the middleware runs.",[879,880,882],"h2",{"id":881},"pipeline","Pipeline",[861,884,885,886,889,890,893,894,897,898,901,902,904],{},"Each request passes through two sequential phases: ",[870,887,888],{},"cheap"," and ",[870,891,892],{},"heavy",". The score check runs after ",[870,895,896],{},"every individual checker"," in both phases. The moment the accumulated score reaches or exceeds ",[865,899,900],{},"banScore",", the pipeline stops processing any remaining checkers in that phase and the visitor is banned. The heavy phase is skipped entirely if the cheap phase already reached ",[865,903,900],{},".",[861,906,907],{},"Two special reason codes short-circuit the pipeline immediately, regardless of score:",[909,910,911,922],"ul",{},[912,913,914,917,918,921],"li",{},[865,915,916],{},"'BAD_BOT_DETECTED'",": throws ",[865,919,920],{},"BadBotDetected",", bans the visitor instantly",[912,923,924,917,927,930],{},[865,925,926],{},"'GOOD_BOT_IDENTIFIED'",[865,928,929],{},"GoodBotDetected",", lets the visitor through instantly",[932,933,938],"pre",{"className":934,"code":936,"language":937},[935],"language-text","Request\n  │    \n  \n[cheap phase] - in-memory checks, header inspection, pre-loaded MMDB lookups\n  │\n  after each checker:\n    ├─ BAD_BOT_DETECTED in reasons? → ban immediately (no further checks)\n    ├─ GOOD_BOT_IDENTIFIED in reasons? → allow immediately (no further checks)\n    └─ score >= banScore? → ban immediately (no further checks)\n  │\n  score still \u003C banScore after all cheap checkers?\n  │\n  \n[heavy phase] - DB queries, external calls, storage reads\n  │\n  after each checker:\n    ├─ BAD_BOT_DETECTED in reasons? → ban immediately\n    ├─ GOOD_BOT_IDENTIFIED in reasons? → allow immediately\n    └─ score >= banScore? - ban immediately (no further checks)\n  │\n  \nFinal decision: allow \u002F ban\n","text",[865,939,936],{"__ignoreMap":940},"",[942,943],"hr",{},[879,945,863,947,949],{"id":946},"the-ibotchecker-interface",[865,948,876],{}," Interface",[861,951,952,953,955],{},"Every checker must implement ",[865,954,876],{},". The interface accepts two type parameters:",[909,957,958,967],{},[912,959,960,963,964,904],{},[865,961,962],{},"Code",", your custom reason code union. You can extend or alias the built in ",[865,965,966],{},"BanReasonCode",[912,968,969,972,973,977,978,981,982,985,986,904],{},[865,970,971],{},"TCustom"," ",[974,975,976],"em",{},"(optional)",", the shape of ",[865,979,980],{},"ctx.custom",", populated by your ",[865,983,984],{},"buildCustomContext"," function. Defaults to ",[865,987,988],{},"Record\u003Cstring, never>",[932,990,994],{"className":991,"code":992,"language":993,"meta":940,"style":940},"language-ts shiki shiki-themes light-plus light-plus dracula","import type { IBotChecker, ValidationContext, BotDetectorConfig } from '@riavzon\u002Fbot-detector';\n\ninterface IBotChecker\u003CCode, TCustom = Record\u003Cstring, never>> {\n  name: string; \u002F\u002F display name, shown in logs\n  phase: 'cheap' | 'heavy'; \u002F\u002F which phase this checker runs in\n  isEnabled(config: BotDetectorConfig): boolean; \u002F\u002F self explanatory\n  run(ctx: ValidationContext\u003CTCustom>, config: BotDetectorConfig ): \u002F\u002F the checker logic\n    | Promise\u003C{ score: number; reasons: Code[] }>\n    | { score: number; reasons: Code[] };\n}\n","ts",[865,995,996,1046,1052,1093,1112,1140,1172,1209,1237,1254],{"__ignoreMap":940},[997,998,1001,1005,1008,1012,1015,1018,1021,1023,1026,1029,1032,1036,1040,1043],"span",{"class":999,"line":1000},"line",1,[997,1002,1004],{"class":1003},"sZ328","import",[997,1006,1007],{"class":1003}," type",[997,1009,1011],{"class":1010},"sDd4n"," { ",[997,1013,876],{"class":1014},"sjsA6",[997,1016,1017],{"class":1010},", ",[997,1019,1020],{"class":1014},"ValidationContext",[997,1022,1017],{"class":1010},[997,1024,1025],{"class":1014},"BotDetectorConfig",[997,1027,1028],{"class":1010}," } ",[997,1030,1031],{"class":1003},"from",[997,1033,1035],{"class":1034},"sFkSl"," '",[997,1037,1039],{"class":1038},"sFB1V","@riavzon\u002Fbot-detector",[997,1041,1042],{"class":1034},"'",[997,1044,1045],{"class":1010},";\n",[997,1047,1049],{"class":999,"line":1048},2,[997,1050,1051],{"emptyLinePlaceholder":8},"\n",[997,1053,1055,1059,1063,1066,1069,1071,1073,1077,1080,1082,1085,1087,1090],{"class":999,"line":1054},3,[997,1056,1058],{"class":1057},"sl46w","interface",[997,1060,1062],{"class":1061},"sFs1U"," IBotChecker",[997,1064,1065],{"class":1010},"\u003C",[997,1067,962],{"class":1068},"sW-rI",[997,1070,1017],{"class":1010},[997,1072,971],{"class":1068},[997,1074,1076],{"class":1075},"saOXh"," =",[997,1078,1079],{"class":1068}," Record",[997,1081,1065],{"class":1010},[997,1083,1084],{"class":1061},"string",[997,1086,1017],{"class":1010},[997,1088,1089],{"class":1061},"never",[997,1091,1092],{"class":1010},">> {\n",[997,1094,1096,1099,1102,1105,1108],{"class":999,"line":1095},4,[997,1097,1098],{"class":1014},"  name",[997,1100,1101],{"class":1075},":",[997,1103,1104],{"class":1061}," string",[997,1106,1107],{"class":1010},"; ",[997,1109,1111],{"class":1110},"sghk6","\u002F\u002F display name, shown in logs\n",[997,1113,1115,1118,1120,1122,1124,1126,1129,1131,1133,1135,1137],{"class":999,"line":1114},5,[997,1116,1117],{"class":1014},"  phase",[997,1119,1101],{"class":1075},[997,1121,1035],{"class":1034},[997,1123,888],{"class":1038},[997,1125,1042],{"class":1034},[997,1127,1128],{"class":1075}," |",[997,1130,1035],{"class":1034},[997,1132,892],{"class":1038},[997,1134,1042],{"class":1034},[997,1136,1107],{"class":1010},[997,1138,1139],{"class":1110},"\u002F\u002F which phase this checker runs in\n",[997,1141,1143,1147,1150,1154,1156,1159,1162,1164,1167,1169],{"class":999,"line":1142},6,[997,1144,1146],{"class":1145},"sHOzp","  isEnabled",[997,1148,1149],{"class":1010},"(",[997,1151,1153],{"class":1152},"sygFZ","config",[997,1155,1101],{"class":1075},[997,1157,1158],{"class":1061}," BotDetectorConfig",[997,1160,1161],{"class":1010},")",[997,1163,1101],{"class":1075},[997,1165,1166],{"class":1061}," boolean",[997,1168,1107],{"class":1010},[997,1170,1171],{"class":1110},"\u002F\u002F self explanatory\n",[997,1173,1175,1178,1180,1183,1185,1188,1190,1192,1195,1197,1199,1201,1204,1206],{"class":999,"line":1174},7,[997,1176,1177],{"class":1145},"  run",[997,1179,1149],{"class":1010},[997,1181,1182],{"class":1152},"ctx",[997,1184,1101],{"class":1075},[997,1186,1187],{"class":1061}," ValidationContext",[997,1189,1065],{"class":1010},[997,1191,971],{"class":1068},[997,1193,1194],{"class":1010},">, ",[997,1196,1153],{"class":1152},[997,1198,1101],{"class":1075},[997,1200,1158],{"class":1061},[997,1202,1203],{"class":1010}," )",[997,1205,1101],{"class":1075},[997,1207,1208],{"class":1110}," \u002F\u002F the checker logic\n",[997,1210,1212,1215,1218,1220,1223,1226,1229,1231,1234],{"class":999,"line":1211},8,[997,1213,1214],{"class":1075},"    |",[997,1216,1217],{"class":1061}," Promise",[997,1219,1065],{"class":1075},[997,1221,1222],{"class":1010},"{ score: ",[997,1224,1225],{"class":1014},"number",[997,1227,1228],{"class":1010},"; reasons: ",[997,1230,962],{"class":1014},[997,1232,1233],{"class":1010},"[] }",[997,1235,1236],{"class":1075},">\n",[997,1238,1240,1242,1245,1247,1249,1251],{"class":999,"line":1239},9,[997,1241,1214],{"class":1075},[997,1243,1244],{"class":1010}," { score: ",[997,1246,1225],{"class":1014},[997,1248,1228],{"class":1010},[997,1250,962],{"class":1014},[997,1252,1253],{"class":1010},"[] };\n",[997,1255,1257],{"class":999,"line":1256},10,[997,1258,1259],{"class":1010},"}\n",[861,1261,863,1262,1264,1265,1268],{},[865,1263,688],{}," method can be synchronous or async. Return a numeric score and an array of reason codes. A score of ",[865,1266,1267],{},"0"," with an empty reasons array means the checker found nothing suspicious.",[942,1270],{},[879,1272,1274],{"id":1273},"phases","Phases",[1276,1277,1278,1294],"table",{},[1279,1280,1281],"thead",{},[1282,1283,1284,1288,1291],"tr",{},[1285,1286,1287],"th",{},"Phase",[1285,1289,1290],{},"When it runs",[1285,1292,1293],{},"Best for",[1295,1296,1297,1312],"tbody",{},[1282,1298,1299,1304,1307],{},[1300,1301,1302],"td",{},[865,1303,888],{},[1300,1305,1306],{},"Every request, before the heavy phase",[1300,1308,1309,1310],{},"In-memory checks, header inspection, reading pre-loaded MMDB data from ",[865,1311,1182],{},[1282,1313,1314,1318,1324],{},[1300,1315,1316],{},[865,1317,892],{},[1300,1319,1320,1321,1323],{},"Only when accumulated score is below ",[865,1322,900],{}," after cheap phase",[1300,1325,1326],{},"Database queries, external API calls, cache reads with I\u002FO fallback",[861,1328,1329,1330,1332,1333,1335],{},"Prefer ",[865,1331,888],{}," for anything that does not require I\u002FO. All MMDB lookups (geo, ASN, Tor, proxy, threat levels) are already resolved before your checker runs, reading them from ",[865,1334,1182],{}," costs nothing.",[942,1337],{},[879,1339,1341,1343],{"id":1340},"validationcontext-context",[865,1342,1020],{}," Context",[861,1345,863,1346,1348],{},[865,1347,1020],{}," object contains everything the pipeline resolves before your checker runs. All fields are pre-populated; your checker only reads them.",[932,1350,1352],{"className":991,"code":1351,"language":993,"meta":940,"style":940},"ctx.req       \u002F\u002F Express Request: full access to headers, path, cookies, method\nctx.ipAddress \u002F\u002F string: the resolved client IP\nctx.cookie    \u002F\u002F string | undefined: canary_id cookie value (visitor identifier)\n",[865,1353,1354,1366,1378],{"__ignoreMap":940},[997,1355,1356,1358,1360,1363],{"class":999,"line":1000},[997,1357,1182],{"class":1014},[997,1359,904],{"class":1010},[997,1361,1362],{"class":1014},"req",[997,1364,1365],{"class":1110},"       \u002F\u002F Express Request: full access to headers, path, cookies, method\n",[997,1367,1368,1370,1372,1375],{"class":999,"line":1048},[997,1369,1182],{"class":1014},[997,1371,904],{"class":1010},[997,1373,1374],{"class":1014},"ipAddress",[997,1376,1377],{"class":1110}," \u002F\u002F string: the resolved client IP\n",[997,1379,1380,1382,1384,1387],{"class":999,"line":1054},[997,1381,1182],{"class":1014},[997,1383,904],{"class":1010},[997,1385,1386],{"class":1014},"cookie",[997,1388,1389],{"class":1110},"    \u002F\u002F string | undefined: canary_id cookie value (visitor identifier)\n",[1391,1392,1394],"h3",{"id":1393},"geo-and-ip-intelligence","Geo and IP Intelligence",[861,1396,1397,1398,1401,1402,1405,1406,1409,1410,1413],{},"All fields are sourced from three MMDB databases merged at request time: ",[865,1399,1400],{},"country.mmdb"," (country-level), ",[865,1403,1404],{},"city.mmdb"," (city-level), ",[865,1407,1408],{},"asn.mmdb"," (BGP\u002Fnetwork), and ",[865,1411,1412],{},"proxy.mmdb",". City-level data takes precedence where both exist. All string values are normalized to lowercase.",[932,1415,1417],{"className":991,"code":1416,"language":993,"meta":940,"style":940},"ctx.geoData     \u002F\u002F Partial\u003CGeoResponse>, all fields optional\n  .country      \u002F\u002F   country name, \"united states\"\n  .countryCode  \u002F\u002F   ISO 3166-1 alpha-2, \"us\"\n  .continent    \u002F\u002F   \"north america\"  (city.mmdb only)\n  .region       \u002F\u002F   region\u002Fprovince code, \"ny\"\n  .regionName   \u002F\u002F   continent ?? subregion fallback\n  .subregion    \u002F\u002F   \"northern america\"\n  .state        \u002F\u002F   \"new york\"\n  .city         \u002F\u002F   \"new york city\"\n  .capital      \u002F\u002F   country capital, \"washington d.c.\"\n  .district     \u002F\u002F   mirrors .state (city.mmdb state field)\n  .zipCode      \u002F\u002F   postal code, \"10001\"\n  .lat          \u002F\u002F   latitude string, \"40.7128\"\n  .lon          \u002F\u002F   longitude string, \"-74.0059\"\n  .timezone     \u002F\u002F   IANA timezone, \"america\u002Fnew_york\"\n  .timeZoneName \u002F\u002F   human-readable, \"eastern standard time\"\n  .utc_offset   \u002F\u002F   \"-05:00\"\n  .isp          \u002F\u002F   ASN org name (asn.mmdb → asn_name), \"cloudflare, inc.\"\n  .org          \u002F\u002F   ASN ID (asn.mmdb → asn_id), \"as13335\"\n  .as_org       \u002F\u002F   same as .isp (asn_name duplicate)\n  .proxy        \u002F\u002F   boolean, IP matched in proxy.mmdb\n  .hosting      \u002F\u002F   boolean, true when asn.classification === \"Content\"\n  .tld          \u002F\u002F   country TLD, \".us\"\n  .nationality  \u002F\u002F   \"american\"\n  .currency     \u002F\u002F   \"usd\"\n  .iso639       \u002F\u002F   primary language code, \"en\"\n  .languages    \u002F\u002F   all languages, \"en-us\"\n  .native       \u002F\u002F   native country name, \"united states\"\n  .phone        \u002F\u002F   country calling code, \"1\"\n  .numericCode  \u002F\u002F   ISO 3166-1 numeric, \"840\"\n",[865,1418,1419,1431,1442,1452,1462,1472,1482,1492,1502,1512,1522,1533,1544,1555,1566,1577,1588,1599,1610,1621,1632,1643,1654,1665,1676,1687,1698,1709,1720,1731],{"__ignoreMap":940},[997,1420,1421,1423,1425,1428],{"class":999,"line":1000},[997,1422,1182],{"class":1014},[997,1424,904],{"class":1010},[997,1426,1427],{"class":1014},"geoData",[997,1429,1430],{"class":1110},"     \u002F\u002F Partial\u003CGeoResponse>, all fields optional\n",[997,1432,1433,1436,1439],{"class":999,"line":1048},[997,1434,1435],{"class":1010},"  .",[997,1437,1438],{"class":1014},"country",[997,1440,1441],{"class":1110},"      \u002F\u002F   country name, \"united states\"\n",[997,1443,1444,1446,1449],{"class":999,"line":1054},[997,1445,1435],{"class":1010},[997,1447,1448],{"class":1014},"countryCode",[997,1450,1451],{"class":1110},"  \u002F\u002F   ISO 3166-1 alpha-2, \"us\"\n",[997,1453,1454,1456,1459],{"class":999,"line":1095},[997,1455,1435],{"class":1010},[997,1457,1458],{"class":1014},"continent",[997,1460,1461],{"class":1110},"    \u002F\u002F   \"north america\"  (city.mmdb only)\n",[997,1463,1464,1466,1469],{"class":999,"line":1114},[997,1465,1435],{"class":1010},[997,1467,1468],{"class":1014},"region",[997,1470,1471],{"class":1110},"       \u002F\u002F   region\u002Fprovince code, \"ny\"\n",[997,1473,1474,1476,1479],{"class":999,"line":1142},[997,1475,1435],{"class":1010},[997,1477,1478],{"class":1014},"regionName",[997,1480,1481],{"class":1110},"   \u002F\u002F   continent ?? subregion fallback\n",[997,1483,1484,1486,1489],{"class":999,"line":1174},[997,1485,1435],{"class":1010},[997,1487,1488],{"class":1014},"subregion",[997,1490,1491],{"class":1110},"    \u002F\u002F   \"northern america\"\n",[997,1493,1494,1496,1499],{"class":999,"line":1211},[997,1495,1435],{"class":1010},[997,1497,1498],{"class":1014},"state",[997,1500,1501],{"class":1110},"        \u002F\u002F   \"new york\"\n",[997,1503,1504,1506,1509],{"class":999,"line":1239},[997,1505,1435],{"class":1010},[997,1507,1508],{"class":1014},"city",[997,1510,1511],{"class":1110},"         \u002F\u002F   \"new york city\"\n",[997,1513,1514,1516,1519],{"class":999,"line":1256},[997,1515,1435],{"class":1010},[997,1517,1518],{"class":1014},"capital",[997,1520,1521],{"class":1110},"      \u002F\u002F   country capital, \"washington d.c.\"\n",[997,1523,1525,1527,1530],{"class":999,"line":1524},11,[997,1526,1435],{"class":1010},[997,1528,1529],{"class":1014},"district",[997,1531,1532],{"class":1110},"     \u002F\u002F   mirrors .state (city.mmdb state field)\n",[997,1534,1536,1538,1541],{"class":999,"line":1535},12,[997,1537,1435],{"class":1010},[997,1539,1540],{"class":1014},"zipCode",[997,1542,1543],{"class":1110},"      \u002F\u002F   postal code, \"10001\"\n",[997,1545,1547,1549,1552],{"class":999,"line":1546},13,[997,1548,1435],{"class":1010},[997,1550,1551],{"class":1014},"lat",[997,1553,1554],{"class":1110},"          \u002F\u002F   latitude string, \"40.7128\"\n",[997,1556,1558,1560,1563],{"class":999,"line":1557},14,[997,1559,1435],{"class":1010},[997,1561,1562],{"class":1014},"lon",[997,1564,1565],{"class":1110},"          \u002F\u002F   longitude string, \"-74.0059\"\n",[997,1567,1569,1571,1574],{"class":999,"line":1568},15,[997,1570,1435],{"class":1010},[997,1572,1573],{"class":1014},"timezone",[997,1575,1576],{"class":1110},"     \u002F\u002F   IANA timezone, \"america\u002Fnew_york\"\n",[997,1578,1580,1582,1585],{"class":999,"line":1579},16,[997,1581,1435],{"class":1010},[997,1583,1584],{"class":1014},"timeZoneName",[997,1586,1587],{"class":1110}," \u002F\u002F   human-readable, \"eastern standard time\"\n",[997,1589,1591,1593,1596],{"class":999,"line":1590},17,[997,1592,1435],{"class":1010},[997,1594,1595],{"class":1014},"utc_offset",[997,1597,1598],{"class":1110},"   \u002F\u002F   \"-05:00\"\n",[997,1600,1602,1604,1607],{"class":999,"line":1601},18,[997,1603,1435],{"class":1010},[997,1605,1606],{"class":1014},"isp",[997,1608,1609],{"class":1110},"          \u002F\u002F   ASN org name (asn.mmdb → asn_name), \"cloudflare, inc.\"\n",[997,1611,1613,1615,1618],{"class":999,"line":1612},19,[997,1614,1435],{"class":1010},[997,1616,1617],{"class":1014},"org",[997,1619,1620],{"class":1110},"          \u002F\u002F   ASN ID (asn.mmdb → asn_id), \"as13335\"\n",[997,1622,1624,1626,1629],{"class":999,"line":1623},20,[997,1625,1435],{"class":1010},[997,1627,1628],{"class":1014},"as_org",[997,1630,1631],{"class":1110},"       \u002F\u002F   same as .isp (asn_name duplicate)\n",[997,1633,1635,1637,1640],{"class":999,"line":1634},21,[997,1636,1435],{"class":1010},[997,1638,1639],{"class":1014},"proxy",[997,1641,1642],{"class":1110},"        \u002F\u002F   boolean, IP matched in proxy.mmdb\n",[997,1644,1646,1648,1651],{"class":999,"line":1645},22,[997,1647,1435],{"class":1010},[997,1649,1650],{"class":1014},"hosting",[997,1652,1653],{"class":1110},"      \u002F\u002F   boolean, true when asn.classification === \"Content\"\n",[997,1655,1657,1659,1662],{"class":999,"line":1656},23,[997,1658,1435],{"class":1010},[997,1660,1661],{"class":1014},"tld",[997,1663,1664],{"class":1110},"          \u002F\u002F   country TLD, \".us\"\n",[997,1666,1668,1670,1673],{"class":999,"line":1667},24,[997,1669,1435],{"class":1010},[997,1671,1672],{"class":1014},"nationality",[997,1674,1675],{"class":1110},"  \u002F\u002F   \"american\"\n",[997,1677,1679,1681,1684],{"class":999,"line":1678},25,[997,1680,1435],{"class":1010},[997,1682,1683],{"class":1014},"currency",[997,1685,1686],{"class":1110},"     \u002F\u002F   \"usd\"\n",[997,1688,1690,1692,1695],{"class":999,"line":1689},26,[997,1691,1435],{"class":1010},[997,1693,1694],{"class":1014},"iso639",[997,1696,1697],{"class":1110},"       \u002F\u002F   primary language code, \"en\"\n",[997,1699,1701,1703,1706],{"class":999,"line":1700},27,[997,1702,1435],{"class":1010},[997,1704,1705],{"class":1014},"languages",[997,1707,1708],{"class":1110},"    \u002F\u002F   all languages, \"en-us\"\n",[997,1710,1712,1714,1717],{"class":999,"line":1711},28,[997,1713,1435],{"class":1010},[997,1715,1716],{"class":1014},"native",[997,1718,1719],{"class":1110},"       \u002F\u002F   native country name, \"united states\"\n",[997,1721,1723,1725,1728],{"class":999,"line":1722},29,[997,1724,1435],{"class":1010},[997,1726,1727],{"class":1014},"phone",[997,1729,1730],{"class":1110},"        \u002F\u002F   country calling code, \"1\"\n",[997,1732,1734,1736,1739],{"class":999,"line":1733},30,[997,1735,1435],{"class":1010},[997,1737,1738],{"class":1014},"numericCode",[997,1740,1741],{"class":1110},"  \u002F\u002F   ISO 3166-1 numeric, \"840\"\n",[932,1743,1745],{"className":991,"code":1744,"language":993,"meta":940,"style":940},"ctx.proxy\n  .isProxy   \u002F\u002F boolean, true if proxy.mmdb matched this IP\n  .proxyType \u002F\u002F string | undefined, ProxyRecord.comment field:\n             \u002F\u002F comma-separated source list names, e.g. \"firehol_proxies,xroxy\"\n",[865,1746,1747,1756,1766,1776],{"__ignoreMap":940},[997,1748,1749,1751,1753],{"class":999,"line":1000},[997,1750,1182],{"class":1014},[997,1752,904],{"class":1010},[997,1754,1755],{"class":1014},"proxy\n",[997,1757,1758,1760,1763],{"class":999,"line":1048},[997,1759,1435],{"class":1010},[997,1761,1762],{"class":1014},"isProxy",[997,1764,1765],{"class":1110},"   \u002F\u002F boolean, true if proxy.mmdb matched this IP\n",[997,1767,1768,1770,1773],{"class":999,"line":1054},[997,1769,1435],{"class":1010},[997,1771,1772],{"class":1014},"proxyType",[997,1774,1775],{"class":1110}," \u002F\u002F string | undefined, ProxyRecord.comment field:\n",[997,1777,1778],{"class":999,"line":1095},[997,1779,1780],{"class":1110},"             \u002F\u002F comma-separated source list names, e.g. \"firehol_proxies,xroxy\"\n",[1391,1782,149],{"id":1783},"tor-analysis",[861,1785,1786,1789,1790,1793,1794,1797],{},[865,1787,1788],{},"ctx.tor"," is ",[865,1791,1792],{},"Partial\u003COmit\u003CTorRecord, 'range'>>",", an empty object when the IP is not a known Tor relay. All fields are sourced directly from ",[865,1795,1796],{},"tor.mmdb"," via the Onionoo dataset.",[932,1799,1801],{"className":991,"code":1800,"language":993,"meta":940,"style":940},"ctx.tor\n  .running                   \u002F\u002F   boolean, relay is currently running\n  .flags                     \u002F\u002F   comma-separated flag string, e.g. \"Exit,Fast,Guard,Running,Stable,Valid\"\n  .exit_addresses            \u002F\u002F   string, non-empty means this is a confirmed exit node\n  .exit_probability          \u002F\u002F   number, probability this relay is chosen as exit (0–1)\n  .guard_probability         \u002F\u002F   number, probability this relay is chosen as guard (0–1)\n  .middle_probability        \u002F\u002F   number, probability chosen as middle relay (0–1)\n  .recommended_version       \u002F\u002F   boolean, running a Tor-project-recommended version\n  .version_status            \u002F\u002F   \"recommended\" | \"obsolete\" | \"new\" | \"unrecommended\" | etc.\n  .exit_policy               \u002F\u002F   string, serialized exit policy rules\n  .exit_policy_summary       \u002F\u002F   string, serialized accept\u002Freject summary\n  .exit_policy_v6_summary    \u002F\u002F   string | undefined, IPv6 exit policy summary\n  .country                   \u002F\u002F   2-letter country code of the relay, e.g. \"de\"\n  .country_name              \u002F\u002F   e.g. \"germany\"\n  .as                        \u002F\u002F   ASN of the relay, e.g. \"AS24940\"\n  .as_name                   \u002F\u002F   ASN org name of the relay, e.g. \"hetzner online gmbh\"\n  .or_addresses              \u002F\u002F   string, onion router address(es)\n  .contact                   \u002F\u002F   string, operator contact info (often obfuscated)\n  .first_seen                \u002F\u002F   ISO date string, when this relay first appeared\n  .last_seen                 \u002F\u002F   ISO date string, last time relay was observed\n  .last_restarted            \u002F\u002F   ISO date string\n  .last_changed_address_or_port \u002F\u002F ISO date string\n  .measured                  \u002F\u002F   boolean, whether bandwidth has been measured\n",[865,1802,1803,1812,1822,1832,1842,1852,1862,1872,1882,1892,1902,1912,1922,1931,1941,1951,1961,1971,1981,1991,2001,2011,2021],{"__ignoreMap":940},[997,1804,1805,1807,1809],{"class":999,"line":1000},[997,1806,1182],{"class":1014},[997,1808,904],{"class":1010},[997,1810,1811],{"class":1014},"tor\n",[997,1813,1814,1816,1819],{"class":999,"line":1048},[997,1815,1435],{"class":1010},[997,1817,1818],{"class":1014},"running",[997,1820,1821],{"class":1110},"                   \u002F\u002F   boolean, relay is currently running\n",[997,1823,1824,1826,1829],{"class":999,"line":1054},[997,1825,1435],{"class":1010},[997,1827,1828],{"class":1014},"flags",[997,1830,1831],{"class":1110},"                     \u002F\u002F   comma-separated flag string, e.g. \"Exit,Fast,Guard,Running,Stable,Valid\"\n",[997,1833,1834,1836,1839],{"class":999,"line":1095},[997,1835,1435],{"class":1010},[997,1837,1838],{"class":1014},"exit_addresses",[997,1840,1841],{"class":1110},"            \u002F\u002F   string, non-empty means this is a confirmed exit node\n",[997,1843,1844,1846,1849],{"class":999,"line":1114},[997,1845,1435],{"class":1010},[997,1847,1848],{"class":1014},"exit_probability",[997,1850,1851],{"class":1110},"          \u002F\u002F   number, probability this relay is chosen as exit (0–1)\n",[997,1853,1854,1856,1859],{"class":999,"line":1142},[997,1855,1435],{"class":1010},[997,1857,1858],{"class":1014},"guard_probability",[997,1860,1861],{"class":1110},"         \u002F\u002F   number, probability this relay is chosen as guard (0–1)\n",[997,1863,1864,1866,1869],{"class":999,"line":1174},[997,1865,1435],{"class":1010},[997,1867,1868],{"class":1014},"middle_probability",[997,1870,1871],{"class":1110},"        \u002F\u002F   number, probability chosen as middle relay (0–1)\n",[997,1873,1874,1876,1879],{"class":999,"line":1211},[997,1875,1435],{"class":1010},[997,1877,1878],{"class":1014},"recommended_version",[997,1880,1881],{"class":1110},"       \u002F\u002F   boolean, running a Tor-project-recommended version\n",[997,1883,1884,1886,1889],{"class":999,"line":1239},[997,1885,1435],{"class":1010},[997,1887,1888],{"class":1014},"version_status",[997,1890,1891],{"class":1110},"            \u002F\u002F   \"recommended\" | \"obsolete\" | \"new\" | \"unrecommended\" | etc.\n",[997,1893,1894,1896,1899],{"class":999,"line":1256},[997,1895,1435],{"class":1010},[997,1897,1898],{"class":1014},"exit_policy",[997,1900,1901],{"class":1110},"               \u002F\u002F   string, serialized exit policy rules\n",[997,1903,1904,1906,1909],{"class":999,"line":1524},[997,1905,1435],{"class":1010},[997,1907,1908],{"class":1014},"exit_policy_summary",[997,1910,1911],{"class":1110},"       \u002F\u002F   string, serialized accept\u002Freject summary\n",[997,1913,1914,1916,1919],{"class":999,"line":1535},[997,1915,1435],{"class":1010},[997,1917,1918],{"class":1014},"exit_policy_v6_summary",[997,1920,1921],{"class":1110},"    \u002F\u002F   string | undefined, IPv6 exit policy summary\n",[997,1923,1924,1926,1928],{"class":999,"line":1546},[997,1925,1435],{"class":1010},[997,1927,1438],{"class":1014},[997,1929,1930],{"class":1110},"                   \u002F\u002F   2-letter country code of the relay, e.g. \"de\"\n",[997,1932,1933,1935,1938],{"class":999,"line":1557},[997,1934,1435],{"class":1010},[997,1936,1937],{"class":1014},"country_name",[997,1939,1940],{"class":1110},"              \u002F\u002F   e.g. \"germany\"\n",[997,1942,1943,1945,1948],{"class":999,"line":1568},[997,1944,1435],{"class":1010},[997,1946,1947],{"class":1014},"as",[997,1949,1950],{"class":1110},"                        \u002F\u002F   ASN of the relay, e.g. \"AS24940\"\n",[997,1952,1953,1955,1958],{"class":999,"line":1579},[997,1954,1435],{"class":1010},[997,1956,1957],{"class":1014},"as_name",[997,1959,1960],{"class":1110},"                   \u002F\u002F   ASN org name of the relay, e.g. \"hetzner online gmbh\"\n",[997,1962,1963,1965,1968],{"class":999,"line":1590},[997,1964,1435],{"class":1010},[997,1966,1967],{"class":1014},"or_addresses",[997,1969,1970],{"class":1110},"              \u002F\u002F   string, onion router address(es)\n",[997,1972,1973,1975,1978],{"class":999,"line":1601},[997,1974,1435],{"class":1010},[997,1976,1977],{"class":1014},"contact",[997,1979,1980],{"class":1110},"                   \u002F\u002F   string, operator contact info (often obfuscated)\n",[997,1982,1983,1985,1988],{"class":999,"line":1612},[997,1984,1435],{"class":1010},[997,1986,1987],{"class":1014},"first_seen",[997,1989,1990],{"class":1110},"                \u002F\u002F   ISO date string, when this relay first appeared\n",[997,1992,1993,1995,1998],{"class":999,"line":1623},[997,1994,1435],{"class":1010},[997,1996,1997],{"class":1014},"last_seen",[997,1999,2000],{"class":1110},"                 \u002F\u002F   ISO date string, last time relay was observed\n",[997,2002,2003,2005,2008],{"class":999,"line":1634},[997,2004,1435],{"class":1010},[997,2006,2007],{"class":1014},"last_restarted",[997,2009,2010],{"class":1110},"            \u002F\u002F   ISO date string\n",[997,2012,2013,2015,2018],{"class":999,"line":1645},[997,2014,1435],{"class":1010},[997,2016,2017],{"class":1014},"last_changed_address_or_port",[997,2019,2020],{"class":1110}," \u002F\u002F ISO date string\n",[997,2022,2023,2025,2028],{"class":999,"line":1656},[997,2024,1435],{"class":1010},[997,2026,2027],{"class":1014},"measured",[997,2029,2030],{"class":1110},"                  \u002F\u002F   boolean, whether bandwidth has been measured\n",[1391,2032,599],{"id":2033},"bgp-asn",[861,2035,2036,1789,2039,2042],{},[865,2037,2038],{},"ctx.bgp",[865,2040,2041],{},"Partial\u003COmit\u003CBgpRecord, 'range'>>",", an empty object when the ASN is not found.",[932,2044,2046],{"className":991,"code":2045,"language":993,"meta":940,"style":940},"ctx.bgp\n  .asn_id                    \u002F\u002F   string, ASN identifier, e.g. \"AS13335\"\n  .asn_name                  \u002F\u002F   string, org name, e.g. \"cloudflare, inc.\"\n  .classification            \u002F\u002F   string, \"Content\" | \"Eyeballs\" | \"Unknown\"\n  .hits                      \u002F\u002F   string, BGP route announcement count\n",[865,2047,2048,2057,2067,2077,2087],{"__ignoreMap":940},[997,2049,2050,2052,2054],{"class":999,"line":1000},[997,2051,1182],{"class":1014},[997,2053,904],{"class":1010},[997,2055,2056],{"class":1014},"bgp\n",[997,2058,2059,2061,2064],{"class":999,"line":1048},[997,2060,1435],{"class":1010},[997,2062,2063],{"class":1014},"asn_id",[997,2065,2066],{"class":1110},"                    \u002F\u002F   string, ASN identifier, e.g. \"AS13335\"\n",[997,2068,2069,2071,2074],{"class":999,"line":1054},[997,2070,1435],{"class":1010},[997,2072,2073],{"class":1014},"asn_name",[997,2075,2076],{"class":1110},"                  \u002F\u002F   string, org name, e.g. \"cloudflare, inc.\"\n",[997,2078,2079,2081,2084],{"class":999,"line":1095},[997,2080,1435],{"class":1010},[997,2082,2083],{"class":1014},"classification",[997,2085,2086],{"class":1110},"            \u002F\u002F   string, \"Content\" | \"Eyeballs\" | \"Unknown\"\n",[997,2088,2089,2091,2094],{"class":999,"line":1114},[997,2090,1435],{"class":1010},[997,2092,2093],{"class":1014},"hits",[997,2095,2096],{"class":1110},"                      \u002F\u002F   string, BGP route announcement count\n",[861,2098,2099,2101,2102,2105,2106,2109,2110,2113],{},[865,2100,2083],{}," is the key field here. ",[865,2103,2104],{},"\"Content\""," means CDN\u002Fhosting (sets ",[865,2107,2108],{},"ctx.geoData.hosting = true","). ",[865,2111,2112],{},"\"Eyeballs\""," means residential\u002Fbusiness internet.",[1391,2115,2117],{"id":2116},"threat-level-and-anonymity","Threat Level and Anonymity",[932,2119,2121],{"className":991,"code":2120,"language":993,"meta":940,"style":940},"ctx.anon        \u002F\u002F boolean: true if IP is in firehol_anonymous.mmdb\nctx.threatLevel \u002F\u002F 1 | 2 | 3 | 4 | null: highest Firehol level matched (1 = most severe)\n                \u002F\u002F   1 = firehol_l1 (confirmed malicious, no false positives)\n                \u002F\u002F   2 = firehol_l2 (abuse + scanning)\n                \u002F\u002F   3 = firehol_l3 (web attacks)\n                \u002F\u002F   4 = firehol_l4 (aggressive but broader)\n",[865,2122,2123,2135,2147,2152,2157,2162],{"__ignoreMap":940},[997,2124,2125,2127,2129,2132],{"class":999,"line":1000},[997,2126,1182],{"class":1014},[997,2128,904],{"class":1010},[997,2130,2131],{"class":1014},"anon",[997,2133,2134],{"class":1110},"        \u002F\u002F boolean: true if IP is in firehol_anonymous.mmdb\n",[997,2136,2137,2139,2141,2144],{"class":999,"line":1048},[997,2138,1182],{"class":1014},[997,2140,904],{"class":1010},[997,2142,2143],{"class":1014},"threatLevel",[997,2145,2146],{"class":1110}," \u002F\u002F 1 | 2 | 3 | 4 | null: highest Firehol level matched (1 = most severe)\n",[997,2148,2149],{"class":999,"line":1054},[997,2150,2151],{"class":1110},"                \u002F\u002F   1 = firehol_l1 (confirmed malicious, no false positives)\n",[997,2153,2154],{"class":999,"line":1095},[997,2155,2156],{"class":1110},"                \u002F\u002F   2 = firehol_l2 (abuse + scanning)\n",[997,2158,2159],{"class":999,"line":1114},[997,2160,2161],{"class":1110},"                \u002F\u002F   3 = firehol_l3 (web attacks)\n",[997,2163,2164],{"class":999,"line":1142},[997,2165,2166],{"class":1110},"                \u002F\u002F   4 = firehol_l4 (aggressive but broader)\n",[861,2168,2169,2170,2172,2173,1017,2176,1789,2178,2181,2182,2185],{},"The levels are mutually exclusive in ",[865,2171,1182],{},", if an IP matches ",[865,2174,2175],{},"firehol_l1",[865,2177,2143],{},[865,2179,2180],{},"1"," and levels 2–4 are not checked. A ",[865,2183,2184],{},"null"," value means no match.",[1391,2187,2189],{"id":2188},"parsed-user-agent","Parsed User Agent",[861,2191,2192,2195,2196,2199],{},[865,2193,2194],{},"ua-parser-js"," resolves all fields synchronously before any checker runs. The result is ",[865,2197,2198],{},"Partial\u003CParsedUAResult>"," so every field may be undefined.",[932,2201,2203],{"className":991,"code":2202,"language":993,"meta":940,"style":940},"ctx.parsedUA\n  .browser        \u002F\u002F   \"chrome\", \"safari\", \"firefox\"\n  .browserType    \u002F\u002F   \"browser\" | \"crawler\" | \"fetcher\" | \"cli\" | \"library\"\n  .browserVersion \u002F\u002F   \"120.0.0\"\n  .os             \u002F\u002F   \"windows\", \"macos\", \"android\"\n  .device         \u002F\u002F   \"desktop\", \"mobile\", \"tablet\"\n  .deviceVendor   \u002F\u002F   \"apple\", \"samsung\"\n  .deviceModel    \u002F\u002F   \"iphone\", \"galaxy s24\"\n  .bot            \u002F\u002F   boolean, ua-parser-js considers this a bot UA\n  .botAI          \u002F\u002F   boolean, known AI crawler (GPTBot, ClaudeBot, etc.)\n  .allResults     \u002F\u002F   IResult, raw ua-parser-js output, for advanced access\n",[865,2204,2205,2214,2224,2234,2244,2254,2264,2274,2284,2294,2304],{"__ignoreMap":940},[997,2206,2207,2209,2211],{"class":999,"line":1000},[997,2208,1182],{"class":1014},[997,2210,904],{"class":1010},[997,2212,2213],{"class":1014},"parsedUA\n",[997,2215,2216,2218,2221],{"class":999,"line":1048},[997,2217,1435],{"class":1010},[997,2219,2220],{"class":1014},"browser",[997,2222,2223],{"class":1110},"        \u002F\u002F   \"chrome\", \"safari\", \"firefox\"\n",[997,2225,2226,2228,2231],{"class":999,"line":1054},[997,2227,1435],{"class":1010},[997,2229,2230],{"class":1014},"browserType",[997,2232,2233],{"class":1110},"    \u002F\u002F   \"browser\" | \"crawler\" | \"fetcher\" | \"cli\" | \"library\"\n",[997,2235,2236,2238,2241],{"class":999,"line":1095},[997,2237,1435],{"class":1010},[997,2239,2240],{"class":1014},"browserVersion",[997,2242,2243],{"class":1110}," \u002F\u002F   \"120.0.0\"\n",[997,2245,2246,2248,2251],{"class":999,"line":1114},[997,2247,1435],{"class":1010},[997,2249,2250],{"class":1014},"os",[997,2252,2253],{"class":1110},"             \u002F\u002F   \"windows\", \"macos\", \"android\"\n",[997,2255,2256,2258,2261],{"class":999,"line":1142},[997,2257,1435],{"class":1010},[997,2259,2260],{"class":1014},"device",[997,2262,2263],{"class":1110},"         \u002F\u002F   \"desktop\", \"mobile\", \"tablet\"\n",[997,2265,2266,2268,2271],{"class":999,"line":1174},[997,2267,1435],{"class":1010},[997,2269,2270],{"class":1014},"deviceVendor",[997,2272,2273],{"class":1110},"   \u002F\u002F   \"apple\", \"samsung\"\n",[997,2275,2276,2278,2281],{"class":999,"line":1211},[997,2277,1435],{"class":1010},[997,2279,2280],{"class":1014},"deviceModel",[997,2282,2283],{"class":1110},"    \u002F\u002F   \"iphone\", \"galaxy s24\"\n",[997,2285,2286,2288,2291],{"class":999,"line":1239},[997,2287,1435],{"class":1010},[997,2289,2290],{"class":1014},"bot",[997,2292,2293],{"class":1110},"            \u002F\u002F   boolean, ua-parser-js considers this a bot UA\n",[997,2295,2296,2298,2301],{"class":999,"line":1256},[997,2297,1435],{"class":1010},[997,2299,2300],{"class":1014},"botAI",[997,2302,2303],{"class":1110},"          \u002F\u002F   boolean, known AI crawler (GPTBot, ClaudeBot, etc.)\n",[997,2305,2306,2308,2311],{"class":999,"line":1524},[997,2307,1435],{"class":1010},[997,2309,2310],{"class":1014},"allResults",[997,2312,2313],{"class":1110},"     \u002F\u002F   IResult, raw ua-parser-js output, for advanced access\n",[861,2315,2316,2318,2319,889,2322,2325,2326,889,2329,2332,2333,904],{},[865,2317,2230],{}," is the fastest signal for bot intent. ",[865,2320,2321],{},"\"crawler\"",[865,2323,2324],{},"\"fetcher\""," trigger the good-bot DNS verification path. ",[865,2327,2328],{},"\"cli\"",[865,2330,2331],{},"\"library\""," are penalised by the built-in ",[865,2334,2335],{},"BrowserDetailsAndDeviceChecker",[1391,2337,2339],{"id":2338},"custom-data","Custom Data",[932,2341,2343],{"className":991,"code":2342,"language":993,"meta":940,"style":940},"ctx.custom \u002F\u002F TCustom, populated by your buildCustomContext function, {} by default\n",[865,2344,2345],{"__ignoreMap":940},[997,2346,2347,2349,2351,2354],{"class":999,"line":1000},[997,2348,1182],{"class":1014},[997,2350,904],{"class":1010},[997,2352,2353],{"class":1014},"custom",[997,2355,2356],{"class":1110}," \u002F\u002F TCustom, populated by your buildCustomContext function, {} by default\n",[861,2358,2359,2360,2365],{},"See ",[2361,2362,2364],"a",{"href":2363},"#custom-context--passing-your-own-data-into-checkers","Custom Context"," below for how to populate and type this field.",[942,2367],{},[879,2369,2371],{"id":2370},"minimal-example-synchronous-cheap-checker","Minimal Example Synchronous Cheap Checker",[861,2373,2374,2375,2377,2378,2381],{},"A cheap checker should be synchronous and rely only on data already present in ",[865,2376,1182],{},". The example below penalises requests from South America that carry no ",[865,2379,2380],{},"Accept-Language"," header.",[932,2383,2386],{"className":991,"code":2384,"filename":2385,"language":993,"meta":940,"style":940},"import { CheckerRegistry } from '@riavzon\u002Fbot-detector';\nimport type { IBotChecker, ValidationContext, BotDetectorConfig } from '@riavzon\u002Fbot-detector';\n\ntype MyReasonCode = 'MISSING_ACCEPT_LANGUAGE' | 'BAD_BOT_DETECTED';\n\nclass MissingLocaleChecker implements IBotChecker\u003CMyReasonCode> {\n  name = 'MissingLocaleChecker';\n  phase = 'cheap' as const;\n\n  isEnabled(_config: BotDetectorConfig): boolean {\n    return true;\n  }\n\n  run(ctx: ValidationContext, _config: BotDetectorConfig) {\n    const reasons: MyReasonCode[] = [];\n    let score = 0;\n\n    if (!ctx.req.get('Accept-Language') && ctx.geoData.continent === 'south america') {\n      score += 15;\n      reasons.push('MISSING_ACCEPT_LANGUAGE');\n    }\n\n    return { score, reasons };\n  }\n}\n\nCheckerRegistry.register(new MissingLocaleChecker());\n","my-custom-checker.ts",[865,2387,2388,2409,2439,2443,2471,2475,2497,2512,2532,2536,2558,2569,2574,2578,2601,2623,2639,2643,2702,2715,2736,2741,2745,2762,2766,2770,2774],{"__ignoreMap":940},[997,2389,2390,2392,2394,2397,2399,2401,2403,2405,2407],{"class":999,"line":1000},[997,2391,1004],{"class":1003},[997,2393,1011],{"class":1010},[997,2395,2396],{"class":1014},"CheckerRegistry",[997,2398,1028],{"class":1010},[997,2400,1031],{"class":1003},[997,2402,1035],{"class":1034},[997,2404,1039],{"class":1038},[997,2406,1042],{"class":1034},[997,2408,1045],{"class":1010},[997,2410,2411,2413,2415,2417,2419,2421,2423,2425,2427,2429,2431,2433,2435,2437],{"class":999,"line":1048},[997,2412,1004],{"class":1003},[997,2414,1007],{"class":1003},[997,2416,1011],{"class":1010},[997,2418,876],{"class":1014},[997,2420,1017],{"class":1010},[997,2422,1020],{"class":1014},[997,2424,1017],{"class":1010},[997,2426,1025],{"class":1014},[997,2428,1028],{"class":1010},[997,2430,1031],{"class":1003},[997,2432,1035],{"class":1034},[997,2434,1039],{"class":1038},[997,2436,1042],{"class":1034},[997,2438,1045],{"class":1010},[997,2440,2441],{"class":999,"line":1054},[997,2442,1051],{"emptyLinePlaceholder":8},[997,2444,2445,2448,2451,2453,2455,2458,2460,2462,2464,2467,2469],{"class":999,"line":1095},[997,2446,2447],{"class":1057},"type",[997,2449,2450],{"class":1061}," MyReasonCode",[997,2452,1076],{"class":1075},[997,2454,1035],{"class":1034},[997,2456,2457],{"class":1038},"MISSING_ACCEPT_LANGUAGE",[997,2459,1042],{"class":1034},[997,2461,1128],{"class":1075},[997,2463,1035],{"class":1034},[997,2465,2466],{"class":1038},"BAD_BOT_DETECTED",[997,2468,1042],{"class":1034},[997,2470,1045],{"class":1010},[997,2472,2473],{"class":999,"line":1114},[997,2474,1051],{"emptyLinePlaceholder":8},[997,2476,2477,2480,2484,2487,2489,2491,2494],{"class":999,"line":1142},[997,2478,2479],{"class":1057},"class",[997,2481,2483],{"class":2482},"s5jk-"," MissingLocaleChecker",[997,2485,2486],{"class":1057}," implements",[997,2488,1062],{"class":1061},[997,2490,1065],{"class":1010},[997,2492,2493],{"class":1068},"MyReasonCode",[997,2495,2496],{"class":1010},"> {\n",[997,2498,2499,2501,2503,2505,2508,2510],{"class":999,"line":1174},[997,2500,1098],{"class":1014},[997,2502,1076],{"class":1075},[997,2504,1035],{"class":1034},[997,2506,2507],{"class":1038},"MissingLocaleChecker",[997,2509,1042],{"class":1034},[997,2511,1045],{"class":1010},[997,2513,2514,2516,2518,2520,2522,2524,2527,2530],{"class":999,"line":1211},[997,2515,1117],{"class":1014},[997,2517,1076],{"class":1075},[997,2519,1035],{"class":1034},[997,2521,888],{"class":1038},[997,2523,1042],{"class":1034},[997,2525,2526],{"class":1003}," as",[997,2528,2529],{"class":1057}," const",[997,2531,1045],{"class":1010},[997,2533,2534],{"class":999,"line":1239},[997,2535,1051],{"emptyLinePlaceholder":8},[997,2537,2538,2540,2542,2545,2547,2549,2551,2553,2555],{"class":999,"line":1256},[997,2539,1146],{"class":1145},[997,2541,1149],{"class":1010},[997,2543,2544],{"class":1152},"_config",[997,2546,1101],{"class":1075},[997,2548,1158],{"class":1061},[997,2550,1161],{"class":1010},[997,2552,1101],{"class":1075},[997,2554,1166],{"class":1061},[997,2556,2557],{"class":1010}," {\n",[997,2559,2560,2563,2567],{"class":999,"line":1524},[997,2561,2562],{"class":1003},"    return",[997,2564,2566],{"class":2565},"sjR7W"," true",[997,2568,1045],{"class":1010},[997,2570,2571],{"class":999,"line":1535},[997,2572,2573],{"class":1010},"  }\n",[997,2575,2576],{"class":999,"line":1546},[997,2577,1051],{"emptyLinePlaceholder":8},[997,2579,2580,2582,2584,2586,2588,2590,2592,2594,2596,2598],{"class":999,"line":1557},[997,2581,1177],{"class":1145},[997,2583,1149],{"class":1010},[997,2585,1182],{"class":1152},[997,2587,1101],{"class":1075},[997,2589,1187],{"class":1061},[997,2591,1017],{"class":1010},[997,2593,2544],{"class":1152},[997,2595,1101],{"class":1075},[997,2597,1158],{"class":1061},[997,2599,2600],{"class":1010},") {\n",[997,2602,2603,2606,2610,2612,2614,2617,2620],{"class":999,"line":1568},[997,2604,2605],{"class":1057},"    const",[997,2607,2609],{"class":2608},"s3JHE"," reasons",[997,2611,1101],{"class":1075},[997,2613,2450],{"class":1061},[997,2615,2616],{"class":1010},"[] ",[997,2618,2619],{"class":1075},"=",[997,2621,2622],{"class":1010}," [];\n",[997,2624,2625,2628,2631,2633,2637],{"class":999,"line":1579},[997,2626,2627],{"class":1057},"    let",[997,2629,2630],{"class":1014}," score",[997,2632,1076],{"class":1075},[997,2634,2636],{"class":2635},"spgvN"," 0",[997,2638,1045],{"class":1010},[997,2640,2641],{"class":999,"line":1590},[997,2642,1051],{"emptyLinePlaceholder":8},[997,2644,2645,2648,2651,2654,2656,2658,2660,2662,2665,2667,2669,2671,2673,2676,2679,2682,2684,2686,2688,2690,2693,2695,2698,2700],{"class":999,"line":1601},[997,2646,2647],{"class":1003},"    if",[997,2649,2650],{"class":1010}," (",[997,2652,2653],{"class":1075},"!",[997,2655,1182],{"class":1014},[997,2657,904],{"class":1010},[997,2659,1362],{"class":1014},[997,2661,904],{"class":1010},[997,2663,2664],{"class":1145},"get",[997,2666,1149],{"class":1010},[997,2668,1042],{"class":1034},[997,2670,2380],{"class":1038},[997,2672,1042],{"class":1034},[997,2674,2675],{"class":1010},") ",[997,2677,2678],{"class":1075},"&&",[997,2680,2681],{"class":1014}," ctx",[997,2683,904],{"class":1010},[997,2685,1427],{"class":1014},[997,2687,904],{"class":1010},[997,2689,1458],{"class":1014},[997,2691,2692],{"class":1075}," ===",[997,2694,1035],{"class":1034},[997,2696,2697],{"class":1038},"south america",[997,2699,1042],{"class":1034},[997,2701,2600],{"class":1010},[997,2703,2704,2707,2710,2713],{"class":999,"line":1612},[997,2705,2706],{"class":1014},"      score",[997,2708,2709],{"class":1075}," +=",[997,2711,2712],{"class":2635}," 15",[997,2714,1045],{"class":1010},[997,2716,2717,2720,2722,2725,2727,2729,2731,2733],{"class":999,"line":1623},[997,2718,2719],{"class":1014},"      reasons",[997,2721,904],{"class":1010},[997,2723,2724],{"class":1145},"push",[997,2726,1149],{"class":1010},[997,2728,1042],{"class":1034},[997,2730,2457],{"class":1038},[997,2732,1042],{"class":1034},[997,2734,2735],{"class":1010},");\n",[997,2737,2738],{"class":999,"line":1634},[997,2739,2740],{"class":1010},"    }\n",[997,2742,2743],{"class":999,"line":1645},[997,2744,1051],{"emptyLinePlaceholder":8},[997,2746,2747,2749,2751,2754,2756,2759],{"class":999,"line":1656},[997,2748,2562],{"class":1003},[997,2750,1011],{"class":1010},[997,2752,2753],{"class":1014},"score",[997,2755,1017],{"class":1010},[997,2757,2758],{"class":1014},"reasons",[997,2760,2761],{"class":1010}," };\n",[997,2763,2764],{"class":999,"line":1667},[997,2765,2573],{"class":1010},[997,2767,2768],{"class":999,"line":1678},[997,2769,1259],{"class":1010},[997,2771,2772],{"class":999,"line":1689},[997,2773,1051],{"emptyLinePlaceholder":8},[997,2775,2776,2778,2780,2783,2785,2789,2791],{"class":999,"line":1700},[997,2777,2396],{"class":1014},[997,2779,904],{"class":1010},[997,2781,2782],{"class":1145},"register",[997,2784,1149],{"class":1010},[997,2786,2788],{"class":2787},"sakC6","new",[997,2790,2483],{"class":1145},[997,2792,2793],{"class":1010},"());\n",[861,2795,2796,2797,972,2800,2803,2804,904],{},"Import the file ",[870,2798,2799],{},"after",[865,2801,2802],{},"defineConfiguration()"," resolves. The side-effect import triggers ",[865,2805,2806],{},"CheckerRegistry.register()",[932,2808,2811],{"className":991,"code":2809,"filename":2810,"language":993,"meta":940,"style":940},"import { defineConfiguration, detectBots } from '@riavzon\u002Fbot-detector';\nimport '.\u002Fmy-custom-checker.js';\n\nawait defineConfiguration({ store: { main: { driver: 'sqlite', name: '.\u002Fbot-detector.db' } }, banScore: 80 });\napp.use(detectBots()); \u002F\u002F always call detectBots() as a factory\n","server.ts",[865,2812,2813,2839,2852,2856,2921],{"__ignoreMap":940},[997,2814,2815,2817,2819,2822,2824,2827,2829,2831,2833,2835,2837],{"class":999,"line":1000},[997,2816,1004],{"class":1003},[997,2818,1011],{"class":1010},[997,2820,2821],{"class":1014},"defineConfiguration",[997,2823,1017],{"class":1010},[997,2825,2826],{"class":1014},"detectBots",[997,2828,1028],{"class":1010},[997,2830,1031],{"class":1003},[997,2832,1035],{"class":1034},[997,2834,1039],{"class":1038},[997,2836,1042],{"class":1034},[997,2838,1045],{"class":1010},[997,2840,2841,2843,2845,2848,2850],{"class":999,"line":1048},[997,2842,1004],{"class":1003},[997,2844,1035],{"class":1034},[997,2846,2847],{"class":1038},".\u002Fmy-custom-checker.js",[997,2849,1042],{"class":1034},[997,2851,1045],{"class":1010},[997,2853,2854],{"class":999,"line":1054},[997,2855,1051],{"emptyLinePlaceholder":8},[997,2857,2858,2861,2864,2867,2870,2873,2875,2878,2880,2882,2885,2887,2889,2892,2894,2896,2899,2901,2903,2906,2908,2911,2913,2915,2918],{"class":999,"line":1095},[997,2859,2860],{"class":1003},"await",[997,2862,2863],{"class":1145}," defineConfiguration",[997,2865,2866],{"class":1010},"({ ",[997,2868,2869],{"class":1014},"store",[997,2871,1101],{"class":2872},"s34zl",[997,2874,1011],{"class":1010},[997,2876,2877],{"class":1014},"main",[997,2879,1101],{"class":2872},[997,2881,1011],{"class":1010},[997,2883,2884],{"class":1014},"driver",[997,2886,1101],{"class":2872},[997,2888,1035],{"class":1034},[997,2890,2891],{"class":1038},"sqlite",[997,2893,1042],{"class":1034},[997,2895,1017],{"class":1010},[997,2897,2898],{"class":1014},"name",[997,2900,1101],{"class":2872},[997,2902,1035],{"class":1034},[997,2904,2905],{"class":1038},".\u002Fbot-detector.db",[997,2907,1042],{"class":1034},[997,2909,2910],{"class":1010}," } }, ",[997,2912,900],{"class":1014},[997,2914,1101],{"class":2872},[997,2916,2917],{"class":2635}," 80",[997,2919,2920],{"class":1010}," });\n",[997,2922,2923,2926,2928,2931,2933,2935,2938],{"class":999,"line":1114},[997,2924,2925],{"class":1014},"app",[997,2927,904],{"class":1010},[997,2929,2930],{"class":1145},"use",[997,2932,1149],{"class":1010},[997,2934,2826],{"class":1145},[997,2936,2937],{"class":1010},"()); ",[997,2939,2940],{"class":1110},"\u002F\u002F always call detectBots() as a factory\n",[942,2942],{},[879,2944,2946],{"id":2945},"custom-context-passing-your-own-data-into-checkers","Custom Context Passing Your Own Data into Checkers",[861,2948,2949,2950,2952,2953,2955,2956,2958,2959,2962,2963,2966,2967,904],{},"Every checker receives a ",[865,2951,980],{}," field. By default it is ",[865,2954,988],{}," empty. You populate it by passing a ",[865,2957,984],{}," function to ",[865,2960,2961],{},"detectBots()",". The generic type flows through to your checker's ",[865,2964,2965],{},"run()"," signature, giving you full IntelliSense on ",[865,2968,980],{},[861,2970,2971],{},"The builder function runs once per request, before any checker executes.",[2973,2974,2976,2980,3059,3065,3248,3252,3266,3661],"steps",{"level":2975},"3",[1391,2977,2979],{"id":2978},"define-your-context-shape","Define Your Context Shape",[932,2981,2984],{"className":991,"code":2982,"filename":2983,"language":993,"meta":940,"style":940},"export interface MyContext {\n  userId: string;\n  plan: 'free' | 'pro' | 'enterprise';\n  isInternal: boolean;\n}\n","myContext.ts",[865,2985,2986,2999,3010,3044,3055],{"__ignoreMap":940},[997,2987,2988,2991,2994,2997],{"class":999,"line":1000},[997,2989,2990],{"class":1003},"export",[997,2992,2993],{"class":1057}," interface",[997,2995,2996],{"class":1061}," MyContext",[997,2998,2557],{"class":1010},[997,3000,3001,3004,3006,3008],{"class":999,"line":1048},[997,3002,3003],{"class":1014},"  userId",[997,3005,1101],{"class":1075},[997,3007,1104],{"class":1061},[997,3009,1045],{"class":1010},[997,3011,3012,3015,3017,3019,3022,3024,3026,3028,3031,3033,3035,3037,3040,3042],{"class":999,"line":1054},[997,3013,3014],{"class":1014},"  plan",[997,3016,1101],{"class":1075},[997,3018,1035],{"class":1034},[997,3020,3021],{"class":1038},"free",[997,3023,1042],{"class":1034},[997,3025,1128],{"class":1075},[997,3027,1035],{"class":1034},[997,3029,3030],{"class":1038},"pro",[997,3032,1042],{"class":1034},[997,3034,1128],{"class":1075},[997,3036,1035],{"class":1034},[997,3038,3039],{"class":1038},"enterprise",[997,3041,1042],{"class":1034},[997,3043,1045],{"class":1010},[997,3045,3046,3049,3051,3053],{"class":999,"line":1095},[997,3047,3048],{"class":1014},"  isInternal",[997,3050,1101],{"class":1075},[997,3052,1166],{"class":1061},[997,3054,1045],{"class":1010},[997,3056,3057],{"class":999,"line":1114},[997,3058,1259],{"class":1010},[1391,3060,3062,3063],{"id":3061},"pass-the-builder-to-detectbots","Pass the Builder to ",[865,3064,2961],{},[932,3066,3068],{"className":991,"code":3067,"filename":2810,"language":993,"meta":940,"style":940},"import { detectBots } from '@riavzon\u002Fbot-detector';\nimport type { MyContext } from '.\u002Ftypes\u002FmyContext.js';\n\napp.use(\n  detectBots\u003CMyContext>((req) => ({\n    userId: req.user?.id   ?? 'anonymous',\n    plan: req.user?.plan ?? 'free',\n    isInternal: req.ip === '127.0.0.1',\n  }))\n);\n",[865,3069,3070,3090,3114,3118,3129,3151,3185,3214,3239,3244],{"__ignoreMap":940},[997,3071,3072,3074,3076,3078,3080,3082,3084,3086,3088],{"class":999,"line":1000},[997,3073,1004],{"class":1003},[997,3075,1011],{"class":1010},[997,3077,2826],{"class":1014},[997,3079,1028],{"class":1010},[997,3081,1031],{"class":1003},[997,3083,1035],{"class":1034},[997,3085,1039],{"class":1038},[997,3087,1042],{"class":1034},[997,3089,1045],{"class":1010},[997,3091,3092,3094,3096,3098,3101,3103,3105,3107,3110,3112],{"class":999,"line":1048},[997,3093,1004],{"class":1003},[997,3095,1007],{"class":1003},[997,3097,1011],{"class":1010},[997,3099,3100],{"class":1014},"MyContext",[997,3102,1028],{"class":1010},[997,3104,1031],{"class":1003},[997,3106,1035],{"class":1034},[997,3108,3109],{"class":1038},".\u002Ftypes\u002FmyContext.js",[997,3111,1042],{"class":1034},[997,3113,1045],{"class":1010},[997,3115,3116],{"class":999,"line":1054},[997,3117,1051],{"emptyLinePlaceholder":8},[997,3119,3120,3122,3124,3126],{"class":999,"line":1095},[997,3121,2925],{"class":1014},[997,3123,904],{"class":1010},[997,3125,2930],{"class":1145},[997,3127,3128],{"class":1010},"(\n",[997,3130,3131,3134,3136,3138,3141,3143,3145,3148],{"class":999,"line":1114},[997,3132,3133],{"class":1145},"  detectBots",[997,3135,1065],{"class":1010},[997,3137,3100],{"class":1068},[997,3139,3140],{"class":1010},">((",[997,3142,1362],{"class":1152},[997,3144,2675],{"class":1010},[997,3146,3147],{"class":1057},"=>",[997,3149,3150],{"class":1010}," ({\n",[997,3152,3153,3156,3158,3161,3163,3166,3169,3172,3175,3177,3180,3182],{"class":999,"line":1142},[997,3154,3155],{"class":1014},"    userId",[997,3157,1101],{"class":2872},[997,3159,3160],{"class":1014}," req",[997,3162,904],{"class":1010},[997,3164,3165],{"class":1014},"user",[997,3167,3168],{"class":1010},"?.",[997,3170,3171],{"class":1014},"id",[997,3173,3174],{"class":1075},"   ??",[997,3176,1035],{"class":1034},[997,3178,3179],{"class":1038},"anonymous",[997,3181,1042],{"class":1034},[997,3183,3184],{"class":1010},",\n",[997,3186,3187,3190,3192,3194,3196,3198,3200,3203,3206,3208,3210,3212],{"class":999,"line":1174},[997,3188,3189],{"class":1014},"    plan",[997,3191,1101],{"class":2872},[997,3193,3160],{"class":1014},[997,3195,904],{"class":1010},[997,3197,3165],{"class":1014},[997,3199,3168],{"class":1010},[997,3201,3202],{"class":1014},"plan",[997,3204,3205],{"class":1075}," ??",[997,3207,1035],{"class":1034},[997,3209,3021],{"class":1038},[997,3211,1042],{"class":1034},[997,3213,3184],{"class":1010},[997,3215,3216,3219,3221,3223,3225,3228,3230,3232,3235,3237],{"class":999,"line":1211},[997,3217,3218],{"class":1014},"    isInternal",[997,3220,1101],{"class":2872},[997,3222,3160],{"class":1014},[997,3224,904],{"class":1010},[997,3226,3227],{"class":1014},"ip",[997,3229,2692],{"class":1075},[997,3231,1035],{"class":1034},[997,3233,3234],{"class":1038},"127.0.0.1",[997,3236,1042],{"class":1034},[997,3238,3184],{"class":1010},[997,3240,3241],{"class":999,"line":1239},[997,3242,3243],{"class":1010},"  }))\n",[997,3245,3246],{"class":999,"line":1256},[997,3247,2735],{"class":1010},[1391,3249,3251],{"id":3250},"declare-the-type-on-your-checker","Declare the Type on Your Checker",[861,3253,3254,3255,3257,3258,3260,3261,3263,3264,904],{},"Passing ",[865,3256,3100],{}," as the second type parameter to ",[865,3259,876],{}," makes ",[865,3262,980],{}," fully typed inside ",[865,3265,2965],{},[932,3267,3270],{"className":991,"code":3268,"filename":3269,"language":993,"meta":940,"style":940},"import { CheckerRegistry } from '@riavzon\u002Fbot-detector';\nimport type { IBotChecker, ValidationContext, BotDetectorConfig, BanReasonCode } from '@riavzon\u002Fbot-detector';\nimport type { MyContext } from '.\u002Ftypes\u002FmyContext.js';\n\nclass PlanAbuseChecker implements IBotChecker\u003CBanReasonCode, MyContext> {\n  name = 'PlanAbuseChecker';\n  phase = 'cheap' as const;\n\n  isEnabled(_config: BotDetectorConfig) { \n    return true; \n  }\n \n  run(ctx: ValidationContext\u003CMyContext>, _config: BotDetectorConfig) {\n    const reasons: BanReasonCode[] = [];\n    let score = 0;\n\n    if (ctx.custom.isInternal) return { score, reasons }; \u002F\u002F bypass internal traffic\n\n    if (ctx.custom.plan === 'free' && ctx.geoData.proxy) {\n      score += 20;\n      reasons.push('PROXY_DETECTED');\n    }\n\n    return { score, reasons };\n  }\n}\n\nCheckerRegistry.register(new PlanAbuseChecker());\n","my-plan-checker.ts",[865,3271,3272,3292,3326,3348,3352,3373,3388,3406,3410,3425,3434,3438,3443,3469,3486,3498,3502,3538,3542,3581,3592,3611,3615,3619,3633,3637,3641,3645],{"__ignoreMap":940},[997,3273,3274,3276,3278,3280,3282,3284,3286,3288,3290],{"class":999,"line":1000},[997,3275,1004],{"class":1003},[997,3277,1011],{"class":1010},[997,3279,2396],{"class":1014},[997,3281,1028],{"class":1010},[997,3283,1031],{"class":1003},[997,3285,1035],{"class":1034},[997,3287,1039],{"class":1038},[997,3289,1042],{"class":1034},[997,3291,1045],{"class":1010},[997,3293,3294,3296,3298,3300,3302,3304,3306,3308,3310,3312,3314,3316,3318,3320,3322,3324],{"class":999,"line":1048},[997,3295,1004],{"class":1003},[997,3297,1007],{"class":1003},[997,3299,1011],{"class":1010},[997,3301,876],{"class":1014},[997,3303,1017],{"class":1010},[997,3305,1020],{"class":1014},[997,3307,1017],{"class":1010},[997,3309,1025],{"class":1014},[997,3311,1017],{"class":1010},[997,3313,966],{"class":1014},[997,3315,1028],{"class":1010},[997,3317,1031],{"class":1003},[997,3319,1035],{"class":1034},[997,3321,1039],{"class":1038},[997,3323,1042],{"class":1034},[997,3325,1045],{"class":1010},[997,3327,3328,3330,3332,3334,3336,3338,3340,3342,3344,3346],{"class":999,"line":1054},[997,3329,1004],{"class":1003},[997,3331,1007],{"class":1003},[997,3333,1011],{"class":1010},[997,3335,3100],{"class":1014},[997,3337,1028],{"class":1010},[997,3339,1031],{"class":1003},[997,3341,1035],{"class":1034},[997,3343,3109],{"class":1038},[997,3345,1042],{"class":1034},[997,3347,1045],{"class":1010},[997,3349,3350],{"class":999,"line":1095},[997,3351,1051],{"emptyLinePlaceholder":8},[997,3353,3354,3356,3359,3361,3363,3365,3367,3369,3371],{"class":999,"line":1114},[997,3355,2479],{"class":1057},[997,3357,3358],{"class":2482}," PlanAbuseChecker",[997,3360,2486],{"class":1057},[997,3362,1062],{"class":1061},[997,3364,1065],{"class":1010},[997,3366,966],{"class":1068},[997,3368,1017],{"class":1010},[997,3370,3100],{"class":1068},[997,3372,2496],{"class":1010},[997,3374,3375,3377,3379,3381,3384,3386],{"class":999,"line":1142},[997,3376,1098],{"class":1014},[997,3378,1076],{"class":1075},[997,3380,1035],{"class":1034},[997,3382,3383],{"class":1038},"PlanAbuseChecker",[997,3385,1042],{"class":1034},[997,3387,1045],{"class":1010},[997,3389,3390,3392,3394,3396,3398,3400,3402,3404],{"class":999,"line":1174},[997,3391,1117],{"class":1014},[997,3393,1076],{"class":1075},[997,3395,1035],{"class":1034},[997,3397,888],{"class":1038},[997,3399,1042],{"class":1034},[997,3401,2526],{"class":1003},[997,3403,2529],{"class":1057},[997,3405,1045],{"class":1010},[997,3407,3408],{"class":999,"line":1211},[997,3409,1051],{"emptyLinePlaceholder":8},[997,3411,3412,3414,3416,3418,3420,3422],{"class":999,"line":1239},[997,3413,1146],{"class":1145},[997,3415,1149],{"class":1010},[997,3417,2544],{"class":1152},[997,3419,1101],{"class":1075},[997,3421,1158],{"class":1061},[997,3423,3424],{"class":1010},") { \n",[997,3426,3427,3429,3431],{"class":999,"line":1256},[997,3428,2562],{"class":1003},[997,3430,2566],{"class":2565},[997,3432,3433],{"class":1010},"; \n",[997,3435,3436],{"class":999,"line":1524},[997,3437,2573],{"class":1010},[997,3439,3440],{"class":999,"line":1535},[997,3441,3442],{"class":1010}," \n",[997,3444,3445,3447,3449,3451,3453,3455,3457,3459,3461,3463,3465,3467],{"class":999,"line":1546},[997,3446,1177],{"class":1145},[997,3448,1149],{"class":1010},[997,3450,1182],{"class":1152},[997,3452,1101],{"class":1075},[997,3454,1187],{"class":1061},[997,3456,1065],{"class":1010},[997,3458,3100],{"class":1068},[997,3460,1194],{"class":1010},[997,3462,2544],{"class":1152},[997,3464,1101],{"class":1075},[997,3466,1158],{"class":1061},[997,3468,2600],{"class":1010},[997,3470,3471,3473,3475,3477,3480,3482,3484],{"class":999,"line":1557},[997,3472,2605],{"class":1057},[997,3474,2609],{"class":2608},[997,3476,1101],{"class":1075},[997,3478,3479],{"class":1061}," BanReasonCode",[997,3481,2616],{"class":1010},[997,3483,2619],{"class":1075},[997,3485,2622],{"class":1010},[997,3487,3488,3490,3492,3494,3496],{"class":999,"line":1568},[997,3489,2627],{"class":1057},[997,3491,2630],{"class":1014},[997,3493,1076],{"class":1075},[997,3495,2636],{"class":2635},[997,3497,1045],{"class":1010},[997,3499,3500],{"class":999,"line":1579},[997,3501,1051],{"emptyLinePlaceholder":8},[997,3503,3504,3506,3508,3510,3512,3514,3516,3519,3521,3524,3526,3528,3530,3532,3535],{"class":999,"line":1590},[997,3505,2647],{"class":1003},[997,3507,2650],{"class":1010},[997,3509,1182],{"class":1014},[997,3511,904],{"class":1010},[997,3513,2353],{"class":1014},[997,3515,904],{"class":1010},[997,3517,3518],{"class":1014},"isInternal",[997,3520,2675],{"class":1010},[997,3522,3523],{"class":1003},"return",[997,3525,1011],{"class":1010},[997,3527,2753],{"class":1014},[997,3529,1017],{"class":1010},[997,3531,2758],{"class":1014},[997,3533,3534],{"class":1010}," }; ",[997,3536,3537],{"class":1110},"\u002F\u002F bypass internal traffic\n",[997,3539,3540],{"class":999,"line":1601},[997,3541,1051],{"emptyLinePlaceholder":8},[997,3543,3544,3546,3548,3550,3552,3554,3556,3558,3560,3562,3564,3566,3569,3571,3573,3575,3577,3579],{"class":999,"line":1612},[997,3545,2647],{"class":1003},[997,3547,2650],{"class":1010},[997,3549,1182],{"class":1014},[997,3551,904],{"class":1010},[997,3553,2353],{"class":1014},[997,3555,904],{"class":1010},[997,3557,3202],{"class":1014},[997,3559,2692],{"class":1075},[997,3561,1035],{"class":1034},[997,3563,3021],{"class":1038},[997,3565,1042],{"class":1034},[997,3567,3568],{"class":1075}," &&",[997,3570,2681],{"class":1014},[997,3572,904],{"class":1010},[997,3574,1427],{"class":1014},[997,3576,904],{"class":1010},[997,3578,1639],{"class":1014},[997,3580,2600],{"class":1010},[997,3582,3583,3585,3587,3590],{"class":999,"line":1623},[997,3584,2706],{"class":1014},[997,3586,2709],{"class":1075},[997,3588,3589],{"class":2635}," 20",[997,3591,1045],{"class":1010},[997,3593,3594,3596,3598,3600,3602,3604,3607,3609],{"class":999,"line":1634},[997,3595,2719],{"class":1014},[997,3597,904],{"class":1010},[997,3599,2724],{"class":1145},[997,3601,1149],{"class":1010},[997,3603,1042],{"class":1034},[997,3605,3606],{"class":1038},"PROXY_DETECTED",[997,3608,1042],{"class":1034},[997,3610,2735],{"class":1010},[997,3612,3613],{"class":999,"line":1645},[997,3614,2740],{"class":1010},[997,3616,3617],{"class":999,"line":1656},[997,3618,1051],{"emptyLinePlaceholder":8},[997,3620,3621,3623,3625,3627,3629,3631],{"class":999,"line":1667},[997,3622,2562],{"class":1003},[997,3624,1011],{"class":1010},[997,3626,2753],{"class":1014},[997,3628,1017],{"class":1010},[997,3630,2758],{"class":1014},[997,3632,2761],{"class":1010},[997,3634,3635],{"class":999,"line":1678},[997,3636,2573],{"class":1010},[997,3638,3639],{"class":999,"line":1689},[997,3640,1259],{"class":1010},[997,3642,3643],{"class":999,"line":1700},[997,3644,1051],{"emptyLinePlaceholder":8},[997,3646,3647,3649,3651,3653,3655,3657,3659],{"class":999,"line":1711},[997,3648,2396],{"class":1014},[997,3650,904],{"class":1010},[997,3652,2782],{"class":1145},[997,3654,1149],{"class":1010},[997,3656,2788],{"class":2787},[997,3658,3358],{"class":1145},[997,3660,2793],{"class":1010},[861,3662,3663,3664,3666,3667,3669,3670,3673],{},"When no custom context is needed, call ",[865,3665,2961],{}," with no arguments, ",[865,3668,980],{}," will be ",[865,3671,3672],{},"{}"," and is safe to ignore.",[942,3675],{},[879,3677,3679],{"id":3678},"async-checker-with-database-or-cache","Async Checker with Database or Cache",[861,3681,3682,3683,3686,3687,3689],{},"Checkers that need I\u002FO, database queries, external API calls, or cache reads with a DB fallback, should declare ",[865,3684,3685],{},"phase: 'heavy'",". The heavy phase only runs when the cheap phase score stays below ",[865,3688,900],{},", so expensive work is never done for obvious bots.",[861,3691,3692,3693,3696,3697,904],{},"This example uses the built in storage via ",[865,3694,3695],{},"getStorage()"," to cache results and avoid database queries on repeated IPs. The storage instance is the same one configured in ",[865,3698,2802],{},[932,3700,3703],{"className":991,"code":3701,"filename":3702,"language":993,"meta":940,"style":940},"import { getStorage, CheckerRegistry } from '@riavzon\u002Fbot-detector';\nimport type { IBotChecker, ValidationContext, BotDetectorConfig } from '@riavzon\u002Fbot-detector';\n\nclass MyAsyncChecker implements IBotChecker\u003C'MY_REASON'> {\n  name = 'MyAsyncChecker';\n  phase = 'heavy' as const;\n\n  isEnabled(_config: BotDetectorConfig): boolean {\n    return true;\n  }\n\n  async run(ctx: ValidationContext, _config: BotDetectorConfig) {\n    const reasons: 'MY_REASON'[] = [];\n    let score = 0;\n\n    if (!ctx.cookie) return { score, reasons };\n\n    const storage = getStorage();\n    const cacheKey = `my-checker:${ctx.cookie}`;\n\n    const cached = await storage.getItem\u003Cnumber>(cacheKey);\n    if (cached !== null) {\n      return { score: cached, reasons: cached > 0 ? ['MY_REASON'] : [] };\n    }\n\n    \u002F\u002F Your DB query or external call here\n    const result = await myDb.query('SELECT ...', [ctx.ipAddress]);\n    if (result.isSuspicious) {\n      score = 30;\n      reasons.push('MY_REASON');\n    }\n\n    await storage.setItem(cacheKey, score, { ttl: 300 }); \u002F\u002F 5 min TTL\n    return { score, reasons };\n  }\n}\n\nCheckerRegistry.register(new MyAsyncChecker());\n","my-async-checker.ts",[865,3704,3705,3730,3760,3764,3786,3801,3819,3823,3843,3851,3855,3859,3885,3905,3917,3921,3949,3953,3968,3998,4002,4033,4050,4096,4100,4104,4109,4149,4165,4176,4194,4199,4204,4242,4257,4262,4267,4272],{"__ignoreMap":940},[997,3706,3707,3709,3711,3714,3716,3718,3720,3722,3724,3726,3728],{"class":999,"line":1000},[997,3708,1004],{"class":1003},[997,3710,1011],{"class":1010},[997,3712,3713],{"class":1014},"getStorage",[997,3715,1017],{"class":1010},[997,3717,2396],{"class":1014},[997,3719,1028],{"class":1010},[997,3721,1031],{"class":1003},[997,3723,1035],{"class":1034},[997,3725,1039],{"class":1038},[997,3727,1042],{"class":1034},[997,3729,1045],{"class":1010},[997,3731,3732,3734,3736,3738,3740,3742,3744,3746,3748,3750,3752,3754,3756,3758],{"class":999,"line":1048},[997,3733,1004],{"class":1003},[997,3735,1007],{"class":1003},[997,3737,1011],{"class":1010},[997,3739,876],{"class":1014},[997,3741,1017],{"class":1010},[997,3743,1020],{"class":1014},[997,3745,1017],{"class":1010},[997,3747,1025],{"class":1014},[997,3749,1028],{"class":1010},[997,3751,1031],{"class":1003},[997,3753,1035],{"class":1034},[997,3755,1039],{"class":1038},[997,3757,1042],{"class":1034},[997,3759,1045],{"class":1010},[997,3761,3762],{"class":999,"line":1054},[997,3763,1051],{"emptyLinePlaceholder":8},[997,3765,3766,3768,3771,3773,3775,3777,3779,3782,3784],{"class":999,"line":1095},[997,3767,2479],{"class":1057},[997,3769,3770],{"class":2482}," MyAsyncChecker",[997,3772,2486],{"class":1057},[997,3774,1062],{"class":1061},[997,3776,1065],{"class":1010},[997,3778,1042],{"class":1034},[997,3780,3781],{"class":1038},"MY_REASON",[997,3783,1042],{"class":1034},[997,3785,2496],{"class":1010},[997,3787,3788,3790,3792,3794,3797,3799],{"class":999,"line":1114},[997,3789,1098],{"class":1014},[997,3791,1076],{"class":1075},[997,3793,1035],{"class":1034},[997,3795,3796],{"class":1038},"MyAsyncChecker",[997,3798,1042],{"class":1034},[997,3800,1045],{"class":1010},[997,3802,3803,3805,3807,3809,3811,3813,3815,3817],{"class":999,"line":1142},[997,3804,1117],{"class":1014},[997,3806,1076],{"class":1075},[997,3808,1035],{"class":1034},[997,3810,892],{"class":1038},[997,3812,1042],{"class":1034},[997,3814,2526],{"class":1003},[997,3816,2529],{"class":1057},[997,3818,1045],{"class":1010},[997,3820,3821],{"class":999,"line":1174},[997,3822,1051],{"emptyLinePlaceholder":8},[997,3824,3825,3827,3829,3831,3833,3835,3837,3839,3841],{"class":999,"line":1211},[997,3826,1146],{"class":1145},[997,3828,1149],{"class":1010},[997,3830,2544],{"class":1152},[997,3832,1101],{"class":1075},[997,3834,1158],{"class":1061},[997,3836,1161],{"class":1010},[997,3838,1101],{"class":1075},[997,3840,1166],{"class":1061},[997,3842,2557],{"class":1010},[997,3844,3845,3847,3849],{"class":999,"line":1239},[997,3846,2562],{"class":1003},[997,3848,2566],{"class":2565},[997,3850,1045],{"class":1010},[997,3852,3853],{"class":999,"line":1256},[997,3854,2573],{"class":1010},[997,3856,3857],{"class":999,"line":1524},[997,3858,1051],{"emptyLinePlaceholder":8},[997,3860,3861,3864,3867,3869,3871,3873,3875,3877,3879,3881,3883],{"class":999,"line":1535},[997,3862,3863],{"class":1057},"  async",[997,3865,3866],{"class":1145}," run",[997,3868,1149],{"class":1010},[997,3870,1182],{"class":1152},[997,3872,1101],{"class":1075},[997,3874,1187],{"class":1061},[997,3876,1017],{"class":1010},[997,3878,2544],{"class":1152},[997,3880,1101],{"class":1075},[997,3882,1158],{"class":1061},[997,3884,2600],{"class":1010},[997,3886,3887,3889,3891,3893,3895,3897,3899,3901,3903],{"class":999,"line":1546},[997,3888,2605],{"class":1057},[997,3890,2609],{"class":2608},[997,3892,1101],{"class":1075},[997,3894,1035],{"class":1034},[997,3896,3781],{"class":1038},[997,3898,1042],{"class":1034},[997,3900,2616],{"class":1010},[997,3902,2619],{"class":1075},[997,3904,2622],{"class":1010},[997,3906,3907,3909,3911,3913,3915],{"class":999,"line":1557},[997,3908,2627],{"class":1057},[997,3910,2630],{"class":1014},[997,3912,1076],{"class":1075},[997,3914,2636],{"class":2635},[997,3916,1045],{"class":1010},[997,3918,3919],{"class":999,"line":1568},[997,3920,1051],{"emptyLinePlaceholder":8},[997,3922,3923,3925,3927,3929,3931,3933,3935,3937,3939,3941,3943,3945,3947],{"class":999,"line":1579},[997,3924,2647],{"class":1003},[997,3926,2650],{"class":1010},[997,3928,2653],{"class":1075},[997,3930,1182],{"class":1014},[997,3932,904],{"class":1010},[997,3934,1386],{"class":1014},[997,3936,2675],{"class":1010},[997,3938,3523],{"class":1003},[997,3940,1011],{"class":1010},[997,3942,2753],{"class":1014},[997,3944,1017],{"class":1010},[997,3946,2758],{"class":1014},[997,3948,2761],{"class":1010},[997,3950,3951],{"class":999,"line":1590},[997,3952,1051],{"emptyLinePlaceholder":8},[997,3954,3955,3957,3960,3962,3965],{"class":999,"line":1601},[997,3956,2605],{"class":1057},[997,3958,3959],{"class":2608}," storage",[997,3961,1076],{"class":1075},[997,3963,3964],{"class":1145}," getStorage",[997,3966,3967],{"class":1010},"();\n",[997,3969,3970,3972,3975,3977,3980,3983,3985,3988,3990,3993,3996],{"class":999,"line":1612},[997,3971,2605],{"class":1057},[997,3973,3974],{"class":2608}," cacheKey",[997,3976,1076],{"class":1075},[997,3978,3979],{"class":1038}," `my-checker:",[997,3981,3982],{"class":1057},"${",[997,3984,1182],{"class":1014},[997,3986,904],{"class":3987},"s1lnM",[997,3989,1386],{"class":1014},[997,3991,3992],{"class":1057},"}",[997,3994,3995],{"class":1038},"`",[997,3997,1045],{"class":1010},[997,3999,4000],{"class":999,"line":1623},[997,4001,1051],{"emptyLinePlaceholder":8},[997,4003,4004,4006,4009,4011,4014,4016,4018,4021,4023,4025,4028,4031],{"class":999,"line":1634},[997,4005,2605],{"class":1057},[997,4007,4008],{"class":2608}," cached",[997,4010,1076],{"class":1075},[997,4012,4013],{"class":1003}," await",[997,4015,3959],{"class":1014},[997,4017,904],{"class":1010},[997,4019,4020],{"class":1145},"getItem",[997,4022,1065],{"class":1010},[997,4024,1225],{"class":1061},[997,4026,4027],{"class":1010},">(",[997,4029,4030],{"class":1014},"cacheKey",[997,4032,2735],{"class":1010},[997,4034,4035,4037,4039,4042,4045,4048],{"class":999,"line":1645},[997,4036,2647],{"class":1003},[997,4038,2650],{"class":1010},[997,4040,4041],{"class":1014},"cached",[997,4043,4044],{"class":1075}," !==",[997,4046,4047],{"class":2565}," null",[997,4049,2600],{"class":1010},[997,4051,4052,4055,4057,4059,4061,4063,4065,4067,4069,4071,4074,4076,4079,4082,4084,4086,4088,4091,4093],{"class":999,"line":1656},[997,4053,4054],{"class":1003},"      return",[997,4056,1011],{"class":1010},[997,4058,2753],{"class":1014},[997,4060,1101],{"class":2872},[997,4062,4008],{"class":1014},[997,4064,1017],{"class":1010},[997,4066,2758],{"class":1014},[997,4068,1101],{"class":2872},[997,4070,4008],{"class":1014},[997,4072,4073],{"class":1075}," >",[997,4075,2636],{"class":2635},[997,4077,4078],{"class":1075}," ?",[997,4080,4081],{"class":1010}," [",[997,4083,1042],{"class":1034},[997,4085,3781],{"class":1038},[997,4087,1042],{"class":1034},[997,4089,4090],{"class":1010},"] ",[997,4092,1101],{"class":1075},[997,4094,4095],{"class":1010}," [] };\n",[997,4097,4098],{"class":999,"line":1667},[997,4099,2740],{"class":1010},[997,4101,4102],{"class":999,"line":1678},[997,4103,1051],{"emptyLinePlaceholder":8},[997,4105,4106],{"class":999,"line":1689},[997,4107,4108],{"class":1110},"    \u002F\u002F Your DB query or external call here\n",[997,4110,4111,4113,4116,4118,4120,4123,4125,4128,4130,4132,4135,4137,4140,4142,4144,4146],{"class":999,"line":1700},[997,4112,2605],{"class":1057},[997,4114,4115],{"class":2608}," result",[997,4117,1076],{"class":1075},[997,4119,4013],{"class":1003},[997,4121,4122],{"class":1014}," myDb",[997,4124,904],{"class":1010},[997,4126,4127],{"class":1145},"query",[997,4129,1149],{"class":1010},[997,4131,1042],{"class":1034},[997,4133,4134],{"class":1038},"SELECT ...",[997,4136,1042],{"class":1034},[997,4138,4139],{"class":1010},", [",[997,4141,1182],{"class":1014},[997,4143,904],{"class":1010},[997,4145,1374],{"class":1014},[997,4147,4148],{"class":1010},"]);\n",[997,4150,4151,4153,4155,4158,4160,4163],{"class":999,"line":1711},[997,4152,2647],{"class":1003},[997,4154,2650],{"class":1010},[997,4156,4157],{"class":1014},"result",[997,4159,904],{"class":1010},[997,4161,4162],{"class":1014},"isSuspicious",[997,4164,2600],{"class":1010},[997,4166,4167,4169,4171,4174],{"class":999,"line":1722},[997,4168,2706],{"class":1014},[997,4170,1076],{"class":1075},[997,4172,4173],{"class":2635}," 30",[997,4175,1045],{"class":1010},[997,4177,4178,4180,4182,4184,4186,4188,4190,4192],{"class":999,"line":1733},[997,4179,2719],{"class":1014},[997,4181,904],{"class":1010},[997,4183,2724],{"class":1145},[997,4185,1149],{"class":1010},[997,4187,1042],{"class":1034},[997,4189,3781],{"class":1038},[997,4191,1042],{"class":1034},[997,4193,2735],{"class":1010},[997,4195,4197],{"class":999,"line":4196},31,[997,4198,2740],{"class":1010},[997,4200,4202],{"class":999,"line":4201},32,[997,4203,1051],{"emptyLinePlaceholder":8},[997,4205,4207,4210,4212,4214,4217,4219,4221,4223,4225,4228,4231,4233,4236,4239],{"class":999,"line":4206},33,[997,4208,4209],{"class":1003},"    await",[997,4211,3959],{"class":1014},[997,4213,904],{"class":1010},[997,4215,4216],{"class":1145},"setItem",[997,4218,1149],{"class":1010},[997,4220,4030],{"class":1014},[997,4222,1017],{"class":1010},[997,4224,2753],{"class":1014},[997,4226,4227],{"class":1010},", { ",[997,4229,4230],{"class":1014},"ttl",[997,4232,1101],{"class":2872},[997,4234,4235],{"class":2635}," 300",[997,4237,4238],{"class":1010}," }); ",[997,4240,4241],{"class":1110},"\u002F\u002F 5 min TTL\n",[997,4243,4245,4247,4249,4251,4253,4255],{"class":999,"line":4244},34,[997,4246,2562],{"class":1003},[997,4248,1011],{"class":1010},[997,4250,2753],{"class":1014},[997,4252,1017],{"class":1010},[997,4254,2758],{"class":1014},[997,4256,2761],{"class":1010},[997,4258,4260],{"class":999,"line":4259},35,[997,4261,2573],{"class":1010},[997,4263,4265],{"class":999,"line":4264},36,[997,4266,1259],{"class":1010},[997,4268,4270],{"class":999,"line":4269},37,[997,4271,1051],{"emptyLinePlaceholder":8},[997,4273,4275,4277,4279,4281,4283,4285,4287],{"class":999,"line":4274},38,[997,4276,2396],{"class":1014},[997,4278,904],{"class":1010},[997,4280,2782],{"class":1145},[997,4282,1149],{"class":1010},[997,4284,2788],{"class":2787},[997,4286,3770],{"class":1145},[997,4288,2793],{"class":1010},[4290,4291,4292],"tip",{},[861,4293,4294,4295,4298],{},"Use a namespaced key prefix (",[865,4296,4297],{},"custom:",") to avoid collisions with the built-in cache entries that share the same storage instance.",[942,4300],{},[879,4302,4304],{"id":4303},"triggering-an-immediate-ban","Triggering an Immediate Ban",[861,4306,4307,4308,4310,4311,4313],{},"Return the special reason code ",[865,4309,916],{}," from your checker to trigger an instant ban. The pipeline throws ",[865,4312,920],{}," immediately upon seeing this code, stopping all remaining checkers and banning the visitor without waiting for score accumulation.",[932,4315,4318],{"className":991,"code":4316,"filename":4317,"language":993,"meta":940,"style":940},"run(ctx: ValidationContext, _config: BotDetectorConfig) {\n  const reasons: ('MY_REASON' | 'BAD_BOT_DETECTED')[] = [];\n\n  if (isDefinitelyABot(ctx)) {\n    reasons.push('MY_REASON');\n    reasons.push('BAD_BOT_DETECTED'); \u002F\u002F pipeline stops here, visitor is banned immediately\n    return { score: 0, reasons };\n  }\n\n  return { score: 0, reasons };\n}\n","example.ts",[865,4319,4320,4343,4375,4379,4396,4415,4437,4455,4459,4463,4482],{"__ignoreMap":940},[997,4321,4322,4324,4326,4328,4331,4333,4335,4337,4339,4341],{"class":999,"line":1000},[997,4323,688],{"class":1145},[997,4325,1149],{"class":1010},[997,4327,1182],{"class":1014},[997,4329,4330],{"class":1010},": ",[997,4332,1020],{"class":1014},[997,4334,1017],{"class":1010},[997,4336,2544],{"class":1014},[997,4338,4330],{"class":1010},[997,4340,1025],{"class":1014},[997,4342,2600],{"class":1010},[997,4344,4345,4348,4350,4352,4354,4356,4358,4360,4362,4364,4366,4368,4371,4373],{"class":999,"line":1048},[997,4346,4347],{"class":1057},"  const",[997,4349,2609],{"class":2608},[997,4351,1101],{"class":1075},[997,4353,2650],{"class":1010},[997,4355,1042],{"class":1034},[997,4357,3781],{"class":1038},[997,4359,1042],{"class":1034},[997,4361,1128],{"class":1075},[997,4363,1035],{"class":1034},[997,4365,2466],{"class":1038},[997,4367,1042],{"class":1034},[997,4369,4370],{"class":1010},")[] ",[997,4372,2619],{"class":1075},[997,4374,2622],{"class":1010},[997,4376,4377],{"class":999,"line":1054},[997,4378,1051],{"emptyLinePlaceholder":8},[997,4380,4381,4384,4386,4389,4391,4393],{"class":999,"line":1095},[997,4382,4383],{"class":1003},"  if",[997,4385,2650],{"class":1010},[997,4387,4388],{"class":1145},"isDefinitelyABot",[997,4390,1149],{"class":1010},[997,4392,1182],{"class":1014},[997,4394,4395],{"class":1010},")) {\n",[997,4397,4398,4401,4403,4405,4407,4409,4411,4413],{"class":999,"line":1114},[997,4399,4400],{"class":1014},"    reasons",[997,4402,904],{"class":1010},[997,4404,2724],{"class":1145},[997,4406,1149],{"class":1010},[997,4408,1042],{"class":1034},[997,4410,3781],{"class":1038},[997,4412,1042],{"class":1034},[997,4414,2735],{"class":1010},[997,4416,4417,4419,4421,4423,4425,4427,4429,4431,4434],{"class":999,"line":1142},[997,4418,4400],{"class":1014},[997,4420,904],{"class":1010},[997,4422,2724],{"class":1145},[997,4424,1149],{"class":1010},[997,4426,1042],{"class":1034},[997,4428,2466],{"class":1038},[997,4430,1042],{"class":1034},[997,4432,4433],{"class":1010},"); ",[997,4435,4436],{"class":1110},"\u002F\u002F pipeline stops here, visitor is banned immediately\n",[997,4438,4439,4441,4443,4445,4447,4449,4451,4453],{"class":999,"line":1174},[997,4440,2562],{"class":1003},[997,4442,1011],{"class":1010},[997,4444,2753],{"class":1014},[997,4446,1101],{"class":2872},[997,4448,2636],{"class":2635},[997,4450,1017],{"class":1010},[997,4452,2758],{"class":1014},[997,4454,2761],{"class":1010},[997,4456,4457],{"class":999,"line":1211},[997,4458,2573],{"class":1010},[997,4460,4461],{"class":999,"line":1239},[997,4462,1051],{"emptyLinePlaceholder":8},[997,4464,4465,4468,4470,4472,4474,4476,4478,4480],{"class":999,"line":1256},[997,4466,4467],{"class":1003},"  return",[997,4469,1011],{"class":1010},[997,4471,2753],{"class":1014},[997,4473,1101],{"class":2872},[997,4475,2636],{"class":2635},[997,4477,1017],{"class":1010},[997,4479,2758],{"class":1014},[997,4481,2761],{"class":1010},[997,4483,4484],{"class":999,"line":1524},[997,4485,1259],{"class":1010},[861,4487,4488],{},"The built-in honeypot checker and good-bot DNS verifier both use this same mechanism.",[942,4490],{},[879,4492,4494],{"id":4493},"checker-configuration","Checker Configuration",[861,4496,863,4497,4499],{},[865,4498,1025],{}," schema is read only, you cannot add fields to it from outside the package. Manage your checker's settings in a separate module and import it directly.",[932,4501,4504],{"className":991,"code":4502,"filename":4503,"language":993,"meta":940,"style":940},"export const myCheckerConfig = {\n  threshold: 5,\n  penalty: 25,\n};\n","my-checker-config.ts",[865,4505,4506,4519,4531,4543],{"__ignoreMap":940},[997,4507,4508,4510,4512,4515,4517],{"class":999,"line":1000},[997,4509,2990],{"class":1003},[997,4511,2529],{"class":1057},[997,4513,4514],{"class":2608}," myCheckerConfig",[997,4516,1076],{"class":1075},[997,4518,2557],{"class":1010},[997,4520,4521,4524,4526,4529],{"class":999,"line":1048},[997,4522,4523],{"class":1014},"  threshold",[997,4525,1101],{"class":2872},[997,4527,4528],{"class":2635}," 5",[997,4530,3184],{"class":1010},[997,4532,4533,4536,4538,4541],{"class":999,"line":1054},[997,4534,4535],{"class":1014},"  penalty",[997,4537,1101],{"class":2872},[997,4539,4540],{"class":2635}," 25",[997,4542,3184],{"class":1010},[997,4544,4545],{"class":999,"line":1095},[997,4546,4547],{"class":1010},"};\n",[932,4549,4551],{"className":991,"code":4550,"filename":2385,"language":993,"meta":940,"style":940},"import { myCheckerConfig } from '.\u002Fmy-checker-config.js';\n\nrun(ctx: ValidationContext, _config: BotDetectorConfig) {\n  if (someValue > myCheckerConfig.threshold) {\n    score += myCheckerConfig.penalty;\n  }\n}\n",[865,4552,4553,4575,4579,4601,4621,4637,4641],{"__ignoreMap":940},[997,4554,4555,4557,4559,4562,4564,4566,4568,4571,4573],{"class":999,"line":1000},[997,4556,1004],{"class":1003},[997,4558,1011],{"class":1010},[997,4560,4561],{"class":1014},"myCheckerConfig",[997,4563,1028],{"class":1010},[997,4565,1031],{"class":1003},[997,4567,1035],{"class":1034},[997,4569,4570],{"class":1038},".\u002Fmy-checker-config.js",[997,4572,1042],{"class":1034},[997,4574,1045],{"class":1010},[997,4576,4577],{"class":999,"line":1048},[997,4578,1051],{"emptyLinePlaceholder":8},[997,4580,4581,4583,4585,4587,4589,4591,4593,4595,4597,4599],{"class":999,"line":1054},[997,4582,688],{"class":1145},[997,4584,1149],{"class":1010},[997,4586,1182],{"class":1014},[997,4588,4330],{"class":1010},[997,4590,1020],{"class":1014},[997,4592,1017],{"class":1010},[997,4594,2544],{"class":1014},[997,4596,4330],{"class":1010},[997,4598,1025],{"class":1014},[997,4600,2600],{"class":1010},[997,4602,4603,4605,4607,4610,4612,4614,4616,4619],{"class":999,"line":1095},[997,4604,4383],{"class":1003},[997,4606,2650],{"class":1010},[997,4608,4609],{"class":1014},"someValue",[997,4611,4073],{"class":1075},[997,4613,4514],{"class":1014},[997,4615,904],{"class":1010},[997,4617,4618],{"class":1014},"threshold",[997,4620,2600],{"class":1010},[997,4622,4623,4626,4628,4630,4632,4635],{"class":999,"line":1114},[997,4624,4625],{"class":1014},"    score",[997,4627,2709],{"class":1075},[997,4629,4514],{"class":1014},[997,4631,904],{"class":1010},[997,4633,4634],{"class":1014},"penalty",[997,4636,1045],{"class":1010},[997,4638,4639],{"class":999,"line":1142},[997,4640,2573],{"class":1010},[997,4642,4643],{"class":999,"line":1174},[997,4644,1259],{"class":1010},[942,4646],{},[879,4648,4650],{"id":4649},"checker-registration","Checker Registration",[861,4652,4653,4654,4656],{},"Registration happens at module load time. At the bottom of your checker file, call ",[865,4655,2806],{}," with an instance of your class. The registry is a module-level array, the call is synchronous and takes effect immediately when the file is imported.",[932,4658,4660],{"className":991,"code":4659,"filename":2385,"language":993,"meta":940,"style":940},"\nclass MyChecker implements IBotChecker\u003C'MY_REASON'> {\n  \u002F\u002F ...\n}\n\nCheckerRegistry.register(new MyChecker()); \u002F\u002F this line does the registration\n",[865,4661,4662,4666,4687,4692,4696,4700],{"__ignoreMap":940},[997,4663,4664],{"class":999,"line":1000},[997,4665,1051],{"emptyLinePlaceholder":8},[997,4667,4668,4670,4673,4675,4677,4679,4681,4683,4685],{"class":999,"line":1048},[997,4669,2479],{"class":1057},[997,4671,4672],{"class":2482}," MyChecker",[997,4674,2486],{"class":1057},[997,4676,1062],{"class":1061},[997,4678,1065],{"class":1010},[997,4680,1042],{"class":1034},[997,4682,3781],{"class":1038},[997,4684,1042],{"class":1034},[997,4686,2496],{"class":1010},[997,4688,4689],{"class":999,"line":1054},[997,4690,4691],{"class":1110},"  \u002F\u002F ...\n",[997,4693,4694],{"class":999,"line":1095},[997,4695,1259],{"class":1010},[997,4697,4698],{"class":999,"line":1114},[997,4699,1051],{"emptyLinePlaceholder":8},[997,4701,4702,4704,4706,4708,4710,4712,4714,4716],{"class":999,"line":1142},[997,4703,2396],{"class":1014},[997,4705,904],{"class":1010},[997,4707,2782],{"class":1145},[997,4709,1149],{"class":1010},[997,4711,2788],{"class":2787},[997,4713,4672],{"class":1145},[997,4715,2937],{"class":1010},[997,4717,4718],{"class":1110},"\u002F\u002F this line does the registration\n",[861,4720,4721,4722,972,4724,4726],{},"To activate the checker, import the file in your server entry point ",[870,4723,2799],{},[865,4725,2802],{}," resolves. A bare side-effect import is enough, you don't need to use the exported value.",[932,4728,4730],{"className":991,"code":4729,"filename":2810,"language":993,"meta":940,"style":940},"import { defineConfiguration, detectBots } from '@riavzon\u002Fbot-detector';\nimport '.\u002Fmy-custom-checker.js'; \u002F\u002F side-effect import, triggers CheckerRegistry.register()\n\nawait defineConfiguration({ store: { main: { driver: 'sqlite', name: '.\u002Fbot-detector.db' } } });\napp.use(detectBots());\n",[865,4731,4732,4756,4771,4775,4820],{"__ignoreMap":940},[997,4733,4734,4736,4738,4740,4742,4744,4746,4748,4750,4752,4754],{"class":999,"line":1000},[997,4735,1004],{"class":1003},[997,4737,1011],{"class":1010},[997,4739,2821],{"class":1014},[997,4741,1017],{"class":1010},[997,4743,2826],{"class":1014},[997,4745,1028],{"class":1010},[997,4747,1031],{"class":1003},[997,4749,1035],{"class":1034},[997,4751,1039],{"class":1038},[997,4753,1042],{"class":1034},[997,4755,1045],{"class":1010},[997,4757,4758,4760,4762,4764,4766,4768],{"class":999,"line":1048},[997,4759,1004],{"class":1003},[997,4761,1035],{"class":1034},[997,4763,2847],{"class":1038},[997,4765,1042],{"class":1034},[997,4767,1107],{"class":1010},[997,4769,4770],{"class":1110},"\u002F\u002F side-effect import, triggers CheckerRegistry.register()\n",[997,4772,4773],{"class":999,"line":1054},[997,4774,1051],{"emptyLinePlaceholder":8},[997,4776,4777,4779,4781,4783,4785,4787,4789,4791,4793,4795,4797,4799,4801,4803,4805,4807,4809,4811,4813,4815,4817],{"class":999,"line":1095},[997,4778,2860],{"class":1003},[997,4780,2863],{"class":1145},[997,4782,2866],{"class":1010},[997,4784,2869],{"class":1014},[997,4786,1101],{"class":2872},[997,4788,1011],{"class":1010},[997,4790,2877],{"class":1014},[997,4792,1101],{"class":2872},[997,4794,1011],{"class":1010},[997,4796,2884],{"class":1014},[997,4798,1101],{"class":2872},[997,4800,1035],{"class":1034},[997,4802,2891],{"class":1038},[997,4804,1042],{"class":1034},[997,4806,1017],{"class":1010},[997,4808,2898],{"class":1014},[997,4810,1101],{"class":2872},[997,4812,1035],{"class":1034},[997,4814,2905],{"class":1038},[997,4816,1042],{"class":1034},[997,4818,4819],{"class":1010}," } } });\n",[997,4821,4822,4824,4826,4828,4830,4832],{"class":999,"line":1114},[997,4823,2925],{"class":1014},[997,4825,904],{"class":1010},[997,4827,2930],{"class":1145},[997,4829,1149],{"class":1010},[997,4831,2826],{"class":1145},[997,4833,2793],{"class":1010},[4835,4836,4837],"note",{},[861,4838,4839,4840,4842,4843,889,4845,4848,4849,4851],{},"Importing your checker file before ",[865,4841,2802],{}," resolves is safe for registration itself, but ",[865,4844,3695],{},[865,4846,4847],{},"getDb()"," will throw if called at module initialisation time. Keep any storage or DB access inside ",[865,4850,2965],{},", not at the top level of your module.",[1391,4853,4855],{"id":4854},"registration-order","Registration Order",[861,4857,4858],{},"Checkers run in the order they are registered within their phase. The built-in pipeline executes in this sequence:",[4860,4861,4862,4865,4868,4871],"ol",{},[912,4863,4864],{},"Built-in cheap checkers (IP validation → good bots → browser\u002Fdevice → locale → UA\u002Fheaders → ...)",[912,4866,4867],{},"Your custom cheap checkers (in import order)",[912,4869,4870],{},"Built-in heavy checkers (rate tracking → proxy\u002FISP → session coherence → velocity → bad UA patterns)",[912,4872,4873],{},"Your custom heavy checkers (in import order)",[861,4875,4876],{},"When the order of your custom checkers matters, control it through import sequence:",[932,4878,4880],{"className":991,"code":4879,"filename":2810,"language":993,"meta":940,"style":940},"import '.\u002Fmy-cheap-checker-1.js'; \u002F\u002F registers first\nimport '.\u002Fmy-cheap-checker-2.js'; \u002F\u002F registers second\nimport { defineConfiguration, detectBots } from '@riavzon\u002Fbot-detector';\n\nawait defineConfiguration({ store: { main: { driver: 'sqlite', name: '.\u002Fbot-detector.db' } } });\napp.use(detectBots());\n",[865,4881,4882,4898,4914,4938,4942,4986],{"__ignoreMap":940},[997,4883,4884,4886,4888,4891,4893,4895],{"class":999,"line":1000},[997,4885,1004],{"class":1003},[997,4887,1035],{"class":1034},[997,4889,4890],{"class":1038},".\u002Fmy-cheap-checker-1.js",[997,4892,1042],{"class":1034},[997,4894,1107],{"class":1010},[997,4896,4897],{"class":1110},"\u002F\u002F registers first\n",[997,4899,4900,4902,4904,4907,4909,4911],{"class":999,"line":1048},[997,4901,1004],{"class":1003},[997,4903,1035],{"class":1034},[997,4905,4906],{"class":1038},".\u002Fmy-cheap-checker-2.js",[997,4908,1042],{"class":1034},[997,4910,1107],{"class":1010},[997,4912,4913],{"class":1110},"\u002F\u002F registers second\n",[997,4915,4916,4918,4920,4922,4924,4926,4928,4930,4932,4934,4936],{"class":999,"line":1054},[997,4917,1004],{"class":1003},[997,4919,1011],{"class":1010},[997,4921,2821],{"class":1014},[997,4923,1017],{"class":1010},[997,4925,2826],{"class":1014},[997,4927,1028],{"class":1010},[997,4929,1031],{"class":1003},[997,4931,1035],{"class":1034},[997,4933,1039],{"class":1038},[997,4935,1042],{"class":1034},[997,4937,1045],{"class":1010},[997,4939,4940],{"class":999,"line":1095},[997,4941,1051],{"emptyLinePlaceholder":8},[997,4943,4944,4946,4948,4950,4952,4954,4956,4958,4960,4962,4964,4966,4968,4970,4972,4974,4976,4978,4980,4982,4984],{"class":999,"line":1114},[997,4945,2860],{"class":1003},[997,4947,2863],{"class":1145},[997,4949,2866],{"class":1010},[997,4951,2869],{"class":1014},[997,4953,1101],{"class":2872},[997,4955,1011],{"class":1010},[997,4957,2877],{"class":1014},[997,4959,1101],{"class":2872},[997,4961,1011],{"class":1010},[997,4963,2884],{"class":1014},[997,4965,1101],{"class":2872},[997,4967,1035],{"class":1034},[997,4969,2891],{"class":1038},[997,4971,1042],{"class":1034},[997,4973,1017],{"class":1010},[997,4975,2898],{"class":1014},[997,4977,1101],{"class":2872},[997,4979,1035],{"class":1034},[997,4981,2905],{"class":1038},[997,4983,1042],{"class":1034},[997,4985,4819],{"class":1010},[997,4987,4988,4990,4992,4994,4996,4998],{"class":999,"line":1142},[997,4989,2925],{"class":1014},[997,4991,904],{"class":1010},[997,4993,2930],{"class":1145},[997,4995,1149],{"class":1010},[997,4997,2826],{"class":1145},[997,4999,2793],{"class":1010},[942,5001],{},[879,5003,5005,5006,5008],{"id":5004},"built-in-banreasoncode-values","Built-in ",[865,5007,966],{}," Values",[861,5010,5011],{},"Your custom checker can return any of the 57 built-in reason codes alongside your own. The most commonly reused ones are listed below.",[1276,5013,5014,5023],{},[1279,5015,5016],{},[1282,5017,5018,5020],{},[1285,5019,962],{},[1285,5021,5022],{},"Meaning",[1295,5024,5025,5034,5044,5053,5063,5073,5083,5093,5103,5116,5128,5138],{},[1282,5026,5027,5031],{},[1300,5028,5029],{},[865,5030,2466],{},[1300,5032,5033],{},"Triggers an immediate ban. No further checkers run.",[1282,5035,5036,5041],{},[1300,5037,5038],{},[865,5039,5040],{},"GOOD_BOT_IDENTIFIED",[1300,5042,5043],{},"Marks the request as a verified legitimate crawler. No further checkers run.",[1282,5045,5046,5050],{},[1300,5047,5048],{},[865,5049,3606],{},[1300,5051,5052],{},"IP matched in proxy database",[1282,5054,5055,5060],{},[1300,5056,5057],{},[865,5058,5059],{},"HOSTING_DETECTED",[1300,5061,5062],{},"ASN classified as hosting\u002FContent or confirmed Tor exit",[1282,5064,5065,5070],{},[1300,5066,5067],{},[865,5068,5069],{},"ANONYMITY_NETWORK",[1300,5071,5072],{},"IP appears in Firehol anonymous list",[1282,5074,5075,5080],{},[1300,5076,5077],{},[865,5078,5079],{},"HEADLESS_BROWSER_DETECTED",[1300,5081,5082],{},"Headless browser keyword found in the user agent",[1282,5084,5085,5090],{},[1300,5086,5087],{},[865,5088,5089],{},"BEHAVIOR_TOO_FAST",[1300,5091,5092],{},"Request rate exceeds the configured behavioral threshold",[1282,5094,5095,5100],{},[1300,5096,5097],{},[865,5098,5099],{},"TIMING_TOO_REGULAR",[1300,5101,5102],{},"Inter-request intervals are too consistent (low coefficient of variation)",[1282,5104,5105,5110],{},[1300,5106,5107],{},[865,5108,5109],{},"PREVIOUSLY_BANNED_IP",[1300,5111,5112,5113],{},"IP exists in ",[865,5114,5115],{},"banned.mmdb",[1282,5117,5118,5123],{},[1300,5119,5120],{},[865,5121,5122],{},"PREVIOUSLY_HIGH_RISK_IP",[1300,5124,5112,5125],{},[865,5126,5127],{},"highRisk.mmdb",[1282,5129,5130,5135],{},[1300,5131,5132],{},[865,5133,5134],{},"HONEYPOT_PATH_HIT",[1300,5136,5137],{},"Request targeted a honeypot path",[1282,5139,5140,5145],{},[1300,5141,5142],{},[865,5143,5144],{},"BANNED_COUNTRY",[1300,5146,5147],{},"Request originates from a configured banned country",[861,5149,5150,5151,5153,5154,5157],{},"Import ",[865,5152,966],{}," from ",[865,5155,5156],{},"'@riavzon\u002Fbot-detector'"," to use any of these in your checker's type signature.",[942,5159],{},[879,5161,5163],{"id":5162},"summary","Summary",[1276,5165,5166,5176],{},[1279,5167,5168],{},[1282,5169,5170,5173],{},[1285,5171,5172],{},"What you need",[1285,5174,5175],{},"Where to find it",[1295,5177,5178,5188,5197,5208,5223,5240,5253,5263,5274,5285,5296,5307],{},[1282,5179,5180,5183],{},[1300,5181,5182],{},"Request data",[1300,5184,5185,5187],{},[865,5186,1020],{}," fields",[1282,5189,5190,5193],{},[1300,5191,5192],{},"Geo data or builded in contexes",[1300,5194,5195,5187],{},[865,5196,1020],{},[1282,5198,5199,5202],{},[1300,5200,5201],{},"Access to the raw Datasources",[1300,5203,5204,5207],{},[865,5205,5206],{},"getDataSources()"," import it",[1282,5209,5210,5213],{},[1300,5211,5212],{},"Your own per-request data",[1300,5214,5215,5217,5218,5220,5221],{},[865,5216,984],{}," passed to ",[865,5219,2961],{}," → ",[865,5222,980],{},[1282,5224,5225,5231],{},[1300,5226,5227,5228,5230],{},"Typed ",[865,5229,980],{}," in checker",[1300,5232,5233,5236,5237],{},[865,5234,5235],{},"IBotChecker\u003CCode, MyContext>"," + ",[865,5238,5239],{},"ValidationContext\u003CMyContext>",[1282,5241,5242,5245],{},[1300,5243,5244],{},"Immediate ban",[1300,5246,5247,5248,5250,5251],{},"Return ",[865,5249,916],{}," in ",[865,5252,2758],{},[1282,5254,5255,5258],{},[1300,5256,5257],{},"Score accumulation",[1300,5259,5247,5260],{},[865,5261,5262],{},"{ score: N, reasons: ['YOUR_CODE'] }",[1282,5264,5265,5268],{},[1300,5266,5267],{},"Async \u002F DB work",[1300,5269,5270,5271,5273],{},"Use ",[865,5272,3685],{}," with a local LRU cache",[1282,5275,5276,5279],{},[1300,5277,5278],{},"Built-in reason codes",[1300,5280,5281,5153,5283],{},[865,5282,966],{},[865,5284,5156],{},[1282,5286,5287,5290],{},[1300,5288,5289],{},"Register a checker",[1300,5291,5292,5295],{},[865,5293,5294],{},"CheckerRegistry.register(new YourChecker())"," at module bottom",[1282,5297,5298,5301],{},[1300,5299,5300],{},"Middleware setup",[1300,5302,5303,5306],{},[865,5304,5305],{},"app.use(detectBots())"," always call as a factory",[1282,5308,5309,5312],{},[1300,5310,5311],{},"External checker config",[1300,5313,5314],{},"Define a separate config module and import it directly",[5316,5317,5318],"style",{},"html pre.shiki code .sZ328, html code.shiki .sZ328{--shiki-light:#AF00DB;--shiki-default:#AF00DB;--shiki-dark:#FF79C6}html pre.shiki code .sDd4n, html code.shiki .sDd4n{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#F8F8F2}html pre.shiki code .sjsA6, html code.shiki .sjsA6{--shiki-light:#001080;--shiki-default:#001080;--shiki-dark:#F8F8F2}html pre.shiki code .sFkSl, html code.shiki .sFkSl{--shiki-light:#A31515;--shiki-default:#A31515;--shiki-dark:#E9F284}html pre.shiki code .sFB1V, html code.shiki .sFB1V{--shiki-light:#A31515;--shiki-default:#A31515;--shiki-dark:#F1FA8C}html pre.shiki code .sl46w, html code.shiki .sl46w{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#FF79C6}html pre.shiki code .sFs1U, html code.shiki .sFs1U{--shiki-light:#267F99;--shiki-light-font-style:inherit;--shiki-default:#267F99;--shiki-default-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic}html pre.shiki code .sW-rI, html code.shiki .sW-rI{--shiki-light:#267F99;--shiki-light-font-style:inherit;--shiki-default:#267F99;--shiki-default-font-style:inherit;--shiki-dark:#FFB86C;--shiki-dark-font-style:italic}html pre.shiki code .saOXh, html code.shiki .saOXh{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#FF79C6}html pre.shiki code .sghk6, html code.shiki .sghk6{--shiki-light:#008000;--shiki-default:#008000;--shiki-dark:#6272A4}html pre.shiki code .sHOzp, html code.shiki .sHOzp{--shiki-light:#795E26;--shiki-default:#795E26;--shiki-dark:#50FA7B}html pre.shiki code .sygFZ, html code.shiki .sygFZ{--shiki-light:#001080;--shiki-light-font-style:inherit;--shiki-default:#001080;--shiki-default-font-style:inherit;--shiki-dark:#FFB86C;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s5jk-, html code.shiki .s5jk-{--shiki-light:#267F99;--shiki-default:#267F99;--shiki-dark:#8BE9FD}html pre.shiki code .sjR7W, html code.shiki .sjR7W{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#BD93F9}html pre.shiki code .s3JHE, html code.shiki .s3JHE{--shiki-light:#0070C1;--shiki-default:#0070C1;--shiki-dark:#F8F8F2}html pre.shiki code .spgvN, html code.shiki .spgvN{--shiki-light:#098658;--shiki-default:#098658;--shiki-dark:#BD93F9}html pre.shiki code .sakC6, html code.shiki .sakC6{--shiki-light:#0000FF;--shiki-light-font-weight:inherit;--shiki-default:#0000FF;--shiki-default-font-weight:inherit;--shiki-dark:#FF79C6;--shiki-dark-font-weight:bold}html pre.shiki code .s34zl, html code.shiki .s34zl{--shiki-light:#001080;--shiki-default:#001080;--shiki-dark:#FF79C6}html pre.shiki code .s1lnM, html code.shiki .s1lnM{--shiki-light:#000000FF;--shiki-default:#000000FF;--shiki-dark:#F8F8F2}",{"title":940,"searchDepth":1048,"depth":1048,"links":5320},[5321,5322,5324,5325,5334,5335,5341,5342,5343,5344,5347,5349],{"id":881,"depth":1048,"text":882},{"id":946,"depth":1048,"text":5323},"The IBotChecker Interface",{"id":1273,"depth":1048,"text":1274},{"id":1340,"depth":1048,"text":5326,"children":5327},"ValidationContext Context",[5328,5329,5330,5331,5332,5333],{"id":1393,"depth":1054,"text":1394},{"id":1783,"depth":1054,"text":149},{"id":2033,"depth":1054,"text":599},{"id":2116,"depth":1054,"text":2117},{"id":2188,"depth":1054,"text":2189},{"id":2338,"depth":1054,"text":2339},{"id":2370,"depth":1048,"text":2371},{"id":2945,"depth":1048,"text":2946,"children":5336},[5337,5338,5340],{"id":2978,"depth":1054,"text":2979},{"id":3061,"depth":1054,"text":5339},"Pass the Builder to detectBots()",{"id":3250,"depth":1054,"text":3251},{"id":3678,"depth":1048,"text":3679},{"id":4303,"depth":1048,"text":4304},{"id":4493,"depth":1048,"text":4494},{"id":4649,"depth":1048,"text":4650,"children":5345},[5346],{"id":4854,"depth":1054,"text":4855},{"id":5004,"depth":1048,"text":5348},"Built-in BanReasonCode Values",{"id":5162,"depth":1048,"text":5163},"Learn how to add a custom checker.","md",{},null,"---\ntitle: Custom Checkers\ndescription: Learn how to add a custom checker.\nicon: i-lucide-puzzle\n---\n\n\nThe `bot detector` is built around a **checker registry**, every check is a class that implements `IBotChecker`, self registers on import, and gets picked up automatically when the middleware runs.\n\n## Pipeline\n\nEach request passes through two sequential phases: **cheap** and **heavy**. The score check runs after **every individual checker** in both phases. The moment the accumulated score reaches or exceeds `banScore`, the pipeline stops processing any remaining checkers in that phase and the visitor is banned. The heavy phase is skipped entirely if the cheap phase already reached `banScore`.\n\nTwo special reason codes short-circuit the pipeline immediately, regardless of score:\n\n- `'BAD_BOT_DETECTED'`: throws `BadBotDetected`, bans the visitor instantly\n- `'GOOD_BOT_IDENTIFIED'`: throws `GoodBotDetected`, lets the visitor through instantly\n\n```\nRequest\n  │    \n  \n[cheap phase] - in-memory checks, header inspection, pre-loaded MMDB lookups\n  │\n  after each checker:\n    ├─ BAD_BOT_DETECTED in reasons? → ban immediately (no further checks)\n    ├─ GOOD_BOT_IDENTIFIED in reasons? → allow immediately (no further checks)\n    └─ score >= banScore? → ban immediately (no further checks)\n  │\n  score still \u003C banScore after all cheap checkers?\n  │\n  \n[heavy phase] - DB queries, external calls, storage reads\n  │\n  after each checker:\n    ├─ BAD_BOT_DETECTED in reasons? → ban immediately\n    ├─ GOOD_BOT_IDENTIFIED in reasons? → allow immediately\n    └─ score >= banScore? - ban immediately (no further checks)\n  │\n  \nFinal decision: allow \u002F ban\n```\n\n---\n\n## The `IBotChecker` Interface\n\nEvery checker must implement `IBotChecker`. The interface accepts two type parameters:\n\n- `Code`, your custom reason code union. You can extend or alias the built in `BanReasonCode`.\n- `TCustom` *(optional)*, the shape of `ctx.custom`, populated by your `buildCustomContext` function. Defaults to `Record\u003Cstring, never>`.\n\n```ts\nimport type { IBotChecker, ValidationContext, BotDetectorConfig } from '@riavzon\u002Fbot-detector';\n\ninterface IBotChecker\u003CCode, TCustom = Record\u003Cstring, never>> {\n  name: string; \u002F\u002F display name, shown in logs\n  phase: 'cheap' | 'heavy'; \u002F\u002F which phase this checker runs in\n  isEnabled(config: BotDetectorConfig): boolean; \u002F\u002F self explanatory\n  run(ctx: ValidationContext\u003CTCustom>, config: BotDetectorConfig ): \u002F\u002F the checker logic\n    | Promise\u003C{ score: number; reasons: Code[] }>\n    | { score: number; reasons: Code[] };\n}\n```\n\nThe `run` method can be synchronous or async. Return a numeric score and an array of reason codes. A score of `0` with an empty reasons array means the checker found nothing suspicious.\n\n---\n\n## Phases\n\n| Phase | When it runs | Best for |\n|---|---|---|\n| `cheap` | Every request, before the heavy phase | In-memory checks, header inspection, reading pre-loaded MMDB data from `ctx` |\n| `heavy` | Only when accumulated score is below `banScore` after cheap phase | Database queries, external API calls, cache reads with I\u002FO fallback |\n\nPrefer `cheap` for anything that does not require I\u002FO. All MMDB lookups (geo, ASN, Tor, proxy, threat levels) are already resolved before your checker runs, reading them from `ctx` costs nothing.\n\n---\n\n## `ValidationContext` Context\n\nThe `ValidationContext` object contains everything the pipeline resolves before your checker runs. All fields are pre-populated; your checker only reads them.\n\n```ts\nctx.req       \u002F\u002F Express Request: full access to headers, path, cookies, method\nctx.ipAddress \u002F\u002F string: the resolved client IP\nctx.cookie    \u002F\u002F string | undefined: canary_id cookie value (visitor identifier)\n```\n\n### Geo and IP Intelligence\n\nAll fields are sourced from three MMDB databases merged at request time: `country.mmdb` (country-level), `city.mmdb` (city-level), `asn.mmdb` (BGP\u002Fnetwork), and `proxy.mmdb`. City-level data takes precedence where both exist. All string values are normalized to lowercase.\n\n```ts\nctx.geoData     \u002F\u002F Partial\u003CGeoResponse>, all fields optional\n  .country      \u002F\u002F   country name, \"united states\"\n  .countryCode  \u002F\u002F   ISO 3166-1 alpha-2, \"us\"\n  .continent    \u002F\u002F   \"north america\"  (city.mmdb only)\n  .region       \u002F\u002F   region\u002Fprovince code, \"ny\"\n  .regionName   \u002F\u002F   continent ?? subregion fallback\n  .subregion    \u002F\u002F   \"northern america\"\n  .state        \u002F\u002F   \"new york\"\n  .city         \u002F\u002F   \"new york city\"\n  .capital      \u002F\u002F   country capital, \"washington d.c.\"\n  .district     \u002F\u002F   mirrors .state (city.mmdb state field)\n  .zipCode      \u002F\u002F   postal code, \"10001\"\n  .lat          \u002F\u002F   latitude string, \"40.7128\"\n  .lon          \u002F\u002F   longitude string, \"-74.0059\"\n  .timezone     \u002F\u002F   IANA timezone, \"america\u002Fnew_york\"\n  .timeZoneName \u002F\u002F   human-readable, \"eastern standard time\"\n  .utc_offset   \u002F\u002F   \"-05:00\"\n  .isp          \u002F\u002F   ASN org name (asn.mmdb → asn_name), \"cloudflare, inc.\"\n  .org          \u002F\u002F   ASN ID (asn.mmdb → asn_id), \"as13335\"\n  .as_org       \u002F\u002F   same as .isp (asn_name duplicate)\n  .proxy        \u002F\u002F   boolean, IP matched in proxy.mmdb\n  .hosting      \u002F\u002F   boolean, true when asn.classification === \"Content\"\n  .tld          \u002F\u002F   country TLD, \".us\"\n  .nationality  \u002F\u002F   \"american\"\n  .currency     \u002F\u002F   \"usd\"\n  .iso639       \u002F\u002F   primary language code, \"en\"\n  .languages    \u002F\u002F   all languages, \"en-us\"\n  .native       \u002F\u002F   native country name, \"united states\"\n  .phone        \u002F\u002F   country calling code, \"1\"\n  .numericCode  \u002F\u002F   ISO 3166-1 numeric, \"840\"\n```\n\n```ts\nctx.proxy\n  .isProxy   \u002F\u002F boolean, true if proxy.mmdb matched this IP\n  .proxyType \u002F\u002F string | undefined, ProxyRecord.comment field:\n             \u002F\u002F comma-separated source list names, e.g. \"firehol_proxies,xroxy\"\n```\n\n### Tor Analysis\n\n`ctx.tor` is `Partial\u003COmit\u003CTorRecord, 'range'>>`, an empty object when the IP is not a known Tor relay. All fields are sourced directly from `tor.mmdb` via the Onionoo dataset.\n\n```ts\nctx.tor\n  .running                   \u002F\u002F   boolean, relay is currently running\n  .flags                     \u002F\u002F   comma-separated flag string, e.g. \"Exit,Fast,Guard,Running,Stable,Valid\"\n  .exit_addresses            \u002F\u002F   string, non-empty means this is a confirmed exit node\n  .exit_probability          \u002F\u002F   number, probability this relay is chosen as exit (0–1)\n  .guard_probability         \u002F\u002F   number, probability this relay is chosen as guard (0–1)\n  .middle_probability        \u002F\u002F   number, probability chosen as middle relay (0–1)\n  .recommended_version       \u002F\u002F   boolean, running a Tor-project-recommended version\n  .version_status            \u002F\u002F   \"recommended\" | \"obsolete\" | \"new\" | \"unrecommended\" | etc.\n  .exit_policy               \u002F\u002F   string, serialized exit policy rules\n  .exit_policy_summary       \u002F\u002F   string, serialized accept\u002Freject summary\n  .exit_policy_v6_summary    \u002F\u002F   string | undefined, IPv6 exit policy summary\n  .country                   \u002F\u002F   2-letter country code of the relay, e.g. \"de\"\n  .country_name              \u002F\u002F   e.g. \"germany\"\n  .as                        \u002F\u002F   ASN of the relay, e.g. \"AS24940\"\n  .as_name                   \u002F\u002F   ASN org name of the relay, e.g. \"hetzner online gmbh\"\n  .or_addresses              \u002F\u002F   string, onion router address(es)\n  .contact                   \u002F\u002F   string, operator contact info (often obfuscated)\n  .first_seen                \u002F\u002F   ISO date string, when this relay first appeared\n  .last_seen                 \u002F\u002F   ISO date string, last time relay was observed\n  .last_restarted            \u002F\u002F   ISO date string\n  .last_changed_address_or_port \u002F\u002F ISO date string\n  .measured                  \u002F\u002F   boolean, whether bandwidth has been measured\n```\n\n### BGP \u002F ASN\n\n`ctx.bgp` is `Partial\u003COmit\u003CBgpRecord, 'range'>>`, an empty object when the ASN is not found.\n\n```ts\nctx.bgp\n  .asn_id                    \u002F\u002F   string, ASN identifier, e.g. \"AS13335\"\n  .asn_name                  \u002F\u002F   string, org name, e.g. \"cloudflare, inc.\"\n  .classification            \u002F\u002F   string, \"Content\" | \"Eyeballs\" | \"Unknown\"\n  .hits                      \u002F\u002F   string, BGP route announcement count\n```\n\n`classification` is the key field here. `\"Content\"` means CDN\u002Fhosting (sets `ctx.geoData.hosting = true`). `\"Eyeballs\"` means residential\u002Fbusiness internet.\n\n### Threat Level and Anonymity\n\n```ts\nctx.anon        \u002F\u002F boolean: true if IP is in firehol_anonymous.mmdb\nctx.threatLevel \u002F\u002F 1 | 2 | 3 | 4 | null: highest Firehol level matched (1 = most severe)\n                \u002F\u002F   1 = firehol_l1 (confirmed malicious, no false positives)\n                \u002F\u002F   2 = firehol_l2 (abuse + scanning)\n                \u002F\u002F   3 = firehol_l3 (web attacks)\n                \u002F\u002F   4 = firehol_l4 (aggressive but broader)\n```\n\nThe levels are mutually exclusive in `ctx`, if an IP matches `firehol_l1`, `threatLevel` is `1` and levels 2–4 are not checked. A `null` value means no match.\n\n### Parsed User Agent\n\n`ua-parser-js` resolves all fields synchronously before any checker runs. The result is `Partial\u003CParsedUAResult>` so every field may be undefined.\n\n```ts\nctx.parsedUA\n  .browser        \u002F\u002F   \"chrome\", \"safari\", \"firefox\"\n  .browserType    \u002F\u002F   \"browser\" | \"crawler\" | \"fetcher\" | \"cli\" | \"library\"\n  .browserVersion \u002F\u002F   \"120.0.0\"\n  .os             \u002F\u002F   \"windows\", \"macos\", \"android\"\n  .device         \u002F\u002F   \"desktop\", \"mobile\", \"tablet\"\n  .deviceVendor   \u002F\u002F   \"apple\", \"samsung\"\n  .deviceModel    \u002F\u002F   \"iphone\", \"galaxy s24\"\n  .bot            \u002F\u002F   boolean, ua-parser-js considers this a bot UA\n  .botAI          \u002F\u002F   boolean, known AI crawler (GPTBot, ClaudeBot, etc.)\n  .allResults     \u002F\u002F   IResult, raw ua-parser-js output, for advanced access\n```\n\n`browserType` is the fastest signal for bot intent. `\"crawler\"` and `\"fetcher\"` trigger the good-bot DNS verification path. `\"cli\"` and `\"library\"` are penalised by the built-in `BrowserDetailsAndDeviceChecker`.\n\n### Custom Data\n\n```ts\nctx.custom \u002F\u002F TCustom, populated by your buildCustomContext function, {} by default\n```\n\nSee [Custom Context](#custom-context--passing-your-own-data-into-checkers) below for how to populate and type this field.\n\n---\n\n## Minimal Example Synchronous Cheap Checker\n\nA cheap checker should be synchronous and rely only on data already present in `ctx`. The example below penalises requests from South America that carry no `Accept-Language` header.\n\n```ts [my-custom-checker.ts]\nimport { CheckerRegistry } from '@riavzon\u002Fbot-detector';\nimport type { IBotChecker, ValidationContext, BotDetectorConfig } from '@riavzon\u002Fbot-detector';\n\ntype MyReasonCode = 'MISSING_ACCEPT_LANGUAGE' | 'BAD_BOT_DETECTED';\n\nclass MissingLocaleChecker implements IBotChecker\u003CMyReasonCode> {\n  name = 'MissingLocaleChecker';\n  phase = 'cheap' as const;\n\n  isEnabled(_config: BotDetectorConfig): boolean {\n    return true;\n  }\n\n  run(ctx: ValidationContext, _config: BotDetectorConfig) {\n    const reasons: MyReasonCode[] = [];\n    let score = 0;\n\n    if (!ctx.req.get('Accept-Language') && ctx.geoData.continent === 'south america') {\n      score += 15;\n      reasons.push('MISSING_ACCEPT_LANGUAGE');\n    }\n\n    return { score, reasons };\n  }\n}\n\nCheckerRegistry.register(new MissingLocaleChecker());\n```\n\nImport the file **after** `defineConfiguration()` resolves. The side-effect import triggers `CheckerRegistry.register()`.\n\n```ts [server.ts]\nimport { defineConfiguration, detectBots } from '@riavzon\u002Fbot-detector';\nimport '.\u002Fmy-custom-checker.js';\n\nawait defineConfiguration({ store: { main: { driver: 'sqlite', name: '.\u002Fbot-detector.db' } }, banScore: 80 });\napp.use(detectBots()); \u002F\u002F always call detectBots() as a factory\n```\n\n---\n\n## Custom Context Passing Your Own Data into Checkers\n\nEvery checker receives a `ctx.custom` field. By default it is `Record\u003Cstring, never>` empty. You populate it by passing a `buildCustomContext` function to `detectBots()`. The generic type flows through to your checker's `run()` signature, giving you full IntelliSense on `ctx.custom`.\n\nThe builder function runs once per request, before any checker executes.\n\n::steps{level=\"3\"}\n\n### Define Your Context Shape\n\n```ts [myContext.ts]\nexport interface MyContext {\n  userId: string;\n  plan: 'free' | 'pro' | 'enterprise';\n  isInternal: boolean;\n}\n```\n\n### Pass the Builder to `detectBots()`\n\n```ts [server.ts]\nimport { detectBots } from '@riavzon\u002Fbot-detector';\nimport type { MyContext } from '.\u002Ftypes\u002FmyContext.js';\n\napp.use(\n  detectBots\u003CMyContext>((req) => ({\n    userId: req.user?.id   ?? 'anonymous',\n    plan: req.user?.plan ?? 'free',\n    isInternal: req.ip === '127.0.0.1',\n  }))\n);\n```\n\n### Declare the Type on Your Checker\n\nPassing `MyContext` as the second type parameter to `IBotChecker` makes `ctx.custom` fully typed inside `run()`.\n\n```ts [my-plan-checker.ts]\nimport { CheckerRegistry } from '@riavzon\u002Fbot-detector';\nimport type { IBotChecker, ValidationContext, BotDetectorConfig, BanReasonCode } from '@riavzon\u002Fbot-detector';\nimport type { MyContext } from '.\u002Ftypes\u002FmyContext.js';\n\nclass PlanAbuseChecker implements IBotChecker\u003CBanReasonCode, MyContext> {\n  name = 'PlanAbuseChecker';\n  phase = 'cheap' as const;\n\n  isEnabled(_config: BotDetectorConfig) { \n    return true; \n  }\n \n  run(ctx: ValidationContext\u003CMyContext>, _config: BotDetectorConfig) {\n    const reasons: BanReasonCode[] = [];\n    let score = 0;\n\n    if (ctx.custom.isInternal) return { score, reasons }; \u002F\u002F bypass internal traffic\n\n    if (ctx.custom.plan === 'free' && ctx.geoData.proxy) {\n      score += 20;\n      reasons.push('PROXY_DETECTED');\n    }\n\n    return { score, reasons };\n  }\n}\n\nCheckerRegistry.register(new PlanAbuseChecker());\n```\n\nWhen no custom context is needed, call `detectBots()` with no arguments, `ctx.custom` will be `{}` and is safe to ignore.\n::\n\n---\n\n## Async Checker with Database or Cache\n\nCheckers that need I\u002FO, database queries, external API calls, or cache reads with a DB fallback, should declare `phase: 'heavy'`. The heavy phase only runs when the cheap phase score stays below `banScore`, so expensive work is never done for obvious bots.\n\nThis example uses the built in storage via `getStorage()` to cache results and avoid database queries on repeated IPs. The storage instance is the same one configured in `defineConfiguration()`.\n\n```ts [my-async-checker.ts]\nimport { getStorage, CheckerRegistry } from '@riavzon\u002Fbot-detector';\nimport type { IBotChecker, ValidationContext, BotDetectorConfig } from '@riavzon\u002Fbot-detector';\n\nclass MyAsyncChecker implements IBotChecker\u003C'MY_REASON'> {\n  name = 'MyAsyncChecker';\n  phase = 'heavy' as const;\n\n  isEnabled(_config: BotDetectorConfig): boolean {\n    return true;\n  }\n\n  async run(ctx: ValidationContext, _config: BotDetectorConfig) {\n    const reasons: 'MY_REASON'[] = [];\n    let score = 0;\n\n    if (!ctx.cookie) return { score, reasons };\n\n    const storage = getStorage();\n    const cacheKey = `my-checker:${ctx.cookie}`;\n\n    const cached = await storage.getItem\u003Cnumber>(cacheKey);\n    if (cached !== null) {\n      return { score: cached, reasons: cached > 0 ? ['MY_REASON'] : [] };\n    }\n\n    \u002F\u002F Your DB query or external call here\n    const result = await myDb.query('SELECT ...', [ctx.ipAddress]);\n    if (result.isSuspicious) {\n      score = 30;\n      reasons.push('MY_REASON');\n    }\n\n    await storage.setItem(cacheKey, score, { ttl: 300 }); \u002F\u002F 5 min TTL\n    return { score, reasons };\n  }\n}\n\nCheckerRegistry.register(new MyAsyncChecker());\n```\n\n::tip\nUse a namespaced key prefix (`custom:`) to avoid collisions with the built-in cache entries that share the same storage instance.\n::\n\n---\n\n## Triggering an Immediate Ban\n\nReturn the special reason code `'BAD_BOT_DETECTED'` from your checker to trigger an instant ban. The pipeline throws `BadBotDetected` immediately upon seeing this code, stopping all remaining checkers and banning the visitor without waiting for score accumulation.\n\n```ts [example.ts]\nrun(ctx: ValidationContext, _config: BotDetectorConfig) {\n  const reasons: ('MY_REASON' | 'BAD_BOT_DETECTED')[] = [];\n\n  if (isDefinitelyABot(ctx)) {\n    reasons.push('MY_REASON');\n    reasons.push('BAD_BOT_DETECTED'); \u002F\u002F pipeline stops here, visitor is banned immediately\n    return { score: 0, reasons };\n  }\n\n  return { score: 0, reasons };\n}\n```\n\nThe built-in honeypot checker and good-bot DNS verifier both use this same mechanism.\n\n---\n\n## Checker Configuration\n\nThe `BotDetectorConfig` schema is read only, you cannot add fields to it from outside the package. Manage your checker's settings in a separate module and import it directly.\n\n```ts [my-checker-config.ts]\nexport const myCheckerConfig = {\n  threshold: 5,\n  penalty: 25,\n};\n```\n\n```ts [my-custom-checker.ts]\nimport { myCheckerConfig } from '.\u002Fmy-checker-config.js';\n\nrun(ctx: ValidationContext, _config: BotDetectorConfig) {\n  if (someValue > myCheckerConfig.threshold) {\n    score += myCheckerConfig.penalty;\n  }\n}\n```\n\n---\n\n## Checker Registration\n\nRegistration happens at module load time. At the bottom of your checker file, call `CheckerRegistry.register()` with an instance of your class. The registry is a module-level array, the call is synchronous and takes effect immediately when the file is imported.\n\n```ts [my-custom-checker.ts]\n\nclass MyChecker implements IBotChecker\u003C'MY_REASON'> {\n  \u002F\u002F ...\n}\n\nCheckerRegistry.register(new MyChecker()); \u002F\u002F this line does the registration\n```\n\nTo activate the checker, import the file in your server entry point **after** `defineConfiguration()` resolves. A bare side-effect import is enough, you don't need to use the exported value.\n\n```ts [server.ts]\nimport { defineConfiguration, detectBots } from '@riavzon\u002Fbot-detector';\nimport '.\u002Fmy-custom-checker.js'; \u002F\u002F side-effect import, triggers CheckerRegistry.register()\n\nawait defineConfiguration({ store: { main: { driver: 'sqlite', name: '.\u002Fbot-detector.db' } } });\napp.use(detectBots());\n```\n\n::note\nImporting your checker file before `defineConfiguration()` resolves is safe for registration itself, but `getStorage()` and `getDb()` will throw if called at module initialisation time. Keep any storage or DB access inside `run()`, not at the top level of your module.\n::\n\n### Registration Order\n\nCheckers run in the order they are registered within their phase. The built-in pipeline executes in this sequence:\n\n1. Built-in cheap checkers (IP validation → good bots → browser\u002Fdevice → locale → UA\u002Fheaders → ...)\n2. Your custom cheap checkers (in import order)\n3. Built-in heavy checkers (rate tracking → proxy\u002FISP → session coherence → velocity → bad UA patterns)\n4. Your custom heavy checkers (in import order)\n\nWhen the order of your custom checkers matters, control it through import sequence:\n\n```ts [server.ts]\nimport '.\u002Fmy-cheap-checker-1.js'; \u002F\u002F registers first\nimport '.\u002Fmy-cheap-checker-2.js'; \u002F\u002F registers second\nimport { defineConfiguration, detectBots } from '@riavzon\u002Fbot-detector';\n\nawait defineConfiguration({ store: { main: { driver: 'sqlite', name: '.\u002Fbot-detector.db' } } });\napp.use(detectBots());\n```\n\n---\n\n## Built-in `BanReasonCode` Values\n\nYour custom checker can return any of the 57 built-in reason codes alongside your own. The most commonly reused ones are listed below.\n\n| Code | Meaning |\n|---|---|\n| `BAD_BOT_DETECTED` | Triggers an immediate ban. No further checkers run. |\n| `GOOD_BOT_IDENTIFIED` | Marks the request as a verified legitimate crawler. No further checkers run. |\n| `PROXY_DETECTED` | IP matched in proxy database |\n| `HOSTING_DETECTED` | ASN classified as hosting\u002FContent or confirmed Tor exit |\n| `ANONYMITY_NETWORK` | IP appears in Firehol anonymous list |\n| `HEADLESS_BROWSER_DETECTED` | Headless browser keyword found in the user agent |\n| `BEHAVIOR_TOO_FAST` | Request rate exceeds the configured behavioral threshold |\n| `TIMING_TOO_REGULAR` | Inter-request intervals are too consistent (low coefficient of variation) |\n| `PREVIOUSLY_BANNED_IP` | IP exists in `banned.mmdb` |\n| `PREVIOUSLY_HIGH_RISK_IP` | IP exists in `highRisk.mmdb` |\n| `HONEYPOT_PATH_HIT` | Request targeted a honeypot path |\n| `BANNED_COUNTRY` | Request originates from a configured banned country |\n\nImport `BanReasonCode` from `'@riavzon\u002Fbot-detector'` to use any of these in your checker's type signature.\n\n---\n\n## Summary\n\n| What you need | Where to find it |\n|---|---|\n| Request data | `ValidationContext` fields |\n| Geo data or builded in contexes | `ValidationContext` fields |\n| Access to the raw Datasources | `getDataSources()` import it |\n| Your own per-request data | `buildCustomContext` passed to `detectBots()` → `ctx.custom` |\n| Typed `ctx.custom` in checker | `IBotChecker\u003CCode, MyContext>` + `ValidationContext\u003CMyContext>` |\n| Immediate ban | Return `'BAD_BOT_DETECTED'` in `reasons` |\n| Score accumulation | Return `{ score: N, reasons: ['YOUR_CODE'] }` |\n| Async \u002F DB work | Use `phase: 'heavy'` with a local LRU cache |\n| Built-in reason codes | `BanReasonCode` from `'@riavzon\u002Fbot-detector'` |\n| Register a checker | `CheckerRegistry.register(new YourChecker())` at module bottom |\n| Middleware setup | `app.use(detectBots())` always call as a factory |\n| External checker config | Define a separate config module and import it directly |\n",{"title":94,"description":5350},"M110eS5ab43N_jHrBux6MproAm21pHsxXMnqN-8AkY0",[5358,5359],{"title":84,"path":85,"stem":86,"children":-1},{"title":99,"path":100,"stem":101,"children":-1},{"id":856,"title":94,"body":5361,"description":5350,"extension":5351,"icon":97,"meta":9071,"module":5353,"navigation":8,"path":95,"rawbody":5354,"seo":9072,"stem":96,"__hash__":5356},{"type":858,"value":5362,"toc":9044},[5363,5371,5373,5385,5387,5401,5406,5408,5412,5416,5436,5628,5634,5636,5638,5676,5682,5684,5688,5692,5726,5728,5738,5984,6016,6018,6026,6214,6216,6222,6266,6276,6278,6318,6330,6332,6338,6430,6444,6446,6460,6464,6466,6468,6474,6830,6838,6950,6952,6954,6968,6970,7606,7608,7610,7616,7622,8156,8162,8164,8166,8172,8332,8334,8336,8338,8342,8382,8472,8474,8476,8480,8538,8544,8648,8660,8662,8664,8674,8676,8794,8796,8800,8802,8914,8920,8922,8924,9042],[861,5364,863,5365,868,5367,873,5369,877],{},[865,5366,867],{},[870,5368,872],{},[865,5370,876],{},[879,5372,882],{"id":881},[861,5374,885,5375,889,5377,893,5379,897,5381,901,5383,904],{},[870,5376,888],{},[870,5378,892],{},[870,5380,896],{},[865,5382,900],{},[865,5384,900],{},[861,5386,907],{},[909,5388,5389,5395],{},[912,5390,5391,917,5393,921],{},[865,5392,916],{},[865,5394,920],{},[912,5396,5397,917,5399,930],{},[865,5398,926],{},[865,5400,929],{},[932,5402,5404],{"className":5403,"code":936,"language":937},[935],[865,5405,936],{"__ignoreMap":940},[942,5407],{},[879,5409,863,5410,949],{"id":946},[865,5411,876],{},[861,5413,952,5414,955],{},[865,5415,876],{},[909,5417,5418,5424],{},[912,5419,5420,963,5422,904],{},[865,5421,962],{},[865,5423,966],{},[912,5425,5426,972,5428,977,5430,981,5432,985,5434,904],{},[865,5427,971],{},[974,5429,976],{},[865,5431,980],{},[865,5433,984],{},[865,5435,988],{},[932,5437,5438],{"className":991,"code":992,"language":993,"meta":940,"style":940},[865,5439,5440,5470,5474,5502,5514,5538,5560,5590,5610,5624],{"__ignoreMap":940},[997,5441,5442,5444,5446,5448,5450,5452,5454,5456,5458,5460,5462,5464,5466,5468],{"class":999,"line":1000},[997,5443,1004],{"class":1003},[997,5445,1007],{"class":1003},[997,5447,1011],{"class":1010},[997,5449,876],{"class":1014},[997,5451,1017],{"class":1010},[997,5453,1020],{"class":1014},[997,5455,1017],{"class":1010},[997,5457,1025],{"class":1014},[997,5459,1028],{"class":1010},[997,5461,1031],{"class":1003},[997,5463,1035],{"class":1034},[997,5465,1039],{"class":1038},[997,5467,1042],{"class":1034},[997,5469,1045],{"class":1010},[997,5471,5472],{"class":999,"line":1048},[997,5473,1051],{"emptyLinePlaceholder":8},[997,5475,5476,5478,5480,5482,5484,5486,5488,5490,5492,5494,5496,5498,5500],{"class":999,"line":1054},[997,5477,1058],{"class":1057},[997,5479,1062],{"class":1061},[997,5481,1065],{"class":1010},[997,5483,962],{"class":1068},[997,5485,1017],{"class":1010},[997,5487,971],{"class":1068},[997,5489,1076],{"class":1075},[997,5491,1079],{"class":1068},[997,5493,1065],{"class":1010},[997,5495,1084],{"class":1061},[997,5497,1017],{"class":1010},[997,5499,1089],{"class":1061},[997,5501,1092],{"class":1010},[997,5503,5504,5506,5508,5510,5512],{"class":999,"line":1095},[997,5505,1098],{"class":1014},[997,5507,1101],{"class":1075},[997,5509,1104],{"class":1061},[997,5511,1107],{"class":1010},[997,5513,1111],{"class":1110},[997,5515,5516,5518,5520,5522,5524,5526,5528,5530,5532,5534,5536],{"class":999,"line":1114},[997,5517,1117],{"class":1014},[997,5519,1101],{"class":1075},[997,5521,1035],{"class":1034},[997,5523,888],{"class":1038},[997,5525,1042],{"class":1034},[997,5527,1128],{"class":1075},[997,5529,1035],{"class":1034},[997,5531,892],{"class":1038},[997,5533,1042],{"class":1034},[997,5535,1107],{"class":1010},[997,5537,1139],{"class":1110},[997,5539,5540,5542,5544,5546,5548,5550,5552,5554,5556,5558],{"class":999,"line":1142},[997,5541,1146],{"class":1145},[997,5543,1149],{"class":1010},[997,5545,1153],{"class":1152},[997,5547,1101],{"class":1075},[997,5549,1158],{"class":1061},[997,5551,1161],{"class":1010},[997,5553,1101],{"class":1075},[997,5555,1166],{"class":1061},[997,5557,1107],{"class":1010},[997,5559,1171],{"class":1110},[997,5561,5562,5564,5566,5568,5570,5572,5574,5576,5578,5580,5582,5584,5586,5588],{"class":999,"line":1174},[997,5563,1177],{"class":1145},[997,5565,1149],{"class":1010},[997,5567,1182],{"class":1152},[997,5569,1101],{"class":1075},[997,5571,1187],{"class":1061},[997,5573,1065],{"class":1010},[997,5575,971],{"class":1068},[997,5577,1194],{"class":1010},[997,5579,1153],{"class":1152},[997,5581,1101],{"class":1075},[997,5583,1158],{"class":1061},[997,5585,1203],{"class":1010},[997,5587,1101],{"class":1075},[997,5589,1208],{"class":1110},[997,5591,5592,5594,5596,5598,5600,5602,5604,5606,5608],{"class":999,"line":1211},[997,5593,1214],{"class":1075},[997,5595,1217],{"class":1061},[997,5597,1065],{"class":1075},[997,5599,1222],{"class":1010},[997,5601,1225],{"class":1014},[997,5603,1228],{"class":1010},[997,5605,962],{"class":1014},[997,5607,1233],{"class":1010},[997,5609,1236],{"class":1075},[997,5611,5612,5614,5616,5618,5620,5622],{"class":999,"line":1239},[997,5613,1214],{"class":1075},[997,5615,1244],{"class":1010},[997,5617,1225],{"class":1014},[997,5619,1228],{"class":1010},[997,5621,962],{"class":1014},[997,5623,1253],{"class":1010},[997,5625,5626],{"class":999,"line":1256},[997,5627,1259],{"class":1010},[861,5629,863,5630,1264,5632,1268],{},[865,5631,688],{},[865,5633,1267],{},[942,5635],{},[879,5637,1274],{"id":1273},[1276,5639,5640,5650],{},[1279,5641,5642],{},[1282,5643,5644,5646,5648],{},[1285,5645,1287],{},[1285,5647,1290],{},[1285,5649,1293],{},[1295,5651,5652,5664],{},[1282,5653,5654,5658,5660],{},[1300,5655,5656],{},[865,5657,888],{},[1300,5659,1306],{},[1300,5661,1309,5662],{},[865,5663,1182],{},[1282,5665,5666,5670,5674],{},[1300,5667,5668],{},[865,5669,892],{},[1300,5671,1320,5672,1323],{},[865,5673,900],{},[1300,5675,1326],{},[861,5677,1329,5678,1332,5680,1335],{},[865,5679,888],{},[865,5681,1182],{},[942,5683],{},[879,5685,5686,1343],{"id":1340},[865,5687,1020],{},[861,5689,863,5690,1348],{},[865,5691,1020],{},[932,5693,5694],{"className":991,"code":1351,"language":993,"meta":940,"style":940},[865,5695,5696,5706,5716],{"__ignoreMap":940},[997,5697,5698,5700,5702,5704],{"class":999,"line":1000},[997,5699,1182],{"class":1014},[997,5701,904],{"class":1010},[997,5703,1362],{"class":1014},[997,5705,1365],{"class":1110},[997,5707,5708,5710,5712,5714],{"class":999,"line":1048},[997,5709,1182],{"class":1014},[997,5711,904],{"class":1010},[997,5713,1374],{"class":1014},[997,5715,1377],{"class":1110},[997,5717,5718,5720,5722,5724],{"class":999,"line":1054},[997,5719,1182],{"class":1014},[997,5721,904],{"class":1010},[997,5723,1386],{"class":1014},[997,5725,1389],{"class":1110},[1391,5727,1394],{"id":1393},[861,5729,1397,5730,1401,5732,1405,5734,1409,5736,1413],{},[865,5731,1400],{},[865,5733,1404],{},[865,5735,1408],{},[865,5737,1412],{},[932,5739,5740],{"className":991,"code":1416,"language":993,"meta":940,"style":940},[865,5741,5742,5752,5760,5768,5776,5784,5792,5800,5808,5816,5824,5832,5840,5848,5856,5864,5872,5880,5888,5896,5904,5912,5920,5928,5936,5944,5952,5960,5968,5976],{"__ignoreMap":940},[997,5743,5744,5746,5748,5750],{"class":999,"line":1000},[997,5745,1182],{"class":1014},[997,5747,904],{"class":1010},[997,5749,1427],{"class":1014},[997,5751,1430],{"class":1110},[997,5753,5754,5756,5758],{"class":999,"line":1048},[997,5755,1435],{"class":1010},[997,5757,1438],{"class":1014},[997,5759,1441],{"class":1110},[997,5761,5762,5764,5766],{"class":999,"line":1054},[997,5763,1435],{"class":1010},[997,5765,1448],{"class":1014},[997,5767,1451],{"class":1110},[997,5769,5770,5772,5774],{"class":999,"line":1095},[997,5771,1435],{"class":1010},[997,5773,1458],{"class":1014},[997,5775,1461],{"class":1110},[997,5777,5778,5780,5782],{"class":999,"line":1114},[997,5779,1435],{"class":1010},[997,5781,1468],{"class":1014},[997,5783,1471],{"class":1110},[997,5785,5786,5788,5790],{"class":999,"line":1142},[997,5787,1435],{"class":1010},[997,5789,1478],{"class":1014},[997,5791,1481],{"class":1110},[997,5793,5794,5796,5798],{"class":999,"line":1174},[997,5795,1435],{"class":1010},[997,5797,1488],{"class":1014},[997,5799,1491],{"class":1110},[997,5801,5802,5804,5806],{"class":999,"line":1211},[997,5803,1435],{"class":1010},[997,5805,1498],{"class":1014},[997,5807,1501],{"class":1110},[997,5809,5810,5812,5814],{"class":999,"line":1239},[997,5811,1435],{"class":1010},[997,5813,1508],{"class":1014},[997,5815,1511],{"class":1110},[997,5817,5818,5820,5822],{"class":999,"line":1256},[997,5819,1435],{"class":1010},[997,5821,1518],{"class":1014},[997,5823,1521],{"class":1110},[997,5825,5826,5828,5830],{"class":999,"line":1524},[997,5827,1435],{"class":1010},[997,5829,1529],{"class":1014},[997,5831,1532],{"class":1110},[997,5833,5834,5836,5838],{"class":999,"line":1535},[997,5835,1435],{"class":1010},[997,5837,1540],{"class":1014},[997,5839,1543],{"class":1110},[997,5841,5842,5844,5846],{"class":999,"line":1546},[997,5843,1435],{"class":1010},[997,5845,1551],{"class":1014},[997,5847,1554],{"class":1110},[997,5849,5850,5852,5854],{"class":999,"line":1557},[997,5851,1435],{"class":1010},[997,5853,1562],{"class":1014},[997,5855,1565],{"class":1110},[997,5857,5858,5860,5862],{"class":999,"line":1568},[997,5859,1435],{"class":1010},[997,5861,1573],{"class":1014},[997,5863,1576],{"class":1110},[997,5865,5866,5868,5870],{"class":999,"line":1579},[997,5867,1435],{"class":1010},[997,5869,1584],{"class":1014},[997,5871,1587],{"class":1110},[997,5873,5874,5876,5878],{"class":999,"line":1590},[997,5875,1435],{"class":1010},[997,5877,1595],{"class":1014},[997,5879,1598],{"class":1110},[997,5881,5882,5884,5886],{"class":999,"line":1601},[997,5883,1435],{"class":1010},[997,5885,1606],{"class":1014},[997,5887,1609],{"class":1110},[997,5889,5890,5892,5894],{"class":999,"line":1612},[997,5891,1435],{"class":1010},[997,5893,1617],{"class":1014},[997,5895,1620],{"class":1110},[997,5897,5898,5900,5902],{"class":999,"line":1623},[997,5899,1435],{"class":1010},[997,5901,1628],{"class":1014},[997,5903,1631],{"class":1110},[997,5905,5906,5908,5910],{"class":999,"line":1634},[997,5907,1435],{"class":1010},[997,5909,1639],{"class":1014},[997,5911,1642],{"class":1110},[997,5913,5914,5916,5918],{"class":999,"line":1645},[997,5915,1435],{"class":1010},[997,5917,1650],{"class":1014},[997,5919,1653],{"class":1110},[997,5921,5922,5924,5926],{"class":999,"line":1656},[997,5923,1435],{"class":1010},[997,5925,1661],{"class":1014},[997,5927,1664],{"class":1110},[997,5929,5930,5932,5934],{"class":999,"line":1667},[997,5931,1435],{"class":1010},[997,5933,1672],{"class":1014},[997,5935,1675],{"class":1110},[997,5937,5938,5940,5942],{"class":999,"line":1678},[997,5939,1435],{"class":1010},[997,5941,1683],{"class":1014},[997,5943,1686],{"class":1110},[997,5945,5946,5948,5950],{"class":999,"line":1689},[997,5947,1435],{"class":1010},[997,5949,1694],{"class":1014},[997,5951,1697],{"class":1110},[997,5953,5954,5956,5958],{"class":999,"line":1700},[997,5955,1435],{"class":1010},[997,5957,1705],{"class":1014},[997,5959,1708],{"class":1110},[997,5961,5962,5964,5966],{"class":999,"line":1711},[997,5963,1435],{"class":1010},[997,5965,1716],{"class":1014},[997,5967,1719],{"class":1110},[997,5969,5970,5972,5974],{"class":999,"line":1722},[997,5971,1435],{"class":1010},[997,5973,1727],{"class":1014},[997,5975,1730],{"class":1110},[997,5977,5978,5980,5982],{"class":999,"line":1733},[997,5979,1435],{"class":1010},[997,5981,1738],{"class":1014},[997,5983,1741],{"class":1110},[932,5985,5986],{"className":991,"code":1744,"language":993,"meta":940,"style":940},[865,5987,5988,5996,6004,6012],{"__ignoreMap":940},[997,5989,5990,5992,5994],{"class":999,"line":1000},[997,5991,1182],{"class":1014},[997,5993,904],{"class":1010},[997,5995,1755],{"class":1014},[997,5997,5998,6000,6002],{"class":999,"line":1048},[997,5999,1435],{"class":1010},[997,6001,1762],{"class":1014},[997,6003,1765],{"class":1110},[997,6005,6006,6008,6010],{"class":999,"line":1054},[997,6007,1435],{"class":1010},[997,6009,1772],{"class":1014},[997,6011,1775],{"class":1110},[997,6013,6014],{"class":999,"line":1095},[997,6015,1780],{"class":1110},[1391,6017,149],{"id":1783},[861,6019,6020,1789,6022,1793,6024,1797],{},[865,6021,1788],{},[865,6023,1792],{},[865,6025,1796],{},[932,6027,6028],{"className":991,"code":1800,"language":993,"meta":940,"style":940},[865,6029,6030,6038,6046,6054,6062,6070,6078,6086,6094,6102,6110,6118,6126,6134,6142,6150,6158,6166,6174,6182,6190,6198,6206],{"__ignoreMap":940},[997,6031,6032,6034,6036],{"class":999,"line":1000},[997,6033,1182],{"class":1014},[997,6035,904],{"class":1010},[997,6037,1811],{"class":1014},[997,6039,6040,6042,6044],{"class":999,"line":1048},[997,6041,1435],{"class":1010},[997,6043,1818],{"class":1014},[997,6045,1821],{"class":1110},[997,6047,6048,6050,6052],{"class":999,"line":1054},[997,6049,1435],{"class":1010},[997,6051,1828],{"class":1014},[997,6053,1831],{"class":1110},[997,6055,6056,6058,6060],{"class":999,"line":1095},[997,6057,1435],{"class":1010},[997,6059,1838],{"class":1014},[997,6061,1841],{"class":1110},[997,6063,6064,6066,6068],{"class":999,"line":1114},[997,6065,1435],{"class":1010},[997,6067,1848],{"class":1014},[997,6069,1851],{"class":1110},[997,6071,6072,6074,6076],{"class":999,"line":1142},[997,6073,1435],{"class":1010},[997,6075,1858],{"class":1014},[997,6077,1861],{"class":1110},[997,6079,6080,6082,6084],{"class":999,"line":1174},[997,6081,1435],{"class":1010},[997,6083,1868],{"class":1014},[997,6085,1871],{"class":1110},[997,6087,6088,6090,6092],{"class":999,"line":1211},[997,6089,1435],{"class":1010},[997,6091,1878],{"class":1014},[997,6093,1881],{"class":1110},[997,6095,6096,6098,6100],{"class":999,"line":1239},[997,6097,1435],{"class":1010},[997,6099,1888],{"class":1014},[997,6101,1891],{"class":1110},[997,6103,6104,6106,6108],{"class":999,"line":1256},[997,6105,1435],{"class":1010},[997,6107,1898],{"class":1014},[997,6109,1901],{"class":1110},[997,6111,6112,6114,6116],{"class":999,"line":1524},[997,6113,1435],{"class":1010},[997,6115,1908],{"class":1014},[997,6117,1911],{"class":1110},[997,6119,6120,6122,6124],{"class":999,"line":1535},[997,6121,1435],{"class":1010},[997,6123,1918],{"class":1014},[997,6125,1921],{"class":1110},[997,6127,6128,6130,6132],{"class":999,"line":1546},[997,6129,1435],{"class":1010},[997,6131,1438],{"class":1014},[997,6133,1930],{"class":1110},[997,6135,6136,6138,6140],{"class":999,"line":1557},[997,6137,1435],{"class":1010},[997,6139,1937],{"class":1014},[997,6141,1940],{"class":1110},[997,6143,6144,6146,6148],{"class":999,"line":1568},[997,6145,1435],{"class":1010},[997,6147,1947],{"class":1014},[997,6149,1950],{"class":1110},[997,6151,6152,6154,6156],{"class":999,"line":1579},[997,6153,1435],{"class":1010},[997,6155,1957],{"class":1014},[997,6157,1960],{"class":1110},[997,6159,6160,6162,6164],{"class":999,"line":1590},[997,6161,1435],{"class":1010},[997,6163,1967],{"class":1014},[997,6165,1970],{"class":1110},[997,6167,6168,6170,6172],{"class":999,"line":1601},[997,6169,1435],{"class":1010},[997,6171,1977],{"class":1014},[997,6173,1980],{"class":1110},[997,6175,6176,6178,6180],{"class":999,"line":1612},[997,6177,1435],{"class":1010},[997,6179,1987],{"class":1014},[997,6181,1990],{"class":1110},[997,6183,6184,6186,6188],{"class":999,"line":1623},[997,6185,1435],{"class":1010},[997,6187,1997],{"class":1014},[997,6189,2000],{"class":1110},[997,6191,6192,6194,6196],{"class":999,"line":1634},[997,6193,1435],{"class":1010},[997,6195,2007],{"class":1014},[997,6197,2010],{"class":1110},[997,6199,6200,6202,6204],{"class":999,"line":1645},[997,6201,1435],{"class":1010},[997,6203,2017],{"class":1014},[997,6205,2020],{"class":1110},[997,6207,6208,6210,6212],{"class":999,"line":1656},[997,6209,1435],{"class":1010},[997,6211,2027],{"class":1014},[997,6213,2030],{"class":1110},[1391,6215,599],{"id":2033},[861,6217,6218,1789,6220,2042],{},[865,6219,2038],{},[865,6221,2041],{},[932,6223,6224],{"className":991,"code":2045,"language":993,"meta":940,"style":940},[865,6225,6226,6234,6242,6250,6258],{"__ignoreMap":940},[997,6227,6228,6230,6232],{"class":999,"line":1000},[997,6229,1182],{"class":1014},[997,6231,904],{"class":1010},[997,6233,2056],{"class":1014},[997,6235,6236,6238,6240],{"class":999,"line":1048},[997,6237,1435],{"class":1010},[997,6239,2063],{"class":1014},[997,6241,2066],{"class":1110},[997,6243,6244,6246,6248],{"class":999,"line":1054},[997,6245,1435],{"class":1010},[997,6247,2073],{"class":1014},[997,6249,2076],{"class":1110},[997,6251,6252,6254,6256],{"class":999,"line":1095},[997,6253,1435],{"class":1010},[997,6255,2083],{"class":1014},[997,6257,2086],{"class":1110},[997,6259,6260,6262,6264],{"class":999,"line":1114},[997,6261,1435],{"class":1010},[997,6263,2093],{"class":1014},[997,6265,2096],{"class":1110},[861,6267,6268,2101,6270,2105,6272,2109,6274,2113],{},[865,6269,2083],{},[865,6271,2104],{},[865,6273,2108],{},[865,6275,2112],{},[1391,6277,2117],{"id":2116},[932,6279,6280],{"className":991,"code":2120,"language":993,"meta":940,"style":940},[865,6281,6282,6292,6302,6306,6310,6314],{"__ignoreMap":940},[997,6283,6284,6286,6288,6290],{"class":999,"line":1000},[997,6285,1182],{"class":1014},[997,6287,904],{"class":1010},[997,6289,2131],{"class":1014},[997,6291,2134],{"class":1110},[997,6293,6294,6296,6298,6300],{"class":999,"line":1048},[997,6295,1182],{"class":1014},[997,6297,904],{"class":1010},[997,6299,2143],{"class":1014},[997,6301,2146],{"class":1110},[997,6303,6304],{"class":999,"line":1054},[997,6305,2151],{"class":1110},[997,6307,6308],{"class":999,"line":1095},[997,6309,2156],{"class":1110},[997,6311,6312],{"class":999,"line":1114},[997,6313,2161],{"class":1110},[997,6315,6316],{"class":999,"line":1142},[997,6317,2166],{"class":1110},[861,6319,2169,6320,2172,6322,1017,6324,1789,6326,2181,6328,2185],{},[865,6321,1182],{},[865,6323,2175],{},[865,6325,2143],{},[865,6327,2180],{},[865,6329,2184],{},[1391,6331,2189],{"id":2188},[861,6333,6334,2195,6336,2199],{},[865,6335,2194],{},[865,6337,2198],{},[932,6339,6340],{"className":991,"code":2202,"language":993,"meta":940,"style":940},[865,6341,6342,6350,6358,6366,6374,6382,6390,6398,6406,6414,6422],{"__ignoreMap":940},[997,6343,6344,6346,6348],{"class":999,"line":1000},[997,6345,1182],{"class":1014},[997,6347,904],{"class":1010},[997,6349,2213],{"class":1014},[997,6351,6352,6354,6356],{"class":999,"line":1048},[997,6353,1435],{"class":1010},[997,6355,2220],{"class":1014},[997,6357,2223],{"class":1110},[997,6359,6360,6362,6364],{"class":999,"line":1054},[997,6361,1435],{"class":1010},[997,6363,2230],{"class":1014},[997,6365,2233],{"class":1110},[997,6367,6368,6370,6372],{"class":999,"line":1095},[997,6369,1435],{"class":1010},[997,6371,2240],{"class":1014},[997,6373,2243],{"class":1110},[997,6375,6376,6378,6380],{"class":999,"line":1114},[997,6377,1435],{"class":1010},[997,6379,2250],{"class":1014},[997,6381,2253],{"class":1110},[997,6383,6384,6386,6388],{"class":999,"line":1142},[997,6385,1435],{"class":1010},[997,6387,2260],{"class":1014},[997,6389,2263],{"class":1110},[997,6391,6392,6394,6396],{"class":999,"line":1174},[997,6393,1435],{"class":1010},[997,6395,2270],{"class":1014},[997,6397,2273],{"class":1110},[997,6399,6400,6402,6404],{"class":999,"line":1211},[997,6401,1435],{"class":1010},[997,6403,2280],{"class":1014},[997,6405,2283],{"class":1110},[997,6407,6408,6410,6412],{"class":999,"line":1239},[997,6409,1435],{"class":1010},[997,6411,2290],{"class":1014},[997,6413,2293],{"class":1110},[997,6415,6416,6418,6420],{"class":999,"line":1256},[997,6417,1435],{"class":1010},[997,6419,2300],{"class":1014},[997,6421,2303],{"class":1110},[997,6423,6424,6426,6428],{"class":999,"line":1524},[997,6425,1435],{"class":1010},[997,6427,2310],{"class":1014},[997,6429,2313],{"class":1110},[861,6431,6432,2318,6434,889,6436,2325,6438,889,6440,2332,6442,904],{},[865,6433,2230],{},[865,6435,2321],{},[865,6437,2324],{},[865,6439,2328],{},[865,6441,2331],{},[865,6443,2335],{},[1391,6445,2339],{"id":2338},[932,6447,6448],{"className":991,"code":2342,"language":993,"meta":940,"style":940},[865,6449,6450],{"__ignoreMap":940},[997,6451,6452,6454,6456,6458],{"class":999,"line":1000},[997,6453,1182],{"class":1014},[997,6455,904],{"class":1010},[997,6457,2353],{"class":1014},[997,6459,2356],{"class":1110},[861,6461,2359,6462,2365],{},[2361,6463,2364],{"href":2363},[942,6465],{},[879,6467,2371],{"id":2370},[861,6469,2374,6470,2377,6472,2381],{},[865,6471,1182],{},[865,6473,2380],{},[932,6475,6476],{"className":991,"code":2384,"filename":2385,"language":993,"meta":940,"style":940},[865,6477,6478,6498,6528,6532,6556,6560,6576,6590,6608,6612,6632,6640,6644,6648,6670,6686,6698,6702,6752,6762,6780,6784,6788,6802,6806,6810,6814],{"__ignoreMap":940},[997,6479,6480,6482,6484,6486,6488,6490,6492,6494,6496],{"class":999,"line":1000},[997,6481,1004],{"class":1003},[997,6483,1011],{"class":1010},[997,6485,2396],{"class":1014},[997,6487,1028],{"class":1010},[997,6489,1031],{"class":1003},[997,6491,1035],{"class":1034},[997,6493,1039],{"class":1038},[997,6495,1042],{"class":1034},[997,6497,1045],{"class":1010},[997,6499,6500,6502,6504,6506,6508,6510,6512,6514,6516,6518,6520,6522,6524,6526],{"class":999,"line":1048},[997,6501,1004],{"class":1003},[997,6503,1007],{"class":1003},[997,6505,1011],{"class":1010},[997,6507,876],{"class":1014},[997,6509,1017],{"class":1010},[997,6511,1020],{"class":1014},[997,6513,1017],{"class":1010},[997,6515,1025],{"class":1014},[997,6517,1028],{"class":1010},[997,6519,1031],{"class":1003},[997,6521,1035],{"class":1034},[997,6523,1039],{"class":1038},[997,6525,1042],{"class":1034},[997,6527,1045],{"class":1010},[997,6529,6530],{"class":999,"line":1054},[997,6531,1051],{"emptyLinePlaceholder":8},[997,6533,6534,6536,6538,6540,6542,6544,6546,6548,6550,6552,6554],{"class":999,"line":1095},[997,6535,2447],{"class":1057},[997,6537,2450],{"class":1061},[997,6539,1076],{"class":1075},[997,6541,1035],{"class":1034},[997,6543,2457],{"class":1038},[997,6545,1042],{"class":1034},[997,6547,1128],{"class":1075},[997,6549,1035],{"class":1034},[997,6551,2466],{"class":1038},[997,6553,1042],{"class":1034},[997,6555,1045],{"class":1010},[997,6557,6558],{"class":999,"line":1114},[997,6559,1051],{"emptyLinePlaceholder":8},[997,6561,6562,6564,6566,6568,6570,6572,6574],{"class":999,"line":1142},[997,6563,2479],{"class":1057},[997,6565,2483],{"class":2482},[997,6567,2486],{"class":1057},[997,6569,1062],{"class":1061},[997,6571,1065],{"class":1010},[997,6573,2493],{"class":1068},[997,6575,2496],{"class":1010},[997,6577,6578,6580,6582,6584,6586,6588],{"class":999,"line":1174},[997,6579,1098],{"class":1014},[997,6581,1076],{"class":1075},[997,6583,1035],{"class":1034},[997,6585,2507],{"class":1038},[997,6587,1042],{"class":1034},[997,6589,1045],{"class":1010},[997,6591,6592,6594,6596,6598,6600,6602,6604,6606],{"class":999,"line":1211},[997,6593,1117],{"class":1014},[997,6595,1076],{"class":1075},[997,6597,1035],{"class":1034},[997,6599,888],{"class":1038},[997,6601,1042],{"class":1034},[997,6603,2526],{"class":1003},[997,6605,2529],{"class":1057},[997,6607,1045],{"class":1010},[997,6609,6610],{"class":999,"line":1239},[997,6611,1051],{"emptyLinePlaceholder":8},[997,6613,6614,6616,6618,6620,6622,6624,6626,6628,6630],{"class":999,"line":1256},[997,6615,1146],{"class":1145},[997,6617,1149],{"class":1010},[997,6619,2544],{"class":1152},[997,6621,1101],{"class":1075},[997,6623,1158],{"class":1061},[997,6625,1161],{"class":1010},[997,6627,1101],{"class":1075},[997,6629,1166],{"class":1061},[997,6631,2557],{"class":1010},[997,6633,6634,6636,6638],{"class":999,"line":1524},[997,6635,2562],{"class":1003},[997,6637,2566],{"class":2565},[997,6639,1045],{"class":1010},[997,6641,6642],{"class":999,"line":1535},[997,6643,2573],{"class":1010},[997,6645,6646],{"class":999,"line":1546},[997,6647,1051],{"emptyLinePlaceholder":8},[997,6649,6650,6652,6654,6656,6658,6660,6662,6664,6666,6668],{"class":999,"line":1557},[997,6651,1177],{"class":1145},[997,6653,1149],{"class":1010},[997,6655,1182],{"class":1152},[997,6657,1101],{"class":1075},[997,6659,1187],{"class":1061},[997,6661,1017],{"class":1010},[997,6663,2544],{"class":1152},[997,6665,1101],{"class":1075},[997,6667,1158],{"class":1061},[997,6669,2600],{"class":1010},[997,6671,6672,6674,6676,6678,6680,6682,6684],{"class":999,"line":1568},[997,6673,2605],{"class":1057},[997,6675,2609],{"class":2608},[997,6677,1101],{"class":1075},[997,6679,2450],{"class":1061},[997,6681,2616],{"class":1010},[997,6683,2619],{"class":1075},[997,6685,2622],{"class":1010},[997,6687,6688,6690,6692,6694,6696],{"class":999,"line":1579},[997,6689,2627],{"class":1057},[997,6691,2630],{"class":1014},[997,6693,1076],{"class":1075},[997,6695,2636],{"class":2635},[997,6697,1045],{"class":1010},[997,6699,6700],{"class":999,"line":1590},[997,6701,1051],{"emptyLinePlaceholder":8},[997,6703,6704,6706,6708,6710,6712,6714,6716,6718,6720,6722,6724,6726,6728,6730,6732,6734,6736,6738,6740,6742,6744,6746,6748,6750],{"class":999,"line":1601},[997,6705,2647],{"class":1003},[997,6707,2650],{"class":1010},[997,6709,2653],{"class":1075},[997,6711,1182],{"class":1014},[997,6713,904],{"class":1010},[997,6715,1362],{"class":1014},[997,6717,904],{"class":1010},[997,6719,2664],{"class":1145},[997,6721,1149],{"class":1010},[997,6723,1042],{"class":1034},[997,6725,2380],{"class":1038},[997,6727,1042],{"class":1034},[997,6729,2675],{"class":1010},[997,6731,2678],{"class":1075},[997,6733,2681],{"class":1014},[997,6735,904],{"class":1010},[997,6737,1427],{"class":1014},[997,6739,904],{"class":1010},[997,6741,1458],{"class":1014},[997,6743,2692],{"class":1075},[997,6745,1035],{"class":1034},[997,6747,2697],{"class":1038},[997,6749,1042],{"class":1034},[997,6751,2600],{"class":1010},[997,6753,6754,6756,6758,6760],{"class":999,"line":1612},[997,6755,2706],{"class":1014},[997,6757,2709],{"class":1075},[997,6759,2712],{"class":2635},[997,6761,1045],{"class":1010},[997,6763,6764,6766,6768,6770,6772,6774,6776,6778],{"class":999,"line":1623},[997,6765,2719],{"class":1014},[997,6767,904],{"class":1010},[997,6769,2724],{"class":1145},[997,6771,1149],{"class":1010},[997,6773,1042],{"class":1034},[997,6775,2457],{"class":1038},[997,6777,1042],{"class":1034},[997,6779,2735],{"class":1010},[997,6781,6782],{"class":999,"line":1634},[997,6783,2740],{"class":1010},[997,6785,6786],{"class":999,"line":1645},[997,6787,1051],{"emptyLinePlaceholder":8},[997,6789,6790,6792,6794,6796,6798,6800],{"class":999,"line":1656},[997,6791,2562],{"class":1003},[997,6793,1011],{"class":1010},[997,6795,2753],{"class":1014},[997,6797,1017],{"class":1010},[997,6799,2758],{"class":1014},[997,6801,2761],{"class":1010},[997,6803,6804],{"class":999,"line":1667},[997,6805,2573],{"class":1010},[997,6807,6808],{"class":999,"line":1678},[997,6809,1259],{"class":1010},[997,6811,6812],{"class":999,"line":1689},[997,6813,1051],{"emptyLinePlaceholder":8},[997,6815,6816,6818,6820,6822,6824,6826,6828],{"class":999,"line":1700},[997,6817,2396],{"class":1014},[997,6819,904],{"class":1010},[997,6821,2782],{"class":1145},[997,6823,1149],{"class":1010},[997,6825,2788],{"class":2787},[997,6827,2483],{"class":1145},[997,6829,2793],{"class":1010},[861,6831,2796,6832,972,6834,2803,6836,904],{},[870,6833,2799],{},[865,6835,2802],{},[865,6837,2806],{},[932,6839,6840],{"className":991,"code":2809,"filename":2810,"language":993,"meta":940,"style":940},[865,6841,6842,6866,6878,6882,6934],{"__ignoreMap":940},[997,6843,6844,6846,6848,6850,6852,6854,6856,6858,6860,6862,6864],{"class":999,"line":1000},[997,6845,1004],{"class":1003},[997,6847,1011],{"class":1010},[997,6849,2821],{"class":1014},[997,6851,1017],{"class":1010},[997,6853,2826],{"class":1014},[997,6855,1028],{"class":1010},[997,6857,1031],{"class":1003},[997,6859,1035],{"class":1034},[997,6861,1039],{"class":1038},[997,6863,1042],{"class":1034},[997,6865,1045],{"class":1010},[997,6867,6868,6870,6872,6874,6876],{"class":999,"line":1048},[997,6869,1004],{"class":1003},[997,6871,1035],{"class":1034},[997,6873,2847],{"class":1038},[997,6875,1042],{"class":1034},[997,6877,1045],{"class":1010},[997,6879,6880],{"class":999,"line":1054},[997,6881,1051],{"emptyLinePlaceholder":8},[997,6883,6884,6886,6888,6890,6892,6894,6896,6898,6900,6902,6904,6906,6908,6910,6912,6914,6916,6918,6920,6922,6924,6926,6928,6930,6932],{"class":999,"line":1095},[997,6885,2860],{"class":1003},[997,6887,2863],{"class":1145},[997,6889,2866],{"class":1010},[997,6891,2869],{"class":1014},[997,6893,1101],{"class":2872},[997,6895,1011],{"class":1010},[997,6897,2877],{"class":1014},[997,6899,1101],{"class":2872},[997,6901,1011],{"class":1010},[997,6903,2884],{"class":1014},[997,6905,1101],{"class":2872},[997,6907,1035],{"class":1034},[997,6909,2891],{"class":1038},[997,6911,1042],{"class":1034},[997,6913,1017],{"class":1010},[997,6915,2898],{"class":1014},[997,6917,1101],{"class":2872},[997,6919,1035],{"class":1034},[997,6921,2905],{"class":1038},[997,6923,1042],{"class":1034},[997,6925,2910],{"class":1010},[997,6927,900],{"class":1014},[997,6929,1101],{"class":2872},[997,6931,2917],{"class":2635},[997,6933,2920],{"class":1010},[997,6935,6936,6938,6940,6942,6944,6946,6948],{"class":999,"line":1114},[997,6937,2925],{"class":1014},[997,6939,904],{"class":1010},[997,6941,2930],{"class":1145},[997,6943,1149],{"class":1010},[997,6945,2826],{"class":1145},[997,6947,2937],{"class":1010},[997,6949,2940],{"class":1110},[942,6951],{},[879,6953,2946],{"id":2945},[861,6955,2949,6956,2952,6958,2955,6960,2958,6962,2962,6964,2966,6966,904],{},[865,6957,980],{},[865,6959,988],{},[865,6961,984],{},[865,6963,2961],{},[865,6965,2965],{},[865,6967,980],{},[861,6969,2971],{},[2973,6971,6972,6974,7042,7046,7206,7208,7218,7598],{"level":2975},[1391,6973,2979],{"id":2978},[932,6975,6976],{"className":991,"code":2982,"filename":2983,"language":993,"meta":940,"style":940},[865,6977,6978,6988,6998,7028,7038],{"__ignoreMap":940},[997,6979,6980,6982,6984,6986],{"class":999,"line":1000},[997,6981,2990],{"class":1003},[997,6983,2993],{"class":1057},[997,6985,2996],{"class":1061},[997,6987,2557],{"class":1010},[997,6989,6990,6992,6994,6996],{"class":999,"line":1048},[997,6991,3003],{"class":1014},[997,6993,1101],{"class":1075},[997,6995,1104],{"class":1061},[997,6997,1045],{"class":1010},[997,6999,7000,7002,7004,7006,7008,7010,7012,7014,7016,7018,7020,7022,7024,7026],{"class":999,"line":1054},[997,7001,3014],{"class":1014},[997,7003,1101],{"class":1075},[997,7005,1035],{"class":1034},[997,7007,3021],{"class":1038},[997,7009,1042],{"class":1034},[997,7011,1128],{"class":1075},[997,7013,1035],{"class":1034},[997,7015,3030],{"class":1038},[997,7017,1042],{"class":1034},[997,7019,1128],{"class":1075},[997,7021,1035],{"class":1034},[997,7023,3039],{"class":1038},[997,7025,1042],{"class":1034},[997,7027,1045],{"class":1010},[997,7029,7030,7032,7034,7036],{"class":999,"line":1095},[997,7031,3048],{"class":1014},[997,7033,1101],{"class":1075},[997,7035,1166],{"class":1061},[997,7037,1045],{"class":1010},[997,7039,7040],{"class":999,"line":1114},[997,7041,1259],{"class":1010},[1391,7043,3062,7044],{"id":3061},[865,7045,2961],{},[932,7047,7048],{"className":991,"code":3067,"filename":2810,"language":993,"meta":940,"style":940},[865,7049,7050,7070,7092,7096,7106,7124,7150,7176,7198,7202],{"__ignoreMap":940},[997,7051,7052,7054,7056,7058,7060,7062,7064,7066,7068],{"class":999,"line":1000},[997,7053,1004],{"class":1003},[997,7055,1011],{"class":1010},[997,7057,2826],{"class":1014},[997,7059,1028],{"class":1010},[997,7061,1031],{"class":1003},[997,7063,1035],{"class":1034},[997,7065,1039],{"class":1038},[997,7067,1042],{"class":1034},[997,7069,1045],{"class":1010},[997,7071,7072,7074,7076,7078,7080,7082,7084,7086,7088,7090],{"class":999,"line":1048},[997,7073,1004],{"class":1003},[997,7075,1007],{"class":1003},[997,7077,1011],{"class":1010},[997,7079,3100],{"class":1014},[997,7081,1028],{"class":1010},[997,7083,1031],{"class":1003},[997,7085,1035],{"class":1034},[997,7087,3109],{"class":1038},[997,7089,1042],{"class":1034},[997,7091,1045],{"class":1010},[997,7093,7094],{"class":999,"line":1054},[997,7095,1051],{"emptyLinePlaceholder":8},[997,7097,7098,7100,7102,7104],{"class":999,"line":1095},[997,7099,2925],{"class":1014},[997,7101,904],{"class":1010},[997,7103,2930],{"class":1145},[997,7105,3128],{"class":1010},[997,7107,7108,7110,7112,7114,7116,7118,7120,7122],{"class":999,"line":1114},[997,7109,3133],{"class":1145},[997,7111,1065],{"class":1010},[997,7113,3100],{"class":1068},[997,7115,3140],{"class":1010},[997,7117,1362],{"class":1152},[997,7119,2675],{"class":1010},[997,7121,3147],{"class":1057},[997,7123,3150],{"class":1010},[997,7125,7126,7128,7130,7132,7134,7136,7138,7140,7142,7144,7146,7148],{"class":999,"line":1142},[997,7127,3155],{"class":1014},[997,7129,1101],{"class":2872},[997,7131,3160],{"class":1014},[997,7133,904],{"class":1010},[997,7135,3165],{"class":1014},[997,7137,3168],{"class":1010},[997,7139,3171],{"class":1014},[997,7141,3174],{"class":1075},[997,7143,1035],{"class":1034},[997,7145,3179],{"class":1038},[997,7147,1042],{"class":1034},[997,7149,3184],{"class":1010},[997,7151,7152,7154,7156,7158,7160,7162,7164,7166,7168,7170,7172,7174],{"class":999,"line":1174},[997,7153,3189],{"class":1014},[997,7155,1101],{"class":2872},[997,7157,3160],{"class":1014},[997,7159,904],{"class":1010},[997,7161,3165],{"class":1014},[997,7163,3168],{"class":1010},[997,7165,3202],{"class":1014},[997,7167,3205],{"class":1075},[997,7169,1035],{"class":1034},[997,7171,3021],{"class":1038},[997,7173,1042],{"class":1034},[997,7175,3184],{"class":1010},[997,7177,7178,7180,7182,7184,7186,7188,7190,7192,7194,7196],{"class":999,"line":1211},[997,7179,3218],{"class":1014},[997,7181,1101],{"class":2872},[997,7183,3160],{"class":1014},[997,7185,904],{"class":1010},[997,7187,3227],{"class":1014},[997,7189,2692],{"class":1075},[997,7191,1035],{"class":1034},[997,7193,3234],{"class":1038},[997,7195,1042],{"class":1034},[997,7197,3184],{"class":1010},[997,7199,7200],{"class":999,"line":1239},[997,7201,3243],{"class":1010},[997,7203,7204],{"class":999,"line":1256},[997,7205,2735],{"class":1010},[1391,7207,3251],{"id":3250},[861,7209,3254,7210,3257,7212,3260,7214,3263,7216,904],{},[865,7211,3100],{},[865,7213,876],{},[865,7215,980],{},[865,7217,2965],{},[932,7219,7220],{"className":991,"code":3268,"filename":3269,"language":993,"meta":940,"style":940},[865,7221,7222,7242,7276,7298,7302,7322,7336,7354,7358,7372,7380,7384,7388,7414,7430,7442,7446,7478,7482,7520,7530,7548,7552,7556,7570,7574,7578,7582],{"__ignoreMap":940},[997,7223,7224,7226,7228,7230,7232,7234,7236,7238,7240],{"class":999,"line":1000},[997,7225,1004],{"class":1003},[997,7227,1011],{"class":1010},[997,7229,2396],{"class":1014},[997,7231,1028],{"class":1010},[997,7233,1031],{"class":1003},[997,7235,1035],{"class":1034},[997,7237,1039],{"class":1038},[997,7239,1042],{"class":1034},[997,7241,1045],{"class":1010},[997,7243,7244,7246,7248,7250,7252,7254,7256,7258,7260,7262,7264,7266,7268,7270,7272,7274],{"class":999,"line":1048},[997,7245,1004],{"class":1003},[997,7247,1007],{"class":1003},[997,7249,1011],{"class":1010},[997,7251,876],{"class":1014},[997,7253,1017],{"class":1010},[997,7255,1020],{"class":1014},[997,7257,1017],{"class":1010},[997,7259,1025],{"class":1014},[997,7261,1017],{"class":1010},[997,7263,966],{"class":1014},[997,7265,1028],{"class":1010},[997,7267,1031],{"class":1003},[997,7269,1035],{"class":1034},[997,7271,1039],{"class":1038},[997,7273,1042],{"class":1034},[997,7275,1045],{"class":1010},[997,7277,7278,7280,7282,7284,7286,7288,7290,7292,7294,7296],{"class":999,"line":1054},[997,7279,1004],{"class":1003},[997,7281,1007],{"class":1003},[997,7283,1011],{"class":1010},[997,7285,3100],{"class":1014},[997,7287,1028],{"class":1010},[997,7289,1031],{"class":1003},[997,7291,1035],{"class":1034},[997,7293,3109],{"class":1038},[997,7295,1042],{"class":1034},[997,7297,1045],{"class":1010},[997,7299,7300],{"class":999,"line":1095},[997,7301,1051],{"emptyLinePlaceholder":8},[997,7303,7304,7306,7308,7310,7312,7314,7316,7318,7320],{"class":999,"line":1114},[997,7305,2479],{"class":1057},[997,7307,3358],{"class":2482},[997,7309,2486],{"class":1057},[997,7311,1062],{"class":1061},[997,7313,1065],{"class":1010},[997,7315,966],{"class":1068},[997,7317,1017],{"class":1010},[997,7319,3100],{"class":1068},[997,7321,2496],{"class":1010},[997,7323,7324,7326,7328,7330,7332,7334],{"class":999,"line":1142},[997,7325,1098],{"class":1014},[997,7327,1076],{"class":1075},[997,7329,1035],{"class":1034},[997,7331,3383],{"class":1038},[997,7333,1042],{"class":1034},[997,7335,1045],{"class":1010},[997,7337,7338,7340,7342,7344,7346,7348,7350,7352],{"class":999,"line":1174},[997,7339,1117],{"class":1014},[997,7341,1076],{"class":1075},[997,7343,1035],{"class":1034},[997,7345,888],{"class":1038},[997,7347,1042],{"class":1034},[997,7349,2526],{"class":1003},[997,7351,2529],{"class":1057},[997,7353,1045],{"class":1010},[997,7355,7356],{"class":999,"line":1211},[997,7357,1051],{"emptyLinePlaceholder":8},[997,7359,7360,7362,7364,7366,7368,7370],{"class":999,"line":1239},[997,7361,1146],{"class":1145},[997,7363,1149],{"class":1010},[997,7365,2544],{"class":1152},[997,7367,1101],{"class":1075},[997,7369,1158],{"class":1061},[997,7371,3424],{"class":1010},[997,7373,7374,7376,7378],{"class":999,"line":1256},[997,7375,2562],{"class":1003},[997,7377,2566],{"class":2565},[997,7379,3433],{"class":1010},[997,7381,7382],{"class":999,"line":1524},[997,7383,2573],{"class":1010},[997,7385,7386],{"class":999,"line":1535},[997,7387,3442],{"class":1010},[997,7389,7390,7392,7394,7396,7398,7400,7402,7404,7406,7408,7410,7412],{"class":999,"line":1546},[997,7391,1177],{"class":1145},[997,7393,1149],{"class":1010},[997,7395,1182],{"class":1152},[997,7397,1101],{"class":1075},[997,7399,1187],{"class":1061},[997,7401,1065],{"class":1010},[997,7403,3100],{"class":1068},[997,7405,1194],{"class":1010},[997,7407,2544],{"class":1152},[997,7409,1101],{"class":1075},[997,7411,1158],{"class":1061},[997,7413,2600],{"class":1010},[997,7415,7416,7418,7420,7422,7424,7426,7428],{"class":999,"line":1557},[997,7417,2605],{"class":1057},[997,7419,2609],{"class":2608},[997,7421,1101],{"class":1075},[997,7423,3479],{"class":1061},[997,7425,2616],{"class":1010},[997,7427,2619],{"class":1075},[997,7429,2622],{"class":1010},[997,7431,7432,7434,7436,7438,7440],{"class":999,"line":1568},[997,7433,2627],{"class":1057},[997,7435,2630],{"class":1014},[997,7437,1076],{"class":1075},[997,7439,2636],{"class":2635},[997,7441,1045],{"class":1010},[997,7443,7444],{"class":999,"line":1579},[997,7445,1051],{"emptyLinePlaceholder":8},[997,7447,7448,7450,7452,7454,7456,7458,7460,7462,7464,7466,7468,7470,7472,7474,7476],{"class":999,"line":1590},[997,7449,2647],{"class":1003},[997,7451,2650],{"class":1010},[997,7453,1182],{"class":1014},[997,7455,904],{"class":1010},[997,7457,2353],{"class":1014},[997,7459,904],{"class":1010},[997,7461,3518],{"class":1014},[997,7463,2675],{"class":1010},[997,7465,3523],{"class":1003},[997,7467,1011],{"class":1010},[997,7469,2753],{"class":1014},[997,7471,1017],{"class":1010},[997,7473,2758],{"class":1014},[997,7475,3534],{"class":1010},[997,7477,3537],{"class":1110},[997,7479,7480],{"class":999,"line":1601},[997,7481,1051],{"emptyLinePlaceholder":8},[997,7483,7484,7486,7488,7490,7492,7494,7496,7498,7500,7502,7504,7506,7508,7510,7512,7514,7516,7518],{"class":999,"line":1612},[997,7485,2647],{"class":1003},[997,7487,2650],{"class":1010},[997,7489,1182],{"class":1014},[997,7491,904],{"class":1010},[997,7493,2353],{"class":1014},[997,7495,904],{"class":1010},[997,7497,3202],{"class":1014},[997,7499,2692],{"class":1075},[997,7501,1035],{"class":1034},[997,7503,3021],{"class":1038},[997,7505,1042],{"class":1034},[997,7507,3568],{"class":1075},[997,7509,2681],{"class":1014},[997,7511,904],{"class":1010},[997,7513,1427],{"class":1014},[997,7515,904],{"class":1010},[997,7517,1639],{"class":1014},[997,7519,2600],{"class":1010},[997,7521,7522,7524,7526,7528],{"class":999,"line":1623},[997,7523,2706],{"class":1014},[997,7525,2709],{"class":1075},[997,7527,3589],{"class":2635},[997,7529,1045],{"class":1010},[997,7531,7532,7534,7536,7538,7540,7542,7544,7546],{"class":999,"line":1634},[997,7533,2719],{"class":1014},[997,7535,904],{"class":1010},[997,7537,2724],{"class":1145},[997,7539,1149],{"class":1010},[997,7541,1042],{"class":1034},[997,7543,3606],{"class":1038},[997,7545,1042],{"class":1034},[997,7547,2735],{"class":1010},[997,7549,7550],{"class":999,"line":1645},[997,7551,2740],{"class":1010},[997,7553,7554],{"class":999,"line":1656},[997,7555,1051],{"emptyLinePlaceholder":8},[997,7557,7558,7560,7562,7564,7566,7568],{"class":999,"line":1667},[997,7559,2562],{"class":1003},[997,7561,1011],{"class":1010},[997,7563,2753],{"class":1014},[997,7565,1017],{"class":1010},[997,7567,2758],{"class":1014},[997,7569,2761],{"class":1010},[997,7571,7572],{"class":999,"line":1678},[997,7573,2573],{"class":1010},[997,7575,7576],{"class":999,"line":1689},[997,7577,1259],{"class":1010},[997,7579,7580],{"class":999,"line":1700},[997,7581,1051],{"emptyLinePlaceholder":8},[997,7583,7584,7586,7588,7590,7592,7594,7596],{"class":999,"line":1711},[997,7585,2396],{"class":1014},[997,7587,904],{"class":1010},[997,7589,2782],{"class":1145},[997,7591,1149],{"class":1010},[997,7593,2788],{"class":2787},[997,7595,3358],{"class":1145},[997,7597,2793],{"class":1010},[861,7599,3663,7600,3666,7602,3669,7604,3673],{},[865,7601,2961],{},[865,7603,980],{},[865,7605,3672],{},[942,7607],{},[879,7609,3679],{"id":3678},[861,7611,3682,7612,3686,7614,3689],{},[865,7613,3685],{},[865,7615,900],{},[861,7617,3692,7618,3696,7620,904],{},[865,7619,3695],{},[865,7621,2802],{},[932,7623,7624],{"className":991,"code":3701,"filename":3702,"language":993,"meta":940,"style":940},[865,7625,7626,7650,7680,7684,7704,7718,7736,7740,7760,7768,7772,7776,7800,7820,7832,7836,7864,7868,7880,7904,7908,7934,7948,7988,7992,7996,8000,8034,8048,8058,8076,8080,8084,8114,8128,8132,8136,8140],{"__ignoreMap":940},[997,7627,7628,7630,7632,7634,7636,7638,7640,7642,7644,7646,7648],{"class":999,"line":1000},[997,7629,1004],{"class":1003},[997,7631,1011],{"class":1010},[997,7633,3713],{"class":1014},[997,7635,1017],{"class":1010},[997,7637,2396],{"class":1014},[997,7639,1028],{"class":1010},[997,7641,1031],{"class":1003},[997,7643,1035],{"class":1034},[997,7645,1039],{"class":1038},[997,7647,1042],{"class":1034},[997,7649,1045],{"class":1010},[997,7651,7652,7654,7656,7658,7660,7662,7664,7666,7668,7670,7672,7674,7676,7678],{"class":999,"line":1048},[997,7653,1004],{"class":1003},[997,7655,1007],{"class":1003},[997,7657,1011],{"class":1010},[997,7659,876],{"class":1014},[997,7661,1017],{"class":1010},[997,7663,1020],{"class":1014},[997,7665,1017],{"class":1010},[997,7667,1025],{"class":1014},[997,7669,1028],{"class":1010},[997,7671,1031],{"class":1003},[997,7673,1035],{"class":1034},[997,7675,1039],{"class":1038},[997,7677,1042],{"class":1034},[997,7679,1045],{"class":1010},[997,7681,7682],{"class":999,"line":1054},[997,7683,1051],{"emptyLinePlaceholder":8},[997,7685,7686,7688,7690,7692,7694,7696,7698,7700,7702],{"class":999,"line":1095},[997,7687,2479],{"class":1057},[997,7689,3770],{"class":2482},[997,7691,2486],{"class":1057},[997,7693,1062],{"class":1061},[997,7695,1065],{"class":1010},[997,7697,1042],{"class":1034},[997,7699,3781],{"class":1038},[997,7701,1042],{"class":1034},[997,7703,2496],{"class":1010},[997,7705,7706,7708,7710,7712,7714,7716],{"class":999,"line":1114},[997,7707,1098],{"class":1014},[997,7709,1076],{"class":1075},[997,7711,1035],{"class":1034},[997,7713,3796],{"class":1038},[997,7715,1042],{"class":1034},[997,7717,1045],{"class":1010},[997,7719,7720,7722,7724,7726,7728,7730,7732,7734],{"class":999,"line":1142},[997,7721,1117],{"class":1014},[997,7723,1076],{"class":1075},[997,7725,1035],{"class":1034},[997,7727,892],{"class":1038},[997,7729,1042],{"class":1034},[997,7731,2526],{"class":1003},[997,7733,2529],{"class":1057},[997,7735,1045],{"class":1010},[997,7737,7738],{"class":999,"line":1174},[997,7739,1051],{"emptyLinePlaceholder":8},[997,7741,7742,7744,7746,7748,7750,7752,7754,7756,7758],{"class":999,"line":1211},[997,7743,1146],{"class":1145},[997,7745,1149],{"class":1010},[997,7747,2544],{"class":1152},[997,7749,1101],{"class":1075},[997,7751,1158],{"class":1061},[997,7753,1161],{"class":1010},[997,7755,1101],{"class":1075},[997,7757,1166],{"class":1061},[997,7759,2557],{"class":1010},[997,7761,7762,7764,7766],{"class":999,"line":1239},[997,7763,2562],{"class":1003},[997,7765,2566],{"class":2565},[997,7767,1045],{"class":1010},[997,7769,7770],{"class":999,"line":1256},[997,7771,2573],{"class":1010},[997,7773,7774],{"class":999,"line":1524},[997,7775,1051],{"emptyLinePlaceholder":8},[997,7777,7778,7780,7782,7784,7786,7788,7790,7792,7794,7796,7798],{"class":999,"line":1535},[997,7779,3863],{"class":1057},[997,7781,3866],{"class":1145},[997,7783,1149],{"class":1010},[997,7785,1182],{"class":1152},[997,7787,1101],{"class":1075},[997,7789,1187],{"class":1061},[997,7791,1017],{"class":1010},[997,7793,2544],{"class":1152},[997,7795,1101],{"class":1075},[997,7797,1158],{"class":1061},[997,7799,2600],{"class":1010},[997,7801,7802,7804,7806,7808,7810,7812,7814,7816,7818],{"class":999,"line":1546},[997,7803,2605],{"class":1057},[997,7805,2609],{"class":2608},[997,7807,1101],{"class":1075},[997,7809,1035],{"class":1034},[997,7811,3781],{"class":1038},[997,7813,1042],{"class":1034},[997,7815,2616],{"class":1010},[997,7817,2619],{"class":1075},[997,7819,2622],{"class":1010},[997,7821,7822,7824,7826,7828,7830],{"class":999,"line":1557},[997,7823,2627],{"class":1057},[997,7825,2630],{"class":1014},[997,7827,1076],{"class":1075},[997,7829,2636],{"class":2635},[997,7831,1045],{"class":1010},[997,7833,7834],{"class":999,"line":1568},[997,7835,1051],{"emptyLinePlaceholder":8},[997,7837,7838,7840,7842,7844,7846,7848,7850,7852,7854,7856,7858,7860,7862],{"class":999,"line":1579},[997,7839,2647],{"class":1003},[997,7841,2650],{"class":1010},[997,7843,2653],{"class":1075},[997,7845,1182],{"class":1014},[997,7847,904],{"class":1010},[997,7849,1386],{"class":1014},[997,7851,2675],{"class":1010},[997,7853,3523],{"class":1003},[997,7855,1011],{"class":1010},[997,7857,2753],{"class":1014},[997,7859,1017],{"class":1010},[997,7861,2758],{"class":1014},[997,7863,2761],{"class":1010},[997,7865,7866],{"class":999,"line":1590},[997,7867,1051],{"emptyLinePlaceholder":8},[997,7869,7870,7872,7874,7876,7878],{"class":999,"line":1601},[997,7871,2605],{"class":1057},[997,7873,3959],{"class":2608},[997,7875,1076],{"class":1075},[997,7877,3964],{"class":1145},[997,7879,3967],{"class":1010},[997,7881,7882,7884,7886,7888,7890,7892,7894,7896,7898,7900,7902],{"class":999,"line":1612},[997,7883,2605],{"class":1057},[997,7885,3974],{"class":2608},[997,7887,1076],{"class":1075},[997,7889,3979],{"class":1038},[997,7891,3982],{"class":1057},[997,7893,1182],{"class":1014},[997,7895,904],{"class":3987},[997,7897,1386],{"class":1014},[997,7899,3992],{"class":1057},[997,7901,3995],{"class":1038},[997,7903,1045],{"class":1010},[997,7905,7906],{"class":999,"line":1623},[997,7907,1051],{"emptyLinePlaceholder":8},[997,7909,7910,7912,7914,7916,7918,7920,7922,7924,7926,7928,7930,7932],{"class":999,"line":1634},[997,7911,2605],{"class":1057},[997,7913,4008],{"class":2608},[997,7915,1076],{"class":1075},[997,7917,4013],{"class":1003},[997,7919,3959],{"class":1014},[997,7921,904],{"class":1010},[997,7923,4020],{"class":1145},[997,7925,1065],{"class":1010},[997,7927,1225],{"class":1061},[997,7929,4027],{"class":1010},[997,7931,4030],{"class":1014},[997,7933,2735],{"class":1010},[997,7935,7936,7938,7940,7942,7944,7946],{"class":999,"line":1645},[997,7937,2647],{"class":1003},[997,7939,2650],{"class":1010},[997,7941,4041],{"class":1014},[997,7943,4044],{"class":1075},[997,7945,4047],{"class":2565},[997,7947,2600],{"class":1010},[997,7949,7950,7952,7954,7956,7958,7960,7962,7964,7966,7968,7970,7972,7974,7976,7978,7980,7982,7984,7986],{"class":999,"line":1656},[997,7951,4054],{"class":1003},[997,7953,1011],{"class":1010},[997,7955,2753],{"class":1014},[997,7957,1101],{"class":2872},[997,7959,4008],{"class":1014},[997,7961,1017],{"class":1010},[997,7963,2758],{"class":1014},[997,7965,1101],{"class":2872},[997,7967,4008],{"class":1014},[997,7969,4073],{"class":1075},[997,7971,2636],{"class":2635},[997,7973,4078],{"class":1075},[997,7975,4081],{"class":1010},[997,7977,1042],{"class":1034},[997,7979,3781],{"class":1038},[997,7981,1042],{"class":1034},[997,7983,4090],{"class":1010},[997,7985,1101],{"class":1075},[997,7987,4095],{"class":1010},[997,7989,7990],{"class":999,"line":1667},[997,7991,2740],{"class":1010},[997,7993,7994],{"class":999,"line":1678},[997,7995,1051],{"emptyLinePlaceholder":8},[997,7997,7998],{"class":999,"line":1689},[997,7999,4108],{"class":1110},[997,8001,8002,8004,8006,8008,8010,8012,8014,8016,8018,8020,8022,8024,8026,8028,8030,8032],{"class":999,"line":1700},[997,8003,2605],{"class":1057},[997,8005,4115],{"class":2608},[997,8007,1076],{"class":1075},[997,8009,4013],{"class":1003},[997,8011,4122],{"class":1014},[997,8013,904],{"class":1010},[997,8015,4127],{"class":1145},[997,8017,1149],{"class":1010},[997,8019,1042],{"class":1034},[997,8021,4134],{"class":1038},[997,8023,1042],{"class":1034},[997,8025,4139],{"class":1010},[997,8027,1182],{"class":1014},[997,8029,904],{"class":1010},[997,8031,1374],{"class":1014},[997,8033,4148],{"class":1010},[997,8035,8036,8038,8040,8042,8044,8046],{"class":999,"line":1711},[997,8037,2647],{"class":1003},[997,8039,2650],{"class":1010},[997,8041,4157],{"class":1014},[997,8043,904],{"class":1010},[997,8045,4162],{"class":1014},[997,8047,2600],{"class":1010},[997,8049,8050,8052,8054,8056],{"class":999,"line":1722},[997,8051,2706],{"class":1014},[997,8053,1076],{"class":1075},[997,8055,4173],{"class":2635},[997,8057,1045],{"class":1010},[997,8059,8060,8062,8064,8066,8068,8070,8072,8074],{"class":999,"line":1733},[997,8061,2719],{"class":1014},[997,8063,904],{"class":1010},[997,8065,2724],{"class":1145},[997,8067,1149],{"class":1010},[997,8069,1042],{"class":1034},[997,8071,3781],{"class":1038},[997,8073,1042],{"class":1034},[997,8075,2735],{"class":1010},[997,8077,8078],{"class":999,"line":4196},[997,8079,2740],{"class":1010},[997,8081,8082],{"class":999,"line":4201},[997,8083,1051],{"emptyLinePlaceholder":8},[997,8085,8086,8088,8090,8092,8094,8096,8098,8100,8102,8104,8106,8108,8110,8112],{"class":999,"line":4206},[997,8087,4209],{"class":1003},[997,8089,3959],{"class":1014},[997,8091,904],{"class":1010},[997,8093,4216],{"class":1145},[997,8095,1149],{"class":1010},[997,8097,4030],{"class":1014},[997,8099,1017],{"class":1010},[997,8101,2753],{"class":1014},[997,8103,4227],{"class":1010},[997,8105,4230],{"class":1014},[997,8107,1101],{"class":2872},[997,8109,4235],{"class":2635},[997,8111,4238],{"class":1010},[997,8113,4241],{"class":1110},[997,8115,8116,8118,8120,8122,8124,8126],{"class":999,"line":4244},[997,8117,2562],{"class":1003},[997,8119,1011],{"class":1010},[997,8121,2753],{"class":1014},[997,8123,1017],{"class":1010},[997,8125,2758],{"class":1014},[997,8127,2761],{"class":1010},[997,8129,8130],{"class":999,"line":4259},[997,8131,2573],{"class":1010},[997,8133,8134],{"class":999,"line":4264},[997,8135,1259],{"class":1010},[997,8137,8138],{"class":999,"line":4269},[997,8139,1051],{"emptyLinePlaceholder":8},[997,8141,8142,8144,8146,8148,8150,8152,8154],{"class":999,"line":4274},[997,8143,2396],{"class":1014},[997,8145,904],{"class":1010},[997,8147,2782],{"class":1145},[997,8149,1149],{"class":1010},[997,8151,2788],{"class":2787},[997,8153,3770],{"class":1145},[997,8155,2793],{"class":1010},[4290,8157,8158],{},[861,8159,4294,8160,4298],{},[865,8161,4297],{},[942,8163],{},[879,8165,4304],{"id":4303},[861,8167,4307,8168,4310,8170,4313],{},[865,8169,916],{},[865,8171,920],{},[932,8173,8174],{"className":991,"code":4316,"filename":4317,"language":993,"meta":940,"style":940},[865,8175,8176,8198,8228,8232,8246,8264,8284,8302,8306,8310,8328],{"__ignoreMap":940},[997,8177,8178,8180,8182,8184,8186,8188,8190,8192,8194,8196],{"class":999,"line":1000},[997,8179,688],{"class":1145},[997,8181,1149],{"class":1010},[997,8183,1182],{"class":1014},[997,8185,4330],{"class":1010},[997,8187,1020],{"class":1014},[997,8189,1017],{"class":1010},[997,8191,2544],{"class":1014},[997,8193,4330],{"class":1010},[997,8195,1025],{"class":1014},[997,8197,2600],{"class":1010},[997,8199,8200,8202,8204,8206,8208,8210,8212,8214,8216,8218,8220,8222,8224,8226],{"class":999,"line":1048},[997,8201,4347],{"class":1057},[997,8203,2609],{"class":2608},[997,8205,1101],{"class":1075},[997,8207,2650],{"class":1010},[997,8209,1042],{"class":1034},[997,8211,3781],{"class":1038},[997,8213,1042],{"class":1034},[997,8215,1128],{"class":1075},[997,8217,1035],{"class":1034},[997,8219,2466],{"class":1038},[997,8221,1042],{"class":1034},[997,8223,4370],{"class":1010},[997,8225,2619],{"class":1075},[997,8227,2622],{"class":1010},[997,8229,8230],{"class":999,"line":1054},[997,8231,1051],{"emptyLinePlaceholder":8},[997,8233,8234,8236,8238,8240,8242,8244],{"class":999,"line":1095},[997,8235,4383],{"class":1003},[997,8237,2650],{"class":1010},[997,8239,4388],{"class":1145},[997,8241,1149],{"class":1010},[997,8243,1182],{"class":1014},[997,8245,4395],{"class":1010},[997,8247,8248,8250,8252,8254,8256,8258,8260,8262],{"class":999,"line":1114},[997,8249,4400],{"class":1014},[997,8251,904],{"class":1010},[997,8253,2724],{"class":1145},[997,8255,1149],{"class":1010},[997,8257,1042],{"class":1034},[997,8259,3781],{"class":1038},[997,8261,1042],{"class":1034},[997,8263,2735],{"class":1010},[997,8265,8266,8268,8270,8272,8274,8276,8278,8280,8282],{"class":999,"line":1142},[997,8267,4400],{"class":1014},[997,8269,904],{"class":1010},[997,8271,2724],{"class":1145},[997,8273,1149],{"class":1010},[997,8275,1042],{"class":1034},[997,8277,2466],{"class":1038},[997,8279,1042],{"class":1034},[997,8281,4433],{"class":1010},[997,8283,4436],{"class":1110},[997,8285,8286,8288,8290,8292,8294,8296,8298,8300],{"class":999,"line":1174},[997,8287,2562],{"class":1003},[997,8289,1011],{"class":1010},[997,8291,2753],{"class":1014},[997,8293,1101],{"class":2872},[997,8295,2636],{"class":2635},[997,8297,1017],{"class":1010},[997,8299,2758],{"class":1014},[997,8301,2761],{"class":1010},[997,8303,8304],{"class":999,"line":1211},[997,8305,2573],{"class":1010},[997,8307,8308],{"class":999,"line":1239},[997,8309,1051],{"emptyLinePlaceholder":8},[997,8311,8312,8314,8316,8318,8320,8322,8324,8326],{"class":999,"line":1256},[997,8313,4467],{"class":1003},[997,8315,1011],{"class":1010},[997,8317,2753],{"class":1014},[997,8319,1101],{"class":2872},[997,8321,2636],{"class":2635},[997,8323,1017],{"class":1010},[997,8325,2758],{"class":1014},[997,8327,2761],{"class":1010},[997,8329,8330],{"class":999,"line":1524},[997,8331,1259],{"class":1010},[861,8333,4488],{},[942,8335],{},[879,8337,4494],{"id":4493},[861,8339,863,8340,4499],{},[865,8341,1025],{},[932,8343,8344],{"className":991,"code":4502,"filename":4503,"language":993,"meta":940,"style":940},[865,8345,8346,8358,8368,8378],{"__ignoreMap":940},[997,8347,8348,8350,8352,8354,8356],{"class":999,"line":1000},[997,8349,2990],{"class":1003},[997,8351,2529],{"class":1057},[997,8353,4514],{"class":2608},[997,8355,1076],{"class":1075},[997,8357,2557],{"class":1010},[997,8359,8360,8362,8364,8366],{"class":999,"line":1048},[997,8361,4523],{"class":1014},[997,8363,1101],{"class":2872},[997,8365,4528],{"class":2635},[997,8367,3184],{"class":1010},[997,8369,8370,8372,8374,8376],{"class":999,"line":1054},[997,8371,4535],{"class":1014},[997,8373,1101],{"class":2872},[997,8375,4540],{"class":2635},[997,8377,3184],{"class":1010},[997,8379,8380],{"class":999,"line":1095},[997,8381,4547],{"class":1010},[932,8383,8384],{"className":991,"code":4550,"filename":2385,"language":993,"meta":940,"style":940},[865,8385,8386,8406,8410,8432,8450,8464,8468],{"__ignoreMap":940},[997,8387,8388,8390,8392,8394,8396,8398,8400,8402,8404],{"class":999,"line":1000},[997,8389,1004],{"class":1003},[997,8391,1011],{"class":1010},[997,8393,4561],{"class":1014},[997,8395,1028],{"class":1010},[997,8397,1031],{"class":1003},[997,8399,1035],{"class":1034},[997,8401,4570],{"class":1038},[997,8403,1042],{"class":1034},[997,8405,1045],{"class":1010},[997,8407,8408],{"class":999,"line":1048},[997,8409,1051],{"emptyLinePlaceholder":8},[997,8411,8412,8414,8416,8418,8420,8422,8424,8426,8428,8430],{"class":999,"line":1054},[997,8413,688],{"class":1145},[997,8415,1149],{"class":1010},[997,8417,1182],{"class":1014},[997,8419,4330],{"class":1010},[997,8421,1020],{"class":1014},[997,8423,1017],{"class":1010},[997,8425,2544],{"class":1014},[997,8427,4330],{"class":1010},[997,8429,1025],{"class":1014},[997,8431,2600],{"class":1010},[997,8433,8434,8436,8438,8440,8442,8444,8446,8448],{"class":999,"line":1095},[997,8435,4383],{"class":1003},[997,8437,2650],{"class":1010},[997,8439,4609],{"class":1014},[997,8441,4073],{"class":1075},[997,8443,4514],{"class":1014},[997,8445,904],{"class":1010},[997,8447,4618],{"class":1014},[997,8449,2600],{"class":1010},[997,8451,8452,8454,8456,8458,8460,8462],{"class":999,"line":1114},[997,8453,4625],{"class":1014},[997,8455,2709],{"class":1075},[997,8457,4514],{"class":1014},[997,8459,904],{"class":1010},[997,8461,4634],{"class":1014},[997,8463,1045],{"class":1010},[997,8465,8466],{"class":999,"line":1142},[997,8467,2573],{"class":1010},[997,8469,8470],{"class":999,"line":1174},[997,8471,1259],{"class":1010},[942,8473],{},[879,8475,4650],{"id":4649},[861,8477,4653,8478,4656],{},[865,8479,2806],{},[932,8481,8482],{"className":991,"code":4659,"filename":2385,"language":993,"meta":940,"style":940},[865,8483,8484,8488,8508,8512,8516,8520],{"__ignoreMap":940},[997,8485,8486],{"class":999,"line":1000},[997,8487,1051],{"emptyLinePlaceholder":8},[997,8489,8490,8492,8494,8496,8498,8500,8502,8504,8506],{"class":999,"line":1048},[997,8491,2479],{"class":1057},[997,8493,4672],{"class":2482},[997,8495,2486],{"class":1057},[997,8497,1062],{"class":1061},[997,8499,1065],{"class":1010},[997,8501,1042],{"class":1034},[997,8503,3781],{"class":1038},[997,8505,1042],{"class":1034},[997,8507,2496],{"class":1010},[997,8509,8510],{"class":999,"line":1054},[997,8511,4691],{"class":1110},[997,8513,8514],{"class":999,"line":1095},[997,8515,1259],{"class":1010},[997,8517,8518],{"class":999,"line":1114},[997,8519,1051],{"emptyLinePlaceholder":8},[997,8521,8522,8524,8526,8528,8530,8532,8534,8536],{"class":999,"line":1142},[997,8523,2396],{"class":1014},[997,8525,904],{"class":1010},[997,8527,2782],{"class":1145},[997,8529,1149],{"class":1010},[997,8531,2788],{"class":2787},[997,8533,4672],{"class":1145},[997,8535,2937],{"class":1010},[997,8537,4718],{"class":1110},[861,8539,4721,8540,972,8542,4726],{},[870,8541,2799],{},[865,8543,2802],{},[932,8545,8546],{"className":991,"code":4729,"filename":2810,"language":993,"meta":940,"style":940},[865,8547,8548,8572,8586,8590,8634],{"__ignoreMap":940},[997,8549,8550,8552,8554,8556,8558,8560,8562,8564,8566,8568,8570],{"class":999,"line":1000},[997,8551,1004],{"class":1003},[997,8553,1011],{"class":1010},[997,8555,2821],{"class":1014},[997,8557,1017],{"class":1010},[997,8559,2826],{"class":1014},[997,8561,1028],{"class":1010},[997,8563,1031],{"class":1003},[997,8565,1035],{"class":1034},[997,8567,1039],{"class":1038},[997,8569,1042],{"class":1034},[997,8571,1045],{"class":1010},[997,8573,8574,8576,8578,8580,8582,8584],{"class":999,"line":1048},[997,8575,1004],{"class":1003},[997,8577,1035],{"class":1034},[997,8579,2847],{"class":1038},[997,8581,1042],{"class":1034},[997,8583,1107],{"class":1010},[997,8585,4770],{"class":1110},[997,8587,8588],{"class":999,"line":1054},[997,8589,1051],{"emptyLinePlaceholder":8},[997,8591,8592,8594,8596,8598,8600,8602,8604,8606,8608,8610,8612,8614,8616,8618,8620,8622,8624,8626,8628,8630,8632],{"class":999,"line":1095},[997,8593,2860],{"class":1003},[997,8595,2863],{"class":1145},[997,8597,2866],{"class":1010},[997,8599,2869],{"class":1014},[997,8601,1101],{"class":2872},[997,8603,1011],{"class":1010},[997,8605,2877],{"class":1014},[997,8607,1101],{"class":2872},[997,8609,1011],{"class":1010},[997,8611,2884],{"class":1014},[997,8613,1101],{"class":2872},[997,8615,1035],{"class":1034},[997,8617,2891],{"class":1038},[997,8619,1042],{"class":1034},[997,8621,1017],{"class":1010},[997,8623,2898],{"class":1014},[997,8625,1101],{"class":2872},[997,8627,1035],{"class":1034},[997,8629,2905],{"class":1038},[997,8631,1042],{"class":1034},[997,8633,4819],{"class":1010},[997,8635,8636,8638,8640,8642,8644,8646],{"class":999,"line":1114},[997,8637,2925],{"class":1014},[997,8639,904],{"class":1010},[997,8641,2930],{"class":1145},[997,8643,1149],{"class":1010},[997,8645,2826],{"class":1145},[997,8647,2793],{"class":1010},[4835,8649,8650],{},[861,8651,4839,8652,4842,8654,889,8656,4848,8658,4851],{},[865,8653,2802],{},[865,8655,3695],{},[865,8657,4847],{},[865,8659,2965],{},[1391,8661,4855],{"id":4854},[861,8663,4858],{},[4860,8665,8666,8668,8670,8672],{},[912,8667,4864],{},[912,8669,4867],{},[912,8671,4870],{},[912,8673,4873],{},[861,8675,4876],{},[932,8677,8678],{"className":991,"code":4879,"filename":2810,"language":993,"meta":940,"style":940},[865,8679,8680,8694,8708,8732,8736,8780],{"__ignoreMap":940},[997,8681,8682,8684,8686,8688,8690,8692],{"class":999,"line":1000},[997,8683,1004],{"class":1003},[997,8685,1035],{"class":1034},[997,8687,4890],{"class":1038},[997,8689,1042],{"class":1034},[997,8691,1107],{"class":1010},[997,8693,4897],{"class":1110},[997,8695,8696,8698,8700,8702,8704,8706],{"class":999,"line":1048},[997,8697,1004],{"class":1003},[997,8699,1035],{"class":1034},[997,8701,4906],{"class":1038},[997,8703,1042],{"class":1034},[997,8705,1107],{"class":1010},[997,8707,4913],{"class":1110},[997,8709,8710,8712,8714,8716,8718,8720,8722,8724,8726,8728,8730],{"class":999,"line":1054},[997,8711,1004],{"class":1003},[997,8713,1011],{"class":1010},[997,8715,2821],{"class":1014},[997,8717,1017],{"class":1010},[997,8719,2826],{"class":1014},[997,8721,1028],{"class":1010},[997,8723,1031],{"class":1003},[997,8725,1035],{"class":1034},[997,8727,1039],{"class":1038},[997,8729,1042],{"class":1034},[997,8731,1045],{"class":1010},[997,8733,8734],{"class":999,"line":1095},[997,8735,1051],{"emptyLinePlaceholder":8},[997,8737,8738,8740,8742,8744,8746,8748,8750,8752,8754,8756,8758,8760,8762,8764,8766,8768,8770,8772,8774,8776,8778],{"class":999,"line":1114},[997,8739,2860],{"class":1003},[997,8741,2863],{"class":1145},[997,8743,2866],{"class":1010},[997,8745,2869],{"class":1014},[997,8747,1101],{"class":2872},[997,8749,1011],{"class":1010},[997,8751,2877],{"class":1014},[997,8753,1101],{"class":2872},[997,8755,1011],{"class":1010},[997,8757,2884],{"class":1014},[997,8759,1101],{"class":2872},[997,8761,1035],{"class":1034},[997,8763,2891],{"class":1038},[997,8765,1042],{"class":1034},[997,8767,1017],{"class":1010},[997,8769,2898],{"class":1014},[997,8771,1101],{"class":2872},[997,8773,1035],{"class":1034},[997,8775,2905],{"class":1038},[997,8777,1042],{"class":1034},[997,8779,4819],{"class":1010},[997,8781,8782,8784,8786,8788,8790,8792],{"class":999,"line":1142},[997,8783,2925],{"class":1014},[997,8785,904],{"class":1010},[997,8787,2930],{"class":1145},[997,8789,1149],{"class":1010},[997,8791,2826],{"class":1145},[997,8793,2793],{"class":1010},[942,8795],{},[879,8797,5005,8798,5008],{"id":5004},[865,8799,966],{},[861,8801,5011],{},[1276,8803,8804,8812],{},[1279,8805,8806],{},[1282,8807,8808,8810],{},[1285,8809,962],{},[1285,8811,5022],{},[1295,8813,8814,8822,8830,8838,8846,8854,8862,8870,8878,8888,8898,8906],{},[1282,8815,8816,8820],{},[1300,8817,8818],{},[865,8819,2466],{},[1300,8821,5033],{},[1282,8823,8824,8828],{},[1300,8825,8826],{},[865,8827,5040],{},[1300,8829,5043],{},[1282,8831,8832,8836],{},[1300,8833,8834],{},[865,8835,3606],{},[1300,8837,5052],{},[1282,8839,8840,8844],{},[1300,8841,8842],{},[865,8843,5059],{},[1300,8845,5062],{},[1282,8847,8848,8852],{},[1300,8849,8850],{},[865,8851,5069],{},[1300,8853,5072],{},[1282,8855,8856,8860],{},[1300,8857,8858],{},[865,8859,5079],{},[1300,8861,5082],{},[1282,8863,8864,8868],{},[1300,8865,8866],{},[865,8867,5089],{},[1300,8869,5092],{},[1282,8871,8872,8876],{},[1300,8873,8874],{},[865,8875,5099],{},[1300,8877,5102],{},[1282,8879,8880,8884],{},[1300,8881,8882],{},[865,8883,5109],{},[1300,8885,5112,8886],{},[865,8887,5115],{},[1282,8889,8890,8894],{},[1300,8891,8892],{},[865,8893,5122],{},[1300,8895,5112,8896],{},[865,8897,5127],{},[1282,8899,8900,8904],{},[1300,8901,8902],{},[865,8903,5134],{},[1300,8905,5137],{},[1282,8907,8908,8912],{},[1300,8909,8910],{},[865,8911,5144],{},[1300,8913,5147],{},[861,8915,5150,8916,5153,8918,5157],{},[865,8917,966],{},[865,8919,5156],{},[942,8921],{},[879,8923,5163],{"id":5162},[1276,8925,8926,8934],{},[1279,8927,8928],{},[1282,8929,8930,8932],{},[1285,8931,5172],{},[1285,8933,5175],{},[1295,8935,8936,8944,8952,8960,8972,8984,8994,9002,9010,9020,9028,9036],{},[1282,8937,8938,8940],{},[1300,8939,5182],{},[1300,8941,8942,5187],{},[865,8943,1020],{},[1282,8945,8946,8948],{},[1300,8947,5192],{},[1300,8949,8950,5187],{},[865,8951,1020],{},[1282,8953,8954,8956],{},[1300,8955,5201],{},[1300,8957,8958,5207],{},[865,8959,5206],{},[1282,8961,8962,8964],{},[1300,8963,5212],{},[1300,8965,8966,5217,8968,5220,8970],{},[865,8967,984],{},[865,8969,2961],{},[865,8971,980],{},[1282,8973,8974,8978],{},[1300,8975,5227,8976,5230],{},[865,8977,980],{},[1300,8979,8980,5236,8982],{},[865,8981,5235],{},[865,8983,5239],{},[1282,8985,8986,8988],{},[1300,8987,5244],{},[1300,8989,5247,8990,5250,8992],{},[865,8991,916],{},[865,8993,2758],{},[1282,8995,8996,8998],{},[1300,8997,5257],{},[1300,8999,5247,9000],{},[865,9001,5262],{},[1282,9003,9004,9006],{},[1300,9005,5267],{},[1300,9007,5270,9008,5273],{},[865,9009,3685],{},[1282,9011,9012,9014],{},[1300,9013,5278],{},[1300,9015,9016,5153,9018],{},[865,9017,966],{},[865,9019,5156],{},[1282,9021,9022,9024],{},[1300,9023,5289],{},[1300,9025,9026,5295],{},[865,9027,5294],{},[1282,9029,9030,9032],{},[1300,9031,5300],{},[1300,9033,9034,5306],{},[865,9035,5305],{},[1282,9037,9038,9040],{},[1300,9039,5311],{},[1300,9041,5314],{},[5316,9043,5318],{},{"title":940,"searchDepth":1048,"depth":1048,"links":9045},[9046,9047,9048,9049,9057,9058,9063,9064,9065,9066,9069,9070],{"id":881,"depth":1048,"text":882},{"id":946,"depth":1048,"text":5323},{"id":1273,"depth":1048,"text":1274},{"id":1340,"depth":1048,"text":5326,"children":9050},[9051,9052,9053,9054,9055,9056],{"id":1393,"depth":1054,"text":1394},{"id":1783,"depth":1054,"text":149},{"id":2033,"depth":1054,"text":599},{"id":2116,"depth":1054,"text":2117},{"id":2188,"depth":1054,"text":2189},{"id":2338,"depth":1054,"text":2339},{"id":2370,"depth":1048,"text":2371},{"id":2945,"depth":1048,"text":2946,"children":9059},[9060,9061,9062],{"id":2978,"depth":1054,"text":2979},{"id":3061,"depth":1054,"text":5339},{"id":3250,"depth":1054,"text":3251},{"id":3678,"depth":1048,"text":3679},{"id":4303,"depth":1048,"text":4304},{"id":4493,"depth":1048,"text":4494},{"id":4649,"depth":1048,"text":4650,"children":9067},[9068],{"id":4854,"depth":1054,"text":4855},{"id":5004,"depth":1048,"text":5348},{"id":5162,"depth":1048,"text":5163},{},{"title":94,"description":5350},1780564517206]