[{"data":1,"prerenderedAt":10729},["ShallowReactive",2],{"navLinks":3,"sidebar_docs_navigation_\u002Fdocs\u002Fiam":64,"navigation":257,"navLinks_footer":837,"\u002Fdocs\u002Fiam\u002Fessentials\u002Frate-limiting_page":850,"\u002Fdocs\u002Fiam\u002Fessentials\u002Frate-limiting_surround":6283,"\u002Fdocs\u002Fiam\u002Fessentials\u002Frate-limiting":6286},{"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":27,"path":29,"stem":70,"children":71},"docs\u002Fiam\u002Findex",[72,73,76,216,219,236,240],{"title":27,"path":29,"stem":70},{"title":14,"path":74,"stem":75},"\u002Fdocs\u002Fiam\u002Fgetting-started","docs\u002Fiam\u002F00.getting-started",{"title":77,"path":78,"stem":79,"children":80},"Essentials","\u002Fdocs\u002Fiam\u002Fessentials","docs\u002Fiam\u002F01.essentials\u002Findex",[81,82,86,90,94,98,102,106,110,114,118,122,126,130,134,138,142,146,150,154,158,162,166],{"title":77,"path":78,"stem":79},{"title":83,"path":84,"stem":85},"Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Ftokens","docs\u002Fiam\u002F01.essentials\u002F00.tokens",{"title":87,"path":88,"stem":89},"Access Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Faccess-tokens","docs\u002Fiam\u002F01.essentials\u002F01.access-tokens",{"title":91,"path":92,"stem":93},"Refresh Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens","docs\u002Fiam\u002F01.essentials\u002F02.refresh-tokens",{"title":95,"path":96,"stem":97},"Anomaly Detection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies","docs\u002Fiam\u002F01.essentials\u002F03.anomalies",{"title":99,"path":100,"stem":101},"Signup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fsignup","docs\u002Fiam\u002F01.essentials\u002F04.signup",{"title":103,"path":104,"stem":105},"Login","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogin","docs\u002Fiam\u002F01.essentials\u002F05.login",{"title":107,"path":108,"stem":109},"Logout","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogout","docs\u002Fiam\u002F01.essentials\u002F06.logout",{"title":111,"path":112,"stem":113},"OAuth","\u002Fdocs\u002Fiam\u002Fessentials\u002Foauth","docs\u002Fiam\u002F01.essentials\u002F07.oauth",{"title":115,"path":116,"stem":117},"Magic Links","\u002Fdocs\u002Fiam\u002Fessentials\u002Fmagic-links","docs\u002Fiam\u002F01.essentials\u002F08.magic-links",{"title":119,"path":120,"stem":121},"Emails","\u002Fdocs\u002Fiam\u002Fessentials\u002Femails","docs\u002Fiam\u002F01.essentials\u002F09.emails",{"title":123,"path":124,"stem":125},"MFA","\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa","docs\u002Fiam\u002F01.essentials\u002F10.mfa",{"title":127,"path":128,"stem":129},"Fingerprinting","\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting","docs\u002Fiam\u002F01.essentials\u002F11.fingerprinting",{"title":131,"path":132,"stem":133},"Backend for Frontend","\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff","docs\u002Fiam\u002F01.essentials\u002F12.bff",{"title":135,"path":136,"stem":137},"HMAC Authentication","\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac","docs\u002Fiam\u002F01.essentials\u002F13.hmac",{"title":139,"path":140,"stem":141},"XSS Protection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fxss","docs\u002Fiam\u002F01.essentials\u002F14.xss",{"title":143,"path":144,"stem":145},"Logging","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogging","docs\u002Fiam\u002F01.essentials\u002F15.logging",{"title":147,"path":148,"stem":149},"Rate Limiting","\u002Fdocs\u002Fiam\u002Fessentials\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F16.rate-limiting",{"title":151,"path":152,"stem":153},"Database","\u002Fdocs\u002Fiam\u002Fessentials\u002Fdatabase","docs\u002Fiam\u002F01.essentials\u002F17.database",{"title":155,"path":156,"stem":157},"Cookies","\u002Fdocs\u002Fiam\u002Fessentials\u002Fcookies","docs\u002Fiam\u002F01.essentials\u002F18.cookies",{"title":159,"path":160,"stem":161},"Service Startup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fservice","docs\u002Fiam\u002F01.essentials\u002F19.service",{"title":163,"path":164,"stem":165},"Password Reset","\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset","docs\u002Fiam\u002F01.essentials\u002F20.password-reset",{"title":167,"path":168,"stem":169,"children":170},"API Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi","docs\u002Fiam\u002F01.essentials\u002F21.api\u002Findex",[171,172,176,180,210,213],{"title":167,"path":168,"stem":169},{"title":173,"path":174,"stem":175},"Creating Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fcreation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F00.creation",{"title":177,"path":178,"stem":179},"Verifying Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fverification","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F01.verification",{"title":181,"path":182,"stem":183,"children":184},"Manage Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002Findex",[185,186,190,194,198,202,206],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189},"Privileges","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fprivilege","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F00.privilege",{"title":191,"path":192,"stem":193},"Revocation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frevocation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F01.revocation",{"title":195,"path":196,"stem":197},"Rotation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frotation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F02.rotation",{"title":199,"path":200,"stem":201},"IP Restriction","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fip-updates","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F03.ip-updates",{"title":203,"path":204,"stem":205},"Metadata","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fmetadata","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F04.metadata",{"title":207,"path":208,"stem":209},"Token Listing","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Flist","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F05.list",{"title":147,"path":211,"stem":212},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F03.rate-limiting",{"title":38,"path":214,"stem":215},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fsecurity","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F04.security",{"title":38,"path":217,"stem":218},"\u002Fdocs\u002Fiam\u002Fsecurity","docs\u002Fiam\u002F02.security",{"title":220,"path":221,"stem":222,"children":223,"page":53},"Guides","\u002Fdocs\u002Fiam\u002Fguides","docs\u002Fiam\u002F03.guides",[224,228,232],{"title":225,"path":226,"stem":227},"Deployment","\u002Fdocs\u002Fiam\u002Fguides\u002Fdeployment","docs\u002Fiam\u002F03.guides\u002Fdeployment",{"title":229,"path":230,"stem":231},"Operation Scripts","\u002Fdocs\u002Fiam\u002Fguides\u002Foperation-scripts","docs\u002Fiam\u002F03.guides\u002Foperation-scripts",{"title":233,"path":234,"stem":235},"Role-Based Access Control","\u002Fdocs\u002Fiam\u002Fguides\u002Frbac","docs\u002Fiam\u002F03.guides\u002Frbac",{"title":237,"path":238,"stem":239},"Configuration","\u002Fdocs\u002Fiam\u002Fconfiguration","docs\u002Fiam\u002F04.configuration",{"title":241,"path":242,"stem":243,"children":244,"page":53},"Api","\u002Fdocs\u002Fiam\u002Fapi","docs\u002Fiam\u002F05.API",[245,249,253],{"title":246,"path":247,"stem":248},"API Reference","\u002Fdocs\u002Fiam\u002Fapi\u002Fapi","docs\u002Fiam\u002F05.API\u002F00.api",{"title":250,"path":251,"stem":252},"Middleware Reference","\u002Fdocs\u002Fiam\u002Fapi\u002Fmiddlewares","docs\u002Fiam\u002F05.API\u002F02.middlewares",{"title":254,"path":255,"stem":256},"Routes Reference","\u002Fdocs\u002Fiam\u002Fapi\u002Froutes","docs\u002Fiam\u002F05.API\u002F03.routes",[258],{"title":9,"path":66,"stem":67,"children":259,"page":53},[260,398,516,521,577,644],{"title":20,"path":22,"stem":261,"children":262},"docs\u002Fauth-h3client\u002Findex",[263,264,273,307,331,353,356,376,379],{"title":20,"path":22,"stem":261},{"title":14,"path":265,"stem":266,"children":267},"\u002Fdocs\u002Fauth-h3client\u002Fgetting-started","docs\u002Fauth-h3client\u002F00.getting-started\u002Findex",[268,269],{"title":14,"path":265,"stem":266},{"title":270,"path":271,"stem":272},"Nuxt Module","\u002Fdocs\u002Fauth-h3client\u002Fgetting-started\u002Fnuxt","docs\u002Fauth-h3client\u002F00.getting-started\u002F00.nuxt",{"title":77,"path":274,"stem":275,"children":276},"\u002Fdocs\u002Fauth-h3client\u002Fessentials","docs\u002Fauth-h3client\u002F01.essentials\u002Findex",[277,278,282,286,290,294,298,301,304],{"title":77,"path":274,"stem":275},{"title":279,"path":280,"stem":281},"Session Management","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fsession","docs\u002Fauth-h3client\u002F01.essentials\u002F00.session",{"title":283,"path":284,"stem":285},"Route Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Froute-protection","docs\u002Fauth-h3client\u002F01.essentials\u002F01.route-protection",{"title":287,"path":288,"stem":289},"CSRF Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcsrf","docs\u002Fauth-h3client\u002F01.essentials\u002F02.csrf",{"title":291,"path":292,"stem":293},"Auth Flows","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fauth-flows","docs\u002Fauth-h3client\u002F01.essentials\u002F03.auth-flows",{"title":295,"path":296,"stem":297},"OAuth and OIDC","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Foauth","docs\u002Fauth-h3client\u002F01.essentials\u002F04.oauth",{"title":33,"path":299,"stem":300},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fbot-detection","docs\u002Fauth-h3client\u002F01.essentials\u002F05.bot-detection",{"title":155,"path":302,"stem":303},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcookies","docs\u002Fauth-h3client\u002F01.essentials\u002F06.cookies",{"title":143,"path":305,"stem":306},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Flogging","docs\u002Fauth-h3client\u002F01.essentials\u002F07.logging",{"title":123,"path":308,"stem":309,"children":310},"\u002Fdocs\u002Fauth-h3client\u002Fmfa","docs\u002Fauth-h3client\u002F02.mfa\u002Findex",[311,312,316,319,323,327],{"title":123,"path":308,"stem":309},{"title":313,"path":314,"stem":315},"Built-in MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fbuilt-in-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F01.built-in-flow",{"title":163,"path":317,"stem":318},"\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fpassword-reset","docs\u002Fauth-h3client\u002F02.mfa\u002F02.password-reset",{"title":320,"path":321,"stem":322},"Email Change","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Femail-change","docs\u002Fauth-h3client\u002F02.mfa\u002F03.email-change",{"title":324,"path":325,"stem":326},"Custom MFA Flow","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fcustom-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F04.custom-flow",{"title":328,"path":329,"stem":330},"Client-Side MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fclient-side","docs\u002Fauth-h3client\u002F02.mfa\u002F05.client-side",{"title":332,"path":333,"stem":334,"children":335},"Client-side","\u002Fdocs\u002Fauth-h3client\u002Fclient","docs\u002Fauth-h3client\u002F03.client\u002Findex",[336,337,341,345,349],{"title":332,"path":333,"stem":334},{"title":338,"path":339,"stem":340},"useAuthData","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-auth-data","docs\u002Fauth-h3client\u002F03.client\u002F00.use-auth-data",{"title":342,"path":343,"stem":344},"useMagicLink","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-magic-link","docs\u002Fauth-h3client\u002F03.client\u002F01.use-magic-link",{"title":346,"path":347,"stem":348},"executeRequest","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fexecute-request","docs\u002Fauth-h3client\u002F03.client\u002F02.execute-request",{"title":350,"path":351,"stem":352},"getCsrfToken","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fget-csrf-token","docs\u002Fauth-h3client\u002F03.client\u002F03.get-csrf-token",{"title":38,"path":354,"stem":355},"\u002Fdocs\u002Fauth-h3client\u002Fsecurity","docs\u002Fauth-h3client\u002F04.security",{"title":220,"path":357,"stem":358,"children":359,"page":53},"\u002Fdocs\u002Fauth-h3client\u002Fguides","docs\u002Fauth-h3client\u002F05.guides",[360,364,368,372],{"title":361,"path":362,"stem":363},"H3 and Nitro Setup","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fh3-nitro","docs\u002Fauth-h3client\u002F05.guides\u002F00.h3-nitro",{"title":365,"path":366,"stem":367},"HMAC Inter-service Auth","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fhmac","docs\u002Fauth-h3client\u002F05.guides\u002Fhmac",{"title":369,"path":370,"stem":371},"Image Upload","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fimage-upload","docs\u002Fauth-h3client\u002F05.guides\u002Fimage-upload",{"title":373,"path":374,"stem":375},"mTLS Configuration","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fmtls","docs\u002Fauth-h3client\u002F05.guides\u002Fmtls",{"title":237,"path":377,"stem":378},"\u002Fdocs\u002Fauth-h3client\u002Fconfiguration","docs\u002Fauth-h3client\u002F06.configuration",{"title":246,"path":380,"stem":381,"children":382},"\u002Fdocs\u002Fauth-h3client\u002Fapi","docs\u002Fauth-h3client\u002F07.api\u002Findex",[383,384,387,390,394],{"title":246,"path":380,"stem":381},{"title":254,"path":385,"stem":386},"\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcontrollers","docs\u002Fauth-h3client\u002F07.api\u002F00.controllers",{"title":250,"path":388,"stem":389},"\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fmiddleware","docs\u002Fauth-h3client\u002F07.api\u002F01.middleware",{"title":391,"path":392,"stem":393},"Client-side Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcomposables","docs\u002Fauth-h3client\u002F07.api\u002F02.composables",{"title":395,"path":396,"stem":397},"Utilities","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Futilities","docs\u002Fauth-h3client\u002F07.api\u002F03.utilities",{"title":399,"path":35,"stem":400,"children":401},"Bot Detector","docs\u002Fbot-detection\u002Findex",[402,403,406,410,414,433,507,510,513],{"title":399,"path":35,"stem":400},{"title":14,"path":404,"stem":405},"\u002Fdocs\u002Fbot-detection\u002Fgetting-started","docs\u002Fbot-detection\u002F00.getting-started",{"title":407,"path":408,"stem":409},"CLI","\u002Fdocs\u002Fbot-detection\u002Fcli","docs\u002Fbot-detection\u002F01.cli",{"title":411,"path":412,"stem":413},"Data Sources","\u002Fdocs\u002Fbot-detection\u002Fdata-sources","docs\u002Fbot-detection\u002F02.data-sources",{"title":220,"path":415,"stem":416,"children":417,"page":53},"\u002Fdocs\u002Fbot-detection\u002Fguides","docs\u002Fbot-detection\u002F03.guides",[418,422,426,429],{"title":419,"path":420,"stem":421},"Custom Checkers","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fcustom","docs\u002Fbot-detection\u002F03.guides\u002FCUSTOM",{"title":423,"path":424,"stem":425},"Scheduling Database Generation","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fgenerate","docs\u002Fbot-detection\u002F03.guides\u002FGENERATE",{"title":143,"path":427,"stem":428},"\u002Fdocs\u002Fbot-detection\u002Fguides\u002Flogging","docs\u002Fbot-detection\u002F03.guides\u002FLOGGING",{"title":430,"path":431,"stem":432},"Score Modes and Reputation Healing","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fscore","docs\u002Fbot-detection\u002F03.guides\u002FSCORE",{"title":434,"path":435,"stem":436,"children":437},"Checkers","\u002Fdocs\u002Fbot-detection\u002Fcheckers","docs\u002Fbot-detection\u002F04.checkers\u002Findex",[438,439,443,447,451,455,459,463,467,471,475,479,483,487,491,495,499,503],{"title":434,"path":435,"stem":436},{"title":440,"path":441,"stem":442},"IP Validation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fip-validation","docs\u002Fbot-detection\u002F04.checkers\u002F01.ip-validation",{"title":444,"path":445,"stem":446},"Good \u002F Bad Bot Verification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgood-bots","docs\u002Fbot-detection\u002F04.checkers\u002F02.good-bots",{"title":448,"path":449,"stem":450},"Browser & Device Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbrowser-device","docs\u002Fbot-detection\u002F04.checkers\u002F03.browser-device",{"title":452,"path":453,"stem":454},"Locale Map","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Flocale-map","docs\u002Fbot-detection\u002F04.checkers\u002F04.locale-map",{"title":456,"path":457,"stem":458},"Known Threats","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-threats","docs\u002Fbot-detection\u002F04.checkers\u002F05.known-threats",{"title":460,"path":461,"stem":462},"ASN Classification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fasn-classification","docs\u002Fbot-detection\u002F04.checkers\u002F06.asn-classification",{"title":464,"path":465,"stem":466},"Tor Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftor-analysis","docs\u002Fbot-detection\u002F04.checkers\u002F07.tor-analysis",{"title":468,"path":469,"stem":470},"Timezone Consistency","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftimezone-consistency","docs\u002Fbot-detection\u002F04.checkers\u002F08.timezone-consistency",{"title":472,"path":473,"stem":474},"Honeypot","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fhoneypot","docs\u002Fbot-detection\u002F04.checkers\u002F09.honeypot",{"title":476,"path":477,"stem":478},"Known Bad IPs","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ips","docs\u002Fbot-detection\u002F04.checkers\u002F10.known-bad-ips",{"title":480,"path":481,"stem":482},"Behavior Rate","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbehavior-rate","docs\u002Fbot-detection\u002F04.checkers\u002F11.behavior-rate",{"title":484,"path":485,"stem":486},"Proxy \u002F ISP \u002F Cookie","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fproxy-isp-cookies","docs\u002Fbot-detection\u002F04.checkers\u002F12.proxy-isp-cookies",{"title":488,"path":489,"stem":490},"Session Coherence","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fsession-coherence","docs\u002Fbot-detection\u002F04.checkers\u002F13.session-coherence",{"title":492,"path":493,"stem":494},"Velocity Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fvelocity-fingerprint","docs\u002Fbot-detection\u002F04.checkers\u002F14.velocity-fingerprint",{"title":496,"path":497,"stem":498},"UA & Header Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fua-header","docs\u002Fbot-detection\u002F04.checkers\u002F15.ua-header",{"title":500,"path":501,"stem":502},"Geolocation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgeolocation","docs\u002Fbot-detection\u002F04.checkers\u002F16.geolocation",{"title":504,"path":505,"stem":506},"Known Bad User-Agents","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ua","docs\u002Fbot-detection\u002F04.checkers\u002F17.known-bad-ua",{"title":38,"path":508,"stem":509},"\u002Fdocs\u002Fbot-detection\u002Fsecurity","docs\u002Fbot-detection\u002F04.security",{"title":246,"path":511,"stem":512},"\u002Fdocs\u002Fbot-detection\u002Fapi","docs\u002Fbot-detection\u002F05.api",{"title":237,"path":514,"stem":515},"\u002Fdocs\u002Fbot-detection\u002Fconfiguration","docs\u002Fbot-detection\u002F06.configuration",{"title":517,"path":11,"stem":518,"children":519},"Introduction","docs\u002Fgetting-started\u002Findex",[520],{"title":517,"path":11,"stem":518},{"title":27,"path":29,"stem":70,"children":522},[523,524,525,565,566,571,572],{"title":27,"path":29,"stem":70},{"title":14,"path":74,"stem":75},{"title":77,"path":78,"stem":79,"children":526},[527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549],{"title":77,"path":78,"stem":79},{"title":83,"path":84,"stem":85},{"title":87,"path":88,"stem":89},{"title":91,"path":92,"stem":93},{"title":95,"path":96,"stem":97},{"title":99,"path":100,"stem":101},{"title":103,"path":104,"stem":105},{"title":107,"path":108,"stem":109},{"title":111,"path":112,"stem":113},{"title":115,"path":116,"stem":117},{"title":119,"path":120,"stem":121},{"title":123,"path":124,"stem":125},{"title":127,"path":128,"stem":129},{"title":131,"path":132,"stem":133},{"title":135,"path":136,"stem":137},{"title":139,"path":140,"stem":141},{"title":143,"path":144,"stem":145},{"title":147,"path":148,"stem":149},{"title":151,"path":152,"stem":153},{"title":155,"path":156,"stem":157},{"title":159,"path":160,"stem":161},{"title":163,"path":164,"stem":165},{"title":167,"path":168,"stem":169,"children":550},[551,552,553,554,563,564],{"title":167,"path":168,"stem":169},{"title":173,"path":174,"stem":175},{"title":177,"path":178,"stem":179},{"title":181,"path":182,"stem":183,"children":555},[556,557,558,559,560,561,562],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189},{"title":191,"path":192,"stem":193},{"title":195,"path":196,"stem":197},{"title":199,"path":200,"stem":201},{"title":203,"path":204,"stem":205},{"title":207,"path":208,"stem":209},{"title":147,"path":211,"stem":212},{"title":38,"path":214,"stem":215},{"title":38,"path":217,"stem":218},{"title":220,"path":221,"stem":222,"children":567,"page":53},[568,569,570],{"title":225,"path":226,"stem":227},{"title":229,"path":230,"stem":231},{"title":233,"path":234,"stem":235},{"title":237,"path":238,"stem":239},{"title":241,"path":242,"stem":243,"children":573,"page":53},[574,575,576],{"title":246,"path":247,"stem":248},{"title":250,"path":251,"stem":252},{"title":254,"path":255,"stem":256},{"title":40,"path":42,"stem":578,"children":579},"docs\u002Fshield-base\u002Findex",[580,581,584,588,629,633,637,641],{"title":40,"path":42,"stem":578},{"title":14,"path":582,"stem":583},"\u002Fdocs\u002Fshield-base\u002Fgetting-started","docs\u002Fshield-base\u002F00.getting-started",{"title":585,"path":586,"stem":587},"CLI Reference","\u002Fdocs\u002Fshield-base\u002Fcli","docs\u002Fshield-base\u002F01.cli",{"title":411,"path":589,"stem":590,"children":591},"\u002Fdocs\u002Fshield-base\u002Fdata-sources","docs\u002Fshield-base\u002F02.data-sources\u002Findex",[592,593,597,601,605,609,613,617,621,625],{"title":411,"path":589,"stem":590},{"title":594,"path":595,"stem":596},"BGP \u002F ASN","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fbgp","docs\u002Fshield-base\u002F02.data-sources\u002Fbgp",{"title":598,"path":599,"stem":600},"City Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcity","docs\u002Fshield-base\u002F02.data-sources\u002Fcity",{"title":602,"path":603,"stem":604},"Country Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcountry","docs\u002Fshield-base\u002F02.data-sources\u002Fcountry",{"title":606,"path":607,"stem":608},"Verified Crawlers","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcrawlers","docs\u002Fshield-base\u002F02.data-sources\u002Fcrawlers",{"title":610,"path":611,"stem":612},"Disposable Emails","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Femail","docs\u002Fshield-base\u002F02.data-sources\u002Femail",{"title":614,"path":615,"stem":616},"FireHOL Threat Intelligence","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ffirehol","docs\u002Fshield-base\u002F02.data-sources\u002Ffirehol",{"title":618,"path":619,"stem":620},"Proxy Detection","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fproxy","docs\u002Fshield-base\u002F02.data-sources\u002Fproxy",{"title":622,"path":623,"stem":624},"Tor Nodes","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ftor","docs\u002Fshield-base\u002F02.data-sources\u002Ftor",{"title":626,"path":627,"stem":628},"Suspicious User-Agents","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fuseragent","docs\u002Fshield-base\u002F02.data-sources\u002Fuseragent",{"title":630,"path":631,"stem":632},"Programmatic Usage","\u002Fdocs\u002Fshield-base\u002Fusage","docs\u002Fshield-base\u002F03.usage",{"title":634,"path":635,"stem":636},"Custom Data Sources","\u002Fdocs\u002Fshield-base\u002Fcustom-data-sources","docs\u002Fshield-base\u002F04.custom-data-sources",{"title":638,"path":639,"stem":640},"TypeScript Types","\u002Fdocs\u002Fshield-base\u002Ftypes","docs\u002Fshield-base\u002F05.types",{"title":246,"path":642,"stem":643},"\u002Fdocs\u002Fshield-base\u002Fapi","docs\u002Fshield-base\u002F06.api",{"title":395,"path":48,"stem":645,"children":646},"docs\u002Futils\u002Findex",[647,648,665,698,795],{"title":395,"path":48,"stem":645},{"title":649,"path":650,"stem":651,"children":652,"page":53},"Eslint","\u002Fdocs\u002Futils\u002Feslint","docs\u002Futils\u002Feslint",[653,657,661],{"title":654,"path":655,"stem":656},"React Config","\u002Fdocs\u002Futils\u002Feslint\u002Freact","docs\u002Futils\u002Feslint\u002Freact",{"title":658,"path":659,"stem":660},"TypeScript Config","\u002Fdocs\u002Futils\u002Feslint\u002Ftypescript","docs\u002Futils\u002Feslint\u002Ftypescript",{"title":662,"path":663,"stem":664},"Vue Config","\u002Fdocs\u002Futils\u002Feslint\u002Fvue","docs\u002Futils\u002Feslint\u002Fvue",{"title":666,"path":667,"stem":668,"children":669,"page":53},"Server","\u002Fdocs\u002Futils\u002Fserver","docs\u002Futils\u002Fserver",[670,674,678,682,686,690,694],{"title":671,"path":672,"stem":673},"Encryption","\u002Fdocs\u002Futils\u002Fserver\u002Fencryption","docs\u002Futils\u002Fserver\u002Fencryption",{"title":675,"path":676,"stem":677},"Path Resolver","\u002Fdocs\u002Futils\u002Fserver\u002Fpathresolver","docs\u002Futils\u002Fserver\u002FpathResolver",{"title":679,"path":680,"stem":681},"File Replacements","\u002Fdocs\u002Futils\u002Fserver\u002Freplace","docs\u002Futils\u002Fserver\u002Freplace",{"title":683,"path":684,"stem":685},"run","\u002Fdocs\u002Futils\u002Fserver\u002Frun","docs\u002Futils\u002Fserver\u002Frun",{"title":687,"path":688,"stem":689},"scheduleTask","\u002Fdocs\u002Futils\u002Fserver\u002Fscheduletask","docs\u002Futils\u002Fserver\u002FscheduleTask",{"title":691,"path":692,"stem":693},"spawnRun","\u002Fdocs\u002Futils\u002Fserver\u002Fspawnrun","docs\u002Futils\u002Fserver\u002FspawnRun",{"title":695,"path":696,"stem":697},"uploadCsv","\u002Fdocs\u002Futils\u002Fserver\u002Fuploadcsv","docs\u002Futils\u002Fserver\u002FuploadCsv",{"title":699,"path":700,"stem":701,"children":702,"page":53},"Shared","\u002Fdocs\u002Futils\u002Fshared","docs\u002Futils\u002Fshared",[703,707,711,715,719,723,727,731,735,739,743,747,751,755,759,763,767,771,775,779,783,787,791],{"title":704,"path":705,"stem":706},"BatchQueue","\u002Fdocs\u002Futils\u002Fshared\u002Fbatchqueue","docs\u002Futils\u002Fshared\u002FbatchQueue",{"title":708,"path":709,"stem":710},"capitalize","\u002Fdocs\u002Futils\u002Fshared\u002Fcapitalize","docs\u002Futils\u002Fshared\u002Fcapitalize",{"title":712,"path":713,"stem":714},"chunkProcess","\u002Fdocs\u002Futils\u002Fshared\u002Fchunkprocess","docs\u002Futils\u002Fshared\u002FchunkProcess",{"title":716,"path":717,"stem":718},"cleanObject","\u002Fdocs\u002Futils\u002Fshared\u002Fcleanobject","docs\u002Futils\u002Fshared\u002FcleanObject",{"title":720,"path":721,"stem":722},"createConfigManager","\u002Fdocs\u002Futils\u002Fshared\u002Fconfigurationdefiner","docs\u002Futils\u002Fshared\u002FconfigurationDefiner",{"title":724,"path":725,"stem":726},"debounce","\u002Fdocs\u002Futils\u002Fshared\u002Fdebounce","docs\u002Futils\u002Fshared\u002Fdebounce",{"title":728,"path":729,"stem":730},"ensureArray","\u002Fdocs\u002Futils\u002Fshared\u002Fensurearray","docs\u002Futils\u002Fshared\u002FensureArray",{"title":732,"path":733,"stem":734},"fetchWithRetry","\u002Fdocs\u002Futils\u002Fshared\u002Ffetchwithretry","docs\u002Futils\u002Fshared\u002FfetchWithRetry",{"title":736,"path":737,"stem":738},"filterEmptyValues","\u002Fdocs\u002Futils\u002Fshared\u002Ffilteremptyvalues","docs\u002Futils\u002Fshared\u002FfilterEmptyValues",{"title":740,"path":741,"stem":742},"findStringsInObject","\u002Fdocs\u002Futils\u002Fshared\u002Ffindobjectvalues","docs\u002Futils\u002Fshared\u002FfindObjectValues",{"title":744,"path":745,"stem":746},"fisherYatesShuffle","\u002Fdocs\u002Futils\u002Fshared\u002Ffisheryatesshuffle","docs\u002Futils\u002Fshared\u002FfisherYatesShuffle",{"title":748,"path":749,"stem":750},"getRandomImage","\u002Fdocs\u002Futils\u002Fshared\u002Fgetrandomimage","docs\u002Futils\u002Fshared\u002FgetRandomImage",{"title":752,"path":753,"stem":754},"isObjectHasValues","\u002Fdocs\u002Futils\u002Fshared\u002Fisobjecthasvalues","docs\u002Futils\u002Fshared\u002FisObjectHasValues",{"title":756,"path":757,"stem":758},"isAsyncOrPromise","\u002Fdocs\u002Futils\u002Fshared\u002Fispromise","docs\u002Futils\u002Fshared\u002FisPromise",{"title":760,"path":761,"stem":762},"MiniCache","\u002Fdocs\u002Futils\u002Fshared\u002Fminicache","docs\u002Futils\u002Fshared\u002FminiCache",{"title":764,"path":765,"stem":766},"parseCookies","\u002Fdocs\u002Futils\u002Fshared\u002Fparserawcookies","docs\u002Futils\u002Fshared\u002FparseRawCookies",{"title":768,"path":769,"stem":770},"safeAction","\u002Fdocs\u002Futils\u002Fshared\u002Fpromiselocker","docs\u002Futils\u002Fshared\u002FpromiseLocker",{"title":772,"path":773,"stem":774},"Random","\u002Fdocs\u002Futils\u002Fshared\u002Frandom","docs\u002Futils\u002Fshared\u002Frandom",{"title":776,"path":777,"stem":778},"range","\u002Fdocs\u002Futils\u002Fshared\u002Frange","docs\u002Futils\u002Fshared\u002Frange",{"title":780,"path":781,"stem":782},"rateLimiters","\u002Fdocs\u002Futils\u002Fshared\u002Fratelimiters","docs\u002Futils\u002Fshared\u002FrateLimiters",{"title":784,"path":785,"stem":786},"safeObjectMerge","\u002Fdocs\u002Futils\u002Fshared\u002Fsafemerge","docs\u002Futils\u002Fshared\u002FsafeMerge",{"title":788,"path":789,"stem":790},"textTruncation","\u002Fdocs\u002Futils\u002Fshared\u002Ftexttruncation","docs\u002Futils\u002Fshared\u002FtextTruncation",{"title":792,"path":793,"stem":794},"validateZodSchema","\u002Fdocs\u002Futils\u002Fshared\u002Fvalidatezodschema","docs\u002Futils\u002Fshared\u002FvalidateZodSchema",{"title":796,"path":797,"stem":798,"children":799},"Utility Types","\u002Fdocs\u002Futils\u002Ftypes","docs\u002Futils\u002Ftypes\u002Findex",[800,801,805,809,813,817,821,825,829,833],{"title":796,"path":797,"stem":798},{"title":802,"path":803,"stem":804},"Brand","\u002Fdocs\u002Futils\u002Ftypes\u002Fbrand","docs\u002Futils\u002Ftypes\u002FBrand",{"title":806,"path":807,"stem":808},"DeepPartial","\u002Fdocs\u002Futils\u002Ftypes\u002Fdeeppartial","docs\u002Futils\u002Ftypes\u002FDeepPartial",{"title":810,"path":811,"stem":812},"Merge","\u002Fdocs\u002Futils\u002Ftypes\u002Fmerge","docs\u002Futils\u002Ftypes\u002FMerge",{"title":814,"path":815,"stem":816},"NonNullable","\u002Fdocs\u002Futils\u002Ftypes\u002Fnonnullable","docs\u002Futils\u002Ftypes\u002FNonNullable",{"title":818,"path":819,"stem":820},"Prettify","\u002Fdocs\u002Futils\u002Ftypes\u002Fprettify","docs\u002Futils\u002Ftypes\u002FPrettify",{"title":822,"path":823,"stem":824},"PromiseType","\u002Fdocs\u002Futils\u002Ftypes\u002Fpromisetype","docs\u002Futils\u002Ftypes\u002FPromiseType",{"title":826,"path":827,"stem":828},"RequireKeys","\u002Fdocs\u002Futils\u002Ftypes\u002Frequirekeys","docs\u002Futils\u002Ftypes\u002FRequireKeys",{"title":830,"path":831,"stem":832},"StandardResponse","\u002Fdocs\u002Futils\u002Ftypes\u002Fstandardresponse","docs\u002Futils\u002Ftypes\u002FStandardResponse",{"title":834,"path":835,"stem":836},"ValueOf","\u002Fdocs\u002Futils\u002Ftypes\u002Fvalueof","docs\u002Futils\u002Ftypes\u002FValueOf",{"id":4,"extension":5,"links":838,"meta":849,"stem":62,"__hash__":63},[839,847,848],{"nested":8,"label":9,"icon":10,"to":11,"children":840},[841,842,843,844,845,846],{"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":851,"title":147,"body":852,"description":6275,"extension":6276,"icon":6277,"meta":6278,"module":6279,"navigation":8,"path":148,"rawbody":6280,"seo":6281,"stem":149,"__hash__":6282},"docs\u002Fdocs\u002Fiam\u002F01.essentials\u002F16.rate-limiting.md",{"type":853,"value":854,"toc":6232},"minimark",[855,872,896,921,924,929,934,937,990,993,997,1011,1030,1036,1038,1042,1049,1055,1066,1301,1352,1357,1575,1581,1584,1810,1840,1847,1891,1897,1901,1910,2126,2233,2237,2329,2342,2347,2360,2475,2478,2525,2543,2549,2552,2614,2636,2645,2650,2653,2724,2742,2748,2750,2754,2757,2761,2768,2822,2826,2829,2876,2880,2883,2970,2974,2981,3033,3038,3040,3044,3053,3055,3058,3089,3092,3159,3163,3166,3195,3202,3280,3283,3288,3291,3294,3325,3328,3331,3366,3370,3377,3418,3422,3425,3464,3468,3473,3477,3480,3510,3512,3516,3522,3525,3553,3559,3566,3678,3684,3690,3811,3817,3822,3926,3932,3938,4061,4070,4076,4079,4138,4144,4148,4247,4253,4256,4375,4385,4391,4394,4498,4500,4504,4509,6047,6049,6053,6056,6099,6110,6112,6116,6228],[856,857,858,859,866,867,871],"p",{},"The IAM service uses ",[860,861,865],"a",{"href":862,"rel":863},"https:\u002F\u002Fgithub.com\u002Fanimir\u002Fnode-rate-limiter-flexible",[864],"nofollow","rate-limiter-flexible"," for all rate limiting. Every sensitive endpoint has its own named limiter group built at startup from the ",[868,869,870],"code",{},"rate_limiters"," section of the service configuration. The built-in limiters store state in MySQL, via a dedicated callback-based pool separate from the main auth pool, and maintain in-memory mirrors for fast lookups.",[856,873,874,875,879,880,883,884,887,888,891,892,895],{},"The system layers multiple limiters per endpoint. A typical endpoint has a ",[876,877,878],"strong",{},"union limiter"," combining a burst and slow limiter into a single gate, an ",[876,881,882],{},"IP limiter",", and an ",[876,885,886],{},"identity limiter"," (keyed on email, OAuth subject, token hash, or user ID). All layers must pass for a request to proceed. If any layer rejects, the service returns ",[868,889,890],{},"429 Too Many Requests"," with a ",[868,893,894],{},"Retry-After"," header.",[856,897,898,899,902,903,908,909,912,913,916,917,920],{},"On top of the limiter layer, a ",[876,900,901],{},"strike system"," tracks consecutive failures per key in an ",[860,904,907],{"href":905,"rel":906},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCache_replacement_policies#Least_recently_used_(LRU)",[864],"LRU cache",". When a key accumulates enough strikes (the ",[868,910,911],{},"maxBans"," threshold), the ",[868,914,915],{},"guard"," function permanently blocks the key via the limiter's ",[868,918,919],{},"block()"," method and adds it to a global block cache. Blocked keys are rejected immediately on subsequent requests without consuming limiter points.",[922,923],"hr",{},[925,926,928],"h2",{"id":927},"overview","Overview",[930,931,933],"h3",{"id":932},"dual-layer-design","Dual-Layer Design",[856,935,936],{},"Each rate limiter in the system operates on two storage layers simultaneously:",[938,939,940,956],"table",{},[941,942,943],"thead",{},[944,945,946,950,953],"tr",{},[947,948,949],"th",{},"Layer",[947,951,952],{},"Storage",[947,954,955],{},"Purpose",[957,958,959,973],"tbody",{},[944,960,961,967,970],{},[962,963,964],"td",{},[876,965,966],{},"Primary",[962,968,969],{},"MySQL",[962,971,972],{},"Persistent state. Survives process restarts. Shared across multiple IAM instances behind a load balancer.",[944,974,975,980,983],{},[962,976,977],{},[876,978,979],{},"In-memory",[962,981,982],{},"Node.js process memory",[962,984,985,986,989],{},"Fast path. Blocks requests locally when ",[868,987,988],{},"inMemoryBlockOnConsumed"," is exceeded, without hitting the database.",[856,991,992],{},"The in-memory layer acts as an insurance mechanism. If the MySQL connection fails, the in-memory limiter continues to enforce rate limits. If both layers are healthy, the in-memory layer short-circuits obvious violations before they reach the database.",[930,994,996],{"id":995},"storage-pool","Storage Pool",[856,998,999,1000,1003,1004,1006,1007,1010],{},"The rate limiter pool is separate from the main authentication pool. It uses the callback-based ",[868,1001,1002],{},"mysql"," API because ",[868,1005,865],{}," requires a callback-style connection. Configure it in the ",[868,1008,1009],{},"store.rate_limiters_pool"," section:",[1012,1013,1014,1023],"field-group",{},[1015,1016,1020],"field",{"name":1017,"type":1018,":required":1019},"store.rate_limiters_pool.store","mysql.PoolOptions","true",[856,1021,1022],{},"MySQL pool options for the rate limiter connection. Same format as the main pool but typically points to a separate database.",[1015,1024,1027],{"name":1025,"type":1026,":required":1019},"store.rate_limiters_pool.dbName","string",[856,1028,1029],{},"Database name for rate limiter tables. The service uses this value to create the database if it does not exist.",[856,1031,1032,1033,1035],{},"See ",[860,1034,151],{"href":152}," for more details.",[922,1037],{},[925,1039,1041],{"id":1040},"core-utilities","Core Utilities",[856,1043,1044,1045,1048],{},"The rate limiting system is built from four composable functions. When using the IAM service as a library, all four are exported from ",[868,1046,1047],{},"@riavzon\u002Fauth",".",[930,1050,1052],{"id":1051},"makeratelimiter",[868,1053,1054],{},"makeRateLimiter",[856,1056,1057,1058,1061,1062,1065],{},"Creates a single rate limiter instance. The ",[868,1059,1060],{},"sql"," parameter controls whether the limiter persists state in MySQL or runs purely in memory. The ",[868,1063,1064],{},"BlackWhiteList"," parameter wraps the limiter with an allow\u002Fdeny list layer.",[1067,1068,1073],"pre",{"className":1069,"code":1070,"language":1071,"meta":1072,"style":1072},"language-ts shiki shiki-themes light-plus light-plus dracula","import { makeRateLimiter } from '@riavzon\u002Fauth'\n\nconst limiter = makeRateLimiter(true, false, {\n  dbName: 'rate_limiters',\n  storeClient: pool,\n  storeType: 'mysql2',\n  tableName: 'login',\n  keyPrefix: 'login_burst',\n  points: 1,\n  duration: 1,\n  blockDuration: 1800,\n  inMemoryBlockOnConsumed: 2,\n  inMemoryBlockDuration: 1800,\n})\n","ts","",[868,1074,1075,1107,1113,1147,1167,1180,1197,1214,1231,1245,1257,1270,1283,1295],{"__ignoreMap":1072},[1076,1077,1080,1084,1088,1091,1094,1097,1101,1104],"span",{"class":1078,"line":1079},"line",1,[1076,1081,1083],{"class":1082},"sZ328","import",[1076,1085,1087],{"class":1086},"sDd4n"," { ",[1076,1089,1054],{"class":1090},"sjsA6",[1076,1092,1093],{"class":1086}," } ",[1076,1095,1096],{"class":1082},"from",[1076,1098,1100],{"class":1099},"sFkSl"," '",[1076,1102,1047],{"class":1103},"sFB1V",[1076,1105,1106],{"class":1099},"'\n",[1076,1108,1110],{"class":1078,"line":1109},2,[1076,1111,1112],{"emptyLinePlaceholder":8},"\n",[1076,1114,1116,1120,1124,1128,1132,1135,1138,1141,1144],{"class":1078,"line":1115},3,[1076,1117,1119],{"class":1118},"sl46w","const",[1076,1121,1123],{"class":1122},"s3JHE"," limiter",[1076,1125,1127],{"class":1126},"saOXh"," =",[1076,1129,1131],{"class":1130},"sHOzp"," makeRateLimiter",[1076,1133,1134],{"class":1086},"(",[1076,1136,1019],{"class":1137},"sjR7W",[1076,1139,1140],{"class":1086},", ",[1076,1142,1143],{"class":1137},"false",[1076,1145,1146],{"class":1086},", {\n",[1076,1148,1150,1153,1157,1159,1161,1164],{"class":1078,"line":1149},4,[1076,1151,1152],{"class":1090},"  dbName",[1076,1154,1156],{"class":1155},"s34zl",":",[1076,1158,1100],{"class":1099},[1076,1160,870],{"class":1103},[1076,1162,1163],{"class":1099},"'",[1076,1165,1166],{"class":1086},",\n",[1076,1168,1170,1173,1175,1178],{"class":1078,"line":1169},5,[1076,1171,1172],{"class":1090},"  storeClient",[1076,1174,1156],{"class":1155},[1076,1176,1177],{"class":1090}," pool",[1076,1179,1166],{"class":1086},[1076,1181,1183,1186,1188,1190,1193,1195],{"class":1078,"line":1182},6,[1076,1184,1185],{"class":1090},"  storeType",[1076,1187,1156],{"class":1155},[1076,1189,1100],{"class":1099},[1076,1191,1192],{"class":1103},"mysql2",[1076,1194,1163],{"class":1099},[1076,1196,1166],{"class":1086},[1076,1198,1200,1203,1205,1207,1210,1212],{"class":1078,"line":1199},7,[1076,1201,1202],{"class":1090},"  tableName",[1076,1204,1156],{"class":1155},[1076,1206,1100],{"class":1099},[1076,1208,1209],{"class":1103},"login",[1076,1211,1163],{"class":1099},[1076,1213,1166],{"class":1086},[1076,1215,1217,1220,1222,1224,1227,1229],{"class":1078,"line":1216},8,[1076,1218,1219],{"class":1090},"  keyPrefix",[1076,1221,1156],{"class":1155},[1076,1223,1100],{"class":1099},[1076,1225,1226],{"class":1103},"login_burst",[1076,1228,1163],{"class":1099},[1076,1230,1166],{"class":1086},[1076,1232,1234,1237,1239,1243],{"class":1078,"line":1233},9,[1076,1235,1236],{"class":1090},"  points",[1076,1238,1156],{"class":1155},[1076,1240,1242],{"class":1241},"spgvN"," 1",[1076,1244,1166],{"class":1086},[1076,1246,1248,1251,1253,1255],{"class":1078,"line":1247},10,[1076,1249,1250],{"class":1090},"  duration",[1076,1252,1156],{"class":1155},[1076,1254,1242],{"class":1241},[1076,1256,1166],{"class":1086},[1076,1258,1260,1263,1265,1268],{"class":1078,"line":1259},11,[1076,1261,1262],{"class":1090},"  blockDuration",[1076,1264,1156],{"class":1155},[1076,1266,1267],{"class":1241}," 1800",[1076,1269,1166],{"class":1086},[1076,1271,1273,1276,1278,1281],{"class":1078,"line":1272},12,[1076,1274,1275],{"class":1090},"  inMemoryBlockOnConsumed",[1076,1277,1156],{"class":1155},[1076,1279,1280],{"class":1241}," 2",[1076,1282,1166],{"class":1086},[1076,1284,1286,1289,1291,1293],{"class":1078,"line":1285},13,[1076,1287,1288],{"class":1090},"  inMemoryBlockDuration",[1076,1290,1156],{"class":1155},[1076,1292,1267],{"class":1241},[1076,1294,1166],{"class":1086},[1076,1296,1298],{"class":1078,"line":1297},14,[1076,1299,1300],{"class":1086},"})\n",[1012,1302,1303,1325,1340],{},[1015,1304,1306],{"name":1060,"type":1305,":required":1019},"boolean",[856,1307,1308,1309,1311,1312,1315,1316,1319,1320,1322,1323,1048],{},"When ",[868,1310,1019],{},", creates a ",[868,1313,1314],{},"RateLimiterMySQL"," backed by the provided MySQL pool with an in-memory ",[868,1317,1318],{},"RateLimiterMemory"," as the insurance fallback. When ",[868,1321,1143],{},", creates a pure ",[868,1324,1318],{},[1015,1326,1327],{"name":1064,"type":1305,":required":1019},[856,1328,1308,1329,1331,1332,1335,1336,1339],{},[868,1330,1019],{},", wraps the limiter in ",[868,1333,1334],{},"RLWrapperBlackAndWhite",". Whitelisted keys bypass rate limiting entirely. The default whitelist check matches ",[868,1337,1338],{},"10.10.10.10"," (a test IP).",[1015,1341,1344],{"name":1342,"type":1343,":required":1019},"settings","object",[856,1345,1346,1347,1349,1350,1048],{},"Limiter configuration. Contains both the core rate-limiter-flexible fields and MySQL-specific fields which is required when ",[868,1348,1060],{}," is ",[868,1351,1019],{},[1353,1354,1356],"h4",{"id":1355},"settings-fields","Settings Fields",[938,1358,1359,1375],{},[941,1360,1361],{},[944,1362,1363,1366,1369,1372],{},[947,1364,1365],{},"Field",[947,1367,1368],{},"Type",[947,1370,1371],{},"Required",[947,1373,1374],{},"Description",[957,1376,1377,1394,1415,1434,1454,1473,1491,1510,1532,1554],{},[944,1378,1379,1384,1388,1391],{},[962,1380,1381],{},[868,1382,1383],{},"keyPrefix",[962,1385,1386],{},[868,1387,1026],{},[962,1389,1390],{},"Yes",[962,1392,1393],{},"Unique prefix for this limiter in the store. Used as the MySQL table column key and the in-memory map key.",[944,1395,1396,1401,1406,1408],{},[962,1397,1398],{},[868,1399,1400],{},"points",[962,1402,1403],{},[868,1404,1405],{},"number",[962,1407,1390],{},[962,1409,1410,1411,1414],{},"Maximum number of requests allowed within the ",[868,1412,1413],{},"duration"," window.",[944,1416,1417,1421,1425,1427],{},[962,1418,1419],{},[868,1420,1413],{},[962,1422,1423],{},[868,1424,1405],{},[962,1426,1390],{},[962,1428,1429,1430,1433],{},"Time window in ",[876,1431,1432],{},"seconds",". Points reset after this period.",[944,1435,1436,1441,1445,1447],{},[962,1437,1438],{},[868,1439,1440],{},"blockDuration",[962,1442,1443],{},[868,1444,1405],{},[962,1446,1390],{},[962,1448,1449,1450,1453],{},"How long, in seconds, to block the key after all points are consumed. ",[868,1451,1452],{},"0"," means permanent block.",[944,1455,1456,1460,1464,1467],{},[962,1457,1458],{},[868,1459,988],{},[962,1461,1462],{},[868,1463,1405],{},[962,1465,1466],{},"No",[962,1468,1469,1470,1472],{},"When consumed points reach this threshold, the in-memory layer blocks the key without checking MySQL. Typically set slightly above ",[868,1471,1400],{}," to catch bursts.",[944,1474,1475,1480,1484,1486],{},[962,1476,1477],{},[868,1478,1479],{},"inMemoryBlockDuration",[962,1481,1482],{},[868,1483,1405],{},[962,1485,1466],{},[962,1487,1488,1489,1048],{},"How long, in seconds, the in-memory block lasts. Usually matches ",[868,1490,1440],{},[944,1492,1493,1498,1502,1507],{},[962,1494,1495],{},[868,1496,1497],{},"dbName",[962,1499,1500],{},[868,1501,1026],{},[962,1503,1308,1504],{},[868,1505,1506],{},"sql: true",[962,1508,1509],{},"The MySQL database name for this limiter's storage.",[944,1511,1512,1517,1522,1526],{},[962,1513,1514],{},[868,1515,1516],{},"storeClient",[962,1518,1519],{},[868,1520,1521],{},"Pool",[962,1523,1308,1524],{},[868,1525,1506],{},[962,1527,1528,1529,1048],{},"The callback-based MySQL pool from ",[868,1530,1531],{},"poolForLibrary()",[944,1533,1534,1539,1543,1547],{},[962,1535,1536],{},[868,1537,1538],{},"storeType",[962,1540,1541],{},[868,1542,1026],{},[962,1544,1308,1545],{},[868,1546,1506],{},[962,1548,1549,1550,1553],{},"Always ",[868,1551,1552],{},"'mysql2'",". Identifies the pool type for rate-limiter-flexible.",[944,1555,1556,1561,1565,1569],{},[962,1557,1558],{},[868,1559,1560],{},"tableName",[962,1562,1563],{},[868,1564,1026],{},[962,1566,1308,1567],{},[868,1568,1506],{},[962,1570,1571,1572,1574],{},"The MySQL table name where this limiter stores its counters. Multiple limiters can share a table if they have different ",[868,1573,1383],{}," values.",[930,1576,1578],{"id":1577},"unionlimiter",[868,1579,1580],{},"unionLimiter",[856,1582,1583],{},"Combines two or more individual limiters into a single gate. Both limiters in the union are consumed on every attempt. If either limiter rejects, the request is blocked.",[1067,1585,1587],{"className":1069,"code":1586,"language":1071,"meta":1072,"style":1072},"import { makeRateLimiter, unionLimiter } from '@riavzon\u002Fauth'\n\nconst burst = makeRateLimiter(true, false, {\n  keyPrefix: 'login_burst',\n  points: 1,\n  duration: 1,\n  blockDuration: 1800,\n  \u002F\u002F ... other fields\n})\n\nconst slow = makeRateLimiter(true, false, {\n  keyPrefix: 'login_slow',\n  points: 5,\n  duration: 3600,\n  blockDuration: 1800,\n  \u002F\u002F ... other fields\n})\n\nconst loginUnion = unionLimiter([burst, slow], false)\n",[868,1588,1589,1611,1615,1636,1650,1660,1670,1680,1686,1690,1694,1715,1730,1741,1752,1763,1768,1773,1778],{"__ignoreMap":1072},[1076,1590,1591,1593,1595,1597,1599,1601,1603,1605,1607,1609],{"class":1078,"line":1079},[1076,1592,1083],{"class":1082},[1076,1594,1087],{"class":1086},[1076,1596,1054],{"class":1090},[1076,1598,1140],{"class":1086},[1076,1600,1580],{"class":1090},[1076,1602,1093],{"class":1086},[1076,1604,1096],{"class":1082},[1076,1606,1100],{"class":1099},[1076,1608,1047],{"class":1103},[1076,1610,1106],{"class":1099},[1076,1612,1613],{"class":1078,"line":1109},[1076,1614,1112],{"emptyLinePlaceholder":8},[1076,1616,1617,1619,1622,1624,1626,1628,1630,1632,1634],{"class":1078,"line":1115},[1076,1618,1119],{"class":1118},[1076,1620,1621],{"class":1122}," burst",[1076,1623,1127],{"class":1126},[1076,1625,1131],{"class":1130},[1076,1627,1134],{"class":1086},[1076,1629,1019],{"class":1137},[1076,1631,1140],{"class":1086},[1076,1633,1143],{"class":1137},[1076,1635,1146],{"class":1086},[1076,1637,1638,1640,1642,1644,1646,1648],{"class":1078,"line":1149},[1076,1639,1219],{"class":1090},[1076,1641,1156],{"class":1155},[1076,1643,1100],{"class":1099},[1076,1645,1226],{"class":1103},[1076,1647,1163],{"class":1099},[1076,1649,1166],{"class":1086},[1076,1651,1652,1654,1656,1658],{"class":1078,"line":1169},[1076,1653,1236],{"class":1090},[1076,1655,1156],{"class":1155},[1076,1657,1242],{"class":1241},[1076,1659,1166],{"class":1086},[1076,1661,1662,1664,1666,1668],{"class":1078,"line":1182},[1076,1663,1250],{"class":1090},[1076,1665,1156],{"class":1155},[1076,1667,1242],{"class":1241},[1076,1669,1166],{"class":1086},[1076,1671,1672,1674,1676,1678],{"class":1078,"line":1199},[1076,1673,1262],{"class":1090},[1076,1675,1156],{"class":1155},[1076,1677,1267],{"class":1241},[1076,1679,1166],{"class":1086},[1076,1681,1682],{"class":1078,"line":1216},[1076,1683,1685],{"class":1684},"sghk6","  \u002F\u002F ... other fields\n",[1076,1687,1688],{"class":1078,"line":1233},[1076,1689,1300],{"class":1086},[1076,1691,1692],{"class":1078,"line":1247},[1076,1693,1112],{"emptyLinePlaceholder":8},[1076,1695,1696,1698,1701,1703,1705,1707,1709,1711,1713],{"class":1078,"line":1259},[1076,1697,1119],{"class":1118},[1076,1699,1700],{"class":1122}," slow",[1076,1702,1127],{"class":1126},[1076,1704,1131],{"class":1130},[1076,1706,1134],{"class":1086},[1076,1708,1019],{"class":1137},[1076,1710,1140],{"class":1086},[1076,1712,1143],{"class":1137},[1076,1714,1146],{"class":1086},[1076,1716,1717,1719,1721,1723,1726,1728],{"class":1078,"line":1272},[1076,1718,1219],{"class":1090},[1076,1720,1156],{"class":1155},[1076,1722,1100],{"class":1099},[1076,1724,1725],{"class":1103},"login_slow",[1076,1727,1163],{"class":1099},[1076,1729,1166],{"class":1086},[1076,1731,1732,1734,1736,1739],{"class":1078,"line":1285},[1076,1733,1236],{"class":1090},[1076,1735,1156],{"class":1155},[1076,1737,1738],{"class":1241}," 5",[1076,1740,1166],{"class":1086},[1076,1742,1743,1745,1747,1750],{"class":1078,"line":1297},[1076,1744,1250],{"class":1090},[1076,1746,1156],{"class":1155},[1076,1748,1749],{"class":1241}," 3600",[1076,1751,1166],{"class":1086},[1076,1753,1755,1757,1759,1761],{"class":1078,"line":1754},15,[1076,1756,1262],{"class":1090},[1076,1758,1156],{"class":1155},[1076,1760,1267],{"class":1241},[1076,1762,1166],{"class":1086},[1076,1764,1766],{"class":1078,"line":1765},16,[1076,1767,1685],{"class":1684},[1076,1769,1771],{"class":1078,"line":1770},17,[1076,1772,1300],{"class":1086},[1076,1774,1776],{"class":1078,"line":1775},18,[1076,1777,1112],{"emptyLinePlaceholder":8},[1076,1779,1781,1783,1786,1788,1791,1794,1797,1799,1802,1805,1807],{"class":1078,"line":1780},19,[1076,1782,1119],{"class":1118},[1076,1784,1785],{"class":1122}," loginUnion",[1076,1787,1127],{"class":1126},[1076,1789,1790],{"class":1130}," unionLimiter",[1076,1792,1793],{"class":1086},"([",[1076,1795,1796],{"class":1090},"burst",[1076,1798,1140],{"class":1086},[1076,1800,1801],{"class":1090},"slow",[1076,1803,1804],{"class":1086},"], ",[1076,1806,1143],{"class":1137},[1076,1808,1809],{"class":1086},")\n",[1012,1811,1812,1819],{},[1015,1813,1816],{"name":1814,"type":1815,":required":1019},"limiters","Array\u003CRateLimiterMemory | RateLimiterMySQL>",[856,1817,1818],{},"The individual limiters to combine. Typically a burst limiter (low points, short duration) and a slow limiter (higher points, longer duration).",[1015,1820,1822],{"name":1821,"type":1305,":required":1019},"blackWhiteList",[856,1823,1308,1824,1826,1827,1829,1830,1832,1833,1836,1837,1839],{},[868,1825,1019],{},", wraps the union in ",[868,1828,1334],{},". The ",[868,1831,919],{}," and ",[868,1834,1835],{},"delete()"," methods from the underlying union are forwarded to the wrapper so the ",[868,1838,915],{}," function can still block and reset keys.",[856,1841,1842,1843,1846],{},"The returned object extends ",[868,1844,1845],{},"RateLimiterUnion"," with two additional methods:",[938,1848,1849,1858],{},[941,1850,1851],{},[944,1852,1853,1856],{},[947,1854,1855],{},"Method",[947,1857,1374],{},[957,1859,1860,1878],{},[944,1861,1862,1867],{},[962,1863,1864],{},[868,1865,1866],{},"block(key, durationSec)",[962,1868,1869,1870,1873,1874,1877],{},"Blocks the key on ",[876,1871,1872],{},"all"," underlying limiters simultaneously. ",[868,1875,1876],{},"durationSec = 0"," means permanent.",[944,1879,1880,1885],{},[962,1881,1882],{},[868,1883,1884],{},"delete(key)",[962,1886,1887,1888,1890],{},"Deletes the key from ",[876,1889,1872],{}," underlying limiters, resetting their state.",[1892,1893,1894],"tip",{},[856,1895,1896],{},"The burst + slow pairing is the foundation of every built-in limiter group. The burst limiter catches automated flooding (e.g., 1 request per second). The slow limiter catches distributed or low-rate credential stuffing (e.g., 5 attempts per hour). A legitimate user who types slowly will never hit either limit. An attacker who spreads attempts over time will hit the slow limiter even if they avoid the burst threshold.",[930,1898,1899],{"id":915},[868,1900,915],{},[856,1902,1903,1904,1906,1907,1909],{},"The primary entry point for applying rate limiting in a controller. It combines limiter consumption, consecutive failure tracking, and permanent blocking into a single call. Returns ",[868,1905,1019],{}," if the request is allowed, ",[868,1908,1143],{}," if rate limited.",[1067,1911,1913],{"className":1069,"code":1912,"language":1071,"meta":1072,"style":1072},"import { guard, makeConsecutiveCache } from '@riavzon\u002Fauth'\n\nconst consecutiveCache = makeConsecutiveCache\u003C{ countData: number }>(500, 60_000)\n\nconst allowed = await guard(\n  limiter,                \u002F\u002F Any limiter: Memory, MySQL, or BlockableUnion\n  req.ip!,                \u002F\u002F The key to rate limit on\n  consecutiveCache,       \u002F\u002F LRU cache tracking consecutive failures\n  2,                      \u002F\u002F maxBans: strikes before permanent block\n  'ip',                   \u002F\u002F Label for log messages\n  log,                    \u002F\u002F Pino logger\n  res,                    \u002F\u002F Express response (guard sends 429 automatically)\n  3600                    \u002F\u002F Optional: block duration in seconds (0 = permanent)\n)\n\nif (!allowed) return      \u002F\u002F Response already sent\n",[868,1914,1915,1938,1942,1979,1983,2001,2012,2030,2041,2052,2067,2078,2088,2096,2100,2104],{"__ignoreMap":1072},[1076,1916,1917,1919,1921,1923,1925,1928,1930,1932,1934,1936],{"class":1078,"line":1079},[1076,1918,1083],{"class":1082},[1076,1920,1087],{"class":1086},[1076,1922,915],{"class":1090},[1076,1924,1140],{"class":1086},[1076,1926,1927],{"class":1090},"makeConsecutiveCache",[1076,1929,1093],{"class":1086},[1076,1931,1096],{"class":1082},[1076,1933,1100],{"class":1099},[1076,1935,1047],{"class":1103},[1076,1937,1106],{"class":1099},[1076,1939,1940],{"class":1078,"line":1109},[1076,1941,1112],{"emptyLinePlaceholder":8},[1076,1943,1944,1946,1949,1951,1954,1957,1960,1962,1966,1969,1972,1974,1977],{"class":1078,"line":1115},[1076,1945,1119],{"class":1118},[1076,1947,1948],{"class":1122}," consecutiveCache",[1076,1950,1127],{"class":1126},[1076,1952,1953],{"class":1130}," makeConsecutiveCache",[1076,1955,1956],{"class":1086},"\u003C{ ",[1076,1958,1959],{"class":1090},"countData",[1076,1961,1156],{"class":1126},[1076,1963,1965],{"class":1964},"sFs1U"," number",[1076,1967,1968],{"class":1086}," }>(",[1076,1970,1971],{"class":1241},"500",[1076,1973,1140],{"class":1086},[1076,1975,1976],{"class":1241},"60_000",[1076,1978,1809],{"class":1086},[1076,1980,1981],{"class":1078,"line":1149},[1076,1982,1112],{"emptyLinePlaceholder":8},[1076,1984,1985,1987,1990,1992,1995,1998],{"class":1078,"line":1169},[1076,1986,1119],{"class":1118},[1076,1988,1989],{"class":1122}," allowed",[1076,1991,1127],{"class":1126},[1076,1993,1994],{"class":1082}," await",[1076,1996,1997],{"class":1130}," guard",[1076,1999,2000],{"class":1086},"(\n",[1076,2002,2003,2006,2009],{"class":1078,"line":1182},[1076,2004,2005],{"class":1090},"  limiter",[1076,2007,2008],{"class":1086},",                ",[1076,2010,2011],{"class":1684},"\u002F\u002F Any limiter: Memory, MySQL, or BlockableUnion\n",[1076,2013,2014,2017,2019,2022,2025,2027],{"class":1078,"line":1199},[1076,2015,2016],{"class":1090},"  req",[1076,2018,1048],{"class":1086},[1076,2020,2021],{"class":1090},"ip",[1076,2023,2024],{"class":1126},"!",[1076,2026,2008],{"class":1086},[1076,2028,2029],{"class":1684},"\u002F\u002F The key to rate limit on\n",[1076,2031,2032,2035,2038],{"class":1078,"line":1216},[1076,2033,2034],{"class":1090},"  consecutiveCache",[1076,2036,2037],{"class":1086},",       ",[1076,2039,2040],{"class":1684},"\u002F\u002F LRU cache tracking consecutive failures\n",[1076,2042,2043,2046,2049],{"class":1078,"line":1233},[1076,2044,2045],{"class":1241},"  2",[1076,2047,2048],{"class":1086},",                      ",[1076,2050,2051],{"class":1684},"\u002F\u002F maxBans: strikes before permanent block\n",[1076,2053,2054,2057,2059,2061,2064],{"class":1078,"line":1247},[1076,2055,2056],{"class":1099},"  '",[1076,2058,2021],{"class":1103},[1076,2060,1163],{"class":1099},[1076,2062,2063],{"class":1086},",                   ",[1076,2065,2066],{"class":1684},"\u002F\u002F Label for log messages\n",[1076,2068,2069,2072,2075],{"class":1078,"line":1259},[1076,2070,2071],{"class":1090},"  log",[1076,2073,2074],{"class":1086},",                    ",[1076,2076,2077],{"class":1684},"\u002F\u002F Pino logger\n",[1076,2079,2080,2083,2085],{"class":1078,"line":1272},[1076,2081,2082],{"class":1090},"  res",[1076,2084,2074],{"class":1086},[1076,2086,2087],{"class":1684},"\u002F\u002F Express response (guard sends 429 automatically)\n",[1076,2089,2090,2093],{"class":1078,"line":1285},[1076,2091,2092],{"class":1241},"  3600",[1076,2094,2095],{"class":1684},"                    \u002F\u002F Optional: block duration in seconds (0 = permanent)\n",[1076,2097,2098],{"class":1078,"line":1297},[1076,2099,1809],{"class":1086},[1076,2101,2102],{"class":1078,"line":1754},[1076,2103,1112],{"emptyLinePlaceholder":8},[1076,2105,2106,2109,2112,2114,2117,2120,2123],{"class":1078,"line":1765},[1076,2107,2108],{"class":1082},"if",[1076,2110,2111],{"class":1086}," (",[1076,2113,2024],{"class":1126},[1076,2115,2116],{"class":1090},"allowed",[1076,2118,2119],{"class":1086},") ",[1076,2121,2122],{"class":1082},"return",[1076,2124,2125],{"class":1684},"      \u002F\u002F Response already sent\n",[1012,2127,2128,2147,2157,2168,2185,2201,2208,2222],{},[1015,2129,2132],{"name":2130,"type":2131,":required":1019},"limiter","Limiter",[856,2133,2134,2135,1140,2137,2139,2140,2143,2144,1048],{},"The rate limiter to consume against. Can be a ",[868,2136,1318],{},[868,2138,1314],{},", or a ",[868,2141,2142],{},"BlockableUnion"," from ",[868,2145,2146],{},"unionLimiter()",[1015,2148,2150],{"name":2149,"type":1026,":required":1019},"key",[856,2151,2152,2153,2156],{},"The identifier to rate limit on. Typically an IP address, email, token hash, or composite key like ",[868,2154,2155],{},"${ip}_${email}",". Keys longer than 255 characters are automatically SHA-256 hashed before use.",[1015,2158,2161],{"name":2159,"type":2160,":required":1019},"cache","LRUCache\u003Cstring, { countData: number }>",[856,2162,2163,2164,2167],{},"An LRU cache that tracks how many consecutive times this key has been rate limited. Each ",[868,2165,2166],{},"guard()"," call that fails increments the counter. Successful calls do not clear the counter (that is the controller's responsibility).",[1015,2169,2170],{"name":911,"type":1405,":required":1019},[856,2171,2172,2173,2176,2177,2180,2181,2184],{},"The number of consecutive failures required before the key is permanently blocked. Lower values are stricter. The built-in limiters use ",[868,2174,2175],{},"1"," for high-confidence keys (token hashes), ",[868,2178,2179],{},"2"," for identity keys (IPs, emails), and ",[868,2182,2183],{},"3"," for composite keys.",[1015,2186,2188],{"name":2187,"type":1026,":required":1019},"label",[856,2189,2190,2191,1140,2194,1140,2197,2200],{},"Descriptive string included in log entries. Used to identify which limiter triggered in the logs (e.g., ",[868,2192,2193],{},"'ip'",[868,2195,2196],{},"'email'",[868,2198,2199],{},"'compositeKey'",").",[1015,2202,2205],{"name":2203,"type":2204,":required":1019},"log","pino.Logger",[856,2206,2207],{},"Pino logger instance. The guard logs warnings on strikes and blocks, and info messages on successful passes (including remaining points and next allowed time).",[1015,2209,2212],{"name":2210,"type":2211,":required":1019},"res","Response",[856,2213,2214,2215,2218,2219,2221],{},"Express response object. When the guard blocks a request, it sends the ",[868,2216,2217],{},"429"," response directly, including the ",[868,2220,894],{}," header and a JSON error body.",[1015,2223,2224],{"name":1432,"type":1405},[856,2225,2226,2227,2229,2230,2232],{},"Block duration in seconds when ",[868,2228,911],{}," is exceeded. Defaults to ",[868,2231,1452],{}," (permanent block for the duration of the block cache TTL, which is 7 days). Set to a positive value for time-limited blocks.",[1353,2234,2236],{"id":2235},"how-the-guard-works","How the Guard Works",[2238,2239,2241,2245,2262,2266,2286,2290,2299,2303,2316,2320],"steps",{"level":2240},"4",[1353,2242,2244],{"id":2243},"check-the-global-block-cache","Check the global block cache",[856,2246,2247,2248,2251,2252,2255,2256,2258,2259,2261],{},"The guard maintains a process-wide LRU cache (",[868,2249,2250],{},"isBlockedCache",") with a 7-day TTL and a maximum of 1,000 entries. If the key is already in this cache with ",[868,2253,2254],{},"Blocked: true",", the guard sends ",[868,2257,2217],{}," immediately and returns ",[868,2260,1143],{},". No limiter points are consumed.",[1353,2263,2265],{"id":2264},"consume-limiter-points","Consume limiter points",[856,2267,2268,2269,2272,2273,2276,2277,2279,2280,2282,2283,1048],{},"Calls ",[868,2270,2271],{},"consumeOrReject()"," to attempt consuming one point from the limiter. If the limiter rejects (all points exhausted), ",[868,2274,2275],{},"consumeOrReject"," sends the ",[868,2278,2217],{}," response with a ",[868,2281,894],{}," header and returns ",[868,2284,2285],{},"null",[1353,2287,2289],{"id":2288},"record-a-strike","Record a strike",[856,2291,2292,2293,2295,2296,2298],{},"If consumption failed, the guard increments the key's strike counter in the consecutive cache. The counter starts at ",[868,2294,1452],{}," and increments by ",[868,2297,2175],{}," on each failure.",[1353,2300,2302],{"id":2301},"check-for-permanent-block","Check for permanent block",[856,2304,2305,2306,2308,2309,2312,2313,2315],{},"If the strike count reaches or exceeds ",[868,2307,911],{},", the guard calls ",[868,2310,2311],{},"limiter.block(key, seconds)"," to permanently block the key at the limiter level, then adds the key to ",[868,2314,2250],{},". Future requests with this key are rejected at step 1 without consuming points.",[1353,2317,2319],{"id":2318},"allow-the-request","Allow the request",[856,2321,2322,2323,2325,2326,2328],{},"If consumption succeeded, the guard deletes the key from ",[868,2324,2250],{}," (in case it was previously blocked and the block expired) and returns ",[868,2327,1019],{},". The controller can proceed.",[2330,2331,2332],"warning",{},[856,2333,2334,2335,2337,2338,2341],{},"The ",[868,2336,2250],{}," is process-local. If you run multiple IAM instances, a key blocked on one instance is not visible to other instances. The MySQL-backed limiter block (from ",[868,2339,2340],{},"limiter.block()",") is shared across instances, but the fast-path cache check only works within the process that triggered it.",[930,2343,2345],{"id":2344},"consumeorreject",[868,2346,2275],{},[856,2348,2349,2350,2352,2353,2355,2356,2359],{},"Low-level function that attempts to consume one point from a limiter. If consumption fails, it sends the ",[868,2351,2217],{}," response and returns ",[868,2354,2285],{},". If consumption succeeds, it returns the ",[868,2357,2358],{},"RateLimiterRes"," object with remaining points and timing data.",[1067,2361,2363],{"className":1069,"code":2362,"language":1071,"meta":1072,"style":1072},"import { consumeOrReject } from '@riavzon\u002Fauth'\n\nconst result = await consumeOrReject(limiter, key, res, log)\n\nif (result === null) {\n  \u002F\u002F Request already rejected with 429\n  return\n}\n\n\u002F\u002F result.remainingPoints — how many attempts the key has left\n\u002F\u002F result.consumedPoints  — how many attempts the key has used\n\u002F\u002F result.msBeforeNext    — milliseconds until the window resets\n",[868,2364,2365,2383,2387,2419,2423,2441,2446,2451,2456,2460,2465,2470],{"__ignoreMap":1072},[1076,2366,2367,2369,2371,2373,2375,2377,2379,2381],{"class":1078,"line":1079},[1076,2368,1083],{"class":1082},[1076,2370,1087],{"class":1086},[1076,2372,2275],{"class":1090},[1076,2374,1093],{"class":1086},[1076,2376,1096],{"class":1082},[1076,2378,1100],{"class":1099},[1076,2380,1047],{"class":1103},[1076,2382,1106],{"class":1099},[1076,2384,2385],{"class":1078,"line":1109},[1076,2386,1112],{"emptyLinePlaceholder":8},[1076,2388,2389,2391,2394,2396,2398,2401,2403,2405,2407,2409,2411,2413,2415,2417],{"class":1078,"line":1115},[1076,2390,1119],{"class":1118},[1076,2392,2393],{"class":1122}," result",[1076,2395,1127],{"class":1126},[1076,2397,1994],{"class":1082},[1076,2399,2400],{"class":1130}," consumeOrReject",[1076,2402,1134],{"class":1086},[1076,2404,2130],{"class":1090},[1076,2406,1140],{"class":1086},[1076,2408,2149],{"class":1090},[1076,2410,1140],{"class":1086},[1076,2412,2210],{"class":1090},[1076,2414,1140],{"class":1086},[1076,2416,2203],{"class":1090},[1076,2418,1809],{"class":1086},[1076,2420,2421],{"class":1078,"line":1149},[1076,2422,1112],{"emptyLinePlaceholder":8},[1076,2424,2425,2427,2429,2432,2435,2438],{"class":1078,"line":1169},[1076,2426,2108],{"class":1082},[1076,2428,2111],{"class":1086},[1076,2430,2431],{"class":1090},"result",[1076,2433,2434],{"class":1126}," ===",[1076,2436,2437],{"class":1137}," null",[1076,2439,2440],{"class":1086},") {\n",[1076,2442,2443],{"class":1078,"line":1182},[1076,2444,2445],{"class":1684},"  \u002F\u002F Request already rejected with 429\n",[1076,2447,2448],{"class":1078,"line":1199},[1076,2449,2450],{"class":1082},"  return\n",[1076,2452,2453],{"class":1078,"line":1216},[1076,2454,2455],{"class":1086},"}\n",[1076,2457,2458],{"class":1078,"line":1233},[1076,2459,1112],{"emptyLinePlaceholder":8},[1076,2461,2462],{"class":1078,"line":1247},[1076,2463,2464],{"class":1684},"\u002F\u002F result.remainingPoints — how many attempts the key has left\n",[1076,2466,2467],{"class":1078,"line":1259},[1076,2468,2469],{"class":1684},"\u002F\u002F result.consumedPoints  — how many attempts the key has used\n",[1076,2471,2472],{"class":1078,"line":1272},[1076,2473,2474],{"class":1684},"\u002F\u002F result.msBeforeNext    — milliseconds until the window resets\n",[856,2476,2477],{},"When the limiter rejects:",[938,2479,2480,2489],{},[941,2481,2482],{},[944,2483,2484,2486],{},[947,2485,2211],{},[947,2487,2488],{},"Value",[957,2490,2491,2500,2515],{},[944,2492,2493,2496],{},[962,2494,2495],{},"Status",[962,2497,2498],{},[868,2499,890],{},[944,2501,2502,2505],{},[962,2503,2504],{},"Header",[962,2506,2507,2510,2511,2514],{},[868,2508,2509],{},"Retry-After: {seconds}"," (computed as ",[868,2512,2513],{},"Math.ceil(msBeforeNext \u002F 1000)",", minimum 1)",[944,2516,2517,2520],{},[962,2518,2519],{},"Body",[962,2521,2522],{},[868,2523,2524],{},"{ error: 'Too many requests', retry: {seconds} }",[2526,2527,2528],"note",{},[856,2529,2530,2531,2533,2534,2536,2537,2539,2540,2542],{},"Most controllers use ",[868,2532,2166],{}," instead of calling ",[868,2535,2275],{}," directly. Use ",[868,2538,2275],{}," when you need custom logic between the consumption check and the response, or when you do not want the strike\u002Fblock escalation that ",[868,2541,915],{}," provides.",[930,2544,2546],{"id":2545},"resetlimiters",[868,2547,2548],{},"resetLimiters",[856,2550,2551],{},"Resets all accumulated points for a key across an array of limiters. Typically called after a successful authentication to clear penalties that accumulated during failed attempts.",[1067,2553,2555],{"className":1069,"code":2554,"language":1071,"meta":1072,"style":1072},"import { resetLimiters } from '@riavzon\u002Fauth'\n\nresetLimiters(log, compositeKey, [burst, slow, ipLimiter, emailLimiter])\n",[868,2556,2557,2575,2579],{"__ignoreMap":1072},[1076,2558,2559,2561,2563,2565,2567,2569,2571,2573],{"class":1078,"line":1079},[1076,2560,1083],{"class":1082},[1076,2562,1087],{"class":1086},[1076,2564,2548],{"class":1090},[1076,2566,1093],{"class":1086},[1076,2568,1096],{"class":1082},[1076,2570,1100],{"class":1099},[1076,2572,1047],{"class":1103},[1076,2574,1106],{"class":1099},[1076,2576,2577],{"class":1078,"line":1109},[1076,2578,1112],{"emptyLinePlaceholder":8},[1076,2580,2581,2583,2585,2587,2589,2592,2595,2597,2599,2601,2603,2606,2608,2611],{"class":1078,"line":1115},[1076,2582,2548],{"class":1130},[1076,2584,1134],{"class":1086},[1076,2586,2203],{"class":1090},[1076,2588,1140],{"class":1086},[1076,2590,2591],{"class":1090},"compositeKey",[1076,2593,2594],{"class":1086},", [",[1076,2596,1796],{"class":1090},[1076,2598,1140],{"class":1086},[1076,2600,1801],{"class":1090},[1076,2602,1140],{"class":1086},[1076,2604,2605],{"class":1090},"ipLimiter",[1076,2607,1140],{"class":1086},[1076,2609,2610],{"class":1090},"emailLimiter",[1076,2612,2613],{"class":1086},"])\n",[1012,2615,2616,2621,2626],{},[1015,2617,2618],{"name":2203,"type":2204,":required":1019},[856,2619,2620],{},"Logger for tracking which limiters were reset.",[1015,2622,2623],{"name":2149,"type":1026,":required":1019},[856,2624,2625],{},"The key to reset across all limiters.",[1015,2627,2629],{"name":1814,"type":2628,":required":1019},"Array\u003CRateLimiterMemory | RateLimiterMySQL | RLWrapperBlackAndWhite>",[856,2630,2631,2632,2635],{},"The limiter instances to clear. The function calls ",[868,2633,2634],{},".delete(key)"," on each one.",[1892,2637,2638],{},[856,2639,2640,2641,2644],{},"The built-in limiter groups export a convenience function (typically named ",[868,2642,2643],{},"resetLimitersUni",") that resets all limiters in the group at once. Controllers call this on successful authentication rather than tracking individual limiter references.",[930,2646,2648],{"id":2647},"makeconsecutivecache",[868,2649,1927],{},[856,2651,2652],{},"Factory function that creates an LRU cache for tracking consecutive failures. Each cache instance is scoped to a specific key type (IP, email, composite, etc.) and has its own TTL.",[1067,2654,2656],{"className":1069,"code":2655,"language":1071,"meta":1072,"style":1072},"import { makeConsecutiveCache } from '@riavzon\u002Fauth'\n\nconst cache = makeConsecutiveCache\u003C{ countData: number }>(\n  500,       \u002F\u002F Maximum entries the cache can hold\n  60_000     \u002F\u002F TTL per entry in milliseconds\n)\n",[868,2657,2658,2676,2680,2702,2712,2720],{"__ignoreMap":1072},[1076,2659,2660,2662,2664,2666,2668,2670,2672,2674],{"class":1078,"line":1079},[1076,2661,1083],{"class":1082},[1076,2663,1087],{"class":1086},[1076,2665,1927],{"class":1090},[1076,2667,1093],{"class":1086},[1076,2669,1096],{"class":1082},[1076,2671,1100],{"class":1099},[1076,2673,1047],{"class":1103},[1076,2675,1106],{"class":1099},[1076,2677,2678],{"class":1078,"line":1109},[1076,2679,1112],{"emptyLinePlaceholder":8},[1076,2681,2682,2684,2687,2689,2691,2693,2695,2697,2699],{"class":1078,"line":1115},[1076,2683,1119],{"class":1118},[1076,2685,2686],{"class":1122}," cache",[1076,2688,1127],{"class":1126},[1076,2690,1953],{"class":1130},[1076,2692,1956],{"class":1086},[1076,2694,1959],{"class":1090},[1076,2696,1156],{"class":1126},[1076,2698,1965],{"class":1964},[1076,2700,2701],{"class":1086}," }>(\n",[1076,2703,2704,2707,2709],{"class":1078,"line":1149},[1076,2705,2706],{"class":1241},"  500",[1076,2708,2037],{"class":1086},[1076,2710,2711],{"class":1684},"\u002F\u002F Maximum entries the cache can hold\n",[1076,2713,2714,2717],{"class":1078,"line":1169},[1076,2715,2716],{"class":1241},"  60_000",[1076,2718,2719],{"class":1684},"     \u002F\u002F TTL per entry in milliseconds\n",[1076,2721,2722],{"class":1078,"line":1182},[1076,2723,1809],{"class":1086},[1012,2725,2726,2732],{},[1015,2727,2729],{"name":2728,"type":1405,":required":1019},"max",[856,2730,2731],{},"Maximum number of entries. When the cache is full, the least recently used entry is evicted.",[1015,2733,2735],{"name":2734,"type":1405,":required":1019},"ttl",[856,2736,2737,2738,2741],{},"Time-to-live per entry in ",[876,2739,2740],{},"milliseconds",". Entries older than this value are automatically evicted. This controls how long a key's strike history persists.",[856,2743,2744,2745,2747],{},"The consecutive cache is separate from the limiter store. Limiter points reset on their own schedule (based on ",[868,2746,1413],{},"). The consecutive cache tracks how many times a key has been caught by the limiter, regardless of when the limiter's window resets.",[922,2749],{},[925,2751,2753],{"id":2752},"key-strategies","Key Strategies",[856,2755,2756],{},"Rate limiters are identified by the key they track. The IAM service uses four key strategies across its endpoint groups.",[930,2758,2760],{"id":2759},"ip-key","IP Key",[856,2762,2763,2764,2767],{},"The simplest strategy. Uses ",[868,2765,2766],{},"req.ip"," as the limiter key. Protects against a single source flooding an endpoint.",[1067,2769,2771],{"className":1069,"code":2770,"language":1071,"meta":1072,"style":1072},"await guard(ipLimiter, req.ip!, consecutiveForIp, 2, 'ip', log, res)\n",[868,2772,2773],{"__ignoreMap":1072},[1076,2774,2775,2778,2780,2782,2784,2786,2789,2791,2793,2795,2797,2800,2802,2804,2806,2808,2810,2812,2814,2816,2818,2820],{"class":1078,"line":1079},[1076,2776,2777],{"class":1082},"await",[1076,2779,1997],{"class":1130},[1076,2781,1134],{"class":1086},[1076,2783,2605],{"class":1090},[1076,2785,1140],{"class":1086},[1076,2787,2788],{"class":1090},"req",[1076,2790,1048],{"class":1086},[1076,2792,2021],{"class":1090},[1076,2794,2024],{"class":1126},[1076,2796,1140],{"class":1086},[1076,2798,2799],{"class":1090},"consecutiveForIp",[1076,2801,1140],{"class":1086},[1076,2803,2179],{"class":1241},[1076,2805,1140],{"class":1086},[1076,2807,1163],{"class":1099},[1076,2809,2021],{"class":1103},[1076,2811,1163],{"class":1099},[1076,2813,1140],{"class":1086},[1076,2815,2203],{"class":1090},[1076,2817,1140],{"class":1086},[1076,2819,2210],{"class":1090},[1076,2821,1809],{"class":1086},[930,2823,2825],{"id":2824},"identity-key","Identity Key",[856,2827,2828],{},"Uses the user's identity (email, OAuth subject, user ID, or token hash) as the key. Protects against attacks targeting a specific account from rotating IPs.",[1067,2830,2832],{"className":1069,"code":2831,"language":1071,"meta":1072,"style":1072},"await guard(emailLimiter, email, consecutiveForEmail, 2, 'email', log, res)\n",[868,2833,2834],{"__ignoreMap":1072},[1076,2835,2836,2838,2840,2842,2844,2846,2849,2851,2854,2856,2858,2860,2862,2864,2866,2868,2870,2872,2874],{"class":1078,"line":1079},[1076,2837,2777],{"class":1082},[1076,2839,1997],{"class":1130},[1076,2841,1134],{"class":1086},[1076,2843,2610],{"class":1090},[1076,2845,1140],{"class":1086},[1076,2847,2848],{"class":1090},"email",[1076,2850,1140],{"class":1086},[1076,2852,2853],{"class":1090},"consecutiveForEmail",[1076,2855,1140],{"class":1086},[1076,2857,2179],{"class":1241},[1076,2859,1140],{"class":1086},[1076,2861,1163],{"class":1099},[1076,2863,2848],{"class":1103},[1076,2865,1163],{"class":1099},[1076,2867,1140],{"class":1086},[1076,2869,2203],{"class":1090},[1076,2871,1140],{"class":1086},[1076,2873,2210],{"class":1090},[1076,2875,1809],{"class":1086},[930,2877,2879],{"id":2878},"composite-key","Composite Key",[856,2881,2882],{},"Combines the IP and identity into a single key with an underscore separator. Protects against a single source targeting a specific account. More precise than either key alone.",[1067,2884,2886],{"className":1069,"code":2885,"language":1071,"meta":1072,"style":1072},"const compositeKey = `${req.ip!}_${email}`\nawait guard(uniLimiter, compositeKey, consecutive429, 3, 'ip+email', log, res)\n",[868,2887,2888,2927],{"__ignoreMap":1072},[1076,2889,2890,2892,2895,2897,2900,2903,2905,2908,2910,2912,2915,2918,2920,2922,2924],{"class":1078,"line":1079},[1076,2891,1119],{"class":1118},[1076,2893,2894],{"class":1122}," compositeKey",[1076,2896,1127],{"class":1126},[1076,2898,2899],{"class":1103}," `",[1076,2901,2902],{"class":1118},"${",[1076,2904,2788],{"class":1090},[1076,2906,1048],{"class":2907},"s1lnM",[1076,2909,2021],{"class":1090},[1076,2911,2024],{"class":1126},[1076,2913,2914],{"class":1118},"}",[1076,2916,2917],{"class":1103},"_",[1076,2919,2902],{"class":1118},[1076,2921,2848],{"class":1090},[1076,2923,2914],{"class":1118},[1076,2925,2926],{"class":1103},"`\n",[1076,2928,2929,2931,2933,2935,2938,2940,2942,2944,2947,2949,2951,2953,2955,2958,2960,2962,2964,2966,2968],{"class":1078,"line":1109},[1076,2930,2777],{"class":1082},[1076,2932,1997],{"class":1130},[1076,2934,1134],{"class":1086},[1076,2936,2937],{"class":1090},"uniLimiter",[1076,2939,1140],{"class":1086},[1076,2941,2591],{"class":1090},[1076,2943,1140],{"class":1086},[1076,2945,2946],{"class":1090},"consecutive429",[1076,2948,1140],{"class":1086},[1076,2950,2183],{"class":1241},[1076,2952,1140],{"class":1086},[1076,2954,1163],{"class":1099},[1076,2956,2957],{"class":1103},"ip+email",[1076,2959,1163],{"class":1099},[1076,2961,1140],{"class":1086},[1076,2963,2203],{"class":1090},[1076,2965,1140],{"class":1086},[1076,2967,2210],{"class":1090},[1076,2969,1809],{"class":1086},[930,2971,2973],{"id":2972},"global-key","Global Key",[856,2975,2976,2977,2980],{},"Uses a fixed string as the key. Protects against system-wide abuse by capping total requests across all users. The email MFA and password reset flows use ",[868,2978,2979],{},"'global_emails'"," to cap the total number of outbound emails the system sends per day.",[1067,2982,2984],{"className":1069,"code":2983,"language":1071,"meta":1072,"style":1072},"await guard(globalEmailLimiter, 'global_emails', consecutiveForGlobal, 1, 'globalEmailLimiter', log, res)\n",[868,2985,2986],{"__ignoreMap":1072},[1076,2987,2988,2990,2992,2994,2997,2999,3001,3004,3006,3008,3011,3013,3015,3017,3019,3021,3023,3025,3027,3029,3031],{"class":1078,"line":1079},[1076,2989,2777],{"class":1082},[1076,2991,1997],{"class":1130},[1076,2993,1134],{"class":1086},[1076,2995,2996],{"class":1090},"globalEmailLimiter",[1076,2998,1140],{"class":1086},[1076,3000,1163],{"class":1099},[1076,3002,3003],{"class":1103},"global_emails",[1076,3005,1163],{"class":1099},[1076,3007,1140],{"class":1086},[1076,3009,3010],{"class":1090},"consecutiveForGlobal",[1076,3012,1140],{"class":1086},[1076,3014,2175],{"class":1241},[1076,3016,1140],{"class":1086},[1076,3018,1163],{"class":1099},[1076,3020,2996],{"class":1103},[1076,3022,1163],{"class":1099},[1076,3024,1140],{"class":1086},[1076,3026,2203],{"class":1090},[1076,3028,1140],{"class":1086},[1076,3030,2210],{"class":1090},[1076,3032,1809],{"class":1086},[2330,3034,3035],{},[856,3036,3037],{},"Keys longer than 255 characters are automatically hashed with SHA-256 before being used as a limiter key. This handles composite keys and token hashes that could exceed MySQL column length limits.",[922,3039],{},[925,3041,3043],{"id":3042},"guard-patterns-in-controllers","Guard Patterns in Controllers",[856,3045,3046,3047,3049,3050,3052],{},"Every endpoint that uses rate limiting follows the same pattern: call ",[868,3048,2166],{}," one or more times at the top of the controller, each with a different limiter and key. If any guard rejects, the controller returns immediately (the ",[868,3051,2217],{}," response was already sent by the guard).",[930,3054,103],{"id":1209},[856,3056,3057],{},"The login controller applies three guard layers before attempting authentication:",[2238,3059,3060,3064,3070,3074,3079,3083],{"level":2240},[1353,3061,3063],{"id":3062},"ip-guard","IP guard",[856,3065,3066,3067,1048],{},"Limits the total number of login attempts from a single IP. Uses the IP union limiter. ",[868,3068,3069],{},"maxBans: 2",[1353,3071,3073],{"id":3072},"email-guard","Email guard",[856,3075,3076,3077,1048],{},"Limits the number of login attempts targeting a specific email address. Catches credential stuffing against a single account from rotating IPs. ",[868,3078,3069],{},[1353,3080,3082],{"id":3081},"composite-guard","Composite guard",[856,3084,3085,3086,1048],{},"Limits the number of attempts from a specific IP targeting a specific email. The most precise guard. ",[868,3087,3088],{},"maxBans: 3",[856,3090,3091],{},"On successful login, the controller resets all limiters and clears all consecutive caches for the IP, email, and composite key:",[1067,3093,3095],{"className":1069,"code":3094,"language":1071,"meta":1072,"style":1072},"consecutiveForIp.delete(req.ip!)\nconsecutive429.delete(compositeKey)\nconsecutiveForEmail.delete(email)\nawait resetLimitersUni(compositeKey)\n",[868,3096,3097,3118,3132,3146],{"__ignoreMap":1072},[1076,3098,3099,3101,3103,3106,3108,3110,3112,3114,3116],{"class":1078,"line":1079},[1076,3100,2799],{"class":1090},[1076,3102,1048],{"class":1086},[1076,3104,3105],{"class":1130},"delete",[1076,3107,1134],{"class":1086},[1076,3109,2788],{"class":1090},[1076,3111,1048],{"class":1086},[1076,3113,2021],{"class":1090},[1076,3115,2024],{"class":1126},[1076,3117,1809],{"class":1086},[1076,3119,3120,3122,3124,3126,3128,3130],{"class":1078,"line":1109},[1076,3121,2946],{"class":1090},[1076,3123,1048],{"class":1086},[1076,3125,3105],{"class":1130},[1076,3127,1134],{"class":1086},[1076,3129,2591],{"class":1090},[1076,3131,1809],{"class":1086},[1076,3133,3134,3136,3138,3140,3142,3144],{"class":1078,"line":1115},[1076,3135,2853],{"class":1090},[1076,3137,1048],{"class":1086},[1076,3139,3105],{"class":1130},[1076,3141,1134],{"class":1086},[1076,3143,2848],{"class":1090},[1076,3145,1809],{"class":1086},[1076,3147,3148,3150,3153,3155,3157],{"class":1078,"line":1149},[1076,3149,2777],{"class":1082},[1076,3151,3152],{"class":1130}," resetLimitersUni",[1076,3154,1134],{"class":1086},[1076,3156,2591],{"class":1090},[1076,3158,1809],{"class":1086},[930,3160,3162],{"id":3161},"token-rotation","Token Rotation",[856,3164,3165],{},"The rotation controller applies three guards but uses a different strategy than login. It does not key on email (the rotation endpoint does not accept email input). Instead, it keys on the SHA-256 hash of the refresh token.",[2238,3167,3168,3171,3178,3182,3187,3190],{"level":2240},[1353,3169,3063],{"id":3170},"ip-guard-1",[856,3172,3173,3174,3177],{},"Limits the total number of rotation attempts from a single IP. ",[868,3175,3176],{},"maxBans: 1"," (strict, single-strike block).",[1353,3179,3181],{"id":3180},"token-hash-guard","Token hash guard",[856,3183,3184,3185,1048],{},"Limits attempts using the same refresh token hash. Catches replay attacks where a stolen token is used repeatedly. ",[868,3186,3176],{},[1353,3188,3082],{"id":3189},"composite-guard-1",[856,3191,3192,3193,1048],{},"Limits attempts from a specific IP with a specific token hash. ",[868,3194,3176],{},[856,3196,3197,3198,3201],{},"After successful rotation, the controller does not reset limiters. Instead, it ",[876,3199,3200],{},"blocks"," the old token hash and composite key for 3 days:",[1067,3203,3205],{"className":1069,"code":3204,"language":1071,"meta":1072,"style":1072},"await refreshTokenLimiter.block(hashedToken, 60 * 60 * 24 * 3)\nawait refreshAccessTokenLimiter.block(compositeKey, 60 * 60 * 24 * 3)\n",[868,3206,3207,3247],{"__ignoreMap":1072},[1076,3208,3209,3211,3214,3216,3219,3221,3224,3226,3229,3232,3235,3237,3240,3242,3245],{"class":1078,"line":1079},[1076,3210,2777],{"class":1082},[1076,3212,3213],{"class":1090}," refreshTokenLimiter",[1076,3215,1048],{"class":1086},[1076,3217,3218],{"class":1130},"block",[1076,3220,1134],{"class":1086},[1076,3222,3223],{"class":1090},"hashedToken",[1076,3225,1140],{"class":1086},[1076,3227,3228],{"class":1241},"60",[1076,3230,3231],{"class":1126}," *",[1076,3233,3234],{"class":1241}," 60",[1076,3236,3231],{"class":1126},[1076,3238,3239],{"class":1241}," 24",[1076,3241,3231],{"class":1126},[1076,3243,3244],{"class":1241}," 3",[1076,3246,1809],{"class":1086},[1076,3248,3249,3251,3254,3256,3258,3260,3262,3264,3266,3268,3270,3272,3274,3276,3278],{"class":1078,"line":1109},[1076,3250,2777],{"class":1082},[1076,3252,3253],{"class":1090}," refreshAccessTokenLimiter",[1076,3255,1048],{"class":1086},[1076,3257,3218],{"class":1130},[1076,3259,1134],{"class":1086},[1076,3261,2591],{"class":1090},[1076,3263,1140],{"class":1086},[1076,3265,3228],{"class":1241},[1076,3267,3231],{"class":1126},[1076,3269,3234],{"class":1241},[1076,3271,3231],{"class":1126},[1076,3273,3239],{"class":1241},[1076,3275,3231],{"class":1126},[1076,3277,3244],{"class":1241},[1076,3279,1809],{"class":1086},[856,3281,3282],{},"This prevents the old token from being used again even if it has not yet been revoked in the database. The block acts as a fast-path denial layer in front of the database revocation check.",[2330,3284,3285],{},[856,3286,3287],{},"Token rotation blocks the old token hash rather than resetting limiters. This is intentional. Unlike login (where a successful attempt proves the user is legitimate), rotation consumes a token that should never be reused. Blocking the hash ensures that even a race condition between two concurrent rotation attempts with the same token results in the second attempt being rejected.",[930,3289,99],{"id":3290},"signup",[856,3292,3293],{},"The signup controller applies three guards:",[2238,3295,3296,3300,3305,3308,3317,3320],{"level":2240},[1353,3297,3299],{"id":3298},"ip-union-guard","IP union guard",[856,3301,3302,3303,1048],{},"A union limiter (burst + slow) keyed on IP. Prevents mass-registration bots from a single source. ",[868,3304,3069],{},[1353,3306,3082],{"id":3307},"composite-guard-2",[856,3309,3310,3311,3314,3315,1048],{},"A union limiter keyed on ",[868,3312,3313],{},"${IP}_${email}",". Prevents repeated signup attempts for the same email from the same IP. ",[868,3316,3069],{},[1353,3318,3073],{"id":3319},"email-guard-1",[856,3321,3322,3323,1048],{},"A standalone limiter keyed on the email address. Prevents repeated signup attempts for the same email from different IPs. ",[868,3324,3069],{},[930,3326,111],{"id":3327},"oauth",[856,3329,3330],{},"The OAuth controller applies three guards:",[2238,3332,3333,3336,3341,3345,3354,3357],{"level":2240},[1353,3334,3299],{"id":3335},"ip-union-guard-1",[856,3337,3338,3339,1048],{},"A union limiter (burst + slow) keyed on IP. ",[868,3340,3176],{},[1353,3342,3344],{"id":3343},"subject-guard","Subject guard",[856,3346,3347,3348,3351,3352,1048],{},"A standalone limiter keyed on the OAuth provider's subject ID (",[868,3349,3350],{},"sub"," claim). Catches abuse targeting a specific OAuth account. ",[868,3353,3069],{},[1353,3355,3082],{"id":3356},"composite-guard-3",[856,3358,3359,3360,3363,3364,1048],{},"A standalone limiter keyed on ",[868,3361,3362],{},"${IP}_${sub}",". ",[868,3365,3069],{},[930,3367,3369],{"id":3368},"password-reset-initiation","Password Reset Initiation",[856,3371,3372,3373,3376],{},"The password reset initiation controller (",[868,3374,3375],{},"POST \u002Fauth\u002Fforgot-password",") applies four guards:",[2238,3378,3379,3383,3391,3394,3399,3402,3407,3411],{"level":2240},[1353,3380,3382],{"id":3381},"global-email-guard","Global email guard",[856,3384,3385,3386,3388,3389,1048],{},"A global limiter keyed on the fixed string ",[868,3387,2979],{},". Caps the total number of password reset emails the system sends per day. ",[868,3390,3176],{},[1353,3392,3063],{"id":3393},"ip-guard-2",[856,3395,3396,3397,1048],{},"A standalone limiter keyed on IP. ",[868,3398,3069],{},[1353,3400,3073],{"id":3401},"email-guard-2",[856,3403,3404,3405,1048],{},"A standalone limiter keyed on the target email address. Prevents an attacker from flooding a single user's inbox. ",[868,3406,3069],{},[1353,3408,3410],{"id":3409},"composite-guard-on-failure","Composite guard (on failure)",[856,3412,3310,3413,3415,3416,1048],{},[868,3414,3313],{},", applied only when the actual password reset lookup fails. This avoids penalizing legitimate requests that pass validation. ",[868,3417,3088],{},[930,3419,3421],{"id":3420},"email-mfa-initiation","Email MFA Initiation",[856,3423,3424],{},"The email MFA flow controller applies four guards:",[2238,3426,3427,3430,3438,3441,3445,3449,3454,3457],{"level":2240},[1353,3428,3382],{"id":3429},"global-email-guard-1",[856,3431,3432,3433,3435,3436,1048],{},"Same global limiter as password reset. Shared key ",[868,3434,2979],{}," caps system-wide email sends. ",[868,3437,3176],{},[1353,3439,3063],{"id":3440},"ip-guard-3",[856,3442,3396,3443,1048],{},[868,3444,3069],{},[1353,3446,3448],{"id":3447},"identity-guard","Identity guard",[856,3450,3451,3452,1048],{},"A standalone limiter keyed on a combination of the random token and the MFA reason. Prevents repeated MFA triggers for the same session. ",[868,3453,3069],{},[1353,3455,3082],{"id":3456},"composite-guard-4",[856,3458,3310,3459,3363,3462,1048],{},[868,3460,3461],{},"${IP}_${random}_${reason}",[868,3463,3088],{},[930,3465,3467],{"id":3466},"link-verification","Link Verification",[856,3469,3470,3471,1048],{},"Magic link verification (MFA, password reset, custom MFA) applies a single IP union guard. The union limiter contains a burst limiter (2 points per second) and a slow limiter (30 points per 30 minutes). ",[868,3472,3176],{},[930,3474,3476],{"id":3475},"temporary-post-routes","Temporary Post Routes",[856,3478,3479],{},"The password change and MFA code submission endpoints apply three guards:",[2238,3481,3482,3486,3495,3498,3502,3505],{"level":2240},[1353,3483,3485],{"id":3484},"jti-guard","JTI guard",[856,3487,3488,3489,3492,3493,1048],{},"A standalone limiter keyed on the JWT's ",[868,3490,3491],{},"jti"," claim. If the JTI has already been consumed by a previous request, the guard blocks immediately. This is configured with 0 points and 0 duration so that any consumption attempt triggers a block. ",[868,3494,3176],{},[1353,3496,3063],{"id":3497},"ip-guard-4",[856,3499,3396,3500,1048],{},[868,3501,3069],{},[1353,3503,3082],{"id":3504},"composite-guard-5",[856,3506,3507,3508,1048],{},"A union limiter keyed on a composite key. ",[868,3509,3069],{},[922,3511],{},[925,3513,3515],{"id":3514},"built-in-limiter-reference","Built-In Limiter Reference",[856,3517,3518,3519,3521],{},"All limiter groups are configured in the ",[868,3520,870],{}," section of the service configuration. Every group is optional. Omitting a group disables rate limiting for those endpoints and uses hardcoded defaults instead.",[856,3523,3524],{},"Each limiter within a group accepts the same five fields:",[1012,3526,3527,3532,3538,3543,3548],{},[1015,3528,3529],{"name":1400,"type":1405,":required":1019},[856,3530,3531],{},"Maximum requests allowed within the time window.",[1015,3533,3534],{"name":1413,"type":1405,":required":1019},[856,3535,1429,3536,1433],{},[876,3537,1432],{},[1015,3539,3540],{"name":1440,"type":1405,":required":1019},[856,3541,3542],{},"How long (in seconds) to block the key after all points are exhausted.",[1015,3544,3545],{"name":988,"type":1405,":required":1019},[856,3546,3547],{},"At this consumption count, the in-memory layer blocks the key without hitting MySQL.",[1015,3549,3550],{"name":1479,"type":1405,":required":1019},[856,3551,3552],{},"How long (in seconds) the in-memory block lasts.",[930,3554,3556],{"id":3555},"loginlimiters",[868,3557,3558],{},"loginLimiters",[856,3560,3561,3562,3565],{},"Protects ",[868,3563,3564],{},"POST \u002Flogin"," against credential-stuffing attacks.",[938,3567,3568,3588],{},[941,3569,3570],{},[944,3571,3572,3574,3577,3580,3583,3586],{},[947,3573,2131],{},[947,3575,3576],{},"Key",[947,3578,3579],{},"Default points",[947,3581,3582],{},"Default duration",[947,3584,3585],{},"Default block",[947,3587,955],{},[957,3589,3590,3612,3634,3657],{},[944,3591,3592,3597,3601,3603,3606,3609],{},[962,3593,3594],{},[868,3595,3596],{},"unionLimiter.burstLimiter",[962,3598,3599],{},[868,3600,3313],{},[962,3602,2175],{},[962,3604,3605],{},"1s",[962,3607,3608],{},"30min",[962,3610,3611],{},"Blocks rapid-fire login attempts",[944,3613,3614,3619,3623,3626,3629,3631],{},[962,3615,3616],{},[868,3617,3618],{},"unionLimiter.slowLimiter",[962,3620,3621],{},[868,3622,3313],{},[962,3624,3625],{},"5",[962,3627,3628],{},"1hr",[962,3630,3608],{},[962,3632,3633],{},"Catches slow credential stuffing",[944,3635,3636,3640,3645,3648,3651,3654],{},[962,3637,3638],{},[868,3639,2605],{},[962,3641,3642],{},[868,3643,3644],{},"${IP}",[962,3646,3647],{},"15",[962,3649,3650],{},"24hr",[962,3652,3653],{},"3hr",[962,3655,3656],{},"Caps total login attempts per IP per day",[944,3658,3659,3663,3668,3670,3672,3675],{},[962,3660,3661],{},[868,3662,2610],{},[962,3664,3665],{},[868,3666,3667],{},"${email}",[962,3669,3625],{},[962,3671,3650],{},[962,3673,3674],{},"5hr",[962,3676,3677],{},"Caps login attempts per email per day",[930,3679,3681],{"id":3680},"signuplimiters",[868,3682,3683],{},"signupLimiters",[856,3685,3561,3686,3689],{},[868,3687,3688],{},"POST \u002Fsignup"," against mass-registration bots. This group uses two union limiters instead of one.",[938,3691,3692,3708],{},[941,3693,3694],{},[944,3695,3696,3698,3700,3702,3704,3706],{},[947,3697,2131],{},[947,3699,3576],{},[947,3701,3579],{},[947,3703,3582],{},[947,3705,3585],{},[947,3707,955],{},[957,3709,3710,3731,3751,3771,3791],{},[944,3711,3712,3717,3721,3723,3725,3728],{},[962,3713,3714],{},[868,3715,3716],{},"unionLimiters.uniLimiterIp.ipLimit",[962,3718,3719],{},[868,3720,3644],{},[962,3722,2179],{},[962,3724,3605],{},[962,3726,3727],{},"15min",[962,3729,3730],{},"Blocks rapid signup bursts per IP",[944,3732,3733,3738,3742,3744,3746,3748],{},[962,3734,3735],{},[868,3736,3737],{},"unionLimiters.uniLimiterIp.slowIpLimit",[962,3739,3740],{},[868,3741,3644],{},[962,3743,3625],{},[962,3745,3608],{},[962,3747,3727],{},[962,3749,3750],{},"Catches sustained signup attempts per IP",[944,3752,3753,3758,3762,3764,3766,3768],{},[962,3754,3755],{},[868,3756,3757],{},"unionLimiters.uniLimiterComposite.compositeKeyLimit",[962,3759,3760],{},[868,3761,3313],{},[962,3763,2175],{},[962,3765,3605],{},[962,3767,3608],{},[962,3769,3770],{},"Blocks rapid signup for same email from same IP",[944,3772,3773,3778,3782,3784,3786,3788],{},[962,3774,3775],{},[868,3776,3777],{},"unionLimiters.uniLimiterComposite.slowCompositeKeyLimit",[962,3779,3780],{},[868,3781,3313],{},[962,3783,2183],{},[962,3785,3650],{},[962,3787,3650],{},[962,3789,3790],{},"Daily limit for same email+IP combination",[944,3792,3793,3798,3802,3804,3806,3808],{},[962,3794,3795],{},[868,3796,3797],{},"emailLimit",[962,3799,3800],{},[868,3801,3667],{},[962,3803,2183],{},[962,3805,3650],{},[962,3807,3650],{},[962,3809,3810],{},"Global per-email signup cap",[930,3812,3814],{"id":3813},"oauthlimiters",[868,3815,3816],{},"oauthLimiters",[856,3818,3561,3819,1048],{},[868,3820,3821],{},"POST \u002Fauth\u002FOAuth\u002F:providerName",[938,3823,3824,3840],{},[941,3825,3826],{},[944,3827,3828,3830,3832,3834,3836,3838],{},[947,3829,2131],{},[947,3831,3576],{},[947,3833,3579],{},[947,3835,3582],{},[947,3837,3585],{},[947,3839,955],{},[957,3841,3842,3863,3884,3905],{},[944,3843,3844,3849,3853,3855,3857,3860],{},[962,3845,3846],{},[868,3847,3848],{},"unionLimiter.ipLimiterBrute",[962,3850,3851],{},[868,3852,3644],{},[962,3854,2175],{},[962,3856,3605],{},[962,3858,3859],{},"5min",[962,3861,3862],{},"Blocks rapid OAuth attempts per IP",[944,3864,3865,3870,3874,3877,3879,3881],{},[962,3866,3867],{},[868,3868,3869],{},"unionLimiter.ipLimiterSlow",[962,3871,3872],{},[868,3873,3644],{},[962,3875,3876],{},"25",[962,3878,3628],{},[962,3880,3608],{},[962,3882,3883],{},"Hourly cap per IP",[944,3885,3886,3891,3896,3898,3900,3902],{},[962,3887,3888],{},[868,3889,3890],{},"subLimiter",[962,3892,3893],{},[868,3894,3895],{},"${sub}",[962,3897,3625],{},[962,3899,3859],{},[962,3901,3727],{},[962,3903,3904],{},"Limits attempts per OAuth subject",[944,3906,3907,3912,3916,3918,3921,3923],{},[962,3908,3909],{},[868,3910,3911],{},"compositeKeyLimiter",[962,3913,3914],{},[868,3915,3362],{},[962,3917,2183],{},[962,3919,3920],{},"10min",[962,3922,3727],{},[962,3924,3925],{},"Limits attempts per IP+subject combination",[930,3927,3929],{"id":3928},"tokenlimiters",[868,3930,3931],{},"tokenLimiters",[856,3933,3561,3934,3937],{},[868,3935,3936],{},"POST \u002Fauth\u002Fuser\u002Frefresh-session"," (full token rotation).",[938,3939,3940,3956],{},[941,3941,3942],{},[944,3943,3944,3946,3948,3950,3952,3954],{},[947,3945,2131],{},[947,3947,3576],{},[947,3949,3579],{},[947,3951,3582],{},[947,3953,3585],{},[947,3955,955],{},[957,3957,3958,3978,3998,4019,4040],{},[944,3959,3960,3965,3969,3971,3973,3975],{},[962,3961,3962],{},[868,3963,3964],{},"unionLimiters.refreshAccessTokenLimiter.accessTokenBrute",[962,3966,3967],{},[868,3968,3644],{},[962,3970,2179],{},[962,3972,3605],{},[962,3974,3608],{},[962,3976,3977],{},"Blocks rapid access token refreshes per IP",[944,3979,3980,3985,3989,3991,3993,3995],{},[962,3981,3982],{},[868,3983,3984],{},"unionLimiters.refreshAccessTokenLimiter.accessTokenSlow",[962,3986,3987],{},[868,3988,3644],{},[962,3990,2183],{},[962,3992,3920],{},[962,3994,3628],{},[962,3996,3997],{},"Caps access token refreshes per IP over time",[944,3999,4000,4005,4010,4012,4014,4016],{},[962,4001,4002],{},[868,4003,4004],{},"unionLimiters.refreshTokenLimiterUnion.refreshTokenBrute",[962,4006,4007],{},[868,4008,4009],{},"${tokenHash}",[962,4011,2179],{},[962,4013,3605],{},[962,4015,3608],{},[962,4017,4018],{},"Blocks rapid rotation attempts per token",[944,4020,4021,4026,4030,4032,4035,4037],{},[962,4022,4023],{},[868,4024,4025],{},"unionLimiters.refreshTokenLimiterUnion.refreshTokenSlow",[962,4027,4028],{},[868,4029,4009],{},[962,4031,2240],{},[962,4033,4034],{},"12hr",[962,4036,4034],{},[962,4038,4039],{},"Long-window cap per token hash",[944,4041,4042,4047,4051,4053,4055,4058],{},[962,4043,4044],{},[868,4045,4046],{},"refreshTokenLimiter",[962,4048,4049],{},[868,4050,4009],{},[962,4052,2183],{},[962,4054,4034],{},[962,4056,4057],{},"15hr",[962,4059,4060],{},"Standalone token hash limiter for the composite guard",[2526,4062,4063],{},[856,4064,4065,4066,4069],{},"The token limiter group also creates an internal ",[868,4067,4068],{},"blackList"," limiter (20 points, 24hr duration, 3-day block). This limiter is used by the rotation controller to block consumed token hashes for 3 days after successful rotation, preventing any reuse of old tokens even if database revocation has not propagated yet.",[930,4071,4073],{"id":4072},"linkverificationlimiter",[868,4074,4075],{},"linkVerificationLimiter",[856,4077,4078],{},"Protects magic link verification endpoints: MFA email links, password reset links, and custom MFA flow links.",[938,4080,4081,4097],{},[941,4082,4083],{},[944,4084,4085,4087,4089,4091,4093,4095],{},[947,4086,2131],{},[947,4088,3576],{},[947,4090,3579],{},[947,4092,3582],{},[947,4094,3585],{},[947,4096,955],{},[957,4098,4099,4118],{},[944,4100,4101,4105,4109,4111,4113,4115],{},[962,4102,4103],{},[868,4104,3596],{},[962,4106,4107],{},[868,4108,3644],{},[962,4110,2179],{},[962,4112,3605],{},[962,4114,3727],{},[962,4116,4117],{},"Blocks rapid link verification attempts",[944,4119,4120,4124,4128,4131,4133,4135],{},[962,4121,4122],{},[868,4123,3618],{},[962,4125,4126],{},[868,4127,3644],{},[962,4129,4130],{},"30",[962,4132,3608],{},[962,4134,3608],{},[962,4136,4137],{},"Caps total link verification attempts per IP",[930,4139,4141],{"id":4140},"initpasswordresetlimiters",[868,4142,4143],{},"initPasswordResetLimiters",[856,4145,3561,4146,1048],{},[868,4147,3375],{},[938,4149,4150,4166],{},[941,4151,4152],{},[944,4153,4154,4156,4158,4160,4162,4164],{},[947,4155,2131],{},[947,4157,3576],{},[947,4159,3579],{},[947,4161,3582],{},[947,4163,3585],{},[947,4165,955],{},[957,4167,4168,4188,4208,4228],{},[944,4169,4170,4175,4179,4181,4183,4185],{},[962,4171,4172],{},[868,4173,4174],{},"unionLimiters.limit",[962,4176,4177],{},[868,4178,3313],{},[962,4180,2175],{},[962,4182,3605],{},[962,4184,3608],{},[962,4186,4187],{},"Blocks rapid password resets per IP+email",[944,4189,4190,4195,4199,4201,4203,4205],{},[962,4191,4192],{},[868,4193,4194],{},"unionLimiters.longLimiter",[962,4196,4197],{},[868,4198,3313],{},[962,4200,2240],{},[962,4202,3608],{},[962,4204,3727],{},[962,4206,4207],{},"Sustained cap per IP+email",[944,4209,4210,4214,4218,4220,4222,4225],{},[962,4211,4212],{},[868,4213,2605],{},[962,4215,4216],{},[868,4217,3644],{},[962,4219,3625],{},[962,4221,3650],{},[962,4223,4224],{},"4hr",[962,4226,4227],{},"Daily cap per IP",[944,4229,4230,4234,4238,4240,4242,4244],{},[962,4231,4232],{},[868,4233,2610],{},[962,4235,4236],{},[868,4237,3667],{},[962,4239,3625],{},[962,4241,3650],{},[962,4243,4224],{},[962,4245,4246],{},"Daily cap per email",[930,4248,4250],{"id":4249},"emailmfalimiters",[868,4251,4252],{},"emailMfaLimiters",[856,4254,4255],{},"Protects MFA email sending endpoints.",[938,4257,4258,4274],{},[941,4259,4260],{},[944,4261,4262,4264,4266,4268,4270,4272],{},[947,4263,2131],{},[947,4265,3576],{},[947,4267,3579],{},[947,4269,3582],{},[947,4271,3585],{},[947,4273,955],{},[957,4275,4276,4296,4315,4333,4355],{},[944,4277,4278,4282,4287,4289,4291,4293],{},[962,4279,4280],{},[868,4281,4174],{},[962,4283,4284],{},[868,4285,4286],{},"${IP}_${identifier}",[962,4288,2175],{},[962,4290,3605],{},[962,4292,3608],{},[962,4294,4295],{},"Blocks rapid MFA triggers per IP+identifier",[944,4297,4298,4302,4306,4308,4310,4312],{},[962,4299,4300],{},[868,4301,4194],{},[962,4303,4304],{},[868,4305,4286],{},[962,4307,2240],{},[962,4309,3608],{},[962,4311,3727],{},[962,4313,4314],{},"Sustained cap per IP+identifier",[944,4316,4317,4321,4325,4327,4329,4331],{},[962,4318,4319],{},[868,4320,2605],{},[962,4322,4323],{},[868,4324,3644],{},[962,4326,3625],{},[962,4328,3650],{},[962,4330,4224],{},[962,4332,4227],{},[944,4334,4335,4340,4345,4348,4350,4352],{},[962,4336,4337],{},[868,4338,4339],{},"userIdLimiter",[962,4341,4342],{},[868,4343,4344],{},"user_${userId}",[962,4346,4347],{},"8",[962,4349,3650],{},[962,4351,4034],{},[962,4353,4354],{},"Daily cap per user account",[944,4356,4357,4361,4365,4368,4370,4372],{},[962,4358,4359],{},[868,4360,2996],{},[962,4362,4363],{},[868,4364,3003],{},[962,4366,4367],{},"800",[962,4369,3650],{},[962,4371,3650],{},[962,4373,4374],{},"System-wide daily email send cap",[2330,4376,4377],{},[856,4378,2334,4379,4381,4382,4384],{},[868,4380,2996],{}," is shared between the password reset and email MFA flows. Both controllers import it from the email MFA limiter module and guard against the same ",[868,4383,2979],{}," key. If the system hits 800 password reset and MFA emails combined in 24 hours, all further email-sending endpoints are blocked for 24 hours regardless of which specific flow triggered them.",[930,4386,4388],{"id":4387},"temppostrouteslimiters",[868,4389,4390],{},"tempPostRoutesLimiters",[856,4392,4393],{},"Protects password change submissions and MFA code submissions (the POST endpoints that consume temporary magic links).",[938,4395,4396,4412],{},[941,4397,4398],{},[944,4399,4400,4402,4404,4406,4408,4410],{},[947,4401,2131],{},[947,4403,3576],{},[947,4405,3579],{},[947,4407,3582],{},[947,4409,3585],{},[947,4411,955],{},[957,4413,4414,4434,4454,4475],{},[944,4415,4416,4420,4425,4427,4429,4431],{},[962,4417,4418],{},[868,4419,4174],{},[962,4421,4422],{},[868,4423,4424],{},"${compositeKey}",[962,4426,2175],{},[962,4428,3605],{},[962,4430,3608],{},[962,4432,4433],{},"Blocks rapid submissions per composite key",[944,4435,4436,4441,4445,4447,4449,4451],{},[962,4437,4438],{},[868,4439,4440],{},"unionLimiters.slowLimit",[962,4442,4443],{},[868,4444,4424],{},[962,4446,3625],{},[962,4448,3920],{},[962,4450,3920],{},[962,4452,4453],{},"Sustained cap per composite key",[944,4455,4456,4461,4465,4468,4470,4472],{},[962,4457,4458],{},[868,4459,4460],{},"ipLimit",[962,4462,4463],{},[868,4464,3644],{},[962,4466,4467],{},"6",[962,4469,3920],{},[962,4471,3920],{},[962,4473,4474],{},"Caps total submissions per IP",[944,4476,4477,4482,4487,4489,4492,4495],{},[962,4478,4479],{},[868,4480,4481],{},"usedJtiLimiter",[962,4483,4484],{},[868,4485,4486],{},"${jti}",[962,4488,1452],{},[962,4490,4491],{},"0s",[962,4493,4494],{},"20min",[962,4496,4497],{},"Zero-point limiter. Any consumption blocks the JTI for 20 minutes. Prevents reuse of temporary tokens.",[922,4499],{},[925,4501,4503],{"id":4502},"building-a-custom-rate-limiter","Building a Custom Rate Limiter",[856,4505,4506,4507,1048],{},"When you need to protect additional endpoints in your own application, you can compose the same utilities the IAM service uses internally. All factory functions and helpers are exported from ",[868,4508,1047],{},[2238,4510,4511,4515,4518,5529,5540,5544,5554,5640,5644,5649,6035],{"level":2240},[1353,4512,4514],{"id":4513},"define-the-limiter-module","Define the limiter module",[856,4516,4517],{},"Create a module in your project that builds the limiters once and reuses them for the lifetime of the process. The singleton pattern with lazy initialization ensures the MySQL pool and rate limiter tables are created only on first use:",[1067,4519,4521],{"className":1069,"code":4520,"language":1071,"meta":1072,"style":1072},"import {\n  makeRateLimiter,\n  unionLimiter,\n  type BlockableUnion,\n} from '@riavzon\u002Fauth'\nimport type { RateLimiterMySQL, RateLimiterMemory, RLWrapperBlackAndWhite } from 'rate-limiter-flexible'\nimport type { Pool } from 'mysql2'\n\ninterface LimiterBundle {\n  uniLimiter: BlockableUnion | RLWrapperBlackAndWhite\n  ipLimiter: RateLimiterMySQL | RateLimiterMemory\n  resetAll(key: string): Promise\u003Cvoid>\n}\n\nlet instance: LimiterBundle | null = null\n\nfunction build(pool: Pool, dbName: string): LimiterBundle {\n  const burst = makeRateLimiter(true, false, {\n    dbName,\n    storeClient: pool,\n    storeType: 'mysql2',\n    tableName: 'my_endpoint',\n    keyPrefix: 'my_endpoint_burst',\n    points: 2,\n    duration: 1,\n    blockDuration: 1800,\n    inMemoryBlockOnConsumed: 3,\n    inMemoryBlockDuration: 1800,\n  })\n\n  const slow = makeRateLimiter(true, false, {\n    dbName,\n    storeClient: pool,\n    storeType: 'mysql2',\n    tableName: 'my_endpoint',\n    keyPrefix: 'my_endpoint_slow',\n    points: 10,\n    duration: 3600,\n    blockDuration: 3600,\n    inMemoryBlockOnConsumed: 11,\n    inMemoryBlockDuration: 3600,\n  })\n\n  const ipLimiter = makeRateLimiter(true, false, {\n    dbName,\n    storeClient: pool,\n    storeType: 'mysql2',\n    tableName: 'my_endpoint',\n    keyPrefix: 'my_endpoint_ip',\n    points: 20,\n    duration: 86400,\n    blockDuration: 14400,\n    inMemoryBlockOnConsumed: 21,\n    inMemoryBlockDuration: 14400,\n  })\n\n  return {\n    uniLimiter: unionLimiter([burst, slow], false),\n    ipLimiter,\n    resetAll: async (key: string) => {\n      await Promise.all([\n        burst.delete(key),\n        slow.delete(key),\n        ipLimiter.delete(key),\n      ])\n    },\n  }\n}\n\nexport function init(pool: Pool, dbName: string): void {\n  if (!instance) instance = build(pool, dbName)\n}\n\nexport function getLimiters(): LimiterBundle {\n  if (!instance) throw new Error('Limiters not initialized. Call init() first.')\n  return instance\n}\n",[868,4522,4523,4530,4537,4544,4554,4567,4596,4616,4620,4630,4645,4660,4692,4696,4700,4721,4725,4759,4780,4787,4799,4815,4832,4849,4861,4873,4885,4897,4909,4915,4920,4941,4948,4959,4974,4989,5005,5017,5028,5039,5051,5062,5067,5072,5094,5101,5112,5127,5142,5158,5170,5182,5194,5206,5217,5222,5227,5235,5260,5268,5294,5309,5325,5341,5357,5363,5369,5375,5380,5385,5422,5453,5458,5463,5482,5516,5524],{"__ignoreMap":1072},[1076,4524,4525,4527],{"class":1078,"line":1079},[1076,4526,1083],{"class":1082},[1076,4528,4529],{"class":1086}," {\n",[1076,4531,4532,4535],{"class":1078,"line":1109},[1076,4533,4534],{"class":1090},"  makeRateLimiter",[1076,4536,1166],{"class":1086},[1076,4538,4539,4542],{"class":1078,"line":1115},[1076,4540,4541],{"class":1090},"  unionLimiter",[1076,4543,1166],{"class":1086},[1076,4545,4546,4549,4552],{"class":1078,"line":1149},[1076,4547,4548],{"class":1082},"  type",[1076,4550,4551],{"class":1090}," BlockableUnion",[1076,4553,1166],{"class":1086},[1076,4555,4556,4559,4561,4563,4565],{"class":1078,"line":1169},[1076,4557,4558],{"class":1086},"} ",[1076,4560,1096],{"class":1082},[1076,4562,1100],{"class":1099},[1076,4564,1047],{"class":1103},[1076,4566,1106],{"class":1099},[1076,4568,4569,4571,4574,4576,4578,4580,4582,4584,4586,4588,4590,4592,4594],{"class":1078,"line":1182},[1076,4570,1083],{"class":1082},[1076,4572,4573],{"class":1082}," type",[1076,4575,1087],{"class":1086},[1076,4577,1314],{"class":1090},[1076,4579,1140],{"class":1086},[1076,4581,1318],{"class":1090},[1076,4583,1140],{"class":1086},[1076,4585,1334],{"class":1090},[1076,4587,1093],{"class":1086},[1076,4589,1096],{"class":1082},[1076,4591,1100],{"class":1099},[1076,4593,865],{"class":1103},[1076,4595,1106],{"class":1099},[1076,4597,4598,4600,4602,4604,4606,4608,4610,4612,4614],{"class":1078,"line":1199},[1076,4599,1083],{"class":1082},[1076,4601,4573],{"class":1082},[1076,4603,1087],{"class":1086},[1076,4605,1521],{"class":1090},[1076,4607,1093],{"class":1086},[1076,4609,1096],{"class":1082},[1076,4611,1100],{"class":1099},[1076,4613,1192],{"class":1103},[1076,4615,1106],{"class":1099},[1076,4617,4618],{"class":1078,"line":1216},[1076,4619,1112],{"emptyLinePlaceholder":8},[1076,4621,4622,4625,4628],{"class":1078,"line":1233},[1076,4623,4624],{"class":1118},"interface",[1076,4626,4627],{"class":1964}," LimiterBundle",[1076,4629,4529],{"class":1086},[1076,4631,4632,4635,4637,4639,4642],{"class":1078,"line":1247},[1076,4633,4634],{"class":1090},"  uniLimiter",[1076,4636,1156],{"class":1126},[1076,4638,4551],{"class":1964},[1076,4640,4641],{"class":1126}," |",[1076,4643,4644],{"class":1964}," RLWrapperBlackAndWhite\n",[1076,4646,4647,4650,4652,4655,4657],{"class":1078,"line":1259},[1076,4648,4649],{"class":1090},"  ipLimiter",[1076,4651,1156],{"class":1126},[1076,4653,4654],{"class":1964}," RateLimiterMySQL",[1076,4656,4641],{"class":1126},[1076,4658,4659],{"class":1964}," RateLimiterMemory\n",[1076,4661,4662,4665,4667,4670,4672,4675,4678,4680,4683,4686,4689],{"class":1078,"line":1272},[1076,4663,4664],{"class":1130},"  resetAll",[1076,4666,1134],{"class":1086},[1076,4668,2149],{"class":4669},"sygFZ",[1076,4671,1156],{"class":1126},[1076,4673,4674],{"class":1964}," string",[1076,4676,4677],{"class":1086},")",[1076,4679,1156],{"class":1126},[1076,4681,4682],{"class":1964}," Promise",[1076,4684,4685],{"class":1086},"\u003C",[1076,4687,4688],{"class":1964},"void",[1076,4690,4691],{"class":1086},">\n",[1076,4693,4694],{"class":1078,"line":1285},[1076,4695,2455],{"class":1086},[1076,4697,4698],{"class":1078,"line":1297},[1076,4699,1112],{"emptyLinePlaceholder":8},[1076,4701,4702,4705,4708,4710,4712,4714,4716,4718],{"class":1078,"line":1754},[1076,4703,4704],{"class":1118},"let",[1076,4706,4707],{"class":1090}," instance",[1076,4709,1156],{"class":1126},[1076,4711,4627],{"class":1964},[1076,4713,4641],{"class":1126},[1076,4715,2437],{"class":1964},[1076,4717,1127],{"class":1126},[1076,4719,4720],{"class":1137}," null\n",[1076,4722,4723],{"class":1078,"line":1765},[1076,4724,1112],{"emptyLinePlaceholder":8},[1076,4726,4727,4730,4733,4735,4738,4740,4743,4745,4747,4749,4751,4753,4755,4757],{"class":1078,"line":1770},[1076,4728,4729],{"class":1118},"function",[1076,4731,4732],{"class":1130}," build",[1076,4734,1134],{"class":1086},[1076,4736,4737],{"class":4669},"pool",[1076,4739,1156],{"class":1126},[1076,4741,4742],{"class":1964}," Pool",[1076,4744,1140],{"class":1086},[1076,4746,1497],{"class":4669},[1076,4748,1156],{"class":1126},[1076,4750,4674],{"class":1964},[1076,4752,4677],{"class":1086},[1076,4754,1156],{"class":1126},[1076,4756,4627],{"class":1964},[1076,4758,4529],{"class":1086},[1076,4760,4761,4764,4766,4768,4770,4772,4774,4776,4778],{"class":1078,"line":1775},[1076,4762,4763],{"class":1118},"  const",[1076,4765,1621],{"class":1122},[1076,4767,1127],{"class":1126},[1076,4769,1131],{"class":1130},[1076,4771,1134],{"class":1086},[1076,4773,1019],{"class":1137},[1076,4775,1140],{"class":1086},[1076,4777,1143],{"class":1137},[1076,4779,1146],{"class":1086},[1076,4781,4782,4785],{"class":1078,"line":1780},[1076,4783,4784],{"class":1090},"    dbName",[1076,4786,1166],{"class":1086},[1076,4788,4790,4793,4795,4797],{"class":1078,"line":4789},20,[1076,4791,4792],{"class":1090},"    storeClient",[1076,4794,1156],{"class":1155},[1076,4796,1177],{"class":1090},[1076,4798,1166],{"class":1086},[1076,4800,4802,4805,4807,4809,4811,4813],{"class":1078,"line":4801},21,[1076,4803,4804],{"class":1090},"    storeType",[1076,4806,1156],{"class":1155},[1076,4808,1100],{"class":1099},[1076,4810,1192],{"class":1103},[1076,4812,1163],{"class":1099},[1076,4814,1166],{"class":1086},[1076,4816,4818,4821,4823,4825,4828,4830],{"class":1078,"line":4817},22,[1076,4819,4820],{"class":1090},"    tableName",[1076,4822,1156],{"class":1155},[1076,4824,1100],{"class":1099},[1076,4826,4827],{"class":1103},"my_endpoint",[1076,4829,1163],{"class":1099},[1076,4831,1166],{"class":1086},[1076,4833,4835,4838,4840,4842,4845,4847],{"class":1078,"line":4834},23,[1076,4836,4837],{"class":1090},"    keyPrefix",[1076,4839,1156],{"class":1155},[1076,4841,1100],{"class":1099},[1076,4843,4844],{"class":1103},"my_endpoint_burst",[1076,4846,1163],{"class":1099},[1076,4848,1166],{"class":1086},[1076,4850,4852,4855,4857,4859],{"class":1078,"line":4851},24,[1076,4853,4854],{"class":1090},"    points",[1076,4856,1156],{"class":1155},[1076,4858,1280],{"class":1241},[1076,4860,1166],{"class":1086},[1076,4862,4864,4867,4869,4871],{"class":1078,"line":4863},25,[1076,4865,4866],{"class":1090},"    duration",[1076,4868,1156],{"class":1155},[1076,4870,1242],{"class":1241},[1076,4872,1166],{"class":1086},[1076,4874,4876,4879,4881,4883],{"class":1078,"line":4875},26,[1076,4877,4878],{"class":1090},"    blockDuration",[1076,4880,1156],{"class":1155},[1076,4882,1267],{"class":1241},[1076,4884,1166],{"class":1086},[1076,4886,4888,4891,4893,4895],{"class":1078,"line":4887},27,[1076,4889,4890],{"class":1090},"    inMemoryBlockOnConsumed",[1076,4892,1156],{"class":1155},[1076,4894,3244],{"class":1241},[1076,4896,1166],{"class":1086},[1076,4898,4900,4903,4905,4907],{"class":1078,"line":4899},28,[1076,4901,4902],{"class":1090},"    inMemoryBlockDuration",[1076,4904,1156],{"class":1155},[1076,4906,1267],{"class":1241},[1076,4908,1166],{"class":1086},[1076,4910,4912],{"class":1078,"line":4911},29,[1076,4913,4914],{"class":1086},"  })\n",[1076,4916,4918],{"class":1078,"line":4917},30,[1076,4919,1112],{"emptyLinePlaceholder":8},[1076,4921,4923,4925,4927,4929,4931,4933,4935,4937,4939],{"class":1078,"line":4922},31,[1076,4924,4763],{"class":1118},[1076,4926,1700],{"class":1122},[1076,4928,1127],{"class":1126},[1076,4930,1131],{"class":1130},[1076,4932,1134],{"class":1086},[1076,4934,1019],{"class":1137},[1076,4936,1140],{"class":1086},[1076,4938,1143],{"class":1137},[1076,4940,1146],{"class":1086},[1076,4942,4944,4946],{"class":1078,"line":4943},32,[1076,4945,4784],{"class":1090},[1076,4947,1166],{"class":1086},[1076,4949,4951,4953,4955,4957],{"class":1078,"line":4950},33,[1076,4952,4792],{"class":1090},[1076,4954,1156],{"class":1155},[1076,4956,1177],{"class":1090},[1076,4958,1166],{"class":1086},[1076,4960,4962,4964,4966,4968,4970,4972],{"class":1078,"line":4961},34,[1076,4963,4804],{"class":1090},[1076,4965,1156],{"class":1155},[1076,4967,1100],{"class":1099},[1076,4969,1192],{"class":1103},[1076,4971,1163],{"class":1099},[1076,4973,1166],{"class":1086},[1076,4975,4977,4979,4981,4983,4985,4987],{"class":1078,"line":4976},35,[1076,4978,4820],{"class":1090},[1076,4980,1156],{"class":1155},[1076,4982,1100],{"class":1099},[1076,4984,4827],{"class":1103},[1076,4986,1163],{"class":1099},[1076,4988,1166],{"class":1086},[1076,4990,4992,4994,4996,4998,5001,5003],{"class":1078,"line":4991},36,[1076,4993,4837],{"class":1090},[1076,4995,1156],{"class":1155},[1076,4997,1100],{"class":1099},[1076,4999,5000],{"class":1103},"my_endpoint_slow",[1076,5002,1163],{"class":1099},[1076,5004,1166],{"class":1086},[1076,5006,5008,5010,5012,5015],{"class":1078,"line":5007},37,[1076,5009,4854],{"class":1090},[1076,5011,1156],{"class":1155},[1076,5013,5014],{"class":1241}," 10",[1076,5016,1166],{"class":1086},[1076,5018,5020,5022,5024,5026],{"class":1078,"line":5019},38,[1076,5021,4866],{"class":1090},[1076,5023,1156],{"class":1155},[1076,5025,1749],{"class":1241},[1076,5027,1166],{"class":1086},[1076,5029,5031,5033,5035,5037],{"class":1078,"line":5030},39,[1076,5032,4878],{"class":1090},[1076,5034,1156],{"class":1155},[1076,5036,1749],{"class":1241},[1076,5038,1166],{"class":1086},[1076,5040,5042,5044,5046,5049],{"class":1078,"line":5041},40,[1076,5043,4890],{"class":1090},[1076,5045,1156],{"class":1155},[1076,5047,5048],{"class":1241}," 11",[1076,5050,1166],{"class":1086},[1076,5052,5054,5056,5058,5060],{"class":1078,"line":5053},41,[1076,5055,4902],{"class":1090},[1076,5057,1156],{"class":1155},[1076,5059,1749],{"class":1241},[1076,5061,1166],{"class":1086},[1076,5063,5065],{"class":1078,"line":5064},42,[1076,5066,4914],{"class":1086},[1076,5068,5070],{"class":1078,"line":5069},43,[1076,5071,1112],{"emptyLinePlaceholder":8},[1076,5073,5075,5077,5080,5082,5084,5086,5088,5090,5092],{"class":1078,"line":5074},44,[1076,5076,4763],{"class":1118},[1076,5078,5079],{"class":1122}," ipLimiter",[1076,5081,1127],{"class":1126},[1076,5083,1131],{"class":1130},[1076,5085,1134],{"class":1086},[1076,5087,1019],{"class":1137},[1076,5089,1140],{"class":1086},[1076,5091,1143],{"class":1137},[1076,5093,1146],{"class":1086},[1076,5095,5097,5099],{"class":1078,"line":5096},45,[1076,5098,4784],{"class":1090},[1076,5100,1166],{"class":1086},[1076,5102,5104,5106,5108,5110],{"class":1078,"line":5103},46,[1076,5105,4792],{"class":1090},[1076,5107,1156],{"class":1155},[1076,5109,1177],{"class":1090},[1076,5111,1166],{"class":1086},[1076,5113,5115,5117,5119,5121,5123,5125],{"class":1078,"line":5114},47,[1076,5116,4804],{"class":1090},[1076,5118,1156],{"class":1155},[1076,5120,1100],{"class":1099},[1076,5122,1192],{"class":1103},[1076,5124,1163],{"class":1099},[1076,5126,1166],{"class":1086},[1076,5128,5130,5132,5134,5136,5138,5140],{"class":1078,"line":5129},48,[1076,5131,4820],{"class":1090},[1076,5133,1156],{"class":1155},[1076,5135,1100],{"class":1099},[1076,5137,4827],{"class":1103},[1076,5139,1163],{"class":1099},[1076,5141,1166],{"class":1086},[1076,5143,5145,5147,5149,5151,5154,5156],{"class":1078,"line":5144},49,[1076,5146,4837],{"class":1090},[1076,5148,1156],{"class":1155},[1076,5150,1100],{"class":1099},[1076,5152,5153],{"class":1103},"my_endpoint_ip",[1076,5155,1163],{"class":1099},[1076,5157,1166],{"class":1086},[1076,5159,5161,5163,5165,5168],{"class":1078,"line":5160},50,[1076,5162,4854],{"class":1090},[1076,5164,1156],{"class":1155},[1076,5166,5167],{"class":1241}," 20",[1076,5169,1166],{"class":1086},[1076,5171,5173,5175,5177,5180],{"class":1078,"line":5172},51,[1076,5174,4866],{"class":1090},[1076,5176,1156],{"class":1155},[1076,5178,5179],{"class":1241}," 86400",[1076,5181,1166],{"class":1086},[1076,5183,5185,5187,5189,5192],{"class":1078,"line":5184},52,[1076,5186,4878],{"class":1090},[1076,5188,1156],{"class":1155},[1076,5190,5191],{"class":1241}," 14400",[1076,5193,1166],{"class":1086},[1076,5195,5197,5199,5201,5204],{"class":1078,"line":5196},53,[1076,5198,4890],{"class":1090},[1076,5200,1156],{"class":1155},[1076,5202,5203],{"class":1241}," 21",[1076,5205,1166],{"class":1086},[1076,5207,5209,5211,5213,5215],{"class":1078,"line":5208},54,[1076,5210,4902],{"class":1090},[1076,5212,1156],{"class":1155},[1076,5214,5191],{"class":1241},[1076,5216,1166],{"class":1086},[1076,5218,5220],{"class":1078,"line":5219},55,[1076,5221,4914],{"class":1086},[1076,5223,5225],{"class":1078,"line":5224},56,[1076,5226,1112],{"emptyLinePlaceholder":8},[1076,5228,5230,5233],{"class":1078,"line":5229},57,[1076,5231,5232],{"class":1082},"  return",[1076,5234,4529],{"class":1086},[1076,5236,5238,5241,5243,5245,5247,5249,5251,5253,5255,5257],{"class":1078,"line":5237},58,[1076,5239,5240],{"class":1090},"    uniLimiter",[1076,5242,1156],{"class":1155},[1076,5244,1790],{"class":1130},[1076,5246,1793],{"class":1086},[1076,5248,1796],{"class":1090},[1076,5250,1140],{"class":1086},[1076,5252,1801],{"class":1090},[1076,5254,1804],{"class":1086},[1076,5256,1143],{"class":1137},[1076,5258,5259],{"class":1086},"),\n",[1076,5261,5263,5266],{"class":1078,"line":5262},59,[1076,5264,5265],{"class":1090},"    ipLimiter",[1076,5267,1166],{"class":1086},[1076,5269,5271,5274,5276,5279,5281,5283,5285,5287,5289,5292],{"class":1078,"line":5270},60,[1076,5272,5273],{"class":1130},"    resetAll",[1076,5275,1156],{"class":1155},[1076,5277,5278],{"class":1118}," async",[1076,5280,2111],{"class":1086},[1076,5282,2149],{"class":4669},[1076,5284,1156],{"class":1126},[1076,5286,4674],{"class":1964},[1076,5288,2119],{"class":1086},[1076,5290,5291],{"class":1118},"=>",[1076,5293,4529],{"class":1086},[1076,5295,5297,5300,5302,5304,5306],{"class":1078,"line":5296},61,[1076,5298,5299],{"class":1082},"      await",[1076,5301,4682],{"class":1964},[1076,5303,1048],{"class":1086},[1076,5305,1872],{"class":1130},[1076,5307,5308],{"class":1086},"([\n",[1076,5310,5312,5315,5317,5319,5321,5323],{"class":1078,"line":5311},62,[1076,5313,5314],{"class":1090},"        burst",[1076,5316,1048],{"class":1086},[1076,5318,3105],{"class":1130},[1076,5320,1134],{"class":1086},[1076,5322,2149],{"class":1090},[1076,5324,5259],{"class":1086},[1076,5326,5328,5331,5333,5335,5337,5339],{"class":1078,"line":5327},63,[1076,5329,5330],{"class":1090},"        slow",[1076,5332,1048],{"class":1086},[1076,5334,3105],{"class":1130},[1076,5336,1134],{"class":1086},[1076,5338,2149],{"class":1090},[1076,5340,5259],{"class":1086},[1076,5342,5344,5347,5349,5351,5353,5355],{"class":1078,"line":5343},64,[1076,5345,5346],{"class":1090},"        ipLimiter",[1076,5348,1048],{"class":1086},[1076,5350,3105],{"class":1130},[1076,5352,1134],{"class":1086},[1076,5354,2149],{"class":1090},[1076,5356,5259],{"class":1086},[1076,5358,5360],{"class":1078,"line":5359},65,[1076,5361,5362],{"class":1086},"      ])\n",[1076,5364,5366],{"class":1078,"line":5365},66,[1076,5367,5368],{"class":1086},"    },\n",[1076,5370,5372],{"class":1078,"line":5371},67,[1076,5373,5374],{"class":1086},"  }\n",[1076,5376,5378],{"class":1078,"line":5377},68,[1076,5379,2455],{"class":1086},[1076,5381,5383],{"class":1078,"line":5382},69,[1076,5384,1112],{"emptyLinePlaceholder":8},[1076,5386,5388,5391,5394,5397,5399,5401,5403,5405,5407,5409,5411,5413,5415,5417,5420],{"class":1078,"line":5387},70,[1076,5389,5390],{"class":1082},"export",[1076,5392,5393],{"class":1118}," function",[1076,5395,5396],{"class":1130}," init",[1076,5398,1134],{"class":1086},[1076,5400,4737],{"class":4669},[1076,5402,1156],{"class":1126},[1076,5404,4742],{"class":1964},[1076,5406,1140],{"class":1086},[1076,5408,1497],{"class":4669},[1076,5410,1156],{"class":1126},[1076,5412,4674],{"class":1964},[1076,5414,4677],{"class":1086},[1076,5416,1156],{"class":1126},[1076,5418,5419],{"class":1964}," void",[1076,5421,4529],{"class":1086},[1076,5423,5425,5428,5430,5432,5435,5437,5439,5441,5443,5445,5447,5449,5451],{"class":1078,"line":5424},71,[1076,5426,5427],{"class":1082},"  if",[1076,5429,2111],{"class":1086},[1076,5431,2024],{"class":1126},[1076,5433,5434],{"class":1090},"instance",[1076,5436,2119],{"class":1086},[1076,5438,5434],{"class":1090},[1076,5440,1127],{"class":1126},[1076,5442,4732],{"class":1130},[1076,5444,1134],{"class":1086},[1076,5446,4737],{"class":1090},[1076,5448,1140],{"class":1086},[1076,5450,1497],{"class":1090},[1076,5452,1809],{"class":1086},[1076,5454,5456],{"class":1078,"line":5455},72,[1076,5457,2455],{"class":1086},[1076,5459,5461],{"class":1078,"line":5460},73,[1076,5462,1112],{"emptyLinePlaceholder":8},[1076,5464,5466,5468,5470,5473,5476,5478,5480],{"class":1078,"line":5465},74,[1076,5467,5390],{"class":1082},[1076,5469,5393],{"class":1118},[1076,5471,5472],{"class":1130}," getLimiters",[1076,5474,5475],{"class":1086},"()",[1076,5477,1156],{"class":1126},[1076,5479,4627],{"class":1964},[1076,5481,4529],{"class":1086},[1076,5483,5485,5487,5489,5491,5493,5495,5498,5502,5505,5507,5509,5512,5514],{"class":1078,"line":5484},75,[1076,5486,5427],{"class":1082},[1076,5488,2111],{"class":1086},[1076,5490,2024],{"class":1126},[1076,5492,5434],{"class":1090},[1076,5494,2119],{"class":1086},[1076,5496,5497],{"class":1082},"throw",[1076,5499,5501],{"class":5500},"sakC6"," new",[1076,5503,5504],{"class":1130}," Error",[1076,5506,1134],{"class":1086},[1076,5508,1163],{"class":1099},[1076,5510,5511],{"class":1103},"Limiters not initialized. Call init() first.",[1076,5513,1163],{"class":1099},[1076,5515,1809],{"class":1086},[1076,5517,5519,5521],{"class":1078,"line":5518},76,[1076,5520,5232],{"class":1082},[1076,5522,5523],{"class":1090}," instance\n",[1076,5525,5527],{"class":1078,"line":5526},77,[1076,5528,2455],{"class":1086},[856,5530,5531,5532,5535,5536,5539],{},"Call ",[868,5533,5534],{},"init(pool, dbName)"," once at application startup (after your MySQL pool is ready). From then on, every call to ",[868,5537,5538],{},"getLimiters()"," returns the same singleton.",[1353,5541,5543],{"id":5542},"create-consecutive-caches","Create consecutive caches",[856,5545,5546,5547,5549,5550,5553],{},"Each ",[868,5548,2166],{}," call needs its own ",[860,5551,907],{"href":905,"rel":5552},[864]," to track consecutive failures. Declare them at the module level so they persist across requests:",[1067,5555,5557],{"className":1069,"code":5556,"language":1071,"meta":1072,"style":1072},"import { makeConsecutiveCache } from '@riavzon\u002Fauth'\n\nconst consecutiveForIp = makeConsecutiveCache\u003C{ countData: number }>(500, 60_000)\nconst consecutiveForComposite = makeConsecutiveCache\u003C{ countData: number }>(500, 1800_000)\n",[868,5558,5559,5577,5581,5610],{"__ignoreMap":1072},[1076,5560,5561,5563,5565,5567,5569,5571,5573,5575],{"class":1078,"line":1079},[1076,5562,1083],{"class":1082},[1076,5564,1087],{"class":1086},[1076,5566,1927],{"class":1090},[1076,5568,1093],{"class":1086},[1076,5570,1096],{"class":1082},[1076,5572,1100],{"class":1099},[1076,5574,1047],{"class":1103},[1076,5576,1106],{"class":1099},[1076,5578,5579],{"class":1078,"line":1109},[1076,5580,1112],{"emptyLinePlaceholder":8},[1076,5582,5583,5585,5588,5590,5592,5594,5596,5598,5600,5602,5604,5606,5608],{"class":1078,"line":1115},[1076,5584,1119],{"class":1118},[1076,5586,5587],{"class":1122}," consecutiveForIp",[1076,5589,1127],{"class":1126},[1076,5591,1953],{"class":1130},[1076,5593,1956],{"class":1086},[1076,5595,1959],{"class":1090},[1076,5597,1156],{"class":1126},[1076,5599,1965],{"class":1964},[1076,5601,1968],{"class":1086},[1076,5603,1971],{"class":1241},[1076,5605,1140],{"class":1086},[1076,5607,1976],{"class":1241},[1076,5609,1809],{"class":1086},[1076,5611,5612,5614,5617,5619,5621,5623,5625,5627,5629,5631,5633,5635,5638],{"class":1078,"line":1149},[1076,5613,1119],{"class":1118},[1076,5615,5616],{"class":1122}," consecutiveForComposite",[1076,5618,1127],{"class":1126},[1076,5620,1953],{"class":1130},[1076,5622,1956],{"class":1086},[1076,5624,1959],{"class":1090},[1076,5626,1156],{"class":1126},[1076,5628,1965],{"class":1964},[1076,5630,1968],{"class":1086},[1076,5632,1971],{"class":1241},[1076,5634,1140],{"class":1086},[1076,5636,5637],{"class":1241},"1800_000",[1076,5639,1809],{"class":1086},[1353,5641,5643],{"id":5642},"apply-guards-in-sequence","Apply guards in sequence",[856,5645,5531,5646,5648],{},[868,5647,2166],{}," for each limiter layer. Order the checks from cheapest to most specific: IP first, then identity, then composite.",[1067,5650,5652],{"className":1069,"code":5651,"language":1071,"meta":1072,"style":1072},"import { guard } from '@riavzon\u002Fauth'\nimport { getLimiters } from '.\u002FmyEndpointLimiter.js'\n\nexport async function myController(req: Request, res: Response) {\n  const { uniLimiter, ipLimiter } = getLimiters()\n  const log = getLogger().child({ service: 'myApp', branch: 'myEndpoint' })\n\n  \u002F\u002F Guard 1: IP\n  if (!(await guard(ipLimiter, req.ip!, consecutiveForIp, 2, 'ip', log, res))) return\n\n  \u002F\u002F Guard 2: Composite (IP + identity)\n  const compositeKey = `${req.ip!}_${email}`\n  if (!(await guard(uniLimiter, compositeKey, consecutiveForComposite, 3, 'composite', log, res))) return\n\n  \u002F\u002F ... business logic ...\n\n  \u002F\u002F On success: clear strikes and reset limiter points\n  consecutiveForIp.delete(req.ip!)\n  consecutiveForComposite.delete(compositeKey)\n  await getLimiters().resetAll(compositeKey)\n}\n",[868,5653,5654,5672,5692,5696,5727,5749,5799,5803,5808,5866,5870,5875,5907,5959,5963,5968,5972,5977,5998,6013,6031],{"__ignoreMap":1072},[1076,5655,5656,5658,5660,5662,5664,5666,5668,5670],{"class":1078,"line":1079},[1076,5657,1083],{"class":1082},[1076,5659,1087],{"class":1086},[1076,5661,915],{"class":1090},[1076,5663,1093],{"class":1086},[1076,5665,1096],{"class":1082},[1076,5667,1100],{"class":1099},[1076,5669,1047],{"class":1103},[1076,5671,1106],{"class":1099},[1076,5673,5674,5676,5678,5681,5683,5685,5687,5690],{"class":1078,"line":1109},[1076,5675,1083],{"class":1082},[1076,5677,1087],{"class":1086},[1076,5679,5680],{"class":1090},"getLimiters",[1076,5682,1093],{"class":1086},[1076,5684,1096],{"class":1082},[1076,5686,1100],{"class":1099},[1076,5688,5689],{"class":1103},".\u002FmyEndpointLimiter.js",[1076,5691,1106],{"class":1099},[1076,5693,5694],{"class":1078,"line":1115},[1076,5695,1112],{"emptyLinePlaceholder":8},[1076,5697,5698,5700,5702,5704,5707,5709,5711,5713,5716,5718,5720,5722,5725],{"class":1078,"line":1149},[1076,5699,5390],{"class":1082},[1076,5701,5278],{"class":1118},[1076,5703,5393],{"class":1118},[1076,5705,5706],{"class":1130}," myController",[1076,5708,1134],{"class":1086},[1076,5710,2788],{"class":4669},[1076,5712,1156],{"class":1126},[1076,5714,5715],{"class":1964}," Request",[1076,5717,1140],{"class":1086},[1076,5719,2210],{"class":4669},[1076,5721,1156],{"class":1126},[1076,5723,5724],{"class":1964}," Response",[1076,5726,2440],{"class":1086},[1076,5728,5729,5731,5733,5735,5737,5739,5741,5744,5746],{"class":1078,"line":1169},[1076,5730,4763],{"class":1118},[1076,5732,1087],{"class":1086},[1076,5734,2937],{"class":1122},[1076,5736,1140],{"class":1086},[1076,5738,2605],{"class":1122},[1076,5740,1093],{"class":1086},[1076,5742,5743],{"class":1126},"=",[1076,5745,5472],{"class":1130},[1076,5747,5748],{"class":1086},"()\n",[1076,5750,5751,5753,5756,5758,5761,5764,5767,5770,5773,5775,5777,5780,5782,5784,5787,5789,5791,5794,5796],{"class":1078,"line":1182},[1076,5752,4763],{"class":1118},[1076,5754,5755],{"class":1122}," log",[1076,5757,1127],{"class":1126},[1076,5759,5760],{"class":1130}," getLogger",[1076,5762,5763],{"class":1086},"().",[1076,5765,5766],{"class":1130},"child",[1076,5768,5769],{"class":1086},"({ ",[1076,5771,5772],{"class":1090},"service",[1076,5774,1156],{"class":1155},[1076,5776,1100],{"class":1099},[1076,5778,5779],{"class":1103},"myApp",[1076,5781,1163],{"class":1099},[1076,5783,1140],{"class":1086},[1076,5785,5786],{"class":1090},"branch",[1076,5788,1156],{"class":1155},[1076,5790,1100],{"class":1099},[1076,5792,5793],{"class":1103},"myEndpoint",[1076,5795,1163],{"class":1099},[1076,5797,5798],{"class":1086}," })\n",[1076,5800,5801],{"class":1078,"line":1199},[1076,5802,1112],{"emptyLinePlaceholder":8},[1076,5804,5805],{"class":1078,"line":1216},[1076,5806,5807],{"class":1684},"  \u002F\u002F Guard 1: IP\n",[1076,5809,5810,5812,5814,5816,5818,5820,5822,5824,5826,5828,5830,5832,5834,5836,5838,5840,5842,5844,5846,5848,5850,5852,5854,5856,5858,5860,5863],{"class":1078,"line":1233},[1076,5811,5427],{"class":1082},[1076,5813,2111],{"class":1086},[1076,5815,2024],{"class":1126},[1076,5817,1134],{"class":1086},[1076,5819,2777],{"class":1082},[1076,5821,1997],{"class":1130},[1076,5823,1134],{"class":1086},[1076,5825,2605],{"class":1090},[1076,5827,1140],{"class":1086},[1076,5829,2788],{"class":1090},[1076,5831,1048],{"class":1086},[1076,5833,2021],{"class":1090},[1076,5835,2024],{"class":1126},[1076,5837,1140],{"class":1086},[1076,5839,2799],{"class":1090},[1076,5841,1140],{"class":1086},[1076,5843,2179],{"class":1241},[1076,5845,1140],{"class":1086},[1076,5847,1163],{"class":1099},[1076,5849,2021],{"class":1103},[1076,5851,1163],{"class":1099},[1076,5853,1140],{"class":1086},[1076,5855,2203],{"class":1090},[1076,5857,1140],{"class":1086},[1076,5859,2210],{"class":1090},[1076,5861,5862],{"class":1086},"))) ",[1076,5864,5865],{"class":1082},"return\n",[1076,5867,5868],{"class":1078,"line":1247},[1076,5869,1112],{"emptyLinePlaceholder":8},[1076,5871,5872],{"class":1078,"line":1259},[1076,5873,5874],{"class":1684},"  \u002F\u002F Guard 2: Composite (IP + identity)\n",[1076,5876,5877,5879,5881,5883,5885,5887,5889,5891,5893,5895,5897,5899,5901,5903,5905],{"class":1078,"line":1272},[1076,5878,4763],{"class":1118},[1076,5880,2894],{"class":1122},[1076,5882,1127],{"class":1126},[1076,5884,2899],{"class":1103},[1076,5886,2902],{"class":1118},[1076,5888,2788],{"class":1090},[1076,5890,1048],{"class":2907},[1076,5892,2021],{"class":1090},[1076,5894,2024],{"class":1126},[1076,5896,2914],{"class":1118},[1076,5898,2917],{"class":1103},[1076,5900,2902],{"class":1118},[1076,5902,2848],{"class":1090},[1076,5904,2914],{"class":1118},[1076,5906,2926],{"class":1103},[1076,5908,5909,5911,5913,5915,5917,5919,5921,5923,5925,5927,5929,5931,5934,5936,5938,5940,5942,5945,5947,5949,5951,5953,5955,5957],{"class":1078,"line":1285},[1076,5910,5427],{"class":1082},[1076,5912,2111],{"class":1086},[1076,5914,2024],{"class":1126},[1076,5916,1134],{"class":1086},[1076,5918,2777],{"class":1082},[1076,5920,1997],{"class":1130},[1076,5922,1134],{"class":1086},[1076,5924,2937],{"class":1090},[1076,5926,1140],{"class":1086},[1076,5928,2591],{"class":1090},[1076,5930,1140],{"class":1086},[1076,5932,5933],{"class":1090},"consecutiveForComposite",[1076,5935,1140],{"class":1086},[1076,5937,2183],{"class":1241},[1076,5939,1140],{"class":1086},[1076,5941,1163],{"class":1099},[1076,5943,5944],{"class":1103},"composite",[1076,5946,1163],{"class":1099},[1076,5948,1140],{"class":1086},[1076,5950,2203],{"class":1090},[1076,5952,1140],{"class":1086},[1076,5954,2210],{"class":1090},[1076,5956,5862],{"class":1086},[1076,5958,5865],{"class":1082},[1076,5960,5961],{"class":1078,"line":1297},[1076,5962,1112],{"emptyLinePlaceholder":8},[1076,5964,5965],{"class":1078,"line":1754},[1076,5966,5967],{"class":1684},"  \u002F\u002F ... business logic ...\n",[1076,5969,5970],{"class":1078,"line":1765},[1076,5971,1112],{"emptyLinePlaceholder":8},[1076,5973,5974],{"class":1078,"line":1770},[1076,5975,5976],{"class":1684},"  \u002F\u002F On success: clear strikes and reset limiter points\n",[1076,5978,5979,5982,5984,5986,5988,5990,5992,5994,5996],{"class":1078,"line":1775},[1076,5980,5981],{"class":1090},"  consecutiveForIp",[1076,5983,1048],{"class":1086},[1076,5985,3105],{"class":1130},[1076,5987,1134],{"class":1086},[1076,5989,2788],{"class":1090},[1076,5991,1048],{"class":1086},[1076,5993,2021],{"class":1090},[1076,5995,2024],{"class":1126},[1076,5997,1809],{"class":1086},[1076,5999,6000,6003,6005,6007,6009,6011],{"class":1078,"line":1780},[1076,6001,6002],{"class":1090},"  consecutiveForComposite",[1076,6004,1048],{"class":1086},[1076,6006,3105],{"class":1130},[1076,6008,1134],{"class":1086},[1076,6010,2591],{"class":1090},[1076,6012,1809],{"class":1086},[1076,6014,6015,6018,6020,6022,6025,6027,6029],{"class":1078,"line":4789},[1076,6016,6017],{"class":1082},"  await",[1076,6019,5472],{"class":1130},[1076,6021,5763],{"class":1086},[1076,6023,6024],{"class":1130},"resetAll",[1076,6026,1134],{"class":1086},[1076,6028,2591],{"class":1090},[1076,6030,1809],{"class":1086},[1076,6032,6033],{"class":1078,"line":4801},[1076,6034,2455],{"class":1086},[856,6036,6037,6038,6040,6041,6043,6044,6046],{},"If any ",[868,6039,2166],{}," call returns ",[868,6042,1143],{},", the ",[868,6045,2217],{}," response has already been sent. The controller returns immediately without running the remaining guards or the business logic.",[922,6048],{},[925,6050,6052],{"id":6051},"response-format","Response Format",[856,6054,6055],{},"When any guard or limiter rejects a request, the service returns:",[938,6057,6058,6066],{},[941,6059,6060],{},[944,6061,6062,6064],{},[947,6063,1365],{},[947,6065,2488],{},[957,6067,6068,6078,6088],{},[944,6069,6070,6074],{},[962,6071,6072],{},[876,6073,2495],{},[962,6075,6076],{},[868,6077,890],{},[944,6079,6080,6084],{},[962,6081,6082],{},[876,6083,2504],{},[962,6085,6086],{},[868,6087,2509],{},[944,6089,6090,6094],{},[962,6091,6092],{},[876,6093,2519],{},[962,6095,6096],{},[868,6097,6098],{},"{ \"error\": \"Too many requests\", \"retry\": {seconds} }",[856,6100,2334,6101,6103,6104,6106,6107,1048],{},[868,6102,894],{}," value is computed as ",[868,6105,2513],{}," with a minimum of 1 second. For keys in the global block cache, the value is the block expiration timestamp or the string ",[868,6108,6109],{},"'permanent'",[922,6111],{},[925,6113,6115],{"id":6114},"summary","Summary",[938,6117,6118,6127],{},[941,6119,6120],{},[944,6121,6122,6125],{},[947,6123,6124],{},"Concept",[947,6126,1374],{},[957,6128,6129,6139,6151,6161,6171,6185,6195,6205,6214],{},[944,6130,6131,6136],{},[962,6132,6133],{},[876,6134,6135],{},"Union limiter",[962,6137,6138],{},"Pairs a burst limiter (short window, low points) with a slow limiter (long window, higher points). Both must pass.",[944,6140,6141,6146],{},[962,6142,6143],{},[876,6144,6145],{},"Guard",[962,6147,6148,6149,1048],{},"Consumes a limiter, tracks strikes in a consecutive cache, and permanently blocks keys that exceed ",[868,6150,911],{},[944,6152,6153,6158],{},[962,6154,6155],{},[876,6156,6157],{},"Consecutive cache",[962,6159,6160],{},"LRU cache tracking how many times a key has been rate limited. Separate from the limiter's own point tracking.",[944,6162,6163,6168],{},[962,6164,6165],{},[876,6166,6167],{},"Block cache",[962,6169,6170],{},"Global 7-day LRU cache of permanently blocked keys. Checked before limiter consumption for fast-path rejection.",[944,6172,6173,6178],{},[962,6174,6175],{},[876,6176,6177],{},"Key strategies",[962,6179,6180,6181,6184],{},"IP, identity (email\u002Fsub\u002Ftoken hash\u002FuserId), composite (",[868,6182,6183],{},"IP_identity","), and global (fixed string).",[944,6186,6187,6192],{},[962,6188,6189],{},[876,6190,6191],{},"Reset on success",[962,6193,6194],{},"Login, signup, and OAuth reset all limiters and caches on successful authentication.",[944,6196,6197,6202],{},[962,6198,6199],{},[876,6200,6201],{},"Block on success",[962,6203,6204],{},"Token rotation blocks the old token hash for 3 days instead of resetting.",[944,6206,6207,6211],{},[962,6208,6209],{},[876,6210,952],{},[962,6212,6213],{},"MySQL (persistent, shared across instances) + in-memory (fast path, per-process).",[944,6215,6216,6220],{},[962,6217,6218],{},[876,6219,2211],{},[962,6221,6222,6224,6225,6227],{},[868,6223,2217],{}," with ",[868,6226,894],{}," header and JSON body.",[6229,6230,6231],"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 .s3JHE, html code.shiki .s3JHE{--shiki-light:#0070C1;--shiki-default:#0070C1;--shiki-dark:#F8F8F2}html pre.shiki code .saOXh, html code.shiki .saOXh{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#FF79C6}html pre.shiki code .sHOzp, html code.shiki .sHOzp{--shiki-light:#795E26;--shiki-default:#795E26;--shiki-dark:#50FA7B}html pre.shiki code .sjR7W, html code.shiki .sjR7W{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#BD93F9}html pre.shiki code .s34zl, html code.shiki .s34zl{--shiki-light:#001080;--shiki-default:#001080;--shiki-dark:#FF79C6}html pre.shiki code .spgvN, html code.shiki .spgvN{--shiki-light:#098658;--shiki-default:#098658;--shiki-dark:#BD93F9}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 .sghk6, html code.shiki .sghk6{--shiki-light:#008000;--shiki-default:#008000;--shiki-dark:#6272A4}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 .s1lnM, html code.shiki .s1lnM{--shiki-light:#000000FF;--shiki-default:#000000FF;--shiki-dark:#F8F8F2}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 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}",{"title":1072,"searchDepth":1109,"depth":1109,"links":6233},[6234,6238,6246,6252,6262,6272,6273,6274],{"id":927,"depth":1109,"text":928,"children":6235},[6236,6237],{"id":932,"depth":1115,"text":933},{"id":995,"depth":1115,"text":996},{"id":1040,"depth":1109,"text":1041,"children":6239},[6240,6241,6242,6243,6244,6245],{"id":1051,"depth":1115,"text":1054},{"id":1577,"depth":1115,"text":1580},{"id":915,"depth":1115,"text":915},{"id":2344,"depth":1115,"text":2275},{"id":2545,"depth":1115,"text":2548},{"id":2647,"depth":1115,"text":1927},{"id":2752,"depth":1109,"text":2753,"children":6247},[6248,6249,6250,6251],{"id":2759,"depth":1115,"text":2760},{"id":2824,"depth":1115,"text":2825},{"id":2878,"depth":1115,"text":2879},{"id":2972,"depth":1115,"text":2973},{"id":3042,"depth":1109,"text":3043,"children":6253},[6254,6255,6256,6257,6258,6259,6260,6261],{"id":1209,"depth":1115,"text":103},{"id":3161,"depth":1115,"text":3162},{"id":3290,"depth":1115,"text":99},{"id":3327,"depth":1115,"text":111},{"id":3368,"depth":1115,"text":3369},{"id":3420,"depth":1115,"text":3421},{"id":3466,"depth":1115,"text":3467},{"id":3475,"depth":1115,"text":3476},{"id":3514,"depth":1109,"text":3515,"children":6263},[6264,6265,6266,6267,6268,6269,6270,6271],{"id":3555,"depth":1115,"text":3558},{"id":3680,"depth":1115,"text":3683},{"id":3813,"depth":1115,"text":3816},{"id":3928,"depth":1115,"text":3931},{"id":4072,"depth":1115,"text":4075},{"id":4140,"depth":1115,"text":4143},{"id":4249,"depth":1115,"text":4252},{"id":4387,"depth":1115,"text":4390},{"id":4502,"depth":1109,"text":4503},{"id":6051,"depth":1109,"text":6052},{"id":6114,"depth":1109,"text":6115},"How the IAM service uses layered rate limiters with union pairing, strike-based blocking, consecutive failure caches, and per-endpoint limiter groups to protect every sensitive route.","md","i-lucide-gauge",{},null,"---\ntitle: Rate Limiting\ndescription: How the IAM service uses layered rate limiters with union pairing, strike-based blocking, consecutive failure caches, and per-endpoint limiter groups to protect every sensitive route.\nicon: i-lucide-gauge\n---\n\nThe IAM service uses [rate-limiter-flexible](https:\u002F\u002Fgithub.com\u002Fanimir\u002Fnode-rate-limiter-flexible) for all rate limiting. Every sensitive endpoint has its own named limiter group built at startup from the `rate_limiters` section of the service configuration. The built-in limiters store state in MySQL, via a dedicated callback-based pool separate from the main auth pool, and maintain in-memory mirrors for fast lookups.\n\nThe system layers multiple limiters per endpoint. A typical endpoint has a **union limiter** combining a burst and slow limiter into a single gate, an **IP limiter**, and an **identity limiter** (keyed on email, OAuth subject, token hash, or user ID). All layers must pass for a request to proceed. If any layer rejects, the service returns `429 Too Many Requests` with a `Retry-After` header.\n\nOn top of the limiter layer, a **strike system** tracks consecutive failures per key in an [LRU cache](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCache_replacement_policies#Least_recently_used_(LRU)). When a key accumulates enough strikes (the `maxBans` threshold), the `guard` function permanently blocks the key via the limiter's `block()` method and adds it to a global block cache. Blocked keys are rejected immediately on subsequent requests without consuming limiter points.\n\n---\n\n## Overview\n\n### Dual-Layer Design\n\nEach rate limiter in the system operates on two storage layers simultaneously:\n\n| Layer | Storage | Purpose |\n|---|---|---|\n| **Primary** | MySQL | Persistent state. Survives process restarts. Shared across multiple IAM instances behind a load balancer. |\n| **In-memory** | Node.js process memory | Fast path. Blocks requests locally when `inMemoryBlockOnConsumed` is exceeded, without hitting the database. |\n\nThe in-memory layer acts as an insurance mechanism. If the MySQL connection fails, the in-memory limiter continues to enforce rate limits. If both layers are healthy, the in-memory layer short-circuits obvious violations before they reach the database.\n\n### Storage Pool\n\nThe rate limiter pool is separate from the main authentication pool. It uses the callback-based `mysql` API because `rate-limiter-flexible` requires a callback-style connection. Configure it in the `store.rate_limiters_pool` section:\n\n::field-group\n  ::field{name=\"store.rate_limiters_pool.store\" type=\"mysql.PoolOptions\" required}\n  MySQL pool options for the rate limiter connection. Same format as the main pool but typically points to a separate database.\n  ::\n\n  ::field{name=\"store.rate_limiters_pool.dbName\" type=\"string\" required}\n  Database name for rate limiter tables. The service uses this value to create the database if it does not exist.\n  ::\n::\n\nSee [Database](\u002Fdocs\u002Fiam\u002Fessentials\u002Fdatabase) for more details.\n\n---\n\n## Core Utilities\n\nThe rate limiting system is built from four composable functions. When using the IAM service as a library, all four are exported from `@riavzon\u002Fauth`.\n\n### `makeRateLimiter`\n\nCreates a single rate limiter instance. The `sql` parameter controls whether the limiter persists state in MySQL or runs purely in memory. The `BlackWhiteList` parameter wraps the limiter with an allow\u002Fdeny list layer.\n\n```ts\nimport { makeRateLimiter } from '@riavzon\u002Fauth'\n\nconst limiter = makeRateLimiter(true, false, {\n  dbName: 'rate_limiters',\n  storeClient: pool,\n  storeType: 'mysql2',\n  tableName: 'login',\n  keyPrefix: 'login_burst',\n  points: 1,\n  duration: 1,\n  blockDuration: 1800,\n  inMemoryBlockOnConsumed: 2,\n  inMemoryBlockDuration: 1800,\n})\n```\n\n::field-group\n  ::field{name=\"sql\" type=\"boolean\" required}\n  When `true`, creates a `RateLimiterMySQL` backed by the provided MySQL pool with an in-memory `RateLimiterMemory` as the insurance fallback. When `false`, creates a pure `RateLimiterMemory`.\n  ::\n\n  ::field{name=\"BlackWhiteList\" type=\"boolean\" required}\n  When `true`, wraps the limiter in `RLWrapperBlackAndWhite`. Whitelisted keys bypass rate limiting entirely. The default whitelist check matches `10.10.10.10` (a test IP).\n  ::\n\n  ::field{name=\"settings\" type=\"object\" required}\n  Limiter configuration. Contains both the core rate-limiter-flexible fields and MySQL-specific fields which is required when `sql` is `true`.\n  ::\n::\n\n#### Settings Fields\n\n| Field | Type | Required | Description |\n|---|---|---|---|\n| `keyPrefix` | `string` | Yes | Unique prefix for this limiter in the store. Used as the MySQL table column key and the in-memory map key. |\n| `points` | `number` | Yes | Maximum number of requests allowed within the `duration` window. |\n| `duration` | `number` | Yes | Time window in **seconds**. Points reset after this period. |\n| `blockDuration` | `number` | Yes | How long, in seconds, to block the key after all points are consumed. `0` means permanent block. |\n| `inMemoryBlockOnConsumed` | `number` | No | When consumed points reach this threshold, the in-memory layer blocks the key without checking MySQL. Typically set slightly above `points` to catch bursts. |\n| `inMemoryBlockDuration` | `number` | No | How long, in seconds, the in-memory block lasts. Usually matches `blockDuration`. |\n| `dbName` | `string` | When `sql: true` | The MySQL database name for this limiter's storage. |\n| `storeClient` | `Pool` | When `sql: true` | The callback-based MySQL pool from `poolForLibrary()`. |\n| `storeType` | `string` | When `sql: true` | Always `'mysql2'`. Identifies the pool type for rate-limiter-flexible. |\n| `tableName` | `string` | When `sql: true` | The MySQL table name where this limiter stores its counters. Multiple limiters can share a table if they have different `keyPrefix` values. |\n\n### `unionLimiter`\n\nCombines two or more individual limiters into a single gate. Both limiters in the union are consumed on every attempt. If either limiter rejects, the request is blocked.\n\n```ts\nimport { makeRateLimiter, unionLimiter } from '@riavzon\u002Fauth'\n\nconst burst = makeRateLimiter(true, false, {\n  keyPrefix: 'login_burst',\n  points: 1,\n  duration: 1,\n  blockDuration: 1800,\n  \u002F\u002F ... other fields\n})\n\nconst slow = makeRateLimiter(true, false, {\n  keyPrefix: 'login_slow',\n  points: 5,\n  duration: 3600,\n  blockDuration: 1800,\n  \u002F\u002F ... other fields\n})\n\nconst loginUnion = unionLimiter([burst, slow], false)\n```\n\n::field-group\n  ::field{name=\"limiters\" type=\"Array\u003CRateLimiterMemory | RateLimiterMySQL>\" required}\n  The individual limiters to combine. Typically a burst limiter (low points, short duration) and a slow limiter (higher points, longer duration).\n  ::\n\n  ::field{name=\"blackWhiteList\" type=\"boolean\" required}\n  When `true`, wraps the union in `RLWrapperBlackAndWhite`. The `block()` and `delete()` methods from the underlying union are forwarded to the wrapper so the `guard` function can still block and reset keys.\n  ::\n::\n\nThe returned object extends `RateLimiterUnion` with two additional methods:\n\n| Method | Description |\n|---|---|\n| `block(key, durationSec)` | Blocks the key on **all** underlying limiters simultaneously. `durationSec = 0` means permanent. |\n| `delete(key)` | Deletes the key from **all** underlying limiters, resetting their state. |\n\n::tip\nThe burst + slow pairing is the foundation of every built-in limiter group. The burst limiter catches automated flooding (e.g., 1 request per second). The slow limiter catches distributed or low-rate credential stuffing (e.g., 5 attempts per hour). A legitimate user who types slowly will never hit either limit. An attacker who spreads attempts over time will hit the slow limiter even if they avoid the burst threshold.\n::\n\n### `guard`\n\nThe primary entry point for applying rate limiting in a controller. It combines limiter consumption, consecutive failure tracking, and permanent blocking into a single call. Returns `true` if the request is allowed, `false` if rate limited.\n\n```ts\nimport { guard, makeConsecutiveCache } from '@riavzon\u002Fauth'\n\nconst consecutiveCache = makeConsecutiveCache\u003C{ countData: number }>(500, 60_000)\n\nconst allowed = await guard(\n  limiter,                \u002F\u002F Any limiter: Memory, MySQL, or BlockableUnion\n  req.ip!,                \u002F\u002F The key to rate limit on\n  consecutiveCache,       \u002F\u002F LRU cache tracking consecutive failures\n  2,                      \u002F\u002F maxBans: strikes before permanent block\n  'ip',                   \u002F\u002F Label for log messages\n  log,                    \u002F\u002F Pino logger\n  res,                    \u002F\u002F Express response (guard sends 429 automatically)\n  3600                    \u002F\u002F Optional: block duration in seconds (0 = permanent)\n)\n\nif (!allowed) return      \u002F\u002F Response already sent\n```\n\n::field-group\n  ::field{name=\"limiter\" type=\"Limiter\" required}\n  The rate limiter to consume against. Can be a `RateLimiterMemory`, `RateLimiterMySQL`, or a `BlockableUnion` from `unionLimiter()`.\n  ::\n\n  ::field{name=\"key\" type=\"string\" required}\n  The identifier to rate limit on. Typically an IP address, email, token hash, or composite key like `${ip}_${email}`. Keys longer than 255 characters are automatically SHA-256 hashed before use.\n  ::\n\n  ::field{name=\"cache\" type=\"LRUCache\u003Cstring, { countData: number }>\" required}\n  An LRU cache that tracks how many consecutive times this key has been rate limited. Each `guard()` call that fails increments the counter. Successful calls do not clear the counter (that is the controller's responsibility).\n  ::\n\n  ::field{name=\"maxBans\" type=\"number\" required}\n  The number of consecutive failures required before the key is permanently blocked. Lower values are stricter. The built-in limiters use `1` for high-confidence keys (token hashes), `2` for identity keys (IPs, emails), and `3` for composite keys.\n  ::\n\n  ::field{name=\"label\" type=\"string\" required}\n  Descriptive string included in log entries. Used to identify which limiter triggered in the logs (e.g., `'ip'`, `'email'`, `'compositeKey'`).\n  ::\n\n  ::field{name=\"log\" type=\"pino.Logger\" required}\n  Pino logger instance. The guard logs warnings on strikes and blocks, and info messages on successful passes (including remaining points and next allowed time).\n  ::\n\n  ::field{name=\"res\" type=\"Response\" required}\n  Express response object. When the guard blocks a request, it sends the `429` response directly, including the `Retry-After` header and a JSON error body.\n  ::\n\n  ::field{name=\"seconds\" type=\"number\"}\n  Block duration in seconds when `maxBans` is exceeded. Defaults to `0` (permanent block for the duration of the block cache TTL, which is 7 days). Set to a positive value for time-limited blocks.\n  ::\n::\n\n#### How the Guard Works\n\n::steps{level=\"4\"}\n#### Check the global block cache\nThe guard maintains a process-wide LRU cache (`isBlockedCache`) with a 7-day TTL and a maximum of 1,000 entries. If the key is already in this cache with `Blocked: true`, the guard sends `429` immediately and returns `false`. No limiter points are consumed.\n\n#### Consume limiter points\nCalls `consumeOrReject()` to attempt consuming one point from the limiter. If the limiter rejects (all points exhausted), `consumeOrReject` sends the `429` response with a `Retry-After` header and returns `null`.\n\n#### Record a strike\nIf consumption failed, the guard increments the key's strike counter in the consecutive cache. The counter starts at `0` and increments by `1` on each failure.\n\n#### Check for permanent block\nIf the strike count reaches or exceeds `maxBans`, the guard calls `limiter.block(key, seconds)` to permanently block the key at the limiter level, then adds the key to `isBlockedCache`. Future requests with this key are rejected at step 1 without consuming points.\n\n#### Allow the request\nIf consumption succeeded, the guard deletes the key from `isBlockedCache` (in case it was previously blocked and the block expired) and returns `true`. The controller can proceed.\n::\n\n::warning\nThe `isBlockedCache` is process-local. If you run multiple IAM instances, a key blocked on one instance is not visible to other instances. The MySQL-backed limiter block (from `limiter.block()`) is shared across instances, but the fast-path cache check only works within the process that triggered it.\n::\n\n### `consumeOrReject`\n\nLow-level function that attempts to consume one point from a limiter. If consumption fails, it sends the `429` response and returns `null`. If consumption succeeds, it returns the `RateLimiterRes` object with remaining points and timing data.\n\n```ts\nimport { consumeOrReject } from '@riavzon\u002Fauth'\n\nconst result = await consumeOrReject(limiter, key, res, log)\n\nif (result === null) {\n  \u002F\u002F Request already rejected with 429\n  return\n}\n\n\u002F\u002F result.remainingPoints — how many attempts the key has left\n\u002F\u002F result.consumedPoints  — how many attempts the key has used\n\u002F\u002F result.msBeforeNext    — milliseconds until the window resets\n```\n\nWhen the limiter rejects:\n\n| Response | Value |\n|---|---|\n| Status | `429 Too Many Requests` |\n| Header | `Retry-After: {seconds}` (computed as `Math.ceil(msBeforeNext \u002F 1000)`, minimum 1) |\n| Body | `{ error: 'Too many requests', retry: {seconds} }` |\n\n::note\nMost controllers use `guard()` instead of calling `consumeOrReject` directly. Use `consumeOrReject` when you need custom logic between the consumption check and the response, or when you do not want the strike\u002Fblock escalation that `guard` provides.\n::\n\n### `resetLimiters`\n\nResets all accumulated points for a key across an array of limiters. Typically called after a successful authentication to clear penalties that accumulated during failed attempts.\n\n```ts\nimport { resetLimiters } from '@riavzon\u002Fauth'\n\nresetLimiters(log, compositeKey, [burst, slow, ipLimiter, emailLimiter])\n```\n\n::field-group\n  ::field{name=\"log\" type=\"pino.Logger\" required}\n  Logger for tracking which limiters were reset.\n  ::\n\n  ::field{name=\"key\" type=\"string\" required}\n  The key to reset across all limiters.\n  ::\n\n  ::field{name=\"limiters\" type=\"Array\u003CRateLimiterMemory | RateLimiterMySQL | RLWrapperBlackAndWhite>\" required}\n  The limiter instances to clear. The function calls `.delete(key)` on each one.\n  ::\n::\n\n::tip\nThe built-in limiter groups export a convenience function (typically named `resetLimitersUni`) that resets all limiters in the group at once. Controllers call this on successful authentication rather than tracking individual limiter references.\n::\n\n### `makeConsecutiveCache`\n\nFactory function that creates an LRU cache for tracking consecutive failures. Each cache instance is scoped to a specific key type (IP, email, composite, etc.) and has its own TTL.\n\n```ts\nimport { makeConsecutiveCache } from '@riavzon\u002Fauth'\n\nconst cache = makeConsecutiveCache\u003C{ countData: number }>(\n  500,       \u002F\u002F Maximum entries the cache can hold\n  60_000     \u002F\u002F TTL per entry in milliseconds\n)\n```\n\n::field-group\n  ::field{name=\"max\" type=\"number\" required}\n  Maximum number of entries. When the cache is full, the least recently used entry is evicted.\n  ::\n\n  ::field{name=\"ttl\" type=\"number\" required}\n  Time-to-live per entry in **milliseconds**. Entries older than this value are automatically evicted. This controls how long a key's strike history persists.\n  ::\n::\n\nThe consecutive cache is separate from the limiter store. Limiter points reset on their own schedule (based on `duration`). The consecutive cache tracks how many times a key has been caught by the limiter, regardless of when the limiter's window resets.\n\n---\n\n## Key Strategies\n\nRate limiters are identified by the key they track. The IAM service uses four key strategies across its endpoint groups.\n\n### IP Key\n\nThe simplest strategy. Uses `req.ip` as the limiter key. Protects against a single source flooding an endpoint.\n\n```ts\nawait guard(ipLimiter, req.ip!, consecutiveForIp, 2, 'ip', log, res)\n```\n\n### Identity Key\n\nUses the user's identity (email, OAuth subject, user ID, or token hash) as the key. Protects against attacks targeting a specific account from rotating IPs.\n\n```ts\nawait guard(emailLimiter, email, consecutiveForEmail, 2, 'email', log, res)\n```\n\n### Composite Key\n\nCombines the IP and identity into a single key with an underscore separator. Protects against a single source targeting a specific account. More precise than either key alone.\n\n```ts\nconst compositeKey = `${req.ip!}_${email}`\nawait guard(uniLimiter, compositeKey, consecutive429, 3, 'ip+email', log, res)\n```\n\n### Global Key\n\nUses a fixed string as the key. Protects against system-wide abuse by capping total requests across all users. The email MFA and password reset flows use `'global_emails'` to cap the total number of outbound emails the system sends per day.\n\n```ts\nawait guard(globalEmailLimiter, 'global_emails', consecutiveForGlobal, 1, 'globalEmailLimiter', log, res)\n```\n\n::warning\nKeys longer than 255 characters are automatically hashed with SHA-256 before being used as a limiter key. This handles composite keys and token hashes that could exceed MySQL column length limits.\n::\n\n---\n\n## Guard Patterns in Controllers\n\nEvery endpoint that uses rate limiting follows the same pattern: call `guard()` one or more times at the top of the controller, each with a different limiter and key. If any guard rejects, the controller returns immediately (the `429` response was already sent by the guard).\n\n### Login\n\nThe login controller applies three guard layers before attempting authentication:\n\n::steps{level=\"4\"}\n#### IP guard\nLimits the total number of login attempts from a single IP. Uses the IP union limiter. `maxBans: 2`.\n\n#### Email guard\nLimits the number of login attempts targeting a specific email address. Catches credential stuffing against a single account from rotating IPs. `maxBans: 2`.\n\n#### Composite guard\nLimits the number of attempts from a specific IP targeting a specific email. The most precise guard. `maxBans: 3`.\n::\n\nOn successful login, the controller resets all limiters and clears all consecutive caches for the IP, email, and composite key:\n\n```ts\nconsecutiveForIp.delete(req.ip!)\nconsecutive429.delete(compositeKey)\nconsecutiveForEmail.delete(email)\nawait resetLimitersUni(compositeKey)\n```\n\n### Token Rotation\n\nThe rotation controller applies three guards but uses a different strategy than login. It does not key on email (the rotation endpoint does not accept email input). Instead, it keys on the SHA-256 hash of the refresh token.\n\n::steps{level=\"4\"}\n#### IP guard\nLimits the total number of rotation attempts from a single IP. `maxBans: 1` (strict, single-strike block).\n\n#### Token hash guard\nLimits attempts using the same refresh token hash. Catches replay attacks where a stolen token is used repeatedly. `maxBans: 1`.\n\n#### Composite guard\nLimits attempts from a specific IP with a specific token hash. `maxBans: 1`.\n::\n\nAfter successful rotation, the controller does not reset limiters. Instead, it **blocks** the old token hash and composite key for 3 days:\n\n```ts\nawait refreshTokenLimiter.block(hashedToken, 60 * 60 * 24 * 3)\nawait refreshAccessTokenLimiter.block(compositeKey, 60 * 60 * 24 * 3)\n```\n\nThis prevents the old token from being used again even if it has not yet been revoked in the database. The block acts as a fast-path denial layer in front of the database revocation check.\n\n::warning\nToken rotation blocks the old token hash rather than resetting limiters. This is intentional. Unlike login (where a successful attempt proves the user is legitimate), rotation consumes a token that should never be reused. Blocking the hash ensures that even a race condition between two concurrent rotation attempts with the same token results in the second attempt being rejected.\n::\n\n### Signup\n\nThe signup controller applies three guards:\n\n::steps{level=\"4\"}\n#### IP union guard\nA union limiter (burst + slow) keyed on IP. Prevents mass-registration bots from a single source. `maxBans: 2`.\n\n#### Composite guard\nA union limiter keyed on `${IP}_${email}`. Prevents repeated signup attempts for the same email from the same IP. `maxBans: 2`.\n\n#### Email guard\nA standalone limiter keyed on the email address. Prevents repeated signup attempts for the same email from different IPs. `maxBans: 2`.\n::\n\n### OAuth\n\nThe OAuth controller applies three guards:\n\n::steps{level=\"4\"}\n#### IP union guard\nA union limiter (burst + slow) keyed on IP. `maxBans: 1`.\n\n#### Subject guard\nA standalone limiter keyed on the OAuth provider's subject ID (`sub` claim). Catches abuse targeting a specific OAuth account. `maxBans: 2`.\n\n#### Composite guard\nA standalone limiter keyed on `${IP}_${sub}`. `maxBans: 2`.\n::\n\n### Password Reset Initiation\n\nThe password reset initiation controller (`POST \u002Fauth\u002Fforgot-password`) applies four guards:\n\n::steps{level=\"4\"}\n#### Global email guard\nA global limiter keyed on the fixed string `'global_emails'`. Caps the total number of password reset emails the system sends per day. `maxBans: 1`.\n\n#### IP guard\nA standalone limiter keyed on IP. `maxBans: 2`.\n\n#### Email guard\nA standalone limiter keyed on the target email address. Prevents an attacker from flooding a single user's inbox. `maxBans: 2`.\n\n#### Composite guard (on failure)\nA union limiter keyed on `${IP}_${email}`, applied only when the actual password reset lookup fails. This avoids penalizing legitimate requests that pass validation. `maxBans: 3`.\n::\n\n### Email MFA Initiation\n\nThe email MFA flow controller applies four guards:\n\n::steps{level=\"4\"}\n#### Global email guard\nSame global limiter as password reset. Shared key `'global_emails'` caps system-wide email sends. `maxBans: 1`.\n\n#### IP guard\nA standalone limiter keyed on IP. `maxBans: 2`.\n\n#### Identity guard\nA standalone limiter keyed on a combination of the random token and the MFA reason. Prevents repeated MFA triggers for the same session. `maxBans: 2`.\n\n#### Composite guard\nA union limiter keyed on `${IP}_${random}_${reason}`. `maxBans: 3`.\n::\n\n### Link Verification\n\nMagic link verification (MFA, password reset, custom MFA) applies a single IP union guard. The union limiter contains a burst limiter (2 points per second) and a slow limiter (30 points per 30 minutes). `maxBans: 1`.\n\n### Temporary Post Routes\n\nThe password change and MFA code submission endpoints apply three guards:\n\n::steps{level=\"4\"}\n#### JTI guard\nA standalone limiter keyed on the JWT's `jti` claim. If the JTI has already been consumed by a previous request, the guard blocks immediately. This is configured with 0 points and 0 duration so that any consumption attempt triggers a block. `maxBans: 1`.\n\n#### IP guard\nA standalone limiter keyed on IP. `maxBans: 2`.\n\n#### Composite guard\nA union limiter keyed on a composite key. `maxBans: 2`.\n::\n\n---\n\n## Built-In Limiter Reference\n\nAll limiter groups are configured in the `rate_limiters` section of the service configuration. Every group is optional. Omitting a group disables rate limiting for those endpoints and uses hardcoded defaults instead.\n\nEach limiter within a group accepts the same five fields:\n\n::field-group\n  ::field{name=\"points\" type=\"number\" required}\n  Maximum requests allowed within the time window.\n  ::\n\n  ::field{name=\"duration\" type=\"number\" required}\n  Time window in **seconds**. Points reset after this period.\n  ::\n\n  ::field{name=\"blockDuration\" type=\"number\" required}\n  How long (in seconds) to block the key after all points are exhausted.\n  ::\n\n  ::field{name=\"inMemoryBlockOnConsumed\" type=\"number\" required}\n  At this consumption count, the in-memory layer blocks the key without hitting MySQL.\n  ::\n\n  ::field{name=\"inMemoryBlockDuration\" type=\"number\" required}\n  How long (in seconds) the in-memory block lasts.\n  ::\n::\n\n### `loginLimiters`\n\nProtects `POST \u002Flogin` against credential-stuffing attacks.\n\n| Limiter | Key | Default points | Default duration | Default block | Purpose |\n|---|---|---|---|---|---|\n| `unionLimiter.burstLimiter` | `${IP}_${email}` | 1 | 1s | 30min | Blocks rapid-fire login attempts |\n| `unionLimiter.slowLimiter` | `${IP}_${email}` | 5 | 1hr | 30min | Catches slow credential stuffing |\n| `ipLimiter` | `${IP}` | 15 | 24hr | 3hr | Caps total login attempts per IP per day |\n| `emailLimiter` | `${email}` | 5 | 24hr | 5hr | Caps login attempts per email per day |\n\n### `signupLimiters`\n\nProtects `POST \u002Fsignup` against mass-registration bots. This group uses two union limiters instead of one.\n\n| Limiter | Key | Default points | Default duration | Default block | Purpose |\n|---|---|---|---|---|---|\n| `unionLimiters.uniLimiterIp.ipLimit` | `${IP}` | 2 | 1s | 15min | Blocks rapid signup bursts per IP |\n| `unionLimiters.uniLimiterIp.slowIpLimit` | `${IP}` | 5 | 30min | 15min | Catches sustained signup attempts per IP |\n| `unionLimiters.uniLimiterComposite.compositeKeyLimit` | `${IP}_${email}` | 1 | 1s | 30min | Blocks rapid signup for same email from same IP |\n| `unionLimiters.uniLimiterComposite.slowCompositeKeyLimit` | `${IP}_${email}` | 3 | 24hr | 24hr | Daily limit for same email+IP combination |\n| `emailLimit` | `${email}` | 3 | 24hr | 24hr | Global per-email signup cap |\n\n### `oauthLimiters`\n\nProtects `POST \u002Fauth\u002FOAuth\u002F:providerName`.\n\n| Limiter | Key | Default points | Default duration | Default block | Purpose |\n|---|---|---|---|---|---|\n| `unionLimiter.ipLimiterBrute` | `${IP}` | 1 | 1s | 5min | Blocks rapid OAuth attempts per IP |\n| `unionLimiter.ipLimiterSlow` | `${IP}` | 25 | 1hr | 30min | Hourly cap per IP |\n| `subLimiter` | `${sub}` | 5 | 5min | 15min | Limits attempts per OAuth subject |\n| `compositeKeyLimiter` | `${IP}_${sub}` | 3 | 10min | 15min | Limits attempts per IP+subject combination |\n\n### `tokenLimiters`\n\nProtects `POST \u002Fauth\u002Fuser\u002Frefresh-session` (full token rotation).\n\n| Limiter | Key | Default points | Default duration | Default block | Purpose |\n|---|---|---|---|---|---|\n| `unionLimiters.refreshAccessTokenLimiter.accessTokenBrute` | `${IP}` | 2 | 1s | 30min | Blocks rapid access token refreshes per IP |\n| `unionLimiters.refreshAccessTokenLimiter.accessTokenSlow` | `${IP}` | 3 | 10min | 1hr | Caps access token refreshes per IP over time |\n| `unionLimiters.refreshTokenLimiterUnion.refreshTokenBrute` | `${tokenHash}` | 2 | 1s | 30min | Blocks rapid rotation attempts per token |\n| `unionLimiters.refreshTokenLimiterUnion.refreshTokenSlow` | `${tokenHash}` | 4 | 12hr | 12hr | Long-window cap per token hash |\n| `refreshTokenLimiter` | `${tokenHash}` | 3 | 12hr | 15hr | Standalone token hash limiter for the composite guard |\n\n::note\nThe token limiter group also creates an internal `blackList` limiter (20 points, 24hr duration, 3-day block). This limiter is used by the rotation controller to block consumed token hashes for 3 days after successful rotation, preventing any reuse of old tokens even if database revocation has not propagated yet.\n::\n\n### `linkVerificationLimiter`\n\nProtects magic link verification endpoints: MFA email links, password reset links, and custom MFA flow links.\n\n| Limiter | Key | Default points | Default duration | Default block | Purpose |\n|---|---|---|---|---|---|\n| `unionLimiter.burstLimiter` | `${IP}` | 2 | 1s | 15min | Blocks rapid link verification attempts |\n| `unionLimiter.slowLimiter` | `${IP}` | 30 | 30min | 30min | Caps total link verification attempts per IP |\n\n### `initPasswordResetLimiters`\n\nProtects `POST \u002Fauth\u002Fforgot-password`.\n\n| Limiter | Key | Default points | Default duration | Default block | Purpose |\n|---|---|---|---|---|---|\n| `unionLimiters.limit` | `${IP}_${email}` | 1 | 1s | 30min | Blocks rapid password resets per IP+email |\n| `unionLimiters.longLimiter` | `${IP}_${email}` | 4 | 30min | 15min | Sustained cap per IP+email |\n| `ipLimiter` | `${IP}` | 5 | 24hr | 4hr | Daily cap per IP |\n| `emailLimiter` | `${email}` | 5 | 24hr | 4hr | Daily cap per email |\n\n### `emailMfaLimiters`\n\nProtects MFA email sending endpoints.\n\n| Limiter | Key | Default points | Default duration | Default block | Purpose |\n|---|---|---|---|---|---|\n| `unionLimiters.limit` | `${IP}_${identifier}` | 1 | 1s | 30min | Blocks rapid MFA triggers per IP+identifier |\n| `unionLimiters.longLimiter` | `${IP}_${identifier}` | 4 | 30min | 15min | Sustained cap per IP+identifier |\n| `ipLimiter` | `${IP}` | 5 | 24hr | 4hr | Daily cap per IP |\n| `userIdLimiter` | `user_${userId}` | 8 | 24hr | 12hr | Daily cap per user account |\n| `globalEmailLimiter` | `global_emails` | 800 | 24hr | 24hr | System-wide daily email send cap |\n\n::warning\nThe `globalEmailLimiter` is shared between the password reset and email MFA flows. Both controllers import it from the email MFA limiter module and guard against the same `'global_emails'` key. If the system hits 800 password reset and MFA emails combined in 24 hours, all further email-sending endpoints are blocked for 24 hours regardless of which specific flow triggered them.\n::\n\n### `tempPostRoutesLimiters`\n\nProtects password change submissions and MFA code submissions (the POST endpoints that consume temporary magic links).\n\n| Limiter | Key | Default points | Default duration | Default block | Purpose |\n|---|---|---|---|---|---|\n| `unionLimiters.limit` | `${compositeKey}` | 1 | 1s | 30min | Blocks rapid submissions per composite key |\n| `unionLimiters.slowLimit` | `${compositeKey}` | 5 | 10min | 10min | Sustained cap per composite key |\n| `ipLimit` | `${IP}` | 6 | 10min | 10min | Caps total submissions per IP |\n| `usedJtiLimiter` | `${jti}` | 0 | 0s | 20min | Zero-point limiter. Any consumption blocks the JTI for 20 minutes. Prevents reuse of temporary tokens. |\n\n---\n\n## Building a Custom Rate Limiter\n\nWhen you need to protect additional endpoints in your own application, you can compose the same utilities the IAM service uses internally. All factory functions and helpers are exported from `@riavzon\u002Fauth`.\n\n::steps{level=\"4\"}\n#### Define the limiter module\n\nCreate a module in your project that builds the limiters once and reuses them for the lifetime of the process. The singleton pattern with lazy initialization ensures the MySQL pool and rate limiter tables are created only on first use:\n\n```ts\nimport {\n  makeRateLimiter,\n  unionLimiter,\n  type BlockableUnion,\n} from '@riavzon\u002Fauth'\nimport type { RateLimiterMySQL, RateLimiterMemory, RLWrapperBlackAndWhite } from 'rate-limiter-flexible'\nimport type { Pool } from 'mysql2'\n\ninterface LimiterBundle {\n  uniLimiter: BlockableUnion | RLWrapperBlackAndWhite\n  ipLimiter: RateLimiterMySQL | RateLimiterMemory\n  resetAll(key: string): Promise\u003Cvoid>\n}\n\nlet instance: LimiterBundle | null = null\n\nfunction build(pool: Pool, dbName: string): LimiterBundle {\n  const burst = makeRateLimiter(true, false, {\n    dbName,\n    storeClient: pool,\n    storeType: 'mysql2',\n    tableName: 'my_endpoint',\n    keyPrefix: 'my_endpoint_burst',\n    points: 2,\n    duration: 1,\n    blockDuration: 1800,\n    inMemoryBlockOnConsumed: 3,\n    inMemoryBlockDuration: 1800,\n  })\n\n  const slow = makeRateLimiter(true, false, {\n    dbName,\n    storeClient: pool,\n    storeType: 'mysql2',\n    tableName: 'my_endpoint',\n    keyPrefix: 'my_endpoint_slow',\n    points: 10,\n    duration: 3600,\n    blockDuration: 3600,\n    inMemoryBlockOnConsumed: 11,\n    inMemoryBlockDuration: 3600,\n  })\n\n  const ipLimiter = makeRateLimiter(true, false, {\n    dbName,\n    storeClient: pool,\n    storeType: 'mysql2',\n    tableName: 'my_endpoint',\n    keyPrefix: 'my_endpoint_ip',\n    points: 20,\n    duration: 86400,\n    blockDuration: 14400,\n    inMemoryBlockOnConsumed: 21,\n    inMemoryBlockDuration: 14400,\n  })\n\n  return {\n    uniLimiter: unionLimiter([burst, slow], false),\n    ipLimiter,\n    resetAll: async (key: string) => {\n      await Promise.all([\n        burst.delete(key),\n        slow.delete(key),\n        ipLimiter.delete(key),\n      ])\n    },\n  }\n}\n\nexport function init(pool: Pool, dbName: string): void {\n  if (!instance) instance = build(pool, dbName)\n}\n\nexport function getLimiters(): LimiterBundle {\n  if (!instance) throw new Error('Limiters not initialized. Call init() first.')\n  return instance\n}\n```\n\nCall `init(pool, dbName)` once at application startup (after your MySQL pool is ready). From then on, every call to `getLimiters()` returns the same singleton.\n\n#### Create consecutive caches\n\nEach `guard()` call needs its own [LRU cache](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCache_replacement_policies#Least_recently_used_(LRU)) to track consecutive failures. Declare them at the module level so they persist across requests:\n\n```ts\nimport { makeConsecutiveCache } from '@riavzon\u002Fauth'\n\nconst consecutiveForIp = makeConsecutiveCache\u003C{ countData: number }>(500, 60_000)\nconst consecutiveForComposite = makeConsecutiveCache\u003C{ countData: number }>(500, 1800_000)\n```\n\n#### Apply guards in sequence\n\nCall `guard()` for each limiter layer. Order the checks from cheapest to most specific: IP first, then identity, then composite.\n\n```ts\nimport { guard } from '@riavzon\u002Fauth'\nimport { getLimiters } from '.\u002FmyEndpointLimiter.js'\n\nexport async function myController(req: Request, res: Response) {\n  const { uniLimiter, ipLimiter } = getLimiters()\n  const log = getLogger().child({ service: 'myApp', branch: 'myEndpoint' })\n\n  \u002F\u002F Guard 1: IP\n  if (!(await guard(ipLimiter, req.ip!, consecutiveForIp, 2, 'ip', log, res))) return\n\n  \u002F\u002F Guard 2: Composite (IP + identity)\n  const compositeKey = `${req.ip!}_${email}`\n  if (!(await guard(uniLimiter, compositeKey, consecutiveForComposite, 3, 'composite', log, res))) return\n\n  \u002F\u002F ... business logic ...\n\n  \u002F\u002F On success: clear strikes and reset limiter points\n  consecutiveForIp.delete(req.ip!)\n  consecutiveForComposite.delete(compositeKey)\n  await getLimiters().resetAll(compositeKey)\n}\n```\n\nIf any `guard()` call returns `false`, the `429` response has already been sent. The controller returns immediately without running the remaining guards or the business logic.\n::\n\n---\n\n## Response Format\n\nWhen any guard or limiter rejects a request, the service returns:\n\n| Field | Value |\n|---|---|\n| **Status** | `429 Too Many Requests` |\n| **Header** | `Retry-After: {seconds}` |\n| **Body** | `{ \"error\": \"Too many requests\", \"retry\": {seconds} }` |\n\nThe `Retry-After` value is computed as `Math.ceil(msBeforeNext \u002F 1000)` with a minimum of 1 second. For keys in the global block cache, the value is the block expiration timestamp or the string `'permanent'`.\n\n---\n\n## Summary\n\n| Concept | Description |\n|---|---|\n| **Union limiter** | Pairs a burst limiter (short window, low points) with a slow limiter (long window, higher points). Both must pass. |\n| **Guard** | Consumes a limiter, tracks strikes in a consecutive cache, and permanently blocks keys that exceed `maxBans`. |\n| **Consecutive cache** | LRU cache tracking how many times a key has been rate limited. Separate from the limiter's own point tracking. |\n| **Block cache** | Global 7-day LRU cache of permanently blocked keys. Checked before limiter consumption for fast-path rejection. |\n| **Key strategies** | IP, identity (email\u002Fsub\u002Ftoken hash\u002FuserId), composite (`IP_identity`), and global (fixed string). |\n| **Reset on success** | Login, signup, and OAuth reset all limiters and caches on successful authentication. |\n| **Block on success** | Token rotation blocks the old token hash for 3 days instead of resetting. |\n| **Storage** | MySQL (persistent, shared across instances) + in-memory (fast path, per-process). |\n| **Response** | `429` with `Retry-After` header and JSON body. |\n",{"title":147,"description":6275},"l8Av9zRNzQF5pou_VfoqKQPPMuCyq4xww1RCXpBGnHo",[6284,6285],{"title":143,"path":144,"stem":145,"children":-1},{"title":151,"path":152,"stem":153,"children":-1},{"id":851,"title":147,"body":6287,"description":6275,"extension":6276,"icon":6277,"meta":10727,"module":6279,"navigation":8,"path":148,"rawbody":6280,"seo":10728,"stem":149,"__hash__":6282},{"type":853,"value":6288,"toc":10684},[6289,6296,6308,6321,6323,6325,6327,6329,6365,6367,6369,6377,6387,6391,6393,6395,6399,6403,6409,6575,6609,6611,6791,6795,6797,7003,7023,7027,7061,7065,7069,7075,7247,7313,7315,7369,7377,7381,7389,7491,7493,7531,7543,7547,7549,7605,7621,7627,7631,7633,7697,7709,7713,7715,7717,7719,7721,7725,7775,7777,7779,7823,7825,7827,7903,7905,7909,7957,7961,7963,7965,7971,7973,7975,7995,7997,8061,8063,8065,8085,8089,8157,8159,8163,8165,8167,8189,8191,8193,8217,8219,8223,8253,8255,8257,8287,8289,8293,8295,8297,8319,8321,8323,8327,8329,8353,8357,8361,8453,8457,8461,8571,8575,8579,8671,8675,8679,8789,8795,8799,8801,8857,8861,8865,8957,8961,8963,9073,9081,9085,9087,9179,9181,9183,9187,10530,10532,10534,10536,10578,10586,10588,10590,10682],[856,6290,858,6291,866,6294,871],{},[860,6292,865],{"href":862,"rel":6293},[864],[868,6295,870],{},[856,6297,874,6298,879,6300,883,6302,887,6304,891,6306,895],{},[876,6299,878],{},[876,6301,882],{},[876,6303,886],{},[868,6305,890],{},[868,6307,894],{},[856,6309,898,6310,902,6312,908,6315,912,6317,916,6319,920],{},[876,6311,901],{},[860,6313,907],{"href":905,"rel":6314},[864],[868,6316,911],{},[868,6318,915],{},[868,6320,919],{},[922,6322],{},[925,6324,928],{"id":927},[930,6326,933],{"id":932},[856,6328,936],{},[938,6330,6331,6341],{},[941,6332,6333],{},[944,6334,6335,6337,6339],{},[947,6336,949],{},[947,6338,952],{},[947,6340,955],{},[957,6342,6343,6353],{},[944,6344,6345,6349,6351],{},[962,6346,6347],{},[876,6348,966],{},[962,6350,969],{},[962,6352,972],{},[944,6354,6355,6359,6361],{},[962,6356,6357],{},[876,6358,979],{},[962,6360,982],{},[962,6362,985,6363,989],{},[868,6364,988],{},[856,6366,992],{},[930,6368,996],{"id":995},[856,6370,999,6371,1003,6373,1006,6375,1010],{},[868,6372,1002],{},[868,6374,865],{},[868,6376,1009],{},[1012,6378,6379,6383],{},[1015,6380,6381],{"name":1017,"type":1018,":required":1019},[856,6382,1022],{},[1015,6384,6385],{"name":1025,"type":1026,":required":1019},[856,6386,1029],{},[856,6388,1032,6389,1035],{},[860,6390,151],{"href":152},[922,6392],{},[925,6394,1041],{"id":1040},[856,6396,1044,6397,1048],{},[868,6398,1047],{},[930,6400,6401],{"id":1051},[868,6402,1054],{},[856,6404,1057,6405,1061,6407,1065],{},[868,6406,1060],{},[868,6408,1064],{},[1067,6410,6411],{"className":1069,"code":1070,"language":1071,"meta":1072,"style":1072},[868,6412,6413,6431,6435,6455,6469,6479,6493,6507,6521,6531,6541,6551,6561,6571],{"__ignoreMap":1072},[1076,6414,6415,6417,6419,6421,6423,6425,6427,6429],{"class":1078,"line":1079},[1076,6416,1083],{"class":1082},[1076,6418,1087],{"class":1086},[1076,6420,1054],{"class":1090},[1076,6422,1093],{"class":1086},[1076,6424,1096],{"class":1082},[1076,6426,1100],{"class":1099},[1076,6428,1047],{"class":1103},[1076,6430,1106],{"class":1099},[1076,6432,6433],{"class":1078,"line":1109},[1076,6434,1112],{"emptyLinePlaceholder":8},[1076,6436,6437,6439,6441,6443,6445,6447,6449,6451,6453],{"class":1078,"line":1115},[1076,6438,1119],{"class":1118},[1076,6440,1123],{"class":1122},[1076,6442,1127],{"class":1126},[1076,6444,1131],{"class":1130},[1076,6446,1134],{"class":1086},[1076,6448,1019],{"class":1137},[1076,6450,1140],{"class":1086},[1076,6452,1143],{"class":1137},[1076,6454,1146],{"class":1086},[1076,6456,6457,6459,6461,6463,6465,6467],{"class":1078,"line":1149},[1076,6458,1152],{"class":1090},[1076,6460,1156],{"class":1155},[1076,6462,1100],{"class":1099},[1076,6464,870],{"class":1103},[1076,6466,1163],{"class":1099},[1076,6468,1166],{"class":1086},[1076,6470,6471,6473,6475,6477],{"class":1078,"line":1169},[1076,6472,1172],{"class":1090},[1076,6474,1156],{"class":1155},[1076,6476,1177],{"class":1090},[1076,6478,1166],{"class":1086},[1076,6480,6481,6483,6485,6487,6489,6491],{"class":1078,"line":1182},[1076,6482,1185],{"class":1090},[1076,6484,1156],{"class":1155},[1076,6486,1100],{"class":1099},[1076,6488,1192],{"class":1103},[1076,6490,1163],{"class":1099},[1076,6492,1166],{"class":1086},[1076,6494,6495,6497,6499,6501,6503,6505],{"class":1078,"line":1199},[1076,6496,1202],{"class":1090},[1076,6498,1156],{"class":1155},[1076,6500,1100],{"class":1099},[1076,6502,1209],{"class":1103},[1076,6504,1163],{"class":1099},[1076,6506,1166],{"class":1086},[1076,6508,6509,6511,6513,6515,6517,6519],{"class":1078,"line":1216},[1076,6510,1219],{"class":1090},[1076,6512,1156],{"class":1155},[1076,6514,1100],{"class":1099},[1076,6516,1226],{"class":1103},[1076,6518,1163],{"class":1099},[1076,6520,1166],{"class":1086},[1076,6522,6523,6525,6527,6529],{"class":1078,"line":1233},[1076,6524,1236],{"class":1090},[1076,6526,1156],{"class":1155},[1076,6528,1242],{"class":1241},[1076,6530,1166],{"class":1086},[1076,6532,6533,6535,6537,6539],{"class":1078,"line":1247},[1076,6534,1250],{"class":1090},[1076,6536,1156],{"class":1155},[1076,6538,1242],{"class":1241},[1076,6540,1166],{"class":1086},[1076,6542,6543,6545,6547,6549],{"class":1078,"line":1259},[1076,6544,1262],{"class":1090},[1076,6546,1156],{"class":1155},[1076,6548,1267],{"class":1241},[1076,6550,1166],{"class":1086},[1076,6552,6553,6555,6557,6559],{"class":1078,"line":1272},[1076,6554,1275],{"class":1090},[1076,6556,1156],{"class":1155},[1076,6558,1280],{"class":1241},[1076,6560,1166],{"class":1086},[1076,6562,6563,6565,6567,6569],{"class":1078,"line":1285},[1076,6564,1288],{"class":1090},[1076,6566,1156],{"class":1155},[1076,6568,1267],{"class":1241},[1076,6570,1166],{"class":1086},[1076,6572,6573],{"class":1078,"line":1297},[1076,6574,1300],{"class":1086},[1012,6576,6577,6591,6601],{},[1015,6578,6579],{"name":1060,"type":1305,":required":1019},[856,6580,1308,6581,1311,6583,1315,6585,1319,6587,1322,6589,1048],{},[868,6582,1019],{},[868,6584,1314],{},[868,6586,1318],{},[868,6588,1143],{},[868,6590,1318],{},[1015,6592,6593],{"name":1064,"type":1305,":required":1019},[856,6594,1308,6595,1331,6597,1335,6599,1339],{},[868,6596,1019],{},[868,6598,1334],{},[868,6600,1338],{},[1015,6602,6603],{"name":1342,"type":1343,":required":1019},[856,6604,1346,6605,1349,6607,1048],{},[868,6606,1060],{},[868,6608,1019],{},[1353,6610,1356],{"id":1355},[938,6612,6613,6625],{},[941,6614,6615],{},[944,6616,6617,6619,6621,6623],{},[947,6618,1365],{},[947,6620,1368],{},[947,6622,1371],{},[947,6624,1374],{},[957,6626,6627,6641,6657,6673,6689,6705,6721,6737,6755,6773],{},[944,6628,6629,6633,6637,6639],{},[962,6630,6631],{},[868,6632,1383],{},[962,6634,6635],{},[868,6636,1026],{},[962,6638,1390],{},[962,6640,1393],{},[944,6642,6643,6647,6651,6653],{},[962,6644,6645],{},[868,6646,1400],{},[962,6648,6649],{},[868,6650,1405],{},[962,6652,1390],{},[962,6654,1410,6655,1414],{},[868,6656,1413],{},[944,6658,6659,6663,6667,6669],{},[962,6660,6661],{},[868,6662,1413],{},[962,6664,6665],{},[868,6666,1405],{},[962,6668,1390],{},[962,6670,1429,6671,1433],{},[876,6672,1432],{},[944,6674,6675,6679,6683,6685],{},[962,6676,6677],{},[868,6678,1440],{},[962,6680,6681],{},[868,6682,1405],{},[962,6684,1390],{},[962,6686,1449,6687,1453],{},[868,6688,1452],{},[944,6690,6691,6695,6699,6701],{},[962,6692,6693],{},[868,6694,988],{},[962,6696,6697],{},[868,6698,1405],{},[962,6700,1466],{},[962,6702,1469,6703,1472],{},[868,6704,1400],{},[944,6706,6707,6711,6715,6717],{},[962,6708,6709],{},[868,6710,1479],{},[962,6712,6713],{},[868,6714,1405],{},[962,6716,1466],{},[962,6718,1488,6719,1048],{},[868,6720,1440],{},[944,6722,6723,6727,6731,6735],{},[962,6724,6725],{},[868,6726,1497],{},[962,6728,6729],{},[868,6730,1026],{},[962,6732,1308,6733],{},[868,6734,1506],{},[962,6736,1509],{},[944,6738,6739,6743,6747,6751],{},[962,6740,6741],{},[868,6742,1516],{},[962,6744,6745],{},[868,6746,1521],{},[962,6748,1308,6749],{},[868,6750,1506],{},[962,6752,1528,6753,1048],{},[868,6754,1531],{},[944,6756,6757,6761,6765,6769],{},[962,6758,6759],{},[868,6760,1538],{},[962,6762,6763],{},[868,6764,1026],{},[962,6766,1308,6767],{},[868,6768,1506],{},[962,6770,1549,6771,1553],{},[868,6772,1552],{},[944,6774,6775,6779,6783,6787],{},[962,6776,6777],{},[868,6778,1560],{},[962,6780,6781],{},[868,6782,1026],{},[962,6784,1308,6785],{},[868,6786,1506],{},[962,6788,1571,6789,1574],{},[868,6790,1383],{},[930,6792,6793],{"id":1577},[868,6794,1580],{},[856,6796,1583],{},[1067,6798,6799],{"className":1069,"code":1586,"language":1071,"meta":1072,"style":1072},[868,6800,6801,6823,6827,6847,6861,6871,6881,6891,6895,6899,6903,6923,6937,6947,6957,6967,6971,6975,6979],{"__ignoreMap":1072},[1076,6802,6803,6805,6807,6809,6811,6813,6815,6817,6819,6821],{"class":1078,"line":1079},[1076,6804,1083],{"class":1082},[1076,6806,1087],{"class":1086},[1076,6808,1054],{"class":1090},[1076,6810,1140],{"class":1086},[1076,6812,1580],{"class":1090},[1076,6814,1093],{"class":1086},[1076,6816,1096],{"class":1082},[1076,6818,1100],{"class":1099},[1076,6820,1047],{"class":1103},[1076,6822,1106],{"class":1099},[1076,6824,6825],{"class":1078,"line":1109},[1076,6826,1112],{"emptyLinePlaceholder":8},[1076,6828,6829,6831,6833,6835,6837,6839,6841,6843,6845],{"class":1078,"line":1115},[1076,6830,1119],{"class":1118},[1076,6832,1621],{"class":1122},[1076,6834,1127],{"class":1126},[1076,6836,1131],{"class":1130},[1076,6838,1134],{"class":1086},[1076,6840,1019],{"class":1137},[1076,6842,1140],{"class":1086},[1076,6844,1143],{"class":1137},[1076,6846,1146],{"class":1086},[1076,6848,6849,6851,6853,6855,6857,6859],{"class":1078,"line":1149},[1076,6850,1219],{"class":1090},[1076,6852,1156],{"class":1155},[1076,6854,1100],{"class":1099},[1076,6856,1226],{"class":1103},[1076,6858,1163],{"class":1099},[1076,6860,1166],{"class":1086},[1076,6862,6863,6865,6867,6869],{"class":1078,"line":1169},[1076,6864,1236],{"class":1090},[1076,6866,1156],{"class":1155},[1076,6868,1242],{"class":1241},[1076,6870,1166],{"class":1086},[1076,6872,6873,6875,6877,6879],{"class":1078,"line":1182},[1076,6874,1250],{"class":1090},[1076,6876,1156],{"class":1155},[1076,6878,1242],{"class":1241},[1076,6880,1166],{"class":1086},[1076,6882,6883,6885,6887,6889],{"class":1078,"line":1199},[1076,6884,1262],{"class":1090},[1076,6886,1156],{"class":1155},[1076,6888,1267],{"class":1241},[1076,6890,1166],{"class":1086},[1076,6892,6893],{"class":1078,"line":1216},[1076,6894,1685],{"class":1684},[1076,6896,6897],{"class":1078,"line":1233},[1076,6898,1300],{"class":1086},[1076,6900,6901],{"class":1078,"line":1247},[1076,6902,1112],{"emptyLinePlaceholder":8},[1076,6904,6905,6907,6909,6911,6913,6915,6917,6919,6921],{"class":1078,"line":1259},[1076,6906,1119],{"class":1118},[1076,6908,1700],{"class":1122},[1076,6910,1127],{"class":1126},[1076,6912,1131],{"class":1130},[1076,6914,1134],{"class":1086},[1076,6916,1019],{"class":1137},[1076,6918,1140],{"class":1086},[1076,6920,1143],{"class":1137},[1076,6922,1146],{"class":1086},[1076,6924,6925,6927,6929,6931,6933,6935],{"class":1078,"line":1272},[1076,6926,1219],{"class":1090},[1076,6928,1156],{"class":1155},[1076,6930,1100],{"class":1099},[1076,6932,1725],{"class":1103},[1076,6934,1163],{"class":1099},[1076,6936,1166],{"class":1086},[1076,6938,6939,6941,6943,6945],{"class":1078,"line":1285},[1076,6940,1236],{"class":1090},[1076,6942,1156],{"class":1155},[1076,6944,1738],{"class":1241},[1076,6946,1166],{"class":1086},[1076,6948,6949,6951,6953,6955],{"class":1078,"line":1297},[1076,6950,1250],{"class":1090},[1076,6952,1156],{"class":1155},[1076,6954,1749],{"class":1241},[1076,6956,1166],{"class":1086},[1076,6958,6959,6961,6963,6965],{"class":1078,"line":1754},[1076,6960,1262],{"class":1090},[1076,6962,1156],{"class":1155},[1076,6964,1267],{"class":1241},[1076,6966,1166],{"class":1086},[1076,6968,6969],{"class":1078,"line":1765},[1076,6970,1685],{"class":1684},[1076,6972,6973],{"class":1078,"line":1770},[1076,6974,1300],{"class":1086},[1076,6976,6977],{"class":1078,"line":1775},[1076,6978,1112],{"emptyLinePlaceholder":8},[1076,6980,6981,6983,6985,6987,6989,6991,6993,6995,6997,6999,7001],{"class":1078,"line":1780},[1076,6982,1119],{"class":1118},[1076,6984,1785],{"class":1122},[1076,6986,1127],{"class":1126},[1076,6988,1790],{"class":1130},[1076,6990,1793],{"class":1086},[1076,6992,1796],{"class":1090},[1076,6994,1140],{"class":1086},[1076,6996,1801],{"class":1090},[1076,6998,1804],{"class":1086},[1076,7000,1143],{"class":1137},[1076,7002,1809],{"class":1086},[1012,7004,7005,7009],{},[1015,7006,7007],{"name":1814,"type":1815,":required":1019},[856,7008,1818],{},[1015,7010,7011],{"name":1821,"type":1305,":required":1019},[856,7012,1308,7013,1826,7015,1829,7017,1832,7019,1836,7021,1839],{},[868,7014,1019],{},[868,7016,1334],{},[868,7018,919],{},[868,7020,1835],{},[868,7022,915],{},[856,7024,1842,7025,1846],{},[868,7026,1845],{},[938,7028,7029,7037],{},[941,7030,7031],{},[944,7032,7033,7035],{},[947,7034,1855],{},[947,7036,1374],{},[957,7038,7039,7051],{},[944,7040,7041,7045],{},[962,7042,7043],{},[868,7044,1866],{},[962,7046,1869,7047,1873,7049,1877],{},[876,7048,1872],{},[868,7050,1876],{},[944,7052,7053,7057],{},[962,7054,7055],{},[868,7056,1884],{},[962,7058,1887,7059,1890],{},[876,7060,1872],{},[1892,7062,7063],{},[856,7064,1896],{},[930,7066,7067],{"id":915},[868,7068,915],{},[856,7070,1903,7071,1906,7073,1909],{},[868,7072,1019],{},[868,7074,1143],{},[1067,7076,7077],{"className":1069,"code":1912,"language":1071,"meta":1072,"style":1072},[868,7078,7079,7101,7105,7133,7137,7151,7159,7173,7181,7189,7201,7209,7217,7223,7227,7231],{"__ignoreMap":1072},[1076,7080,7081,7083,7085,7087,7089,7091,7093,7095,7097,7099],{"class":1078,"line":1079},[1076,7082,1083],{"class":1082},[1076,7084,1087],{"class":1086},[1076,7086,915],{"class":1090},[1076,7088,1140],{"class":1086},[1076,7090,1927],{"class":1090},[1076,7092,1093],{"class":1086},[1076,7094,1096],{"class":1082},[1076,7096,1100],{"class":1099},[1076,7098,1047],{"class":1103},[1076,7100,1106],{"class":1099},[1076,7102,7103],{"class":1078,"line":1109},[1076,7104,1112],{"emptyLinePlaceholder":8},[1076,7106,7107,7109,7111,7113,7115,7117,7119,7121,7123,7125,7127,7129,7131],{"class":1078,"line":1115},[1076,7108,1119],{"class":1118},[1076,7110,1948],{"class":1122},[1076,7112,1127],{"class":1126},[1076,7114,1953],{"class":1130},[1076,7116,1956],{"class":1086},[1076,7118,1959],{"class":1090},[1076,7120,1156],{"class":1126},[1076,7122,1965],{"class":1964},[1076,7124,1968],{"class":1086},[1076,7126,1971],{"class":1241},[1076,7128,1140],{"class":1086},[1076,7130,1976],{"class":1241},[1076,7132,1809],{"class":1086},[1076,7134,7135],{"class":1078,"line":1149},[1076,7136,1112],{"emptyLinePlaceholder":8},[1076,7138,7139,7141,7143,7145,7147,7149],{"class":1078,"line":1169},[1076,7140,1119],{"class":1118},[1076,7142,1989],{"class":1122},[1076,7144,1127],{"class":1126},[1076,7146,1994],{"class":1082},[1076,7148,1997],{"class":1130},[1076,7150,2000],{"class":1086},[1076,7152,7153,7155,7157],{"class":1078,"line":1182},[1076,7154,2005],{"class":1090},[1076,7156,2008],{"class":1086},[1076,7158,2011],{"class":1684},[1076,7160,7161,7163,7165,7167,7169,7171],{"class":1078,"line":1199},[1076,7162,2016],{"class":1090},[1076,7164,1048],{"class":1086},[1076,7166,2021],{"class":1090},[1076,7168,2024],{"class":1126},[1076,7170,2008],{"class":1086},[1076,7172,2029],{"class":1684},[1076,7174,7175,7177,7179],{"class":1078,"line":1216},[1076,7176,2034],{"class":1090},[1076,7178,2037],{"class":1086},[1076,7180,2040],{"class":1684},[1076,7182,7183,7185,7187],{"class":1078,"line":1233},[1076,7184,2045],{"class":1241},[1076,7186,2048],{"class":1086},[1076,7188,2051],{"class":1684},[1076,7190,7191,7193,7195,7197,7199],{"class":1078,"line":1247},[1076,7192,2056],{"class":1099},[1076,7194,2021],{"class":1103},[1076,7196,1163],{"class":1099},[1076,7198,2063],{"class":1086},[1076,7200,2066],{"class":1684},[1076,7202,7203,7205,7207],{"class":1078,"line":1259},[1076,7204,2071],{"class":1090},[1076,7206,2074],{"class":1086},[1076,7208,2077],{"class":1684},[1076,7210,7211,7213,7215],{"class":1078,"line":1272},[1076,7212,2082],{"class":1090},[1076,7214,2074],{"class":1086},[1076,7216,2087],{"class":1684},[1076,7218,7219,7221],{"class":1078,"line":1285},[1076,7220,2092],{"class":1241},[1076,7222,2095],{"class":1684},[1076,7224,7225],{"class":1078,"line":1297},[1076,7226,1809],{"class":1086},[1076,7228,7229],{"class":1078,"line":1754},[1076,7230,1112],{"emptyLinePlaceholder":8},[1076,7232,7233,7235,7237,7239,7241,7243,7245],{"class":1078,"line":1765},[1076,7234,2108],{"class":1082},[1076,7236,2111],{"class":1086},[1076,7238,2024],{"class":1126},[1076,7240,2116],{"class":1090},[1076,7242,2119],{"class":1086},[1076,7244,2122],{"class":1082},[1076,7246,2125],{"class":1684},[1012,7248,7249,7261,7267,7273,7283,7293,7297,7305],{},[1015,7250,7251],{"name":2130,"type":2131,":required":1019},[856,7252,2134,7253,1140,7255,2139,7257,2143,7259,1048],{},[868,7254,1318],{},[868,7256,1314],{},[868,7258,2142],{},[868,7260,2146],{},[1015,7262,7263],{"name":2149,"type":1026,":required":1019},[856,7264,2152,7265,2156],{},[868,7266,2155],{},[1015,7268,7269],{"name":2159,"type":2160,":required":1019},[856,7270,2163,7271,2167],{},[868,7272,2166],{},[1015,7274,7275],{"name":911,"type":1405,":required":1019},[856,7276,2172,7277,2176,7279,2180,7281,2184],{},[868,7278,2175],{},[868,7280,2179],{},[868,7282,2183],{},[1015,7284,7285],{"name":2187,"type":1026,":required":1019},[856,7286,2190,7287,1140,7289,1140,7291,2200],{},[868,7288,2193],{},[868,7290,2196],{},[868,7292,2199],{},[1015,7294,7295],{"name":2203,"type":2204,":required":1019},[856,7296,2207],{},[1015,7298,7299],{"name":2210,"type":2211,":required":1019},[856,7300,2214,7301,2218,7303,2221],{},[868,7302,2217],{},[868,7304,894],{},[1015,7306,7307],{"name":1432,"type":1405},[856,7308,2226,7309,2229,7311,2232],{},[868,7310,911],{},[868,7312,1452],{},[1353,7314,2236],{"id":2235},[2238,7316,7317,7319,7329,7331,7343,7345,7351,7353,7361,7363],{"level":2240},[1353,7318,2244],{"id":2243},[856,7320,2247,7321,2251,7323,2255,7325,2258,7327,2261],{},[868,7322,2250],{},[868,7324,2254],{},[868,7326,2217],{},[868,7328,1143],{},[1353,7330,2265],{"id":2264},[856,7332,2268,7333,2272,7335,2276,7337,2279,7339,2282,7341,1048],{},[868,7334,2271],{},[868,7336,2275],{},[868,7338,2217],{},[868,7340,894],{},[868,7342,2285],{},[1353,7344,2289],{"id":2288},[856,7346,2292,7347,2295,7349,2298],{},[868,7348,1452],{},[868,7350,2175],{},[1353,7352,2302],{"id":2301},[856,7354,2305,7355,2308,7357,2312,7359,2315],{},[868,7356,911],{},[868,7358,2311],{},[868,7360,2250],{},[1353,7362,2319],{"id":2318},[856,7364,2322,7365,2325,7367,2328],{},[868,7366,2250],{},[868,7368,1019],{},[2330,7370,7371],{},[856,7372,2334,7373,2337,7375,2341],{},[868,7374,2250],{},[868,7376,2340],{},[930,7378,7379],{"id":2344},[868,7380,2275],{},[856,7382,2349,7383,2352,7385,2355,7387,2359],{},[868,7384,2217],{},[868,7386,2285],{},[868,7388,2358],{},[1067,7390,7391],{"className":1069,"code":2362,"language":1071,"meta":1072,"style":1072},[868,7392,7393,7411,7415,7445,7449,7463,7467,7471,7475,7479,7483,7487],{"__ignoreMap":1072},[1076,7394,7395,7397,7399,7401,7403,7405,7407,7409],{"class":1078,"line":1079},[1076,7396,1083],{"class":1082},[1076,7398,1087],{"class":1086},[1076,7400,2275],{"class":1090},[1076,7402,1093],{"class":1086},[1076,7404,1096],{"class":1082},[1076,7406,1100],{"class":1099},[1076,7408,1047],{"class":1103},[1076,7410,1106],{"class":1099},[1076,7412,7413],{"class":1078,"line":1109},[1076,7414,1112],{"emptyLinePlaceholder":8},[1076,7416,7417,7419,7421,7423,7425,7427,7429,7431,7433,7435,7437,7439,7441,7443],{"class":1078,"line":1115},[1076,7418,1119],{"class":1118},[1076,7420,2393],{"class":1122},[1076,7422,1127],{"class":1126},[1076,7424,1994],{"class":1082},[1076,7426,2400],{"class":1130},[1076,7428,1134],{"class":1086},[1076,7430,2130],{"class":1090},[1076,7432,1140],{"class":1086},[1076,7434,2149],{"class":1090},[1076,7436,1140],{"class":1086},[1076,7438,2210],{"class":1090},[1076,7440,1140],{"class":1086},[1076,7442,2203],{"class":1090},[1076,7444,1809],{"class":1086},[1076,7446,7447],{"class":1078,"line":1149},[1076,7448,1112],{"emptyLinePlaceholder":8},[1076,7450,7451,7453,7455,7457,7459,7461],{"class":1078,"line":1169},[1076,7452,2108],{"class":1082},[1076,7454,2111],{"class":1086},[1076,7456,2431],{"class":1090},[1076,7458,2434],{"class":1126},[1076,7460,2437],{"class":1137},[1076,7462,2440],{"class":1086},[1076,7464,7465],{"class":1078,"line":1182},[1076,7466,2445],{"class":1684},[1076,7468,7469],{"class":1078,"line":1199},[1076,7470,2450],{"class":1082},[1076,7472,7473],{"class":1078,"line":1216},[1076,7474,2455],{"class":1086},[1076,7476,7477],{"class":1078,"line":1233},[1076,7478,1112],{"emptyLinePlaceholder":8},[1076,7480,7481],{"class":1078,"line":1247},[1076,7482,2464],{"class":1684},[1076,7484,7485],{"class":1078,"line":1259},[1076,7486,2469],{"class":1684},[1076,7488,7489],{"class":1078,"line":1272},[1076,7490,2474],{"class":1684},[856,7492,2477],{},[938,7494,7495,7503],{},[941,7496,7497],{},[944,7498,7499,7501],{},[947,7500,2211],{},[947,7502,2488],{},[957,7504,7505,7513,7523],{},[944,7506,7507,7509],{},[962,7508,2495],{},[962,7510,7511],{},[868,7512,890],{},[944,7514,7515,7517],{},[962,7516,2504],{},[962,7518,7519,2510,7521,2514],{},[868,7520,2509],{},[868,7522,2513],{},[944,7524,7525,7527],{},[962,7526,2519],{},[962,7528,7529],{},[868,7530,2524],{},[2526,7532,7533],{},[856,7534,2530,7535,2533,7537,2536,7539,2539,7541,2542],{},[868,7536,2166],{},[868,7538,2275],{},[868,7540,2275],{},[868,7542,915],{},[930,7544,7545],{"id":2545},[868,7546,2548],{},[856,7548,2551],{},[1067,7550,7551],{"className":1069,"code":2554,"language":1071,"meta":1072,"style":1072},[868,7552,7553,7571,7575],{"__ignoreMap":1072},[1076,7554,7555,7557,7559,7561,7563,7565,7567,7569],{"class":1078,"line":1079},[1076,7556,1083],{"class":1082},[1076,7558,1087],{"class":1086},[1076,7560,2548],{"class":1090},[1076,7562,1093],{"class":1086},[1076,7564,1096],{"class":1082},[1076,7566,1100],{"class":1099},[1076,7568,1047],{"class":1103},[1076,7570,1106],{"class":1099},[1076,7572,7573],{"class":1078,"line":1109},[1076,7574,1112],{"emptyLinePlaceholder":8},[1076,7576,7577,7579,7581,7583,7585,7587,7589,7591,7593,7595,7597,7599,7601,7603],{"class":1078,"line":1115},[1076,7578,2548],{"class":1130},[1076,7580,1134],{"class":1086},[1076,7582,2203],{"class":1090},[1076,7584,1140],{"class":1086},[1076,7586,2591],{"class":1090},[1076,7588,2594],{"class":1086},[1076,7590,1796],{"class":1090},[1076,7592,1140],{"class":1086},[1076,7594,1801],{"class":1090},[1076,7596,1140],{"class":1086},[1076,7598,2605],{"class":1090},[1076,7600,1140],{"class":1086},[1076,7602,2610],{"class":1090},[1076,7604,2613],{"class":1086},[1012,7606,7607,7611,7615],{},[1015,7608,7609],{"name":2203,"type":2204,":required":1019},[856,7610,2620],{},[1015,7612,7613],{"name":2149,"type":1026,":required":1019},[856,7614,2625],{},[1015,7616,7617],{"name":1814,"type":2628,":required":1019},[856,7618,2631,7619,2635],{},[868,7620,2634],{},[1892,7622,7623],{},[856,7624,2640,7625,2644],{},[868,7626,2643],{},[930,7628,7629],{"id":2647},[868,7630,1927],{},[856,7632,2652],{},[1067,7634,7635],{"className":1069,"code":2655,"language":1071,"meta":1072,"style":1072},[868,7636,7637,7655,7659,7679,7687,7693],{"__ignoreMap":1072},[1076,7638,7639,7641,7643,7645,7647,7649,7651,7653],{"class":1078,"line":1079},[1076,7640,1083],{"class":1082},[1076,7642,1087],{"class":1086},[1076,7644,1927],{"class":1090},[1076,7646,1093],{"class":1086},[1076,7648,1096],{"class":1082},[1076,7650,1100],{"class":1099},[1076,7652,1047],{"class":1103},[1076,7654,1106],{"class":1099},[1076,7656,7657],{"class":1078,"line":1109},[1076,7658,1112],{"emptyLinePlaceholder":8},[1076,7660,7661,7663,7665,7667,7669,7671,7673,7675,7677],{"class":1078,"line":1115},[1076,7662,1119],{"class":1118},[1076,7664,2686],{"class":1122},[1076,7666,1127],{"class":1126},[1076,7668,1953],{"class":1130},[1076,7670,1956],{"class":1086},[1076,7672,1959],{"class":1090},[1076,7674,1156],{"class":1126},[1076,7676,1965],{"class":1964},[1076,7678,2701],{"class":1086},[1076,7680,7681,7683,7685],{"class":1078,"line":1149},[1076,7682,2706],{"class":1241},[1076,7684,2037],{"class":1086},[1076,7686,2711],{"class":1684},[1076,7688,7689,7691],{"class":1078,"line":1169},[1076,7690,2716],{"class":1241},[1076,7692,2719],{"class":1684},[1076,7694,7695],{"class":1078,"line":1182},[1076,7696,1809],{"class":1086},[1012,7698,7699,7703],{},[1015,7700,7701],{"name":2728,"type":1405,":required":1019},[856,7702,2731],{},[1015,7704,7705],{"name":2734,"type":1405,":required":1019},[856,7706,2737,7707,2741],{},[876,7708,2740],{},[856,7710,2744,7711,2747],{},[868,7712,1413],{},[922,7714],{},[925,7716,2753],{"id":2752},[856,7718,2756],{},[930,7720,2760],{"id":2759},[856,7722,2763,7723,2767],{},[868,7724,2766],{},[1067,7726,7727],{"className":1069,"code":2770,"language":1071,"meta":1072,"style":1072},[868,7728,7729],{"__ignoreMap":1072},[1076,7730,7731,7733,7735,7737,7739,7741,7743,7745,7747,7749,7751,7753,7755,7757,7759,7761,7763,7765,7767,7769,7771,7773],{"class":1078,"line":1079},[1076,7732,2777],{"class":1082},[1076,7734,1997],{"class":1130},[1076,7736,1134],{"class":1086},[1076,7738,2605],{"class":1090},[1076,7740,1140],{"class":1086},[1076,7742,2788],{"class":1090},[1076,7744,1048],{"class":1086},[1076,7746,2021],{"class":1090},[1076,7748,2024],{"class":1126},[1076,7750,1140],{"class":1086},[1076,7752,2799],{"class":1090},[1076,7754,1140],{"class":1086},[1076,7756,2179],{"class":1241},[1076,7758,1140],{"class":1086},[1076,7760,1163],{"class":1099},[1076,7762,2021],{"class":1103},[1076,7764,1163],{"class":1099},[1076,7766,1140],{"class":1086},[1076,7768,2203],{"class":1090},[1076,7770,1140],{"class":1086},[1076,7772,2210],{"class":1090},[1076,7774,1809],{"class":1086},[930,7776,2825],{"id":2824},[856,7778,2828],{},[1067,7780,7781],{"className":1069,"code":2831,"language":1071,"meta":1072,"style":1072},[868,7782,7783],{"__ignoreMap":1072},[1076,7784,7785,7787,7789,7791,7793,7795,7797,7799,7801,7803,7805,7807,7809,7811,7813,7815,7817,7819,7821],{"class":1078,"line":1079},[1076,7786,2777],{"class":1082},[1076,7788,1997],{"class":1130},[1076,7790,1134],{"class":1086},[1076,7792,2610],{"class":1090},[1076,7794,1140],{"class":1086},[1076,7796,2848],{"class":1090},[1076,7798,1140],{"class":1086},[1076,7800,2853],{"class":1090},[1076,7802,1140],{"class":1086},[1076,7804,2179],{"class":1241},[1076,7806,1140],{"class":1086},[1076,7808,1163],{"class":1099},[1076,7810,2848],{"class":1103},[1076,7812,1163],{"class":1099},[1076,7814,1140],{"class":1086},[1076,7816,2203],{"class":1090},[1076,7818,1140],{"class":1086},[1076,7820,2210],{"class":1090},[1076,7822,1809],{"class":1086},[930,7824,2879],{"id":2878},[856,7826,2882],{},[1067,7828,7829],{"className":1069,"code":2885,"language":1071,"meta":1072,"style":1072},[868,7830,7831,7863],{"__ignoreMap":1072},[1076,7832,7833,7835,7837,7839,7841,7843,7845,7847,7849,7851,7853,7855,7857,7859,7861],{"class":1078,"line":1079},[1076,7834,1119],{"class":1118},[1076,7836,2894],{"class":1122},[1076,7838,1127],{"class":1126},[1076,7840,2899],{"class":1103},[1076,7842,2902],{"class":1118},[1076,7844,2788],{"class":1090},[1076,7846,1048],{"class":2907},[1076,7848,2021],{"class":1090},[1076,7850,2024],{"class":1126},[1076,7852,2914],{"class":1118},[1076,7854,2917],{"class":1103},[1076,7856,2902],{"class":1118},[1076,7858,2848],{"class":1090},[1076,7860,2914],{"class":1118},[1076,7862,2926],{"class":1103},[1076,7864,7865,7867,7869,7871,7873,7875,7877,7879,7881,7883,7885,7887,7889,7891,7893,7895,7897,7899,7901],{"class":1078,"line":1109},[1076,7866,2777],{"class":1082},[1076,7868,1997],{"class":1130},[1076,7870,1134],{"class":1086},[1076,7872,2937],{"class":1090},[1076,7874,1140],{"class":1086},[1076,7876,2591],{"class":1090},[1076,7878,1140],{"class":1086},[1076,7880,2946],{"class":1090},[1076,7882,1140],{"class":1086},[1076,7884,2183],{"class":1241},[1076,7886,1140],{"class":1086},[1076,7888,1163],{"class":1099},[1076,7890,2957],{"class":1103},[1076,7892,1163],{"class":1099},[1076,7894,1140],{"class":1086},[1076,7896,2203],{"class":1090},[1076,7898,1140],{"class":1086},[1076,7900,2210],{"class":1090},[1076,7902,1809],{"class":1086},[930,7904,2973],{"id":2972},[856,7906,2976,7907,2980],{},[868,7908,2979],{},[1067,7910,7911],{"className":1069,"code":2983,"language":1071,"meta":1072,"style":1072},[868,7912,7913],{"__ignoreMap":1072},[1076,7914,7915,7917,7919,7921,7923,7925,7927,7929,7931,7933,7935,7937,7939,7941,7943,7945,7947,7949,7951,7953,7955],{"class":1078,"line":1079},[1076,7916,2777],{"class":1082},[1076,7918,1997],{"class":1130},[1076,7920,1134],{"class":1086},[1076,7922,2996],{"class":1090},[1076,7924,1140],{"class":1086},[1076,7926,1163],{"class":1099},[1076,7928,3003],{"class":1103},[1076,7930,1163],{"class":1099},[1076,7932,1140],{"class":1086},[1076,7934,3010],{"class":1090},[1076,7936,1140],{"class":1086},[1076,7938,2175],{"class":1241},[1076,7940,1140],{"class":1086},[1076,7942,1163],{"class":1099},[1076,7944,2996],{"class":1103},[1076,7946,1163],{"class":1099},[1076,7948,1140],{"class":1086},[1076,7950,2203],{"class":1090},[1076,7952,1140],{"class":1086},[1076,7954,2210],{"class":1090},[1076,7956,1809],{"class":1086},[2330,7958,7959],{},[856,7960,3037],{},[922,7962],{},[925,7964,3043],{"id":3042},[856,7966,3046,7967,3049,7969,3052],{},[868,7968,2166],{},[868,7970,2217],{},[930,7972,103],{"id":1209},[856,7974,3057],{},[2238,7976,7977,7979,7983,7985,7989,7991],{"level":2240},[1353,7978,3063],{"id":3062},[856,7980,3066,7981,1048],{},[868,7982,3069],{},[1353,7984,3073],{"id":3072},[856,7986,3076,7987,1048],{},[868,7988,3069],{},[1353,7990,3082],{"id":3081},[856,7992,3085,7993,1048],{},[868,7994,3088],{},[856,7996,3091],{},[1067,7998,7999],{"className":1069,"code":3094,"language":1071,"meta":1072,"style":1072},[868,8000,8001,8021,8035,8049],{"__ignoreMap":1072},[1076,8002,8003,8005,8007,8009,8011,8013,8015,8017,8019],{"class":1078,"line":1079},[1076,8004,2799],{"class":1090},[1076,8006,1048],{"class":1086},[1076,8008,3105],{"class":1130},[1076,8010,1134],{"class":1086},[1076,8012,2788],{"class":1090},[1076,8014,1048],{"class":1086},[1076,8016,2021],{"class":1090},[1076,8018,2024],{"class":1126},[1076,8020,1809],{"class":1086},[1076,8022,8023,8025,8027,8029,8031,8033],{"class":1078,"line":1109},[1076,8024,2946],{"class":1090},[1076,8026,1048],{"class":1086},[1076,8028,3105],{"class":1130},[1076,8030,1134],{"class":1086},[1076,8032,2591],{"class":1090},[1076,8034,1809],{"class":1086},[1076,8036,8037,8039,8041,8043,8045,8047],{"class":1078,"line":1115},[1076,8038,2853],{"class":1090},[1076,8040,1048],{"class":1086},[1076,8042,3105],{"class":1130},[1076,8044,1134],{"class":1086},[1076,8046,2848],{"class":1090},[1076,8048,1809],{"class":1086},[1076,8050,8051,8053,8055,8057,8059],{"class":1078,"line":1149},[1076,8052,2777],{"class":1082},[1076,8054,3152],{"class":1130},[1076,8056,1134],{"class":1086},[1076,8058,2591],{"class":1090},[1076,8060,1809],{"class":1086},[930,8062,3162],{"id":3161},[856,8064,3165],{},[2238,8066,8067,8069,8073,8075,8079,8081],{"level":2240},[1353,8068,3063],{"id":3170},[856,8070,3173,8071,3177],{},[868,8072,3176],{},[1353,8074,3181],{"id":3180},[856,8076,3184,8077,1048],{},[868,8078,3176],{},[1353,8080,3082],{"id":3189},[856,8082,3192,8083,1048],{},[868,8084,3176],{},[856,8086,3197,8087,3201],{},[876,8088,3200],{},[1067,8090,8091],{"className":1069,"code":3204,"language":1071,"meta":1072,"style":1072},[868,8092,8093,8125],{"__ignoreMap":1072},[1076,8094,8095,8097,8099,8101,8103,8105,8107,8109,8111,8113,8115,8117,8119,8121,8123],{"class":1078,"line":1079},[1076,8096,2777],{"class":1082},[1076,8098,3213],{"class":1090},[1076,8100,1048],{"class":1086},[1076,8102,3218],{"class":1130},[1076,8104,1134],{"class":1086},[1076,8106,3223],{"class":1090},[1076,8108,1140],{"class":1086},[1076,8110,3228],{"class":1241},[1076,8112,3231],{"class":1126},[1076,8114,3234],{"class":1241},[1076,8116,3231],{"class":1126},[1076,8118,3239],{"class":1241},[1076,8120,3231],{"class":1126},[1076,8122,3244],{"class":1241},[1076,8124,1809],{"class":1086},[1076,8126,8127,8129,8131,8133,8135,8137,8139,8141,8143,8145,8147,8149,8151,8153,8155],{"class":1078,"line":1109},[1076,8128,2777],{"class":1082},[1076,8130,3253],{"class":1090},[1076,8132,1048],{"class":1086},[1076,8134,3218],{"class":1130},[1076,8136,1134],{"class":1086},[1076,8138,2591],{"class":1090},[1076,8140,1140],{"class":1086},[1076,8142,3228],{"class":1241},[1076,8144,3231],{"class":1126},[1076,8146,3234],{"class":1241},[1076,8148,3231],{"class":1126},[1076,8150,3239],{"class":1241},[1076,8152,3231],{"class":1126},[1076,8154,3244],{"class":1241},[1076,8156,1809],{"class":1086},[856,8158,3282],{},[2330,8160,8161],{},[856,8162,3287],{},[930,8164,99],{"id":3290},[856,8166,3293],{},[2238,8168,8169,8171,8175,8177,8183,8185],{"level":2240},[1353,8170,3299],{"id":3298},[856,8172,3302,8173,1048],{},[868,8174,3069],{},[1353,8176,3082],{"id":3307},[856,8178,3310,8179,3314,8181,1048],{},[868,8180,3313],{},[868,8182,3069],{},[1353,8184,3073],{"id":3319},[856,8186,3322,8187,1048],{},[868,8188,3069],{},[930,8190,111],{"id":3327},[856,8192,3330],{},[2238,8194,8195,8197,8201,8203,8209,8211],{"level":2240},[1353,8196,3299],{"id":3335},[856,8198,3338,8199,1048],{},[868,8200,3176],{},[1353,8202,3344],{"id":3343},[856,8204,3347,8205,3351,8207,1048],{},[868,8206,3350],{},[868,8208,3069],{},[1353,8210,3082],{"id":3356},[856,8212,3359,8213,3363,8215,1048],{},[868,8214,3362],{},[868,8216,3069],{},[930,8218,3369],{"id":3368},[856,8220,3372,8221,3376],{},[868,8222,3375],{},[2238,8224,8225,8227,8233,8235,8239,8241,8245,8247],{"level":2240},[1353,8226,3382],{"id":3381},[856,8228,3385,8229,3388,8231,1048],{},[868,8230,2979],{},[868,8232,3176],{},[1353,8234,3063],{"id":3393},[856,8236,3396,8237,1048],{},[868,8238,3069],{},[1353,8240,3073],{"id":3401},[856,8242,3404,8243,1048],{},[868,8244,3069],{},[1353,8246,3410],{"id":3409},[856,8248,3310,8249,3415,8251,1048],{},[868,8250,3313],{},[868,8252,3088],{},[930,8254,3421],{"id":3420},[856,8256,3424],{},[2238,8258,8259,8261,8267,8269,8273,8275,8279,8281],{"level":2240},[1353,8260,3382],{"id":3429},[856,8262,3432,8263,3435,8265,1048],{},[868,8264,2979],{},[868,8266,3176],{},[1353,8268,3063],{"id":3440},[856,8270,3396,8271,1048],{},[868,8272,3069],{},[1353,8274,3448],{"id":3447},[856,8276,3451,8277,1048],{},[868,8278,3069],{},[1353,8280,3082],{"id":3456},[856,8282,3310,8283,3363,8285,1048],{},[868,8284,3461],{},[868,8286,3088],{},[930,8288,3467],{"id":3466},[856,8290,3470,8291,1048],{},[868,8292,3176],{},[930,8294,3476],{"id":3475},[856,8296,3479],{},[2238,8298,8299,8301,8307,8309,8313,8315],{"level":2240},[1353,8300,3485],{"id":3484},[856,8302,3488,8303,3492,8305,1048],{},[868,8304,3491],{},[868,8306,3176],{},[1353,8308,3063],{"id":3497},[856,8310,3396,8311,1048],{},[868,8312,3069],{},[1353,8314,3082],{"id":3504},[856,8316,3507,8317,1048],{},[868,8318,3069],{},[922,8320],{},[925,8322,3515],{"id":3514},[856,8324,3518,8325,3521],{},[868,8326,870],{},[856,8328,3524],{},[1012,8330,8331,8335,8341,8345,8349],{},[1015,8332,8333],{"name":1400,"type":1405,":required":1019},[856,8334,3531],{},[1015,8336,8337],{"name":1413,"type":1405,":required":1019},[856,8338,1429,8339,1433],{},[876,8340,1432],{},[1015,8342,8343],{"name":1440,"type":1405,":required":1019},[856,8344,3542],{},[1015,8346,8347],{"name":988,"type":1405,":required":1019},[856,8348,3547],{},[1015,8350,8351],{"name":1479,"type":1405,":required":1019},[856,8352,3552],{},[930,8354,8355],{"id":3555},[868,8356,3558],{},[856,8358,3561,8359,3565],{},[868,8360,3564],{},[938,8362,8363,8379],{},[941,8364,8365],{},[944,8366,8367,8369,8371,8373,8375,8377],{},[947,8368,2131],{},[947,8370,3576],{},[947,8372,3579],{},[947,8374,3582],{},[947,8376,3585],{},[947,8378,955],{},[957,8380,8381,8399,8417,8435],{},[944,8382,8383,8387,8391,8393,8395,8397],{},[962,8384,8385],{},[868,8386,3596],{},[962,8388,8389],{},[868,8390,3313],{},[962,8392,2175],{},[962,8394,3605],{},[962,8396,3608],{},[962,8398,3611],{},[944,8400,8401,8405,8409,8411,8413,8415],{},[962,8402,8403],{},[868,8404,3618],{},[962,8406,8407],{},[868,8408,3313],{},[962,8410,3625],{},[962,8412,3628],{},[962,8414,3608],{},[962,8416,3633],{},[944,8418,8419,8423,8427,8429,8431,8433],{},[962,8420,8421],{},[868,8422,2605],{},[962,8424,8425],{},[868,8426,3644],{},[962,8428,3647],{},[962,8430,3650],{},[962,8432,3653],{},[962,8434,3656],{},[944,8436,8437,8441,8445,8447,8449,8451],{},[962,8438,8439],{},[868,8440,2610],{},[962,8442,8443],{},[868,8444,3667],{},[962,8446,3625],{},[962,8448,3650],{},[962,8450,3674],{},[962,8452,3677],{},[930,8454,8455],{"id":3680},[868,8456,3683],{},[856,8458,3561,8459,3689],{},[868,8460,3688],{},[938,8462,8463,8479],{},[941,8464,8465],{},[944,8466,8467,8469,8471,8473,8475,8477],{},[947,8468,2131],{},[947,8470,3576],{},[947,8472,3579],{},[947,8474,3582],{},[947,8476,3585],{},[947,8478,955],{},[957,8480,8481,8499,8517,8535,8553],{},[944,8482,8483,8487,8491,8493,8495,8497],{},[962,8484,8485],{},[868,8486,3716],{},[962,8488,8489],{},[868,8490,3644],{},[962,8492,2179],{},[962,8494,3605],{},[962,8496,3727],{},[962,8498,3730],{},[944,8500,8501,8505,8509,8511,8513,8515],{},[962,8502,8503],{},[868,8504,3737],{},[962,8506,8507],{},[868,8508,3644],{},[962,8510,3625],{},[962,8512,3608],{},[962,8514,3727],{},[962,8516,3750],{},[944,8518,8519,8523,8527,8529,8531,8533],{},[962,8520,8521],{},[868,8522,3757],{},[962,8524,8525],{},[868,8526,3313],{},[962,8528,2175],{},[962,8530,3605],{},[962,8532,3608],{},[962,8534,3770],{},[944,8536,8537,8541,8545,8547,8549,8551],{},[962,8538,8539],{},[868,8540,3777],{},[962,8542,8543],{},[868,8544,3313],{},[962,8546,2183],{},[962,8548,3650],{},[962,8550,3650],{},[962,8552,3790],{},[944,8554,8555,8559,8563,8565,8567,8569],{},[962,8556,8557],{},[868,8558,3797],{},[962,8560,8561],{},[868,8562,3667],{},[962,8564,2183],{},[962,8566,3650],{},[962,8568,3650],{},[962,8570,3810],{},[930,8572,8573],{"id":3813},[868,8574,3816],{},[856,8576,3561,8577,1048],{},[868,8578,3821],{},[938,8580,8581,8597],{},[941,8582,8583],{},[944,8584,8585,8587,8589,8591,8593,8595],{},[947,8586,2131],{},[947,8588,3576],{},[947,8590,3579],{},[947,8592,3582],{},[947,8594,3585],{},[947,8596,955],{},[957,8598,8599,8617,8635,8653],{},[944,8600,8601,8605,8609,8611,8613,8615],{},[962,8602,8603],{},[868,8604,3848],{},[962,8606,8607],{},[868,8608,3644],{},[962,8610,2175],{},[962,8612,3605],{},[962,8614,3859],{},[962,8616,3862],{},[944,8618,8619,8623,8627,8629,8631,8633],{},[962,8620,8621],{},[868,8622,3869],{},[962,8624,8625],{},[868,8626,3644],{},[962,8628,3876],{},[962,8630,3628],{},[962,8632,3608],{},[962,8634,3883],{},[944,8636,8637,8641,8645,8647,8649,8651],{},[962,8638,8639],{},[868,8640,3890],{},[962,8642,8643],{},[868,8644,3895],{},[962,8646,3625],{},[962,8648,3859],{},[962,8650,3727],{},[962,8652,3904],{},[944,8654,8655,8659,8663,8665,8667,8669],{},[962,8656,8657],{},[868,8658,3911],{},[962,8660,8661],{},[868,8662,3362],{},[962,8664,2183],{},[962,8666,3920],{},[962,8668,3727],{},[962,8670,3925],{},[930,8672,8673],{"id":3928},[868,8674,3931],{},[856,8676,3561,8677,3937],{},[868,8678,3936],{},[938,8680,8681,8697],{},[941,8682,8683],{},[944,8684,8685,8687,8689,8691,8693,8695],{},[947,8686,2131],{},[947,8688,3576],{},[947,8690,3579],{},[947,8692,3582],{},[947,8694,3585],{},[947,8696,955],{},[957,8698,8699,8717,8735,8753,8771],{},[944,8700,8701,8705,8709,8711,8713,8715],{},[962,8702,8703],{},[868,8704,3964],{},[962,8706,8707],{},[868,8708,3644],{},[962,8710,2179],{},[962,8712,3605],{},[962,8714,3608],{},[962,8716,3977],{},[944,8718,8719,8723,8727,8729,8731,8733],{},[962,8720,8721],{},[868,8722,3984],{},[962,8724,8725],{},[868,8726,3644],{},[962,8728,2183],{},[962,8730,3920],{},[962,8732,3628],{},[962,8734,3997],{},[944,8736,8737,8741,8745,8747,8749,8751],{},[962,8738,8739],{},[868,8740,4004],{},[962,8742,8743],{},[868,8744,4009],{},[962,8746,2179],{},[962,8748,3605],{},[962,8750,3608],{},[962,8752,4018],{},[944,8754,8755,8759,8763,8765,8767,8769],{},[962,8756,8757],{},[868,8758,4025],{},[962,8760,8761],{},[868,8762,4009],{},[962,8764,2240],{},[962,8766,4034],{},[962,8768,4034],{},[962,8770,4039],{},[944,8772,8773,8777,8781,8783,8785,8787],{},[962,8774,8775],{},[868,8776,4046],{},[962,8778,8779],{},[868,8780,4009],{},[962,8782,2183],{},[962,8784,4034],{},[962,8786,4057],{},[962,8788,4060],{},[2526,8790,8791],{},[856,8792,4065,8793,4069],{},[868,8794,4068],{},[930,8796,8797],{"id":4072},[868,8798,4075],{},[856,8800,4078],{},[938,8802,8803,8819],{},[941,8804,8805],{},[944,8806,8807,8809,8811,8813,8815,8817],{},[947,8808,2131],{},[947,8810,3576],{},[947,8812,3579],{},[947,8814,3582],{},[947,8816,3585],{},[947,8818,955],{},[957,8820,8821,8839],{},[944,8822,8823,8827,8831,8833,8835,8837],{},[962,8824,8825],{},[868,8826,3596],{},[962,8828,8829],{},[868,8830,3644],{},[962,8832,2179],{},[962,8834,3605],{},[962,8836,3727],{},[962,8838,4117],{},[944,8840,8841,8845,8849,8851,8853,8855],{},[962,8842,8843],{},[868,8844,3618],{},[962,8846,8847],{},[868,8848,3644],{},[962,8850,4130],{},[962,8852,3608],{},[962,8854,3608],{},[962,8856,4137],{},[930,8858,8859],{"id":4140},[868,8860,4143],{},[856,8862,3561,8863,1048],{},[868,8864,3375],{},[938,8866,8867,8883],{},[941,8868,8869],{},[944,8870,8871,8873,8875,8877,8879,8881],{},[947,8872,2131],{},[947,8874,3576],{},[947,8876,3579],{},[947,8878,3582],{},[947,8880,3585],{},[947,8882,955],{},[957,8884,8885,8903,8921,8939],{},[944,8886,8887,8891,8895,8897,8899,8901],{},[962,8888,8889],{},[868,8890,4174],{},[962,8892,8893],{},[868,8894,3313],{},[962,8896,2175],{},[962,8898,3605],{},[962,8900,3608],{},[962,8902,4187],{},[944,8904,8905,8909,8913,8915,8917,8919],{},[962,8906,8907],{},[868,8908,4194],{},[962,8910,8911],{},[868,8912,3313],{},[962,8914,2240],{},[962,8916,3608],{},[962,8918,3727],{},[962,8920,4207],{},[944,8922,8923,8927,8931,8933,8935,8937],{},[962,8924,8925],{},[868,8926,2605],{},[962,8928,8929],{},[868,8930,3644],{},[962,8932,3625],{},[962,8934,3650],{},[962,8936,4224],{},[962,8938,4227],{},[944,8940,8941,8945,8949,8951,8953,8955],{},[962,8942,8943],{},[868,8944,2610],{},[962,8946,8947],{},[868,8948,3667],{},[962,8950,3625],{},[962,8952,3650],{},[962,8954,4224],{},[962,8956,4246],{},[930,8958,8959],{"id":4249},[868,8960,4252],{},[856,8962,4255],{},[938,8964,8965,8981],{},[941,8966,8967],{},[944,8968,8969,8971,8973,8975,8977,8979],{},[947,8970,2131],{},[947,8972,3576],{},[947,8974,3579],{},[947,8976,3582],{},[947,8978,3585],{},[947,8980,955],{},[957,8982,8983,9001,9019,9037,9055],{},[944,8984,8985,8989,8993,8995,8997,8999],{},[962,8986,8987],{},[868,8988,4174],{},[962,8990,8991],{},[868,8992,4286],{},[962,8994,2175],{},[962,8996,3605],{},[962,8998,3608],{},[962,9000,4295],{},[944,9002,9003,9007,9011,9013,9015,9017],{},[962,9004,9005],{},[868,9006,4194],{},[962,9008,9009],{},[868,9010,4286],{},[962,9012,2240],{},[962,9014,3608],{},[962,9016,3727],{},[962,9018,4314],{},[944,9020,9021,9025,9029,9031,9033,9035],{},[962,9022,9023],{},[868,9024,2605],{},[962,9026,9027],{},[868,9028,3644],{},[962,9030,3625],{},[962,9032,3650],{},[962,9034,4224],{},[962,9036,4227],{},[944,9038,9039,9043,9047,9049,9051,9053],{},[962,9040,9041],{},[868,9042,4339],{},[962,9044,9045],{},[868,9046,4344],{},[962,9048,4347],{},[962,9050,3650],{},[962,9052,4034],{},[962,9054,4354],{},[944,9056,9057,9061,9065,9067,9069,9071],{},[962,9058,9059],{},[868,9060,2996],{},[962,9062,9063],{},[868,9064,3003],{},[962,9066,4367],{},[962,9068,3650],{},[962,9070,3650],{},[962,9072,4374],{},[2330,9074,9075],{},[856,9076,2334,9077,4381,9079,4384],{},[868,9078,2996],{},[868,9080,2979],{},[930,9082,9083],{"id":4387},[868,9084,4390],{},[856,9086,4393],{},[938,9088,9089,9105],{},[941,9090,9091],{},[944,9092,9093,9095,9097,9099,9101,9103],{},[947,9094,2131],{},[947,9096,3576],{},[947,9098,3579],{},[947,9100,3582],{},[947,9102,3585],{},[947,9104,955],{},[957,9106,9107,9125,9143,9161],{},[944,9108,9109,9113,9117,9119,9121,9123],{},[962,9110,9111],{},[868,9112,4174],{},[962,9114,9115],{},[868,9116,4424],{},[962,9118,2175],{},[962,9120,3605],{},[962,9122,3608],{},[962,9124,4433],{},[944,9126,9127,9131,9135,9137,9139,9141],{},[962,9128,9129],{},[868,9130,4440],{},[962,9132,9133],{},[868,9134,4424],{},[962,9136,3625],{},[962,9138,3920],{},[962,9140,3920],{},[962,9142,4453],{},[944,9144,9145,9149,9153,9155,9157,9159],{},[962,9146,9147],{},[868,9148,4460],{},[962,9150,9151],{},[868,9152,3644],{},[962,9154,4467],{},[962,9156,3920],{},[962,9158,3920],{},[962,9160,4474],{},[944,9162,9163,9167,9171,9173,9175,9177],{},[962,9164,9165],{},[868,9166,4481],{},[962,9168,9169],{},[868,9170,4486],{},[962,9172,1452],{},[962,9174,4491],{},[962,9176,4494],{},[962,9178,4497],{},[922,9180],{},[925,9182,4503],{"id":4502},[856,9184,4506,9185,1048],{},[868,9186,1047],{},[2238,9188,9189,9191,9193,10063,10069,10071,10078,10160,10162,10166,10522],{"level":2240},[1353,9190,4514],{"id":4513},[856,9192,4517],{},[1067,9194,9195],{"className":1069,"code":4520,"language":1071,"meta":1072,"style":1072},[868,9196,9197,9203,9209,9215,9223,9235,9263,9283,9287,9295,9307,9319,9343,9347,9351,9369,9373,9403,9423,9429,9439,9453,9467,9481,9491,9501,9511,9521,9531,9535,9539,9559,9565,9575,9589,9603,9617,9627,9637,9647,9657,9667,9671,9675,9695,9701,9711,9725,9739,9753,9763,9773,9783,9793,9803,9807,9811,9817,9839,9845,9867,9879,9893,9907,9921,9925,9929,9933,9937,9941,9973,10001,10005,10009,10025,10053,10059],{"__ignoreMap":1072},[1076,9198,9199,9201],{"class":1078,"line":1079},[1076,9200,1083],{"class":1082},[1076,9202,4529],{"class":1086},[1076,9204,9205,9207],{"class":1078,"line":1109},[1076,9206,4534],{"class":1090},[1076,9208,1166],{"class":1086},[1076,9210,9211,9213],{"class":1078,"line":1115},[1076,9212,4541],{"class":1090},[1076,9214,1166],{"class":1086},[1076,9216,9217,9219,9221],{"class":1078,"line":1149},[1076,9218,4548],{"class":1082},[1076,9220,4551],{"class":1090},[1076,9222,1166],{"class":1086},[1076,9224,9225,9227,9229,9231,9233],{"class":1078,"line":1169},[1076,9226,4558],{"class":1086},[1076,9228,1096],{"class":1082},[1076,9230,1100],{"class":1099},[1076,9232,1047],{"class":1103},[1076,9234,1106],{"class":1099},[1076,9236,9237,9239,9241,9243,9245,9247,9249,9251,9253,9255,9257,9259,9261],{"class":1078,"line":1182},[1076,9238,1083],{"class":1082},[1076,9240,4573],{"class":1082},[1076,9242,1087],{"class":1086},[1076,9244,1314],{"class":1090},[1076,9246,1140],{"class":1086},[1076,9248,1318],{"class":1090},[1076,9250,1140],{"class":1086},[1076,9252,1334],{"class":1090},[1076,9254,1093],{"class":1086},[1076,9256,1096],{"class":1082},[1076,9258,1100],{"class":1099},[1076,9260,865],{"class":1103},[1076,9262,1106],{"class":1099},[1076,9264,9265,9267,9269,9271,9273,9275,9277,9279,9281],{"class":1078,"line":1199},[1076,9266,1083],{"class":1082},[1076,9268,4573],{"class":1082},[1076,9270,1087],{"class":1086},[1076,9272,1521],{"class":1090},[1076,9274,1093],{"class":1086},[1076,9276,1096],{"class":1082},[1076,9278,1100],{"class":1099},[1076,9280,1192],{"class":1103},[1076,9282,1106],{"class":1099},[1076,9284,9285],{"class":1078,"line":1216},[1076,9286,1112],{"emptyLinePlaceholder":8},[1076,9288,9289,9291,9293],{"class":1078,"line":1233},[1076,9290,4624],{"class":1118},[1076,9292,4627],{"class":1964},[1076,9294,4529],{"class":1086},[1076,9296,9297,9299,9301,9303,9305],{"class":1078,"line":1247},[1076,9298,4634],{"class":1090},[1076,9300,1156],{"class":1126},[1076,9302,4551],{"class":1964},[1076,9304,4641],{"class":1126},[1076,9306,4644],{"class":1964},[1076,9308,9309,9311,9313,9315,9317],{"class":1078,"line":1259},[1076,9310,4649],{"class":1090},[1076,9312,1156],{"class":1126},[1076,9314,4654],{"class":1964},[1076,9316,4641],{"class":1126},[1076,9318,4659],{"class":1964},[1076,9320,9321,9323,9325,9327,9329,9331,9333,9335,9337,9339,9341],{"class":1078,"line":1272},[1076,9322,4664],{"class":1130},[1076,9324,1134],{"class":1086},[1076,9326,2149],{"class":4669},[1076,9328,1156],{"class":1126},[1076,9330,4674],{"class":1964},[1076,9332,4677],{"class":1086},[1076,9334,1156],{"class":1126},[1076,9336,4682],{"class":1964},[1076,9338,4685],{"class":1086},[1076,9340,4688],{"class":1964},[1076,9342,4691],{"class":1086},[1076,9344,9345],{"class":1078,"line":1285},[1076,9346,2455],{"class":1086},[1076,9348,9349],{"class":1078,"line":1297},[1076,9350,1112],{"emptyLinePlaceholder":8},[1076,9352,9353,9355,9357,9359,9361,9363,9365,9367],{"class":1078,"line":1754},[1076,9354,4704],{"class":1118},[1076,9356,4707],{"class":1090},[1076,9358,1156],{"class":1126},[1076,9360,4627],{"class":1964},[1076,9362,4641],{"class":1126},[1076,9364,2437],{"class":1964},[1076,9366,1127],{"class":1126},[1076,9368,4720],{"class":1137},[1076,9370,9371],{"class":1078,"line":1765},[1076,9372,1112],{"emptyLinePlaceholder":8},[1076,9374,9375,9377,9379,9381,9383,9385,9387,9389,9391,9393,9395,9397,9399,9401],{"class":1078,"line":1770},[1076,9376,4729],{"class":1118},[1076,9378,4732],{"class":1130},[1076,9380,1134],{"class":1086},[1076,9382,4737],{"class":4669},[1076,9384,1156],{"class":1126},[1076,9386,4742],{"class":1964},[1076,9388,1140],{"class":1086},[1076,9390,1497],{"class":4669},[1076,9392,1156],{"class":1126},[1076,9394,4674],{"class":1964},[1076,9396,4677],{"class":1086},[1076,9398,1156],{"class":1126},[1076,9400,4627],{"class":1964},[1076,9402,4529],{"class":1086},[1076,9404,9405,9407,9409,9411,9413,9415,9417,9419,9421],{"class":1078,"line":1775},[1076,9406,4763],{"class":1118},[1076,9408,1621],{"class":1122},[1076,9410,1127],{"class":1126},[1076,9412,1131],{"class":1130},[1076,9414,1134],{"class":1086},[1076,9416,1019],{"class":1137},[1076,9418,1140],{"class":1086},[1076,9420,1143],{"class":1137},[1076,9422,1146],{"class":1086},[1076,9424,9425,9427],{"class":1078,"line":1780},[1076,9426,4784],{"class":1090},[1076,9428,1166],{"class":1086},[1076,9430,9431,9433,9435,9437],{"class":1078,"line":4789},[1076,9432,4792],{"class":1090},[1076,9434,1156],{"class":1155},[1076,9436,1177],{"class":1090},[1076,9438,1166],{"class":1086},[1076,9440,9441,9443,9445,9447,9449,9451],{"class":1078,"line":4801},[1076,9442,4804],{"class":1090},[1076,9444,1156],{"class":1155},[1076,9446,1100],{"class":1099},[1076,9448,1192],{"class":1103},[1076,9450,1163],{"class":1099},[1076,9452,1166],{"class":1086},[1076,9454,9455,9457,9459,9461,9463,9465],{"class":1078,"line":4817},[1076,9456,4820],{"class":1090},[1076,9458,1156],{"class":1155},[1076,9460,1100],{"class":1099},[1076,9462,4827],{"class":1103},[1076,9464,1163],{"class":1099},[1076,9466,1166],{"class":1086},[1076,9468,9469,9471,9473,9475,9477,9479],{"class":1078,"line":4834},[1076,9470,4837],{"class":1090},[1076,9472,1156],{"class":1155},[1076,9474,1100],{"class":1099},[1076,9476,4844],{"class":1103},[1076,9478,1163],{"class":1099},[1076,9480,1166],{"class":1086},[1076,9482,9483,9485,9487,9489],{"class":1078,"line":4851},[1076,9484,4854],{"class":1090},[1076,9486,1156],{"class":1155},[1076,9488,1280],{"class":1241},[1076,9490,1166],{"class":1086},[1076,9492,9493,9495,9497,9499],{"class":1078,"line":4863},[1076,9494,4866],{"class":1090},[1076,9496,1156],{"class":1155},[1076,9498,1242],{"class":1241},[1076,9500,1166],{"class":1086},[1076,9502,9503,9505,9507,9509],{"class":1078,"line":4875},[1076,9504,4878],{"class":1090},[1076,9506,1156],{"class":1155},[1076,9508,1267],{"class":1241},[1076,9510,1166],{"class":1086},[1076,9512,9513,9515,9517,9519],{"class":1078,"line":4887},[1076,9514,4890],{"class":1090},[1076,9516,1156],{"class":1155},[1076,9518,3244],{"class":1241},[1076,9520,1166],{"class":1086},[1076,9522,9523,9525,9527,9529],{"class":1078,"line":4899},[1076,9524,4902],{"class":1090},[1076,9526,1156],{"class":1155},[1076,9528,1267],{"class":1241},[1076,9530,1166],{"class":1086},[1076,9532,9533],{"class":1078,"line":4911},[1076,9534,4914],{"class":1086},[1076,9536,9537],{"class":1078,"line":4917},[1076,9538,1112],{"emptyLinePlaceholder":8},[1076,9540,9541,9543,9545,9547,9549,9551,9553,9555,9557],{"class":1078,"line":4922},[1076,9542,4763],{"class":1118},[1076,9544,1700],{"class":1122},[1076,9546,1127],{"class":1126},[1076,9548,1131],{"class":1130},[1076,9550,1134],{"class":1086},[1076,9552,1019],{"class":1137},[1076,9554,1140],{"class":1086},[1076,9556,1143],{"class":1137},[1076,9558,1146],{"class":1086},[1076,9560,9561,9563],{"class":1078,"line":4943},[1076,9562,4784],{"class":1090},[1076,9564,1166],{"class":1086},[1076,9566,9567,9569,9571,9573],{"class":1078,"line":4950},[1076,9568,4792],{"class":1090},[1076,9570,1156],{"class":1155},[1076,9572,1177],{"class":1090},[1076,9574,1166],{"class":1086},[1076,9576,9577,9579,9581,9583,9585,9587],{"class":1078,"line":4961},[1076,9578,4804],{"class":1090},[1076,9580,1156],{"class":1155},[1076,9582,1100],{"class":1099},[1076,9584,1192],{"class":1103},[1076,9586,1163],{"class":1099},[1076,9588,1166],{"class":1086},[1076,9590,9591,9593,9595,9597,9599,9601],{"class":1078,"line":4976},[1076,9592,4820],{"class":1090},[1076,9594,1156],{"class":1155},[1076,9596,1100],{"class":1099},[1076,9598,4827],{"class":1103},[1076,9600,1163],{"class":1099},[1076,9602,1166],{"class":1086},[1076,9604,9605,9607,9609,9611,9613,9615],{"class":1078,"line":4991},[1076,9606,4837],{"class":1090},[1076,9608,1156],{"class":1155},[1076,9610,1100],{"class":1099},[1076,9612,5000],{"class":1103},[1076,9614,1163],{"class":1099},[1076,9616,1166],{"class":1086},[1076,9618,9619,9621,9623,9625],{"class":1078,"line":5007},[1076,9620,4854],{"class":1090},[1076,9622,1156],{"class":1155},[1076,9624,5014],{"class":1241},[1076,9626,1166],{"class":1086},[1076,9628,9629,9631,9633,9635],{"class":1078,"line":5019},[1076,9630,4866],{"class":1090},[1076,9632,1156],{"class":1155},[1076,9634,1749],{"class":1241},[1076,9636,1166],{"class":1086},[1076,9638,9639,9641,9643,9645],{"class":1078,"line":5030},[1076,9640,4878],{"class":1090},[1076,9642,1156],{"class":1155},[1076,9644,1749],{"class":1241},[1076,9646,1166],{"class":1086},[1076,9648,9649,9651,9653,9655],{"class":1078,"line":5041},[1076,9650,4890],{"class":1090},[1076,9652,1156],{"class":1155},[1076,9654,5048],{"class":1241},[1076,9656,1166],{"class":1086},[1076,9658,9659,9661,9663,9665],{"class":1078,"line":5053},[1076,9660,4902],{"class":1090},[1076,9662,1156],{"class":1155},[1076,9664,1749],{"class":1241},[1076,9666,1166],{"class":1086},[1076,9668,9669],{"class":1078,"line":5064},[1076,9670,4914],{"class":1086},[1076,9672,9673],{"class":1078,"line":5069},[1076,9674,1112],{"emptyLinePlaceholder":8},[1076,9676,9677,9679,9681,9683,9685,9687,9689,9691,9693],{"class":1078,"line":5074},[1076,9678,4763],{"class":1118},[1076,9680,5079],{"class":1122},[1076,9682,1127],{"class":1126},[1076,9684,1131],{"class":1130},[1076,9686,1134],{"class":1086},[1076,9688,1019],{"class":1137},[1076,9690,1140],{"class":1086},[1076,9692,1143],{"class":1137},[1076,9694,1146],{"class":1086},[1076,9696,9697,9699],{"class":1078,"line":5096},[1076,9698,4784],{"class":1090},[1076,9700,1166],{"class":1086},[1076,9702,9703,9705,9707,9709],{"class":1078,"line":5103},[1076,9704,4792],{"class":1090},[1076,9706,1156],{"class":1155},[1076,9708,1177],{"class":1090},[1076,9710,1166],{"class":1086},[1076,9712,9713,9715,9717,9719,9721,9723],{"class":1078,"line":5114},[1076,9714,4804],{"class":1090},[1076,9716,1156],{"class":1155},[1076,9718,1100],{"class":1099},[1076,9720,1192],{"class":1103},[1076,9722,1163],{"class":1099},[1076,9724,1166],{"class":1086},[1076,9726,9727,9729,9731,9733,9735,9737],{"class":1078,"line":5129},[1076,9728,4820],{"class":1090},[1076,9730,1156],{"class":1155},[1076,9732,1100],{"class":1099},[1076,9734,4827],{"class":1103},[1076,9736,1163],{"class":1099},[1076,9738,1166],{"class":1086},[1076,9740,9741,9743,9745,9747,9749,9751],{"class":1078,"line":5144},[1076,9742,4837],{"class":1090},[1076,9744,1156],{"class":1155},[1076,9746,1100],{"class":1099},[1076,9748,5153],{"class":1103},[1076,9750,1163],{"class":1099},[1076,9752,1166],{"class":1086},[1076,9754,9755,9757,9759,9761],{"class":1078,"line":5160},[1076,9756,4854],{"class":1090},[1076,9758,1156],{"class":1155},[1076,9760,5167],{"class":1241},[1076,9762,1166],{"class":1086},[1076,9764,9765,9767,9769,9771],{"class":1078,"line":5172},[1076,9766,4866],{"class":1090},[1076,9768,1156],{"class":1155},[1076,9770,5179],{"class":1241},[1076,9772,1166],{"class":1086},[1076,9774,9775,9777,9779,9781],{"class":1078,"line":5184},[1076,9776,4878],{"class":1090},[1076,9778,1156],{"class":1155},[1076,9780,5191],{"class":1241},[1076,9782,1166],{"class":1086},[1076,9784,9785,9787,9789,9791],{"class":1078,"line":5196},[1076,9786,4890],{"class":1090},[1076,9788,1156],{"class":1155},[1076,9790,5203],{"class":1241},[1076,9792,1166],{"class":1086},[1076,9794,9795,9797,9799,9801],{"class":1078,"line":5208},[1076,9796,4902],{"class":1090},[1076,9798,1156],{"class":1155},[1076,9800,5191],{"class":1241},[1076,9802,1166],{"class":1086},[1076,9804,9805],{"class":1078,"line":5219},[1076,9806,4914],{"class":1086},[1076,9808,9809],{"class":1078,"line":5224},[1076,9810,1112],{"emptyLinePlaceholder":8},[1076,9812,9813,9815],{"class":1078,"line":5229},[1076,9814,5232],{"class":1082},[1076,9816,4529],{"class":1086},[1076,9818,9819,9821,9823,9825,9827,9829,9831,9833,9835,9837],{"class":1078,"line":5237},[1076,9820,5240],{"class":1090},[1076,9822,1156],{"class":1155},[1076,9824,1790],{"class":1130},[1076,9826,1793],{"class":1086},[1076,9828,1796],{"class":1090},[1076,9830,1140],{"class":1086},[1076,9832,1801],{"class":1090},[1076,9834,1804],{"class":1086},[1076,9836,1143],{"class":1137},[1076,9838,5259],{"class":1086},[1076,9840,9841,9843],{"class":1078,"line":5262},[1076,9842,5265],{"class":1090},[1076,9844,1166],{"class":1086},[1076,9846,9847,9849,9851,9853,9855,9857,9859,9861,9863,9865],{"class":1078,"line":5270},[1076,9848,5273],{"class":1130},[1076,9850,1156],{"class":1155},[1076,9852,5278],{"class":1118},[1076,9854,2111],{"class":1086},[1076,9856,2149],{"class":4669},[1076,9858,1156],{"class":1126},[1076,9860,4674],{"class":1964},[1076,9862,2119],{"class":1086},[1076,9864,5291],{"class":1118},[1076,9866,4529],{"class":1086},[1076,9868,9869,9871,9873,9875,9877],{"class":1078,"line":5296},[1076,9870,5299],{"class":1082},[1076,9872,4682],{"class":1964},[1076,9874,1048],{"class":1086},[1076,9876,1872],{"class":1130},[1076,9878,5308],{"class":1086},[1076,9880,9881,9883,9885,9887,9889,9891],{"class":1078,"line":5311},[1076,9882,5314],{"class":1090},[1076,9884,1048],{"class":1086},[1076,9886,3105],{"class":1130},[1076,9888,1134],{"class":1086},[1076,9890,2149],{"class":1090},[1076,9892,5259],{"class":1086},[1076,9894,9895,9897,9899,9901,9903,9905],{"class":1078,"line":5327},[1076,9896,5330],{"class":1090},[1076,9898,1048],{"class":1086},[1076,9900,3105],{"class":1130},[1076,9902,1134],{"class":1086},[1076,9904,2149],{"class":1090},[1076,9906,5259],{"class":1086},[1076,9908,9909,9911,9913,9915,9917,9919],{"class":1078,"line":5343},[1076,9910,5346],{"class":1090},[1076,9912,1048],{"class":1086},[1076,9914,3105],{"class":1130},[1076,9916,1134],{"class":1086},[1076,9918,2149],{"class":1090},[1076,9920,5259],{"class":1086},[1076,9922,9923],{"class":1078,"line":5359},[1076,9924,5362],{"class":1086},[1076,9926,9927],{"class":1078,"line":5365},[1076,9928,5368],{"class":1086},[1076,9930,9931],{"class":1078,"line":5371},[1076,9932,5374],{"class":1086},[1076,9934,9935],{"class":1078,"line":5377},[1076,9936,2455],{"class":1086},[1076,9938,9939],{"class":1078,"line":5382},[1076,9940,1112],{"emptyLinePlaceholder":8},[1076,9942,9943,9945,9947,9949,9951,9953,9955,9957,9959,9961,9963,9965,9967,9969,9971],{"class":1078,"line":5387},[1076,9944,5390],{"class":1082},[1076,9946,5393],{"class":1118},[1076,9948,5396],{"class":1130},[1076,9950,1134],{"class":1086},[1076,9952,4737],{"class":4669},[1076,9954,1156],{"class":1126},[1076,9956,4742],{"class":1964},[1076,9958,1140],{"class":1086},[1076,9960,1497],{"class":4669},[1076,9962,1156],{"class":1126},[1076,9964,4674],{"class":1964},[1076,9966,4677],{"class":1086},[1076,9968,1156],{"class":1126},[1076,9970,5419],{"class":1964},[1076,9972,4529],{"class":1086},[1076,9974,9975,9977,9979,9981,9983,9985,9987,9989,9991,9993,9995,9997,9999],{"class":1078,"line":5424},[1076,9976,5427],{"class":1082},[1076,9978,2111],{"class":1086},[1076,9980,2024],{"class":1126},[1076,9982,5434],{"class":1090},[1076,9984,2119],{"class":1086},[1076,9986,5434],{"class":1090},[1076,9988,1127],{"class":1126},[1076,9990,4732],{"class":1130},[1076,9992,1134],{"class":1086},[1076,9994,4737],{"class":1090},[1076,9996,1140],{"class":1086},[1076,9998,1497],{"class":1090},[1076,10000,1809],{"class":1086},[1076,10002,10003],{"class":1078,"line":5455},[1076,10004,2455],{"class":1086},[1076,10006,10007],{"class":1078,"line":5460},[1076,10008,1112],{"emptyLinePlaceholder":8},[1076,10010,10011,10013,10015,10017,10019,10021,10023],{"class":1078,"line":5465},[1076,10012,5390],{"class":1082},[1076,10014,5393],{"class":1118},[1076,10016,5472],{"class":1130},[1076,10018,5475],{"class":1086},[1076,10020,1156],{"class":1126},[1076,10022,4627],{"class":1964},[1076,10024,4529],{"class":1086},[1076,10026,10027,10029,10031,10033,10035,10037,10039,10041,10043,10045,10047,10049,10051],{"class":1078,"line":5484},[1076,10028,5427],{"class":1082},[1076,10030,2111],{"class":1086},[1076,10032,2024],{"class":1126},[1076,10034,5434],{"class":1090},[1076,10036,2119],{"class":1086},[1076,10038,5497],{"class":1082},[1076,10040,5501],{"class":5500},[1076,10042,5504],{"class":1130},[1076,10044,1134],{"class":1086},[1076,10046,1163],{"class":1099},[1076,10048,5511],{"class":1103},[1076,10050,1163],{"class":1099},[1076,10052,1809],{"class":1086},[1076,10054,10055,10057],{"class":1078,"line":5518},[1076,10056,5232],{"class":1082},[1076,10058,5523],{"class":1090},[1076,10060,10061],{"class":1078,"line":5526},[1076,10062,2455],{"class":1086},[856,10064,5531,10065,5535,10067,5539],{},[868,10066,5534],{},[868,10068,5538],{},[1353,10070,5543],{"id":5542},[856,10072,5546,10073,5549,10075,5553],{},[868,10074,2166],{},[860,10076,907],{"href":905,"rel":10077},[864],[1067,10079,10080],{"className":1069,"code":5556,"language":1071,"meta":1072,"style":1072},[868,10081,10082,10100,10104,10132],{"__ignoreMap":1072},[1076,10083,10084,10086,10088,10090,10092,10094,10096,10098],{"class":1078,"line":1079},[1076,10085,1083],{"class":1082},[1076,10087,1087],{"class":1086},[1076,10089,1927],{"class":1090},[1076,10091,1093],{"class":1086},[1076,10093,1096],{"class":1082},[1076,10095,1100],{"class":1099},[1076,10097,1047],{"class":1103},[1076,10099,1106],{"class":1099},[1076,10101,10102],{"class":1078,"line":1109},[1076,10103,1112],{"emptyLinePlaceholder":8},[1076,10105,10106,10108,10110,10112,10114,10116,10118,10120,10122,10124,10126,10128,10130],{"class":1078,"line":1115},[1076,10107,1119],{"class":1118},[1076,10109,5587],{"class":1122},[1076,10111,1127],{"class":1126},[1076,10113,1953],{"class":1130},[1076,10115,1956],{"class":1086},[1076,10117,1959],{"class":1090},[1076,10119,1156],{"class":1126},[1076,10121,1965],{"class":1964},[1076,10123,1968],{"class":1086},[1076,10125,1971],{"class":1241},[1076,10127,1140],{"class":1086},[1076,10129,1976],{"class":1241},[1076,10131,1809],{"class":1086},[1076,10133,10134,10136,10138,10140,10142,10144,10146,10148,10150,10152,10154,10156,10158],{"class":1078,"line":1149},[1076,10135,1119],{"class":1118},[1076,10137,5616],{"class":1122},[1076,10139,1127],{"class":1126},[1076,10141,1953],{"class":1130},[1076,10143,1956],{"class":1086},[1076,10145,1959],{"class":1090},[1076,10147,1156],{"class":1126},[1076,10149,1965],{"class":1964},[1076,10151,1968],{"class":1086},[1076,10153,1971],{"class":1241},[1076,10155,1140],{"class":1086},[1076,10157,5637],{"class":1241},[1076,10159,1809],{"class":1086},[1353,10161,5643],{"id":5642},[856,10163,5531,10164,5648],{},[868,10165,2166],{},[1067,10167,10168],{"className":1069,"code":5651,"language":1071,"meta":1072,"style":1072},[868,10169,10170,10188,10206,10210,10238,10258,10298,10302,10306,10362,10366,10370,10402,10452,10456,10460,10464,10468,10488,10502,10518],{"__ignoreMap":1072},[1076,10171,10172,10174,10176,10178,10180,10182,10184,10186],{"class":1078,"line":1079},[1076,10173,1083],{"class":1082},[1076,10175,1087],{"class":1086},[1076,10177,915],{"class":1090},[1076,10179,1093],{"class":1086},[1076,10181,1096],{"class":1082},[1076,10183,1100],{"class":1099},[1076,10185,1047],{"class":1103},[1076,10187,1106],{"class":1099},[1076,10189,10190,10192,10194,10196,10198,10200,10202,10204],{"class":1078,"line":1109},[1076,10191,1083],{"class":1082},[1076,10193,1087],{"class":1086},[1076,10195,5680],{"class":1090},[1076,10197,1093],{"class":1086},[1076,10199,1096],{"class":1082},[1076,10201,1100],{"class":1099},[1076,10203,5689],{"class":1103},[1076,10205,1106],{"class":1099},[1076,10207,10208],{"class":1078,"line":1115},[1076,10209,1112],{"emptyLinePlaceholder":8},[1076,10211,10212,10214,10216,10218,10220,10222,10224,10226,10228,10230,10232,10234,10236],{"class":1078,"line":1149},[1076,10213,5390],{"class":1082},[1076,10215,5278],{"class":1118},[1076,10217,5393],{"class":1118},[1076,10219,5706],{"class":1130},[1076,10221,1134],{"class":1086},[1076,10223,2788],{"class":4669},[1076,10225,1156],{"class":1126},[1076,10227,5715],{"class":1964},[1076,10229,1140],{"class":1086},[1076,10231,2210],{"class":4669},[1076,10233,1156],{"class":1126},[1076,10235,5724],{"class":1964},[1076,10237,2440],{"class":1086},[1076,10239,10240,10242,10244,10246,10248,10250,10252,10254,10256],{"class":1078,"line":1169},[1076,10241,4763],{"class":1118},[1076,10243,1087],{"class":1086},[1076,10245,2937],{"class":1122},[1076,10247,1140],{"class":1086},[1076,10249,2605],{"class":1122},[1076,10251,1093],{"class":1086},[1076,10253,5743],{"class":1126},[1076,10255,5472],{"class":1130},[1076,10257,5748],{"class":1086},[1076,10259,10260,10262,10264,10266,10268,10270,10272,10274,10276,10278,10280,10282,10284,10286,10288,10290,10292,10294,10296],{"class":1078,"line":1182},[1076,10261,4763],{"class":1118},[1076,10263,5755],{"class":1122},[1076,10265,1127],{"class":1126},[1076,10267,5760],{"class":1130},[1076,10269,5763],{"class":1086},[1076,10271,5766],{"class":1130},[1076,10273,5769],{"class":1086},[1076,10275,5772],{"class":1090},[1076,10277,1156],{"class":1155},[1076,10279,1100],{"class":1099},[1076,10281,5779],{"class":1103},[1076,10283,1163],{"class":1099},[1076,10285,1140],{"class":1086},[1076,10287,5786],{"class":1090},[1076,10289,1156],{"class":1155},[1076,10291,1100],{"class":1099},[1076,10293,5793],{"class":1103},[1076,10295,1163],{"class":1099},[1076,10297,5798],{"class":1086},[1076,10299,10300],{"class":1078,"line":1199},[1076,10301,1112],{"emptyLinePlaceholder":8},[1076,10303,10304],{"class":1078,"line":1216},[1076,10305,5807],{"class":1684},[1076,10307,10308,10310,10312,10314,10316,10318,10320,10322,10324,10326,10328,10330,10332,10334,10336,10338,10340,10342,10344,10346,10348,10350,10352,10354,10356,10358,10360],{"class":1078,"line":1233},[1076,10309,5427],{"class":1082},[1076,10311,2111],{"class":1086},[1076,10313,2024],{"class":1126},[1076,10315,1134],{"class":1086},[1076,10317,2777],{"class":1082},[1076,10319,1997],{"class":1130},[1076,10321,1134],{"class":1086},[1076,10323,2605],{"class":1090},[1076,10325,1140],{"class":1086},[1076,10327,2788],{"class":1090},[1076,10329,1048],{"class":1086},[1076,10331,2021],{"class":1090},[1076,10333,2024],{"class":1126},[1076,10335,1140],{"class":1086},[1076,10337,2799],{"class":1090},[1076,10339,1140],{"class":1086},[1076,10341,2179],{"class":1241},[1076,10343,1140],{"class":1086},[1076,10345,1163],{"class":1099},[1076,10347,2021],{"class":1103},[1076,10349,1163],{"class":1099},[1076,10351,1140],{"class":1086},[1076,10353,2203],{"class":1090},[1076,10355,1140],{"class":1086},[1076,10357,2210],{"class":1090},[1076,10359,5862],{"class":1086},[1076,10361,5865],{"class":1082},[1076,10363,10364],{"class":1078,"line":1247},[1076,10365,1112],{"emptyLinePlaceholder":8},[1076,10367,10368],{"class":1078,"line":1259},[1076,10369,5874],{"class":1684},[1076,10371,10372,10374,10376,10378,10380,10382,10384,10386,10388,10390,10392,10394,10396,10398,10400],{"class":1078,"line":1272},[1076,10373,4763],{"class":1118},[1076,10375,2894],{"class":1122},[1076,10377,1127],{"class":1126},[1076,10379,2899],{"class":1103},[1076,10381,2902],{"class":1118},[1076,10383,2788],{"class":1090},[1076,10385,1048],{"class":2907},[1076,10387,2021],{"class":1090},[1076,10389,2024],{"class":1126},[1076,10391,2914],{"class":1118},[1076,10393,2917],{"class":1103},[1076,10395,2902],{"class":1118},[1076,10397,2848],{"class":1090},[1076,10399,2914],{"class":1118},[1076,10401,2926],{"class":1103},[1076,10403,10404,10406,10408,10410,10412,10414,10416,10418,10420,10422,10424,10426,10428,10430,10432,10434,10436,10438,10440,10442,10444,10446,10448,10450],{"class":1078,"line":1285},[1076,10405,5427],{"class":1082},[1076,10407,2111],{"class":1086},[1076,10409,2024],{"class":1126},[1076,10411,1134],{"class":1086},[1076,10413,2777],{"class":1082},[1076,10415,1997],{"class":1130},[1076,10417,1134],{"class":1086},[1076,10419,2937],{"class":1090},[1076,10421,1140],{"class":1086},[1076,10423,2591],{"class":1090},[1076,10425,1140],{"class":1086},[1076,10427,5933],{"class":1090},[1076,10429,1140],{"class":1086},[1076,10431,2183],{"class":1241},[1076,10433,1140],{"class":1086},[1076,10435,1163],{"class":1099},[1076,10437,5944],{"class":1103},[1076,10439,1163],{"class":1099},[1076,10441,1140],{"class":1086},[1076,10443,2203],{"class":1090},[1076,10445,1140],{"class":1086},[1076,10447,2210],{"class":1090},[1076,10449,5862],{"class":1086},[1076,10451,5865],{"class":1082},[1076,10453,10454],{"class":1078,"line":1297},[1076,10455,1112],{"emptyLinePlaceholder":8},[1076,10457,10458],{"class":1078,"line":1754},[1076,10459,5967],{"class":1684},[1076,10461,10462],{"class":1078,"line":1765},[1076,10463,1112],{"emptyLinePlaceholder":8},[1076,10465,10466],{"class":1078,"line":1770},[1076,10467,5976],{"class":1684},[1076,10469,10470,10472,10474,10476,10478,10480,10482,10484,10486],{"class":1078,"line":1775},[1076,10471,5981],{"class":1090},[1076,10473,1048],{"class":1086},[1076,10475,3105],{"class":1130},[1076,10477,1134],{"class":1086},[1076,10479,2788],{"class":1090},[1076,10481,1048],{"class":1086},[1076,10483,2021],{"class":1090},[1076,10485,2024],{"class":1126},[1076,10487,1809],{"class":1086},[1076,10489,10490,10492,10494,10496,10498,10500],{"class":1078,"line":1780},[1076,10491,6002],{"class":1090},[1076,10493,1048],{"class":1086},[1076,10495,3105],{"class":1130},[1076,10497,1134],{"class":1086},[1076,10499,2591],{"class":1090},[1076,10501,1809],{"class":1086},[1076,10503,10504,10506,10508,10510,10512,10514,10516],{"class":1078,"line":4789},[1076,10505,6017],{"class":1082},[1076,10507,5472],{"class":1130},[1076,10509,5763],{"class":1086},[1076,10511,6024],{"class":1130},[1076,10513,1134],{"class":1086},[1076,10515,2591],{"class":1090},[1076,10517,1809],{"class":1086},[1076,10519,10520],{"class":1078,"line":4801},[1076,10521,2455],{"class":1086},[856,10523,6037,10524,6040,10526,6043,10528,6046],{},[868,10525,2166],{},[868,10527,1143],{},[868,10529,2217],{},[922,10531],{},[925,10533,6052],{"id":6051},[856,10535,6055],{},[938,10537,10538,10546],{},[941,10539,10540],{},[944,10541,10542,10544],{},[947,10543,1365],{},[947,10545,2488],{},[957,10547,10548,10558,10568],{},[944,10549,10550,10554],{},[962,10551,10552],{},[876,10553,2495],{},[962,10555,10556],{},[868,10557,890],{},[944,10559,10560,10564],{},[962,10561,10562],{},[876,10563,2504],{},[962,10565,10566],{},[868,10567,2509],{},[944,10569,10570,10574],{},[962,10571,10572],{},[876,10573,2519],{},[962,10575,10576],{},[868,10577,6098],{},[856,10579,2334,10580,6103,10582,6106,10584,1048],{},[868,10581,894],{},[868,10583,2513],{},[868,10585,6109],{},[922,10587],{},[925,10589,6115],{"id":6114},[938,10591,10592,10600],{},[941,10593,10594],{},[944,10595,10596,10598],{},[947,10597,6124],{},[947,10599,1374],{},[957,10601,10602,10610,10620,10628,10636,10646,10654,10662,10670],{},[944,10603,10604,10608],{},[962,10605,10606],{},[876,10607,6135],{},[962,10609,6138],{},[944,10611,10612,10616],{},[962,10613,10614],{},[876,10615,6145],{},[962,10617,6148,10618,1048],{},[868,10619,911],{},[944,10621,10622,10626],{},[962,10623,10624],{},[876,10625,6157],{},[962,10627,6160],{},[944,10629,10630,10634],{},[962,10631,10632],{},[876,10633,6167],{},[962,10635,6170],{},[944,10637,10638,10642],{},[962,10639,10640],{},[876,10641,6177],{},[962,10643,6180,10644,6184],{},[868,10645,6183],{},[944,10647,10648,10652],{},[962,10649,10650],{},[876,10651,6191],{},[962,10653,6194],{},[944,10655,10656,10660],{},[962,10657,10658],{},[876,10659,6201],{},[962,10661,6204],{},[944,10663,10664,10668],{},[962,10665,10666],{},[876,10667,952],{},[962,10669,6213],{},[944,10671,10672,10676],{},[962,10673,10674],{},[876,10675,2211],{},[962,10677,10678,6224,10680,6227],{},[868,10679,2217],{},[868,10681,894],{},[6229,10683,6231],{},{"title":1072,"searchDepth":1109,"depth":1109,"links":10685},[10686,10690,10698,10704,10714,10724,10725,10726],{"id":927,"depth":1109,"text":928,"children":10687},[10688,10689],{"id":932,"depth":1115,"text":933},{"id":995,"depth":1115,"text":996},{"id":1040,"depth":1109,"text":1041,"children":10691},[10692,10693,10694,10695,10696,10697],{"id":1051,"depth":1115,"text":1054},{"id":1577,"depth":1115,"text":1580},{"id":915,"depth":1115,"text":915},{"id":2344,"depth":1115,"text":2275},{"id":2545,"depth":1115,"text":2548},{"id":2647,"depth":1115,"text":1927},{"id":2752,"depth":1109,"text":2753,"children":10699},[10700,10701,10702,10703],{"id":2759,"depth":1115,"text":2760},{"id":2824,"depth":1115,"text":2825},{"id":2878,"depth":1115,"text":2879},{"id":2972,"depth":1115,"text":2973},{"id":3042,"depth":1109,"text":3043,"children":10705},[10706,10707,10708,10709,10710,10711,10712,10713],{"id":1209,"depth":1115,"text":103},{"id":3161,"depth":1115,"text":3162},{"id":3290,"depth":1115,"text":99},{"id":3327,"depth":1115,"text":111},{"id":3368,"depth":1115,"text":3369},{"id":3420,"depth":1115,"text":3421},{"id":3466,"depth":1115,"text":3467},{"id":3475,"depth":1115,"text":3476},{"id":3514,"depth":1109,"text":3515,"children":10715},[10716,10717,10718,10719,10720,10721,10722,10723],{"id":3555,"depth":1115,"text":3558},{"id":3680,"depth":1115,"text":3683},{"id":3813,"depth":1115,"text":3816},{"id":3928,"depth":1115,"text":3931},{"id":4072,"depth":1115,"text":4075},{"id":4140,"depth":1115,"text":4143},{"id":4249,"depth":1115,"text":4252},{"id":4387,"depth":1115,"text":4390},{"id":4502,"depth":1109,"text":4503},{"id":6051,"depth":1109,"text":6052},{"id":6114,"depth":1109,"text":6115},{},{"title":147,"description":6275},1780436283373]