{"version":3,"sources":["../src/locales.ts","../src/block-palette.ts","../src/render.ts","../src/index.ts","../src/errors.ts","../src/middleware.ts","../src/csp.ts","../src/navigation-guard.ts","../src/core.ts","../src/verify-bot.ts","../src/check-license.ts","../src/scripts.ts","../src/validate-site-key.ts"],"sourcesContent":["export type Locale = 'fr' | 'en';\n\nexport const MESSAGES = {\n  fr: {\n    rateLimitTitle: 'Trop de requêtes',\n    rateLimitBody: (t: string) => `Vous avez effectué trop de requêtes en peu de temps. Il reste ${t} avant de pouvoir réessayer.`,\n    rateLimitBadge: 'Rate Limit',\n    blockedTitle: 'Accès bloqué',\n    blockedBadge: 'Blocage',\n    tamperTitle: 'Remplacement de contenu client détecté',\n    tamperBody: \"Nous avons remarqué que vous avez tenté de modifier manuellement le rendu client côté navigateur via les DevTools. Cette pratique est évidemment bloquée par nos services.\",\n    devtoolsBody: \"L'utilisation des DevTools pour remplacer le contenu ou modifier les requêtes réseau a été détectée. L'intégrité de la page est protégée et toute altération est immédiatement bloquée.\",\n    fakeBrowserTitle: 'Requête non navigateur',\n    fakeBrowserBody: \"Votre requête ne provient pas d'un navigateur standard. Utilisez un navigateur (Chrome, Firefox, Safari, Edge) pour accéder à ce site.\",\n    fakeBrowserBadge: 'Accès restreint',\n    retryInSeconds: (s: number) => `Il reste ${s}s avant de pouvoir réessayer.`,\n  },\n  en: {\n    rateLimitTitle: 'Too Many Requests',\n    rateLimitBody: (t: string) => `You have made too many requests in a short time. ${t} remaining before you can try again.`,\n    rateLimitBadge: 'Rate Limit',\n    blockedTitle: 'Access Blocked',\n    blockedBadge: 'Blocked',\n    tamperTitle: 'Client Content Replacement Detected',\n    tamperBody: \"We noticed you attempted to manually modify the client-side rendering via DevTools. This practice is obviously blocked by our services.\",\n    devtoolsBody: \"Using DevTools to replace content or modify network requests has been detected. Page integrity is protected and any alteration is immediately blocked.\",\n    fakeBrowserTitle: 'Non-browser request',\n    fakeBrowserBody: 'Your request did not come from a standard browser. Please use a browser (Chrome, Firefox, Safari, Edge) to access this site.',\n    fakeBrowserBadge: 'Restricted access',\n    retryInSeconds: (s: number) => `Retry in ${s}s.`,\n  },\n} as const;\n\nexport function resolveLocale(explicit: Locale | undefined, acceptLanguage?: string): Locale {\n  if (explicit) return explicit;\n  if (acceptLanguage && /^fr\\b|,\\s*fr\\b/i.test(acceptLanguage)) return 'fr';\n  return 'en';\n}\n","export interface NativeBlockPalette {\n  light: string\n  dark: string\n}\n\nexport function nativeBlockPalette(userAgent: string): NativeBlockPalette {\n  if (/Edg\\//.test(userAgent)) return { light: '#f6f6f6', dark: '#2d2d2d' }\n  if (/Firefox\\//.test(userAgent)) return { light: '#f9f9fb', dark: '#2b2a33' }\n  if (/AppleWebKit/.test(userAgent) && !/Chrome|Chromium|Edg\\/|OPR\\//.test(userAgent)) {\n    return { light: '#f6f6f6', dark: 'rgb(30,30,30)' }\n  }\n  if (/OPR\\//.test(userAgent)) return { light: '#eef3f7', dark: '#101214' }\n  return { light: '#fff', dark: '#202124' }\n}\n\n// Keep browser-rendered cards on the same palette contract as server-rendered pages.\nexport const NATIVE_BLOCK_PALETTE_SCRIPT =\n  'var _sgNativeBlockPalette=function(ua){ua=String(ua||\"\");if(/Edg\\\\//.test(ua))return{light:\"#f6f6f6\",dark:\"#2d2d2d\"};if(/Firefox\\\\//.test(ua))return{light:\"#f9f9fb\",dark:\"#2b2a33\"};if(/AppleWebKit/.test(ua)&&!/Chrome|Chromium|Edg\\\\/|OPR\\\\//.test(ua))return{light:\"#f6f6f6\",dark:\"rgb(30,30,30)\"};if(/OPR\\\\//.test(ua))return{light:\"#eef3f7\",dark:\"#101214\"};return{light:\"#fff\",dark:\"#202124\"}}';\n","// @ts-nocheck\nimport crypto, { createHash } from 'crypto';\nimport { writeFileSync, readFileSync, existsSync, unlinkSync, mkdirSync, readdirSync, chmodSync, statSync } from 'fs';\nimport { join } from 'path';\nimport { tmpdir } from 'os';\nimport { MESSAGES, type Locale } from './locales';\nimport { stripTrace } from './obfuscate';\nimport { NATIVE_BLOCK_PALETTE_SCRIPT } from './block-palette';\n\n// ── Token storage ──\nconst TOKEN_DIR = join(tmpdir(), 'shugoi-render-' + (process.getuid?.() ?? 'x'));\nconst TOKEN_TTL = 120_000;\nconst MAX_ENTRIES = 5000;\nconst MAX_TOTAL_BYTES = 64 * 1024 * 1024;\nconst MAX_TOKEN_READS = 1;\n\n\ninterface StoredEntry { html: string; expiresAt: number; reads: number }\nconst _memoryStore = new Map<string, StoredEntry>();\nconst _siteCache = new Map<string, string>();\n\nlet _diskEnabled = false;\nlet _totalBytes = 0;\n\nif (!existsSync(TOKEN_DIR)) {\n  try { mkdirSync(TOKEN_DIR, { recursive: true, mode: 0o700 }); } catch {}\n}\ntry { chmodSync(TOKEN_DIR, 0o700); } catch {}\n\nfunction tokenFileName(token: string): string {\n  return createHash('sha256').update(token).digest('hex');\n}\n\nfunction startDiskCleanup() {\n  setInterval(() => {\n    try {\n      for (const f of readdirSync(TOKEN_DIR)) {\n        const p = join(TOKEN_DIR, f);\n        try { if (Date.now() - statSync(p).mtimeMs > TOKEN_TTL) unlinkSync(p); } catch {}\n      }\n    } catch {}\n  }, 30_000).unref();\n}\n\nfunction storeToDisk(token: string, html: string) {\n  try { writeFileSync(join(TOKEN_DIR, tokenFileName(token)), html, { encoding: 'utf-8', mode: 0o600 }); } catch {}\n}\n\nfunction readFromDisk(token: string): string | null {\n  try {\n    const p = join(TOKEN_DIR, tokenFileName(token));\n    if (!existsSync(p)) return null;\n    return readFileSync(p, 'utf-8');\n  } catch { return null; }\n}\n\nfunction dropEntry(token: string) {\n  const e = _memoryStore.get(token);\n  if (e) _totalBytes -= Buffer.byteLength(e.html, 'utf-8');\n  _memoryStore.delete(token);\n}\n\nfunction evictOldest() {\n  const first = _memoryStore.keys().next();\n  if (first.done) return;\n  dropEntry(first.value);\n}\n\nexport function storeHtml(token: string, html: string, contentReplaceOn?: boolean) {\n  if (_diskEnabled) {\n    storeToDisk(token, html);\n  }\n  const size = Buffer.byteLength(html, 'utf-8');\n  while ((_memoryStore.size >= MAX_ENTRIES || _totalBytes + size > MAX_TOTAL_BYTES) && _memoryStore.size > 0) {\n    evictOldest();\n  }\n  _memoryStore.set(token, { html, expiresAt: Date.now() + TOKEN_TTL, reads: 0, contentReplaceOn });\n  _totalBytes += size;\n  // Index O(1) par siteKey pour le fallback contentReplace OFF (évite le scan O(n)\n  // de tout le store à chaque render manqué). Toujours frais : écrasé à chaque store.\n  const sk = token.split(':')[0];\n  if (sk) _siteCache.set(sk, html);\n}\n\nfunction readFromMemory(token: string): string | null {\n  const entry = _memoryStore.get(token);\n  if (!entry) return null;\n  if (Date.now() > entry.expiresAt) {\n    dropEntry(token);\n    return null;\n  }\n  entry.reads++;\n  if (entry.reads > MAX_TOKEN_READS) {\n    // Limite atteinte — renderResponseData décidera selon le flag\n    return entry.html;\n  }\n  return entry.html;\n}\n\n// ── Render-grant : preuve anti-bypass \"token-only\" ──\n// Le grant est émis par le wlc (/api/v1/wlc) quand le mid est autorisé (whitelisté OU\n// whitelist désactivée). Le guard l'obtient APRÈS avoir exécuté le fingerprint et\n// l'ajoute à l'URL render. Un bot curl qui extrait le token du challenge sans exécuter\n// le JS n'a pas de mid/grant cohérents → render refuse. Signé avec le même secret que\n// le token (SHUGOKI_SIGNING_SECRET) → vérifiable localement, sans état partagé.\n// Format : base36(timestamp) + \":\" + HMAC(secret, \"render-grant:mid:token:ip:timestamp\").\n// Lié au token + IP + TTL (CH-01/02/03). Factorisé pour les adapters Express/Next/Fastify.\nconst GRANT_TTL_MS = 60_000;\n\nexport function verifyRenderGrant(mid: string | undefined, grant: string | undefined, token?: string, ip?: string, expectedSiteKey?: string): boolean {\n  const gSecret = process.env.SHUGOKI_SIGNING_SECRET || process.env.SHUGOKI_SECRET;\n  if (!gSecret) return true; // fail-safe : pas de secret configuré → pas de vérification\n  if (!grant || !mid || !/^[a-f0-9]{64}$/.test(mid)) return false;\n  const sep = grant.indexOf(':');\n  if (sep < 0) return false;\n  const ts = grant.slice(0, sep);\n  const sig = grant.slice(sep + 1);\n  const tsSec = parseInt(ts, 36);\n  if (isNaN(tsSec) || Date.now() - tsSec * 1000 > GRANT_TTL_MS) return false;\n  // CRITIQUE 1 (§7bis) : le grant est signé AVEC le siteKey du wlc émetteur. Le render\n  // vérifie que ce siteKey == le sien — un grant émis par un autre site (whitelist off)\n  // pour un token d'ici est refusé même si le token est authentique.\n  if (!expectedSiteKey) return false;\n  const payload = 'render-grant:' + [expectedSiteKey, mid, token || '', ip || '', ts].join(':');\n  const exp = crypto.createHmac('sha256', gSecret).update(payload).digest('hex');\n  try { return crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(exp, 'hex')); }\n  catch { return false; }\n}\n\nexport async function renderResponseData(token: string, locale?: Locale, configUrl?: string, mid?: string, grant?: string, ip?: string, expectedSiteKey?: string, _secret?: string): Promise<{ html?: string; error?: string; blocked?: boolean; reason?: string; message?: string; title?: string }> {\n  if (!token || token.length < 16 || token.length > 300) return { error: 'not_found' };\n\n  // CRITIQUE 1 (§7bis) : le token a la forme `siteKey:timestamp:nonce:sig`. Le render\n  // d'un site ne doit servir que les tokens de SON siteKey — sinon un grant émis par un\n  // autre site (whitelist désactivée) serait accepté ici (secret global partagé).\n  if (expectedSiteKey) {\n    const tokSiteKey = token.split(':')[0];\n    if (tokSiteKey !== expectedSiteKey) return { error: 'not_found' };\n  }\n\n  // Audit passe 8 : expiration explicite du token, avant même le grant (le chemin mémoire\n  // court-circuite verifyTokenAndRead). Un token signé mais daté > TOKEN_TTL est refusé.\n  const tokTs = parseInt(token.split(':')[1] || '', 10);\n  if (!isNaN(tokTs) && Date.now() - tokTs > TOKEN_TTL) return { error: 'not_found' };\n\n  // Anti-bypass \"token-only\" : sans grant valide (lié au siteKey), pas de HTML.\n  if (!verifyRenderGrant(mid, grant, token, ip, expectedSiteKey)) return { error: 'not_found' };\n\n  // Vérifier le flag en direct depuis l'API interne (pas de cache)\n  const contentReplaceOn = await fetchContentReplaceFlag(token, configUrl || 'http://127.0.0.1:3098', _secret);\n\n  // 1. Essayer par token (mémoire)\n  const memHtml = readFromMemory(token);\n  if (memHtml) {\n    if (contentReplaceOn) {\n      const entry = _memoryStore.get(token);\n      if (entry && entry.reads > MAX_TOKEN_READS) {\n        dropEntry(token);\n        return { error: 'not_found' };\n      }\n    }\n    return { html: memHtml };\n  }\n\n  // Fallback : quand contentReplace OFF, on RENVOIE la HTML du site (cache O(1)).\n  // Plus de scan O(n) de tout le store en cas de miss (ancien code bouclait sur\n  // _memoryStore pour \"récupérer depuis n'importe quelle entrée\" — coûteux et\n  // potentiellement cross-site ; _siteCache est alimenté par storeHtml).\n  if (!contentReplaceOn) {\n    const siteKey = token.split(':')[0];\n    const siteHtml = _siteCache.get(siteKey);\n    if (siteHtml) return { html: siteHtml };\n  }\n\n  if (_diskEnabled) {\n    const diskHtml = readFromDisk(token);\n    if (diskHtml) return { html: diskHtml };\n  }\n\n  return verifyTokenAndRead(token, locale);\n}\n\nasync function fetchContentReplaceFlag(token: string, internalUrl: string, _secret?: string, retries = 2): Promise<boolean> {\n  try {\n    const siteKey = token.split(':')[0];\n    if (!siteKey) return false;\n    // Le flag est lu via getConfig (cache mémoire + stale-refresh en arrière-plan).\n    // Pas de __clearConfigCache() ici : purger le cache à CHAQUE render forçait un\n    // fetch réseau (~300-400ms) vers l'API interne à chaque requête → render lent.\n    const { flags } = await getConfig(siteKey, internalUrl, _secret);\n    return flags?.enableContentReplacementCheck === true;\n  } catch {\n    if (retries > 0) return fetchContentReplaceFlag(token, internalUrl, _secret, retries - 1);\n    return false;\n  }\n}\n\nfunction verifyTokenAndRead(token: string, locale?: Locale): { html?: string; error?: string; blocked?: boolean; reason?: string; message?: string; title?: string } {\n  const parts = token.split(':');\n  if (parts.length !== 4 || parts[3].length !== 64) {\n    return { error: 'not_found' };\n  }\n\n  const [siteKey, timestamp, nonce, sig] = parts;\n  const ts = parseInt(timestamp, 10);\n\n  if (isNaN(ts)) {\n    // Pas d'oracle : un timestamp invalide retourne la même erreur qu'un HMAC invalide.\n    return { error: 'not_found' };\n  }\n  // Audit passe 8 : expiration explicite du token (défense en profondeur, en plus du\n  // TTL du store). Un token signé mais daté > TOKEN_TTL est refusé, même si son HTML\n  // traînait dans le store disque d'un process rejoué.\n  if (Date.now() - ts > TOKEN_TTL) {\n    return { error: 'not_found' };\n  }\n\n  const secret = process.env.SHUGOKI_SIGNING_SECRET || process.env.SHUGOKI_SECRET;\n  if (secret) {\n    const payload = [siteKey, timestamp, nonce].join(':');\n    const expectedSig = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n\n    if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expectedSig))) {\n      return { error: 'not_found' };\n    }\n  }\n\n  const fullTokenLookup = token;\n  const memHtml = readFromMemory(fullTokenLookup);\n  if (memHtml) return { html: memHtml };\n\n  if (_diskEnabled) {\n    const diskHtml = readFromDisk(fullTokenLookup);\n    if (diskHtml) return { html: diskHtml };\n  }\n\n  if (secret) {\n    return { error: 'not_found' };\n  }\n\n  return { error: 'not_found' };\n}\n\n// Anti-leak du grant (re-audit 2026-08-03, résidu #2) : le render est appelé avec\n// `token` (+ éventuellement `grant`/`mid`) en query string. APRÈS document.write, l'URL\n// de la page est l'URL d'origine (le grant n'y est plus) → un referrer strict-origin\n// suffit : cross-origin → seul l'ORIGINE est envoyée (jamais le grant, jamais le chemin).\n// ⚠️ NE PAS utiliser no-referrer : ça casse les embeds YouTube (erreur 153 —\n// \"embedder identity missing referrer\", YouTube exige un Referrer pour valider l'embedder).\nexport function injectReferrerPolicy(html: string): string {\n  const meta = '<meta name=\"referrer\" content=\"strict-origin-when-cross-origin\">';\n  if (html.includes('<head>')) return html.replace('<head>', '<head>' + meta);\n  if (html.includes('<html')) {\n    const m = html.match(/<html[^>]*>/);\n    if (m) return html.replace(m[0], m[0] + meta);\n  }\n  return meta + html;\n}\n\nexport async function handleRender(token: string, res: { setHeader?: (k: string, v: string) => void; send?: (body: string) => void; end?: (body: string) => void }, configUrl?: string, mid?: string, grant?: string, ip?: string, expectedSiteKey?: string, baseUrl?: string, _secret?: string) {\n  const data = await renderResponseData(token, undefined, configUrl, mid, grant, ip, expectedSiteKey, _secret);\n  if (data.html && mid) data.html = injectNoticeScript(data.html, mid, expectedSiteKey || token.split(':')[0], baseUrl);\n  if (data.html) data.html = injectReferrerPolicy(data.html);\n  const json = JSON.stringify(data);\n  if (res.setHeader) res.setHeader('Content-Type', 'application/json');\n  // Anti-leak du grant : strict-origin-when-cross-origin (pas no-referrer — casserait\n  // les embeds YouTube 153). Cross-origin → origin seule, jamais le grant.\n  if (res.setHeader) res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');\n  // Contenu protégé : JAMAIS mis en cache (round 6, angle cache headers). Un CDN (ex.\n  // Cloudflare) ou un proxy qui mettrait en cache la réponse render la servirait sans\n  // le grant → le contenu whitelisté fuiterait. no-store sur la réponse ET no-transform\n  // (évite qu'un CDN réécrive le HTML rendu).\n  if (res.setHeader) res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, no-transform');\n  if (res.setHeader) res.setHeader('Pragma', 'no-cache');\n  // Audits : pose un cookie d'autorisation __sg_authorized quand le render réussit\n  // (grant valide + HTML servi). Ce cookie permet ensuite de charger les assets\n  // protégés (/assets/*.js) — sans lui, un téléchargement direct du bundle est refusé.\n  // Signé avec SHUGOKI_SIGNING_SECRET (jamais exposé), TTL court = durée de session.\n  if (data.html && res.setHeader) {\n    const authSecret = process.env.SHUGOKI_SIGNING_SECRET || process.env.SHUGOKI_SECRET;\n    if (authSecret) {\n      const ts = Math.floor(Date.now() / 1000);\n      const val = ts + ':' + crypto.createHmac('sha256', authSecret).update('sg_authorized:' + ts).digest('hex');\n      const secure = process.env.NODE_ENV === 'production' ? '; Secure' : '';\n      res.setHeader('Set-Cookie', '__sg_authorized=' + val + '; Path=/; HttpOnly; SameSite=Strict; Max-Age=120' + secure);\n    }\n  }\n  if (res.send) res.send(json);\n  else if (res.end) res.end(json);\n}\n\n// Notice de consentement injectée DANS le HTML rendu (après le split-render).\n// L'ack est UNIQUEMENT serveur (lié au machineId) : la popup vérifie /notice côté\n// client et s'affiche seulement si le machineId n'a pas déjà acké. Au clic OK, elle\n// POST /notice puis se ferme. Injectée ici (dans le render) plutôt que dans le skeleton\n// car document.write du render détruirait une popup posée avant.\nconst NOTICE_SCRIPT = `\n<script>\n(function(){\n  var mid=window.__sg_mid||'';\n  var sk=window.__sg_siteKey||'';\n  if(!mid||!sk||window.__sg_noticeEnabled===false)return;\n  var base=window.__sg_baseUrl||'';\n  var origin=base.replace(/\\\\/api\\\\/v1\\\\/?$/,'');\n  var _OVERLAY_DESK='position:fixed!important;inset:0!important;z-index:2147483647!important;background:rgba(0,0,0,.6)!important;display:flex!important;align-items:center!important;justify-content:center!important;padding:1.2rem!important;pointer-events:auto!important';\n  var _OVERLAY_MOB='position:fixed!important;inset:0!important;z-index:2147483647!important;background:rgba(0,0,0,.6)!important;display:flex!important;align-items:center!important;justify-content:center!important;padding:.6rem!important;pointer-events:auto!important';\n  var _CARD_DESK='background:#fffdfa!important;border:1px solid rgba(43,33,29,.16)!important;border-radius:16px 5px 16px 5px!important;box-shadow:0 10px 30px rgba(43,33,29,.08)!important;padding:2.8rem 2.4rem 2.6rem!important;max-width:460px!important;width:100%!important;text-align:center!important;font-family:system-ui,-apple-system,Arial,sans-serif!important';\n  var _CARD_MOB='background:#fffdfa!important;border:1px solid rgba(43,33,29,.16)!important;border-radius:14px 4px 14px 4px!important;box-shadow:0 10px 30px rgba(43,33,29,.08)!important;padding:2rem 1.3rem 2.2rem!important;max-width:340px!important;width:100%!important;text-align:center!important;font-family:system-ui,-apple-system,Arial,sans-serif!important';\n  var _CARD_DESK_DARK='background:#241a30!important;border:1px solid rgba(241,232,245,.14)!important;border-radius:16px 5px 16px 5px!important;box-shadow:0 10px 30px rgba(0,0,0,.4)!important;padding:2.8rem 2.4rem 2.6rem!important;max-width:460px!important;width:100%!important;text-align:center!important;font-family:system-ui,-apple-system,Arial,sans-serif!important';\n  var _CARD_MOB_DARK='background:#241a30!important;border:1px solid rgba(241,232,245,.14)!important;border-radius:14px 4px 14px 4px!important;box-shadow:0 10px 30px rgba(0,0,0,.4)!important;padding:2rem 1.3rem 2.2rem!important;max-width:340px!important;width:100%!important;text-align:center!important;font-family:system-ui,-apple-system,Arial,sans-serif!important';\n  var _MOBILE=false;\n  function _isMobile(){try{return window.matchMedia&&window.matchMedia('(max-width:640px)').matches}catch(e){return false}}\n  _MOBILE=_isMobile();\n  var _DARK=false;\n  function _isDark(){try{return window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches}catch(e){return false}}\n  _DARK=_isDark();\n  function _OV(){return _MOBILE?_OVERLAY_MOB:_OVERLAY_DESK}\n  function _CC(){return _DARK?(_MOBILE?_CARD_MOB_DARK:_CARD_DESK_DARK):(_MOBILE?_CARD_MOB:_CARD_DESK)}\n  function _CH(){return _DARK?(_MOBILE?_CARD_HTML_MOB_DARK:_CARD_HTML_DESK_DARK):(_MOBILE?_CARD_HTML_MOB:_CARD_HTML_DESK)}\n  var _CARD_HTML_DESK='<img src=\"'+origin+'/favicon-block.png\" alt=\"\" style=\"width:80px;height:80px;pointer-events:none;transform:rotate(-2.5deg);filter:drop-shadow(2px 4px 8px rgba(231,112,144,.55));margin:0 auto .6rem;display:block\"><img src=\"'+origin+'/brand-block.png\" alt=\"Shugoi\" style=\"display:block;margin:0 auto .4rem;pointer-events:none;max-width:100%;height:auto\"><div style=\"display:inline-block;background:#fdf0f4;border:1px solid rgba(194,84,111,.35);border-radius:10px 4px 10px 4px;padding:.3rem .9rem;font-size:.6rem;font-weight:600;text-transform:uppercase;letter-spacing:.16em;color:#c2546f;margin-bottom:1.4rem\">Protection anti-abus</div><p style=\"font-size:.9rem;color:#7a6a62;line-height:1.8;max-width:380px;margin:0 auto .9rem\">Ce site utilise Shugoi pour se prot\\\\u00e9ger contre les abus et la fraude. Des caract\\\\u00e9ristiques techniques de votre navigateur sont analys\\\\u00e9es pour d\\\\u00e9tecter les scripts automatis\\\\u00e9s, Tor, les VPN et les environnements virtuels. Aucune donn\\\\u00e9e personnelle n\\\\'est collect\\\\u00e9e.</p><button id=\"__sg_ok\" style=\"background:#E87090;color:#fff;border:1px solid #c2546f;border-radius:10px 4px 10px 4px;padding:.55rem 3rem;font-size:.8rem;font-weight:600;cursor:pointer;font-family:inherit\">OK</button><p style=\"font-size:.55rem;color:#c2546f;margin-top:1.5rem\"><a href=\"'+origin+'/legal/shugoi-notice\" target=\"_blank\" style=\"color:#c2546f;text-decoration:underline\">En savoir plus \\\\u00b7 shugoi.com</a></p>';\n  var _CARD_HTML_MOB='<img src=\"'+origin+'/favicon-block.png\" alt=\"\" style=\"width:62px;height:62px;pointer-events:none;transform:rotate(-2.5deg);filter:drop-shadow(2px 4px 8px rgba(231,112,144,.55));margin:0 auto .6rem;display:block\"><img src=\"'+origin+'/brand-block.png\" alt=\"Shugoi\" style=\"display:block;margin:0 auto .4rem;pointer-events:none;max-width:100%;height:auto\"><div style=\"display:inline-block;background:#fdf0f4;border:1px solid rgba(194,84,111,.35);border-radius:10px 4px 10px 4px;padding:.3rem .9rem;font-size:.55rem;font-weight:600;text-transform:uppercase;letter-spacing:.16em;color:#c2546f;margin-bottom:1.1rem\">Protection anti-abus</div><p style=\"font-size:.85rem;color:#7a6a62;line-height:1.75;max-width:340px;margin:0 auto .8rem\">Ce site utilise Shugoi pour se prot\\\\u00e9ger contre les abus et la fraude. Des caract\\\\u00e9ristiques techniques de votre navigateur sont analys\\\\u00e9es pour d\\\\u00e9tecter les scripts automatis\\\\u00e9s, Tor, les VPN et les environnements virtuels. Aucune donn\\\\u00e9e personnelle n\\\\'est collect\\\\u00e9e.</p><button id=\"__sg_ok\" style=\"background:#E87090;color:#fff;border:1px solid #c2546f;border-radius:10px 4px 10px 4px;padding:.5rem 2.4rem;font-size:.8rem;font-weight:600;cursor:pointer;font-family:inherit\">OK</button><p style=\"font-size:.55rem;color:#c2546f;margin-top:1.3rem\"><a href=\"'+origin+'/legal/shugoi-notice\" target=\"_blank\" style=\"color:#c2546f;text-decoration:underline\">En savoir plus \\\\u00b7 shugoi.com</a></p>';\n  var _CARD_HTML_DESK_DARK='<img src=\"'+origin+'/favicon-block.png\" alt=\"\" style=\"width:80px;height:80px;pointer-events:none;transform:rotate(-2.5deg);filter:drop-shadow(2px 4px 8px rgba(231,112,144,.55));margin:0 auto .6rem;display:block\"><img src=\"'+origin+'/brand-block.png\" alt=\"Shugoi\" style=\"display:block;margin:0 auto .4rem;pointer-events:none;max-width:100%;height:auto\"><div style=\"display:inline-block;background:rgba(233,137,159,.16);border:1px solid rgba(233,137,159,.5);border-radius:10px 4px 10px 4px;padding:.3rem .9rem;font-size:.6rem;font-weight:600;text-transform:uppercase;letter-spacing:.16em;color:#e9899f;margin-bottom:1.4rem\">Protection anti-abus</div><p style=\"font-size:.9rem;color:#a795b4;line-height:1.8;max-width:380px;margin:0 auto .9rem\">Ce site utilise Shugoi pour se prot\\\\u00e9ger contre les abus et la fraude. Des caract\\\\u00e9ristiques techniques de votre navigateur sont analys\\\\u00e9es pour d\\\\u00e9tecter les scripts automatis\\\\u00e9s, Tor, les VPN et les environnements virtuels. Aucune donn\\\\u00e9e personnelle n\\\\'est collect\\\\u00e9e.</p><button id=\"__sg_ok\" style=\"background:#E87090;color:#fff;border:1px solid #c2546f;border-radius:10px 4px 10px 4px;padding:.55rem 3rem;font-size:.8rem;font-weight:600;cursor:pointer;font-family:inherit\">OK</button><p style=\"font-size:.55rem;color:#e9899f;margin-top:1.5rem\"><a href=\"'+origin+'/legal/shugoi-notice\" target=\"_blank\" style=\"color:#e9899f;text-decoration:underline\">En savoir plus \\\\u00b7 shugoi.com</a></p>';\n  var _CARD_HTML_MOB_DARK='<img src=\"'+origin+'/favicon-block.png\" alt=\"\" style=\"width:62px;height:62px;pointer-events:none;transform:rotate(-2.5deg);filter:drop-shadow(2px 4px 8px rgba(231,112,144,.55));margin:0 auto .6rem;display:block\"><img src=\"'+origin+'/brand-block.png\" alt=\"Shugoi\" style=\"display:block;margin:0 auto .4rem;pointer-events:none;max-width:100%;height:auto\"><div style=\"display:inline-block;background:rgba(233,137,159,.16);border:1px solid rgba(233,137,159,.5);border-radius:10px 4px 10px 4px;padding:.3rem .9rem;font-size:.55rem;font-weight:600;text-transform:uppercase;letter-spacing:.16em;color:#e9899f;margin-bottom:1.1rem\">Protection anti-abus</div><p style=\"font-size:.85rem;color:#a795b4;line-height:1.75;max-width:340px;margin:0 auto .8rem\">Ce site utilise Shugoi pour se prot\\\\u00e9ger contre les abus et la fraude. Des caract\\\\u00e9ristiques techniques de votre navigateur sont analys\\\\u00e9es pour d\\\\u00e9tecter les scripts automatis\\\\u00e9s, Tor, les VPN et les environnements virtuels. Aucune donn\\\\u00e9e personnelle n\\\\'est collect\\\\u00e9e.</p><button id=\"__sg_ok\" style=\"background:#E87090;color:#fff;border:1px solid #c2546f;border-radius:10px 4px 10px 4px;padding:.5rem 2.4rem;font-size:.8rem;font-weight:600;cursor:pointer;font-family:inherit\">OK</button><p style=\"font-size:.55rem;color:#e9899f;margin-top:1.3rem\"><a href=\"'+origin+'/legal/shugoi-notice\" target=\"_blank\" style=\"color:#e9899f;text-decoration:underline\">En savoir plus \\\\u00b7 shugoi.com</a></p>';\n  var _closed=false;\n  var mo=null;\n  var _applying=false;\n  function ack(){try{fetch(base+'/notice',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({machineId:mid,siteKey:sk}),keepalive:true,signal:AbortSignal.timeout(4000)}).catch(function(){})}catch(e){}}\n  function buildOverlay(){\n    var o=document.createElement('div');o.id='__sg_o';o.style.cssText=_OV();\n    var c=document.createElement('div');c.id='__sg_cd';c.style.cssText=_CC();\n    c.innerHTML=_CH();\n    o.appendChild(c);return o;\n  }\n  function close(){_closed=true;try{if(mo)mo.disconnect()}catch(e){}var el=document.getElementById('__sg_o');if(el&&el.parentNode)el.parentNode.removeChild(el);document.body.style.overflow='';document.documentElement.style.overflow='';}\n  function okHandler(){ack();close();}\n  function rebind(){var b=document.getElementById('__sg_ok');if(b)b.onclick=okHandler;}\n  // Anti-bypass 100% MUTATION OBSERVER (aucun setInterval).\n  // CRITIQUE anti-freeze : le flag _applying + mo.takeRecords() cassent la boucle MO —\n  // nos propres modifications (style/innerHTML re-appliqués) ne re-déclenchent PAS le MO\n  // (le navigateur normalise cssText/innerHTML, donc la comparaison échoue toujours et\n  // on ré-appliquerait à l'infini). takeRecords() vide la file des mutations que NOS\n  // changements ont générée → une seule passe par altération réelle, jamais de gel.\n  function enforce(){\n    if(_closed||_applying)return;\n    _applying=true;\n    try{\n      var o=document.getElementById('__sg_o');\n      if(!o){o=buildOverlay();document.documentElement.appendChild(o);}\n      if(o.style.cssText!==_OV())o.style.cssText=_OV();\n      var c=document.getElementById('__sg_cd');\n      if(!c){o.innerHTML='';o.appendChild(buildOverlay().firstChild);}\n      else{\n        if(c.style.cssText!==_CC())c.style.cssText=_CC();\n        if(c.innerHTML!==_CH())c.innerHTML=_CH();\n      }\n      rebind();\n      if(document.body.style.overflow!=='hidden')document.body.style.overflow='hidden';\n      if(document.documentElement.style.overflow!=='hidden')document.documentElement.style.overflow='hidden';\n      try{\n        if(o&&!o.__sgObserved){o.__sgObserved=true;mo&&mo.observe(o,{childList:true,subtree:true,attributes:true,characterData:true,attributeFilter:['style','class','id']});}\n      }catch(e){}\n    }finally{\n      _applying=false;\n      try{if(mo)mo.takeRecords();}catch(e){}\n    }\n  }\n  function show(){\n    _closed=false;\n    var o=buildOverlay();document.documentElement.appendChild(o);\n    document.body.style.overflow='hidden';document.documentElement.style.overflow='hidden';\n    rebind();\n    try{\n      mo=new MutationObserver(function(){enforce();});\n      mo.observe(document.documentElement,{childList:true});\n      try{o.__sgObserved=true;mo.observe(o,{childList:true,subtree:true,attributes:true,characterData:true,attributeFilter:['style','class','id']});}catch(e){}\n    }catch(e){}\n  }\n  // Mobile : bascule desktop/stackée sur redimensionnement (ré-applique via enforce).\n  try{window.addEventListener('resize',function(){var _m=_isMobile();if(_m!==_MOBILE){_MOBILE=_m;enforce();}},{passive:true})}catch(e){}\n  function init(){if(document.body)show();else if(document.addEventListener)document.addEventListener('DOMContentLoaded',show);else setTimeout(init,50)}\n  fetch(base+'/notice?machineId='+encodeURIComponent(mid)+'&siteKey='+encodeURIComponent(sk),{signal:AbortSignal.timeout(4000)}).then(function(r){return r.json()}).then(function(d){if(!d.acknowledged)init()}).catch(function(){init()});\n})();\n</script>`;\n\nfunction injectNoticeScript(html: string, mid: string, siteKey: string, baseUrl?: string): string {\n  const baseVal = baseUrl || '';\n  const inject = NOTICE_SCRIPT\n    .replace('var mid=window.__sg_mid||\\'\\';', 'var mid=' + JSON.stringify(mid) + '||\\'\\';')\n    .replace('var sk=window.__sg_siteKey||\\'\\';', 'var sk=' + JSON.stringify(siteKey) + '||\\'\\';')\n    // base INJECTÉE (ne pas dépendre de window.__sg_baseUrl : nettoyé par _sgCl après 1,5s →\n    // sinon la notice appelle /notice relatif → page d'éval → l'ack ne passe jamais)\n    .replace('var base=window.__sg_baseUrl||\\'\\';', 'var base=' + JSON.stringify(baseVal) + '||window.__sg_baseUrl||\\'\\';')\n    .replace('window.__sg_noticeEnabled', 'window.__sg_noticeEnabled');\n  if (html.includes('</body>')) return html.replace('</body>', inject + '</body>');\n  return html + inject;\n}\n\n// ── Signing ──\nexport function signToken(siteKey: string, timestamp: number, secretOverride?: string): { token: string } {\n  const secret = secretOverride || process.env.SHUGOKI_SIGNING_SECRET || process.env.SHUGOKI_SECRET;\n  if (!secret) return { token: '' };\n  const nonce = crypto.randomBytes(8).toString('hex');\n  const payload = [siteKey, timestamp, nonce].join(':');\n  const sig = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n  return { token: payload + ':' + sig };\n}\n\n// ── Config cache (whitelist + detection flags) ──\nconst CONFIG_CACHE_TTL = 30_000;\nconst CONFIG_STALE_MAX = 600_000;\nconst CONFIG_FETCH_TIMEOUT = 2_000;\nconst MAX_TENANTS = 500;\n\ninterface ConfigEntry {\n  whitelist: string[];\n  flags: Record<string, boolean>;\n  skipPaths: string[];\n  fetchedAt: number;\n  inflight: Promise<void> | null;\n}\n\nconst _configCache = new Map<string, ConfigEntry>();\n\nfunction configKey(baseUrl: string, siteKey: string): string {\n  return baseUrl + '@' + siteKey;\n}\n\nfunction pruneCache<T extends { fetchedAt: number }>(m: Map<string, T>): void {\n  // O(n) borné : Map préserve l'ordre d'insertion → on éjecte les plus anciens\n  // sans tri O(n log n). Suffisant comme LRU approximatif (même borne MAX_TENANTS).\n  while (m.size > MAX_TENANTS) {\n    const oldest = m.keys().next();\n    if (oldest.done) return;\n    m.delete(oldest.value);\n  }\n}\n\nasync function refreshConfig(siteKey: string, baseUrl: string, entry: ConfigEntry, secret?: string): Promise<void> {\n  try {\n    // F2 (divulgation) : la config (detectionFlags/skipPaths) n'est rendue qu'à une clé\n    // prouvant possession du secret (sig = HMAC(secret, cb)). La siteKey est publique.\n    const cb = Date.now();\n    const sig = secret ? crypto.createHmac('sha256', secret).update(cb.toString()).digest('hex') : '';\n    const res = await fetch(baseUrl + '/whitelist?key=' + encodeURIComponent(siteKey) + '&cb=' + cb + (sig ? '&sig=' + sig : ''), {\n      signal: AbortSignal.timeout(CONFIG_FETCH_TIMEOUT),\n    });\n    if (res.ok) {\n      const data = await res.json() as Record<string, unknown>;\n      entry.whitelist = (data.whitelistedMachines as string[]) || [];\n      entry.flags = (data.detectionFlags as Record<string, boolean>) || (data.flags as Record<string, boolean>) || {};\n      entry.skipPaths = (data.skipPaths as string[]) || [];\n    }\n  } catch {}\n  entry.fetchedAt = Date.now();\n}\n\nexport async function getConfig(siteKey: string, baseUrl: string, secret?: string): Promise<{ whitelist: string[]; flags: Record<string, boolean>; skipPaths: string[] }> {\n  const key = configKey(baseUrl, siteKey);\n  let entry = _configCache.get(key);\n  if (!entry) {\n    entry = { whitelist: [], flags: {}, skipPaths: [], fetchedAt: 0, inflight: null };\n    _configCache.set(key, entry);\n    pruneCache(_configCache);\n  }\n\n  const age = Date.now() - entry.fetchedAt;\n\n  if (entry.fetchedAt === 0 || age > CONFIG_STALE_MAX) {\n    if (!entry.inflight) {\n      entry.inflight = refreshConfig(siteKey, baseUrl, entry, secret).finally(() => { entry!.inflight = null; });\n    }\n    await entry.inflight;\n    return { whitelist: entry.whitelist, flags: entry.flags, skipPaths: entry.skipPaths };\n  }\n\n  if (age > CONFIG_CACHE_TTL && !entry.inflight) {\n    entry.inflight = refreshConfig(siteKey, baseUrl, entry, secret).finally(() => { entry!.inflight = null; });\n    entry.inflight.catch(() => {});\n  }\n\n  return { whitelist: entry.whitelist, flags: entry.flags, skipPaths: entry.skipPaths };\n}\n\nexport async function fetchWhitelistForSiteKey(siteKey: string, baseUrl: string): Promise<string[]> {\n  return (await getConfig(siteKey, baseUrl)).whitelist;\n}\n\nexport async function fetchConfigForSiteKey(siteKey: string, baseUrl: string, secret?: string): Promise<Record<string, boolean>> {\n  return (await getConfig(siteKey, baseUrl, secret)).flags;\n}\n\nexport function __clearConfigCache(): void { _configCache.clear(); }\n\n// ── Guard cache ──\nconst GUARD_CACHE_TTL = 300_000;\n\ninterface GuardCacheEntry { detect: string | null; guard: string | null; fetching: boolean; queue: Array<() => void>; fetchedAt: number }\nconst _guardCaches = new Map<string, GuardCacheEntry>();\n\nfunction cacheKey(baseUrl: string, siteKey: string): string {\n  return `${baseUrl}::${siteKey}`;\n}\n\nfunction getCacheEntry(baseUrl: string, siteKey: string): GuardCacheEntry {\n  const key = cacheKey(baseUrl, siteKey);\n  if (!_guardCaches.has(key)) {\n    _guardCaches.set(key, { detect: null, guard: null, fetching: false, queue: [], fetchedAt: 0 });\n  }\n  return _guardCaches.get(key)!;\n}\n\nasync function fetchGuardScripts(baseUrl: string, secret?: string, siteKey?: string): Promise<void> {\n  const sk = siteKey || 'cache';\n  const cache = getCacheEntry(baseUrl, sk);\n  if (cache.fetching) return new Promise<void>((resolve) => { cache.queue.push(resolve); });\n  cache.fetching = true;\n  try {\n    const cb = Date.now();\n    const sig = secret ? crypto.createHmac('sha256', secret).update(cb.toString()).digest('hex') : '';\n    const [dRes, gRes] = await Promise.all([\n      fetch(baseUrl + '/guard-detect?key=' + sk + '&raw=1&cb=' + cb + (sig ? '&sig=' + sig : ''), { signal: AbortSignal.timeout(5000) }),\n      fetch(baseUrl + '/guard?key=' + sk + '&raw=1&cb=' + cb + (sig ? '&sig=' + sig : ''), { signal: AbortSignal.timeout(5000) }),\n    ]);\n    const rawDetect = await dRes.text();\n    const rawGuard = await gRes.text();\n    const seed = cb.toString(36);\n    cache.detect = rawDetect;\n    cache.guard = rawGuard;\n    cache.fetchedAt = Date.now();\n  } catch {\n    cache.detect = cache.detect || 'console.error(\"Shugoi guard-detect unavailable\")';\n    cache.guard = cache.guard || 'console.error(\"Shugoi guard unavailable\")';\n  }\n  cache.fetching = false;\n  cache.queue.forEach((r) => r());\n  cache.queue = [];\n}\n\nexport async function ensureGuardsReady(baseUrl: string, secret?: string, siteKey?: string): Promise<void> {\n  const cache = getCacheEntry(baseUrl, siteKey || 'cache');\n  if (cache.detect && cache.guard && Date.now() - cache.fetchedAt < GUARD_CACHE_TTL) return;\n  await fetchGuardScripts(baseUrl, secret, siteKey);\n}\n\nexport async function generateSkeleton(siteKey: string, token: string, baseUrl: string, restrictedAccess?: boolean, whitelist?: string[], renderUrl?: string, locale?: Locale, flags?: Record<string, boolean>, clockts?: number, signingSecret?: string, timingLogs = false): Promise<string> {\n  await ensureGuardsReady(baseUrl, undefined, siteKey);\n  const rurl = renderUrl || './__shugoi/render';\n  const cfg = flags ?? (await getConfig(siteKey, baseUrl, signingSecret)).flags;\n  const loc = locale || 'en';\n  const msgs = MESSAGES[loc];\n  const cache = getCacheEntry(baseUrl, siteKey);\n  const fragments: string[] = [];\n  fragments.push('window.__sg_siteKey=' + JSON.stringify(siteKey));\n  fragments.push('window.__sg_baseUrl=' + JSON.stringify(baseUrl));\n  fragments.push('window.__sg_config=' + JSON.stringify(cfg));\n  fragments.push('window.__shugoiTimingEnabled=' + (timingLogs ? 'true' : 'false'));\n  // Mode debug (audit #8) : piloté UNIQUEMENT par le serveur. En production ce flag\n  // est toujours false → le guard n'active jamais ses traces via ?sg_probe_debug=1\n  // ou localStorage. (Nom volontairement différent de \"debug\" pour ne pas exposer\n  // un toggle générique que l'attaquant chercherait.)\n  fragments.push('window.__sg_diagEnabled=' + (process.env.NODE_ENV === 'production' ? 'false' : 'true'));\n  // Nettoie l'URL : retire ?sg_proof de la barre d'adresse (le PoW a été validé serveur).\n  // history.replaceState ne recharge pas — le skeleton reste affiché, l'URL devient propre.\n  // Conserve le reste du query (ex. ?sg_probe_debug=1), retire uniquement sg_proof.\n  fragments.push(\"try{if((location.search||'').indexOf('sg_proof=')>=0){var _qs=location.search.replace(/[?&]sg_proof=[^&]*/,'');var _cu=location.pathname+(_qs?_qs:'')+location.hash;history.replaceState(null,'',_cu)}}catch(e){}\");\n  // Challenge PoW anti-curl (audit) : sel = HMAC(secret, ts + ':' + nonce). Le guard le\n  // résout en JS et l'envoie au wlc (pow=ts:nonce:solution). curl n'exécute pas le JS →\n  // pas de grant. Forteresse : nonce aléatoire 64 bits PAR requête (plus de sel\n  // déterministe par seconde → précomputation par lots impossible).\n  const _powTs = Math.floor(Date.now() / 1000);\n  const _powSecret = process.env.SHUGOKI_SIGNING_SECRET || process.env.SHUGOKI_SECRET || '';\n  const _powNonce = (typeof crypto.randomBytes === 'function' ? crypto.randomBytes(8).toString('hex') : String(Math.floor(Math.random() * 0xffffffff)).padStart(8, '0') + String(Math.floor(Math.random() * 0xffffffff)).padStart(8, '0'));\n  const _powSalt = _powSecret ? crypto.createHmac('sha256', _powSecret).update(_powTs + ':' + _powNonce).digest('hex') : '';\n  // ⚠️ DIFFICULTY 10 : 15 demandait ~32k itérations crypto.subtle (~1-3s navigateur) en\n  // plus du pre-flight PoW → 2-5s de chargement. 10 bits suffit pour prouver le JS.\n  // Audit #6 : la difficulté est désormais configurable (SHUGOKI_POW_DIFF, défaut 12),\n  // et DOIT rester synchrone avec core.ts (POW_DIFF) et whitelist.ts (POW_DIFFICULTY).\n  const _powDiff = (() => {\n    const raw = Number(process.env.SHUGOKI_POW_DIFF || '14');\n    return Number.isInteger(raw) && raw >= 8 && raw <= 24 ? raw : 12;\n  })();\n  fragments.push('window.__sg_pow=' + JSON.stringify({ ts: _powTs, nonce: _powNonce, salt: _powSalt, difficulty: _powDiff }));\n  const _ntpDrift = (typeof globalThis !== 'undefined' ? globalThis.__sg_ntpDrift : 0) || 0;\n  const _ntpTime = globalThis.__sg_ntpTime || (Date.now() - _ntpDrift);\n  const _clockts = clockts || _ntpTime;\n  fragments.push('window.__sg_ntp=' + _ntpTime);\n  fragments.push('window.__sg_serverTime=' + _clockts);\n  fragments.push('window.__sg_clockts=' + _clockts);\n  if (!restrictedAccess) fragments.push('window.__sg_disableRestrictedAccess=true');\n  // Fusion des guards (audit) : la notice de consentement est désormais intégrée dans\n  // guard-detect. Le guard séparé (cache.guard) n'est PLUS injecté — les 2 scripts\n  // définissaient chacun window._SG_ST → collision de table → le guard échouait\n  // (_SG_ST is not defined) et la popup ne s'affichait jamais.\n  if (cache.detect) fragments.push(\"try{\" + cache.detect + \"}catch(e){window.__sg_blocked=true}\");\n  // guard.src.js est conservé (R.export, fingerprint) mais non injecté pour éviter la collision.\n  // Locale-aware __sg_showBlock (interpolated at skeleton generation time)\n  const jsStr = (s: string) => JSON.stringify(s).slice(1, -1).replace(/</g, '\\\\x3c');\n  const devtoolsMsg = jsStr(msgs.devtoolsBody);\n  const tamperTitle = jsStr(msgs.tamperTitle);\n  fragments.push(\n    `window.__sg_showBlock=function(msg,title,badge){try{var _loading=document.getElementById(\"__sg_loading\");if(_loading)_loading.remove();var _paint=document.getElementById(\"__sg_paint_guard\");if(_paint)_paint.remove()}catch(_e){}var _saf=/AppleWebKit/i.test(navigator.userAgent||\"\")&&!/(Chrome|CriOS|Chromium|Edg|OPR)/i.test(navigator.userAgent||\"\");if(_saf&&/remplacement\\s+de\\s+contenu|content\\s+replacement/i.test(String(msg||\"\")+\" \"+String(title||\"\")))return;` +\n      NATIVE_BLOCK_PALETTE_SCRIPT +\n      `var _palette=_sgNativeBlockPalette(navigator.userAgent||\"\"),_light=_palette.light,_dark=_palette.dark;` +\n      `var css='@font-face{font-family:\"Reggae One\";src:url(https://shugoi.com/reggae-one.woff2) format(\"woff2\");font-display:swap}*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html,body{height:100%;background:'+_light+'}body{font-family:system-ui,-apple-system,\"Segoe UI\",sans-serif;display:flex;align-items:center;justify-content:center;padding:1.2rem}#c{max-width:460px;width:100%;background:#fffdfa;border:1px solid rgba(43,33,29,.16);border-radius:16px 5px 16px 5px;box-shadow:0 10px 30px rgba(43,33,29,.08);padding:3rem 2.4rem 2.8rem;text-align:center}#c .l{width:80px;height:80px;pointer-events:none;filter:drop-shadow(2px 4px 8px rgba(231,112,144,.55));margin:0 auto .6rem;display:block}#c .b{display:block;margin:0 auto .2rem;pointer-events:none;max-width:100%;height:auto}#c .bdg{display:inline-block;background:#fdf0f4;border:1px solid rgba(194,84,111,.35);border-radius:10px 4px 10px 4px;padding:.3rem .9rem;font-size:.6rem;font-weight:600;text-transform:uppercase;letter-spacing:.16em;color:#a83d5a;margin-bottom:1.4rem}#c h2{font-family:\"Reggae One\",Georgia,serif;font-size:2.2rem;color:#a83d5a;font-weight:400;margin:0 auto .6rem}#c p.desc{font-size:.9rem;color:#7a6a62;line-height:1.8;max-width:380px;margin:0 auto}#c p.ft{font-size:.55rem;color:#a83d5a;margin-top:1.8rem}@media(prefers-color-scheme:dark){html,body{background:'+_dark+'}#c{background:#241a30;border-color:rgba(241,232,245,.14);box-shadow:0 10px 30px rgba(0,0,0,.4)}#c .bdg{background:rgba(233,137,159,.16);border-color:rgba(233,137,159,.5);color:#e9899f}#c h2,#c p.ft{color:#e9899f}#c p.desc{color:#a795b4}}';` +\n      `var h='<head><meta charset=UTF-8><meta name=viewport content=width=device-width,initial-scale=1><meta name=color-scheme content=\"light dark\"><style>'+css+'</style></head><body><div id=c><img src=https://shugoi.com/favicon-block.png class=l><img src=https://shugoi.com/brand-block.png class=b><div class=bdg>'+(badge||${JSON.stringify(msgs.blockedBadge)})+'</div><h2>'+(title||${JSON.stringify(msgs.blockedTitle)})+'</h2><p class=desc>'+(msg||'')+'</p><p class=ft>'+location.hostname+' · Shugoi</p></div></body>';document.documentElement.innerHTML=h}`\n  );\n  fragments.push('var t=\"' + token + '\"');\n  fragments.push('window.__sg_token=\"' + token + '\"');\n  fragments.push('var k=\"' + siteKey + '\"');\n  fragments.push('var b=\"' + baseUrl + '\"');\n  fragments.push('var r=\"' + rurl + '\"');\n  /* Remplacement complet du document par le contenu réel, une fois la machine vérifiée.\n   *\n   * document.write après document.open est le seul moyen de remplacer un document\n   * entier en conservant son URL. Les conséquences, documentées dans\n   * le README § \"Split-Render : ce que cela implique\" :\n   *   - la page n'est plus éligible au cache arrière/avant (bfcache) ;\n   *   - la restauration de défilement natif est perdue → scrollTo(0,0) explicite ;\n   *   - DOMContentLoaded se déclenche deux fois ;\n   *   - un SPA initialisé dans le squelette perd son état.\n   *\n   * Alternative écartée : remplacer document.documentElement.innerHTML ne réexécute\n   * pas les scripts du document réel, ce qui casse toute page dynamique.\n   *\n   * Pour éviter ce mécanisme : splitRender: false.\n   */\n  fragments.push('var _gw=function(cb){if(window.__sg_guardsReady||window.__sg_blocked)cb();else setTimeout(function(){_gw(cb)},100)};function rd(p,n){if(window.__sg_blocked)return;if(!document.body)return setTimeout(function(){rd(p,n)},50);if(n>6){if((window.__sg_config||{}).enableContentReplacementCheck===true)window.__sg_showBlock&&window.__sg_showBlock(\"' + devtoolsMsg + '\",\"' + tamperTitle + '\");return}var _g=(window.__sg_grant||\"\");if(_g){p=p+(\"&grant=\"+encodeURIComponent(_g))}var _m=(window.__sg_detectMid||window.__sg_mid||\"\");if(_m){p=p+(\"&mid=\"+encodeURIComponent(_m))}fetch(p).then(function(x){return x.json()}).then(function(d){if(window.__sg_blocked)return;if(!document.body)return setTimeout(function(){rd(p,n+1)},50);if(d.html){document.open(\"text/html\");document.write(d.html);document.close();window.scrollTo(0,0)}if(d.blocked){window.__sg_showBlock&&window.__sg_showBlock(d.message,d.title)}if(d.error){if((window.__sg_config||{}).enableContentReplacementCheck===true)window.__sg_showBlock&&window.__sg_showBlock(\"' + devtoolsMsg + '\",\"' + tamperTitle + '\")}else if(!d.html&&!d.blocked){setTimeout(function(){rd(p,n+1)},300)}}).catch(function(){setTimeout(function(){rd(p,n+1)},300)})}');\n  fragments.push('function _sgCl(){try{for(var _i in window){if(_i.indexOf(\"__sg\")===0){window[_i]=null;delete window[_i]}}window._sgLogCP=function(){};window.midHex=function(){};window.rd=function(){};window._gw=function(){};window.applyDecision=function(){};window._D=function(){};window.z=function(f){return f()}}catch(_e){}}_gw(function(){rd(r+\"?token=\"+t,0);setTimeout(_sgCl,1500)})');\n  const combinedCode = fragments.join(';');\n  // NOTE bench : la concaténation += bat ici le tableau pré-alloué + join\n  // (V8 internez en ropes, ~3× plus rapide sur 100 Ko). Ne pas \"optimiser\".\n  let encStr = '';\n  for (let i = 0; i < combinedCode.length; i++) encStr += String.fromCodePoint(917504 + combinedCode.charCodeAt(i));\n  const decodedCall = \"[...'\" + encStr + \"'].map(x=>String.fromCodePoint(x.codePointAt(0)-917504)).join('')\";\n  const bootCode = \"eval(\" + decodedCall + \")\";\n  return '<script>' + bootCode + '</script>';\n}\n\nexport async function injectGuardScripts(html: string, siteKey: string, baseUrl: string, whitelist?: string[] | null, restrictedAccess?: boolean, signingSecret?: string, req?: unknown, _allowedOrigins?: string[], locale?: Locale, clockts?: number, timingLogs = false): Promise<string> {\n  await ensureGuardsReady(baseUrl, signingSecret, siteKey);\n  const cfgData = await getConfig(siteKey, baseUrl, signingSecret);\n  const wl = whitelist ?? cfgData.whitelist;\n  const ts = Date.now();\n  const signed = signToken(siteKey, ts, signingSecret);\n  const configVars: string[] = [];\n  if (!restrictedAccess) configVars.push('window.__sg_disableRestrictedAccess=true');\n  const configScript = configVars.length ? '<script>' + configVars.join(';') + '</script>' : '';\n  let injectedHtml = html;\n  const headClose = injectedHtml.indexOf('</head>');\n  if (headClose >= 0) injectedHtml = injectedHtml.slice(0, headClose) + configScript + injectedHtml.slice(headClose);\n  else if (injectedHtml.includes('<body')) { const bm = injectedHtml.match(/<body[^>]*>/); if (bm) { const at = injectedHtml.indexOf(bm[0]) + bm[0].length; injectedHtml = injectedHtml.slice(0, at) + configScript + injectedHtml.slice(at); } }\n  else injectedHtml = configScript + injectedHtml;\n  const renderUrl = './__shugoi/render';\n  storeHtml(signed.token, injectedHtml);\n  return generateSkeleton(siteKey, signed.token, baseUrl, restrictedAccess, wl, renderUrl, locale, cfgData.flags, clockts, signingSecret, timingLogs);\n}\n\nexport function enableDiskStore(multiProcess: boolean) {\n  _diskEnabled = multiProcess;\n  if (multiProcess) startDiskCleanup();\n}\n","export { ShugoiError } from './errors';\nexport { signToken, storeHtml, generateSkeleton, injectGuardScripts, renderResponseData, handleRender, verifyRenderGrant, fetchWhitelistForSiteKey, __clearConfigCache } from './render';\nexport { createShugoiMiddleware, createShugoiPlugin, BLOCK_PAGE, DEFAULT_HEADLESS_PATTERNS, DEFAULT_BOT_WHITELIST } from './middleware';\nexport { buildCsp, mergeCsp } from './csp';\nexport { checkLicense } from './check-license';\nexport { scriptTags } from './scripts';\nexport { validateSiteKey } from './validate-site-key';\nexport type {\n  ShugoiCoreOptions, ShugoiMiddlewareOptions, ShugoiPluginOptions,\n  ScriptTagsOptions, ScriptTagsResult, BlockPageContext,\n  CheckRequest, CheckResponse,\n} from './types';\n","export type ShugoiErrorCode =\n  | 'invalid_site_key'\n  | 'site_key_validation_failed'\n  | 'api_unreachable'\n  | 'api_timeout'\n  | 'missing_site_key'\n  | 'unexpected_api_response'\n  | 'internal_error';\n\nexport class ShugoiError extends Error {\n  constructor(\n    public readonly code: ShugoiErrorCode,\n    message: string,\n    public readonly cause?: unknown,\n  ) {\n    super(message);\n    this.name = 'ShugoiError';\n  }\n}\n","import type { ShugoiCoreOptions } from './types'\nimport { injectGuardScripts, ensureGuardsReady, enableDiskStore, getConfig, storeHtml, signToken, renderResponseData } from './render'\n\nimport { mergeCsp } from './csp'\nimport { addSkipPathNavigationGuard } from './navigation-guard'\nimport { createCore, DEFAULT_HEADLESS_PATTERNS, BLOCK_PAGE, DEFAULT_BOT_WHITELIST } from './core'\nimport { resolveLocale, type Locale } from './locales'\n\ninterface MinimalRequest {\n  path?: string; url?: string; ip?: string; method?: string;\n  headers?: Record<string, string | string[] | undefined>;\n  query?: Record<string, unknown>;\n}\ninterface MinimalResponse {\n  setHeader?(k: string, v: string): void;\n  getHeader?(k: string): string | number | string[] | undefined;\n  status?(code: number): unknown;\n  type?(t: string): unknown;\n  send?(body: unknown): unknown;\n  end?(body?: unknown, ...rest: unknown[]): unknown;\n}\n\nexport { DEFAULT_HEADLESS_PATTERNS, BLOCK_PAGE, DEFAULT_BOT_WHITELIST } from './core';\n\n// Parse les cookies __sg_* par indexOf direct (2 scans C sans allocation de\n// tableau) au lieu de 2 regex .match() ou d'un split(';') + trim() par partie\n// (bench : ~30% plus rapide avec cookies, ~2.7× sans — le cas le plus fréquent).\n// Niveau module : partagé par le middleware Express et le plugin Fastify.\nfunction parseSgCookies(header: unknown): { sgOk?: string; sgAuthorized?: string } {\n  if (typeof header !== 'string' || header.indexOf('__sg_') < 0) return {};\n  let sgOk: string | undefined;\n  let sgAuthorized: string | undefined;\n  let i = header.indexOf('__sg_ok=');\n  if (i >= 0) {\n    i += 8;\n    const end = header.indexOf(';', i);\n    sgOk = end < 0 ? header.slice(i).trim() : header.slice(i, end).trim();\n  }\n  i = header.indexOf('__sg_authorized=');\n  if (i >= 0) {\n    i += 16;\n    const end = header.indexOf(';', i);\n    sgAuthorized = end < 0 ? header.slice(i).trim() : header.slice(i, end).trim();\n  }\n  return { sgOk, sgAuthorized };\n}\n\nfunction methodNotAllowed(res: MinimalResponse): void {\n  if (res.status) res.status(405);\n  if (res.type) res.type('application/json');\n  const body = JSON.stringify({ error: 'method_not_allowed' });\n  if (res.send) res.send(body);\n  else if (res.end) res.end(body);\n}\n\nfunction withSkipPathNavigationGuard(body: unknown, res: MinimalResponse, skipPaths: readonly string[]): unknown {\n  const html = typeof body === 'string' ? body : body instanceof Uint8Array ? new TextDecoder().decode(body) : null;\n  if (html === null) return body;\n  const contentType = res.getHeader?.('content-type');\n  if (contentType && !String(contentType).toLowerCase().includes('text/html')) return body;\n  const csp = res.getHeader?.('content-security-policy');\n  const nonce = typeof csp === 'string' ? csp.match(/'nonce-([^']+)'/)?.[1] : undefined;\n  return addSkipPathNavigationGuard(html, skipPaths, nonce);\n}\n\nexport function createShugoiMiddleware(options: ShugoiCoreOptions) {\n  const core = createCore(options);\n  const autoInject = options.autoInject ?? true;\n  const splitRender = options.splitRender ?? true;\n  const restrictedAccess = options.restrictedAccess ?? false;\n  const signingSecret = options.signingSecret || options.secret;\n  const baseUrl = options.baseUrl ?? 'https://shugoi.com/api/v1';\n  const internalUrl = options.internalUrl || baseUrl;\n\n  if (options.multiProcess) enableDiskStore(true);\n\n  return async function shugoiMiddleware(req: MinimalRequest, res: MinimalResponse, next: () => void) {\n    try {\n      const path = (req.path ?? req.url ?? '/').split('?')[0];\n\n      // Render endpoint — handled by middleware adapter\n      if (path.endsWith('/__shugoi/render')) {\n        // Round 13 : restreindre à GET/HEAD (le client fetch en GET). POST/PUT/DELETE\n        // renvoyaient un 200 sans effet d'état — surface réduite, méthode normalisée.\n        const m = String((req.method || 'GET')).toUpperCase();\n        if (m !== 'GET' && m !== 'HEAD') {\n          methodNotAllowed(res);\n          return;\n        }\n        const { handleRender } = await import('./render');\n        const q = (req.query && (req.query as Record<string, string>)) || {};\n        const ip = (typeof req.headers?.['x-forwarded-for'] === 'string'\n          ? req.headers['x-forwarded-for'].split(',')[0]?.trim()\n          : undefined) || (typeof req.ip === 'string' ? req.ip : 'unknown');\n        // CRITIQUE 1 (§7bis) : le render vérifie que le token appartient à CE site\n        // (options.siteKey) — un grant émis par un autre site (pyxelze) est refusé ici.\n        // baseUrl transmis à la notice (injectée) : __sg_baseUrl est nettoyé par _sgCl.\n        return handleRender(q.token || '', res, internalUrl, q.mid || '', q.grant || '', ip, options.siteKey, baseUrl, signingSecret);\n      }\n\n      // Challenge page : GET/HEAD uniquement (round 13, même normalisation).\n      if (path === '/__sg_challenge') {\n        const m = String((req.method || 'GET')).toUpperCase();\n        if (m !== 'GET' && m !== 'HEAD') {\n          methodNotAllowed(res);\n          return;\n        }\n      }\n\n      // CSP: merge with existing header\n      if (core.cspEnabled && res.setHeader) {\n        if (res.getHeader) {\n          const existing = res.getHeader('Content-Security-Policy');\n          res.setHeader('Content-Security-Policy', mergeCsp(\n            typeof existing === 'string' ? existing : undefined,\n            core.csp,\n          ));\n        } else {\n          res.setHeader('Content-Security-Policy', core.csp);\n        }\n      }\n\n      // Delegate evaluation to core\n      const ua = (typeof req.headers?.['user-agent'] === 'string' ? req.headers['user-agent'] : '') || '';\n      const ip = (typeof req.headers?.['x-forwarded-for'] === 'string'\n        ? req.headers['x-forwarded-for'].split(',')[0]?.trim()\n        : undefined) || (typeof req.ip === 'string' ? req.ip : 'unknown');\n      const reqLocale: Locale = resolveLocale(options.locale, typeof req.headers?.['accept-language'] === 'string' ? req.headers?.['accept-language'] : undefined);\n      const sgCookies = parseSgCookies(req.headers?.cookie);\n\n      // SkipPaths check BEFORE detection — ces routes contournent toute protection.\n      // Audit passe 8 (§3.1) : MATCH EXACT uniquement (plus de prefix-match). Un skipPath\n      // `/docs` ne couvre PAS `/docs/anything` — sinon un skipPath large (`/api`, `/`)\n      // exposerait toutes les sous-routes sans guard. L'utilisateur liste chaque chemin.\n      if (autoInject && options.siteKey) {\n        try {\n          const { skipPaths } = await getConfig(options.siteKey, internalUrl, signingSecret);\n          if (skipPaths?.some((p: string) => path === p)) {\n            if (res.send) {\n              const originalSend = res.send.bind(res);\n              res.send = function (body: unknown) {\n                return originalSend(withSkipPathNavigationGuard(body, res, skipPaths));\n              };\n            }\n            if (res.end) {\n              const originalEnd = res.end.bind(res);\n              res.end = function (body?: unknown, ...rest: unknown[]) {\n                return originalEnd(withSkipPathNavigationGuard(body, res, skipPaths), ...rest);\n              };\n            }\n            return next();\n          }\n        } catch {}\n      }\n\n      const decision = await core.evaluate({\n        path,\n        ua,\n        ip,\n        host: typeof req.headers?.['host'] === 'string' ? req.headers.host : undefined,\n        acceptLanguage: typeof req.headers?.['accept-language'] === 'string' ? req.headers['accept-language'] : undefined,\n        secFetchDest: typeof req.headers?.['sec-fetch-dest'] === 'string' ? req.headers['sec-fetch-dest'] : undefined,\n        secFetchMode: typeof req.headers?.['sec-fetch-mode'] === 'string' ? req.headers['sec-fetch-mode'] : undefined,\n        sgProof: (req.query && typeof (req.query as Record<string, unknown>).sg_proof === 'string') ? (req.query as Record<string, unknown>).sg_proof as string : undefined,\n        sgOk: sgCookies.sgOk,\n        sgAuthorized: sgCookies.sgAuthorized,\n        forwardedPrefix: (typeof req.headers?.['x-forwarded-prefix'] === 'string' ? req.headers['x-forwarded-prefix'] : undefined),\n      });\n\n      if (decision) {\n        if (decision.headers) {\n          for (const [k, v] of Object.entries(decision.headers)) {\n            if (res.setHeader) res.setHeader(k, v);\n          }\n        }\n        if (res.status) res.status(decision.status);\n        if (decision.headers && decision.headers['Content-Type']) {\n          if (res.setHeader) res.setHeader('Content-Type', decision.headers['Content-Type']);\n        } else if (res.type) {\n          res.type(decision.contentType.split('/')[1]);\n        }\n        if (decision.body) {\n          if (res.send) res.send(decision.body);\n          else if (res.end) res.end(decision.body);\n        } else if (res.end) {\n          res.end();\n        }\n        return;\n      }\n\n      // PoW validé → pose le cookie __sg_ok sur la réponse du skeleton (navigations\n      // suivantes sans challenge, donc chargement rapide).\n      const sgProofQ = (req.query && typeof (req.query as Record<string, unknown>).sg_proof === 'string') ? (req.query as Record<string, unknown>).sg_proof as string : undefined;\n      if (sgProofQ && res.setHeader) {\n        const okCookie = core.sgOkCookie(sgProofQ, ip, ua);\n        if (okCookie) res.setHeader('Set-Cookie', okCookie);\n      }\n\n      // Split-render: inject skeleton for HTML pages (skip for allowlisted paths and\n      // whitelisted bots). Les bots (moteurs + partage social) reçoivent le HTML BRUT\n      // (og:image, indexation) — ils ne peuvent pas exécuter le skeleton eval().\n      const isBot = (await core.isTrustedBot(ua, ip)) || core.isWhitelistedBot(ua);\n\n      if (autoInject && splitRender && !isBot && !core.isAllowlisted(path)) {\n        let injected = false;\n        const originalSend = res.send?.bind(res) as ((body?: unknown) => unknown) | undefined;\n        const originalEnd = res.end?.bind(res) as ((chunk?: unknown, encoding?: string, cb?: () => void) => unknown) | undefined;\n\n        const doInject = async (body: unknown): Promise<unknown> => {\n          if (injected) return body;\n          if (typeof body === 'string') {\n            const ct = res.getHeader ? res.getHeader('content-type') : undefined;\n            if (!ct || String(ct).includes('text/html')) {\n              try { body = await injectGuardScripts(body, options.siteKey, baseUrl, undefined, restrictedAccess, signingSecret, req as any, undefined, reqLocale, undefined, options.timingLogs ?? false); } catch (e) { core.log('inject error:', e); }\n              injected = true;\n            }\n          }\n          return body;\n        };\n\n        if (originalSend) {\n          res.send = function (body: unknown) { return doInject(body).then(b => originalSend(b)); };\n        }\n        if (originalEnd) {\n          res.end = function (chunk?: unknown, encoding?: string, cb?: () => void) {\n            doInject(chunk).then(b => {\n              if (cb) originalEnd(b, encoding, cb);\n              else originalEnd(b, encoding);\n            });\n            return this;\n          };\n        }\n      }\n\n      next();\n    } catch (err) {\n      core.log('Unhandled error:', err);\n      next();\n    }\n  };\n}\n\nexport function createShugoiPlugin(options: ShugoiCoreOptions) {\n  const core = createCore(options);\n  const restrictedAccess = options.restrictedAccess ?? false;\n  const signingSecret = options.signingSecret || options.secret;\n  const baseUrl = options.baseUrl ?? 'https://shugoi.com/api/v1';\n\n  if (options.multiProcess) enableDiskStore(true);\n\n  return async function shugoiPlugin(fastify: any) {\n    const skippedRequests = new WeakMap<object, readonly string[]>();\n    // CSP onRequest hook\n    fastify.addHook('onRequest', async (request: any, reply: any) => {\n      if (core.cspEnabled && reply.getHeader) {\n        const existing = reply.getHeader('Content-Security-Policy');\n        reply.header('Content-Security-Policy', mergeCsp(\n          typeof existing === 'string' ? existing : undefined,\n          core.csp,\n        ));\n      } else if (core.cspEnabled) {\n        reply.header('Content-Security-Policy', core.csp);\n      }\n    });\n\n    // Render endpoint\n    fastify.get('/__shugoi/render', async (request: any, reply: any) => {\n      const { renderResponseData, injectReferrerPolicy } = await import('./render');\n      const ip = (typeof request.headers?.['x-forwarded-for'] === 'string'\n        ? request.headers['x-forwarded-for'].split(',')[0]?.trim()\n        : undefined) || (typeof request.ip === 'string' ? request.ip : 'unknown');\n      const data = await renderResponseData(request.query.token || '', undefined, options.baseUrl, request.query.mid || '', request.query.grant || '', ip, options.siteKey);\n      // Anti-leak du grant : strict-origin-when-cross-origin (pas no-referrer — casserait\n      // les embeds YouTube 153 ; le grant n'est plus dans l'URL de la page après document.write).\n      if (data.html) data.html = injectReferrerPolicy(data.html);\n      reply.header('Referrer-Policy', 'strict-origin-when-cross-origin');\n      // Contenu protégé : jamais mis en cache (round 6 — CDN bypass).\n      reply.header('Cache-Control', 'no-store, no-cache, must-revalidate, no-transform');\n      reply.header('Pragma', 'no-cache');\n      reply.send(data);\n    });\n\n    fastify.head('/__shugoi/healthcheck', async (request: any, reply: any) => reply.send(''));\n\n    // PreHandler: evaluation pipeline\n    fastify.addHook('preHandler', async (request: any, reply: any) => {\n      try {\n        const path = request.url.split('?')[0];\n        if (path.endsWith('/__shugoi/render') || path.endsWith('/__shugoi/healthcheck')) return;\n        if ((options.autoInject ?? true) && options.siteKey) {\n          try {\n            const { skipPaths } = await getConfig(options.siteKey, options.internalUrl || options.baseUrl || 'https://shugoi.com/api/v1', signingSecret);\n            if (skipPaths?.some((p: string) => path === p)) {\n              skippedRequests.set(request, skipPaths);\n              return;\n            }\n          } catch {}\n        }\n        if (core.isAllowlisted(path)) return;\n\n        const ua = request.headers['user-agent'] ?? '';\n        const ip = request.headers['x-forwarded-for']?.split(',')[0]?.trim() || request.ip || 'unknown';\n        const fCookies = parseSgCookies(request.headers.cookie);\n\n        const decision = await core.evaluate({\n          path,\n          ua,\n          ip,\n          host: request.headers?.host,\n          acceptLanguage: request.headers['accept-language'],\n          secFetchDest: request.headers['sec-fetch-dest'],\n          secFetchMode: request.headers['sec-fetch-mode'],\n          sgProof: (request.query && typeof request.query?.sg_proof === 'string') ? request.query.sg_proof as string : undefined,\n          sgOk: fCookies.sgOk,\n          sgAuthorized: fCookies.sgAuthorized,\n          forwardedPrefix: (typeof request.headers['x-forwarded-prefix'] === 'string' ? request.headers['x-forwarded-prefix'] : undefined),\n        });\n\n        if (decision) {\n          if (decision.headers) {\n            for (const [k, v] of Object.entries(decision.headers)) reply.header(k, v);\n          }\n          reply.code(decision.status).type(decision.contentType === 'text/html' ? 'text/html' : 'text/plain').send(decision.body);\n          return;\n        }\n\n        if (typeof request.query?.sg_proof === 'string') {\n          const okCookie = core.sgOkCookie(request.query.sg_proof as string, ip, ua);\n          if (okCookie) reply.header('Set-Cookie', okCookie);\n        }\n      } catch (err) {\n        core.log('preHandler error:', err);\n      }\n    });\n\n    // onSend: split-render injection\n    fastify.addHook('onSend', async (request: any, reply: any, payload: any) => {\n      if (typeof payload !== 'string') return payload;\n      const path = request.url.split('?')[0];\n      if (path.endsWith('/__shugoi/render') || path.endsWith('/__shugoi/healthcheck')) return payload;\n      if (reply.statusCode !== 200) return payload;\n      const skipPaths = skippedRequests.get(request);\n      if (skipPaths) {\n        skippedRequests.delete(request);\n        const ct = reply.getHeader('content-type');\n        if (!ct || String(ct).toLowerCase().includes('text/html')) {\n          const csp = reply.getHeader('content-security-policy');\n          const nonce = typeof csp === 'string' ? csp.match(/'nonce-([^']+)'/)?.[1] : undefined;\n          return addSkipPathNavigationGuard(payload, skipPaths, nonce);\n        }\n        return payload;\n      }\n      // Bots (moteurs + partage social) : HTML brut sans skeleton — ils ne peuvent pas\n      // exécuter le skeleton eval() (og:image / indexation).\n      const ua = typeof request.headers?.['user-agent'] === 'string' ? request.headers['user-agent'] : '';\n      if (core.isWhitelistedBot(ua)) return payload;\n      const ct = reply.getHeader('content-type');\n      if (!ct || String(ct).includes('text/html')) {\n        const pluginLocale: Locale = resolveLocale(options.locale, typeof request.headers?.['accept-language'] === 'string' ? request.headers?.['accept-language'] : undefined);\n        return await injectGuardScripts(payload, options.siteKey, baseUrl, undefined, restrictedAccess, signingSecret, { url: path } as any, undefined, pluginLocale, undefined, options.timingLogs ?? false);\n      }\n      return payload;\n    });\n  };\n}\n","export interface CspOptions {\n  siteKey: string;\n  extraDirectives?: Record<string, string[]>;\n  splitRender?: boolean;\n  apiOrigin?: string;\n}\n\nexport function originOf(baseUrl: string | undefined): string | null {\n  if (!baseUrl) return null;\n  try {\n    const u = new URL(baseUrl);\n    if (u.protocol !== 'http:' && u.protocol !== 'https:') return null;\n    return u.protocol + '//' + u.host;\n  } catch { return null; }\n}\n\nconst SHUGOI_ORIGIN = 'https://shugoi.com';\n\nfunction baseDirectives(apiOrigin: string): Record<string, string[]> {\n  const api = [...new Set([SHUGOI_ORIGIN, apiOrigin].filter(Boolean))];\n  return {\n    'default-src': [\"'self'\"],\n    'script-src': [\"'self'\", \"'unsafe-inline'\", \"'unsafe-eval'\", ...api, 'blob:'],\n    'worker-src': [\"'self'\", 'blob:', ...api],\n    'connect-src': [\"'self'\", ...api],\n    'style-src': [\"'self'\", \"'unsafe-inline'\", ...api],\n    'font-src': [\"'self'\", ...api, 'data:'],\n    'img-src': [\"'self'\", ...api, 'data:', 'blob:'],\n    'frame-ancestors': [\"'self'\"],\n    'object-src': [\"'none'\"],\n    'base-uri': [\"'self'\"],\n    'form-action': [\"'self'\"],\n  };\n}\n\nexport function buildCsp(options: CspOptions): string {\n  const apiOrigin = options.apiOrigin ?? SHUGOI_ORIGIN;\n  const merged: Record<string, string[]> = {};\n  for (const [k, v] of Object.entries(baseDirectives(apiOrigin))) merged[k] = [...v];\n\n  if (options.extraDirectives) {\n    for (const [key, values] of Object.entries(options.extraDirectives)) {\n      merged[key] = [...new Set([...(merged[key] ?? []), ...values])];\n    }\n  }\n\n  if (options.splitRender === false && merged['script-src']) {\n    merged['script-src'] = merged['script-src'].filter((v) => v !== \"'unsafe-eval'\");\n  }\n\n  return Object.entries(merged)\n    .map(([key, values]) => `${key} ${values.join(' ')}`)\n    .join('; ');\n}\n\n// Cache du parse de la CSP ajoutée (constante par instance, mergeCsp par requête).\nconst _addedParseCache = new Map<string, Map<string, Set<string>>>();\n\nexport function mergeCsp(existing: string | undefined, added: string): string {\n  if (!existing) return added;\n  const parse = (s: string): Map<string, Set<string>> => {\n    const m = new Map<string, Set<string>>();\n    for (const part of s.split(';')) {\n      const [name, ...vals] = part.trim().split(/\\s+/);\n      if (!name) continue;\n      const set = m.get(name) ?? new Set<string>();\n      vals.forEach((v) => set.add(v));\n      m.set(name, set);\n    }\n    return m;\n  };\n  const base = parse(existing);\n  // `added` est constant par instance (core.csp) mais mergeCsp tourne par requête :\n  // on mémoïse son parse pour ne payer le split qu'une fois par valeur distincte.\n  let addedMap = _addedParseCache.get(added);\n  if (!addedMap) {\n    addedMap = parse(added);\n    if (_addedParseCache.size >= 32) _addedParseCache.clear();\n    _addedParseCache.set(added, addedMap);\n  }\n  for (const [k, v] of addedMap) {\n    const set = base.get(k) ?? new Set<string>();\n    v.forEach((x) => set.add(x));\n    base.set(k, set);\n  }\n  // Spec CSP : le mot-clé 'none' doit être SEUL dans une directive — sinon il est ignoré\n  // par le navigateur (warning + comportement ambigu). Lors d'un merge (ex. un site client\n  // définit frame-ancestors 'none' et le module ajoute 'self'), on garde uniquement 'none'\n  // (le plus restrictif) → CSP toujours valide, quelle que soit la CSP préexistante.\n  for (const [, set] of base) {\n    if (set.has(\"'none'\") && set.size > 1) {\n      set.clear();\n      set.add(\"'none'\");\n    }\n  }\n  return [...base.entries()].map(([k, v]) => `${k} ${[...v].join(' ')}`).join('; ');\n}\n","export function addSkipPathNavigationGuard(html: string, skipPaths: readonly string[], nonce?: string): string {\n  if (!/<(?:!doctype\\s+html|html\\b)/i.test(html) || html.includes('data-shugoi-skip-navigation')) return html;\n  const paths = JSON.stringify(skipPaths).replace(/</g, '\\\\u003c');\n  const nonceAttribute = nonce ? ' nonce=\"' + nonce.replace(/[&\"<>]/g, '') + '\"' : '';\n  const script = '<script data-shugoi-skip-navigation' + nonceAttribute + '>(function(s){function skip(p){return s.some(function(x){return x===p})}function leave(u){try{var n=new URL(u,location.href);if(n.origin===location.origin&&!skip(n.pathname)){location.assign(n.href);return true}}catch(e){}return false}var p=history.pushState,r=history.replaceState;history.pushState=function(){if(arguments.length>2&&leave(arguments[2]))return;return p.apply(this,arguments)};history.replaceState=function(){if(arguments.length>2&&leave(arguments[2]))return;return r.apply(this,arguments)};window.addEventListener(\"popstate\",function(){if(!skip(location.pathname))location.reload()});document.addEventListener(\"click\",function(e){if(e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;var n=e.target;while(n&&n.nodeType===1&&n.tagName!==\"A\")n=n.parentNode;if(!n||n.tagName!==\"A\"||n.target&&n.target!==\"_self\"||n.hasAttribute(\"download\"))return;if(leave(n.href))e.preventDefault()},true)})( ' + paths + ' );</script>';\n  if (/<\\/body\\s*>/i.test(html)) return html.replace(/<\\/body\\s*>/i, script + '</body>');\n  if (/<\\/html\\s*>/i.test(html)) return html.replace(/<\\/html\\s*>/i, script + '</html>');\n  return html + script;\n}\n","import type { ShugoiCoreOptions, BlockPageContext } from './types'\nimport { ensureGuardsReady, fetchConfigForSiteKey } from './render'\nimport { buildCsp, originOf } from './csp'\nimport { resolveLocale, type Locale, MESSAGES } from './locales'\nimport { nativeBlockPalette } from './block-palette'\nimport { verifyBotIp, VERIFIABLE_BOTS } from './verify-bot'\nimport crypto from 'node:crypto'\n\nexport const DEFAULT_HEADLESS_PATTERNS = [\n  /^curl/i, /^wget/i, /^python/i, /^Go-http-client/i, /^Java\\//,\n  /HTTPie/i, /^node-fetch/i, /axios/i, /^okhttp/i, /^scrapy/i,\n  /PowerShell/i, /WinHttp/i,\n];\n\nexport const BLOCK_PAGE = [\n  \"+---------------------------------------------+\",\n  \"|           BLOCKED BY SHUGOI                 |\",\n  \"+---------------------------------------------+\",\n  \"|  Bots, scrapers and headless clients        |\",\n  \"|  are blocked by Shugoi protection.          |\",\n  \"|                                             |\",\n  \"|  Use a standard browser to access           |\",\n  \"|  this site.                                 |\",\n  \"|                                             |\",\n  \"|  - web: https://shugoi.com -                |\",\n  \"+---------------------------------------------+\",\n].join('\\n') + '\\n';\n\nexport const DEFAULT_BOT_WHITELIST = [\n  /Googlebot/i, /Bingbot/i, /Slurp/i, /DuckDuckBot/i, /YandexBot/i, /Applebot/i,\n  /facebookexternalhit/i, /Twitterbot/i, /LinkedInBot/i, /Discordbot/i, /Slackbot/i,\n  /WhatsApp/i, /TelegramBot/i,\n];\n\nexport function escapeHtml(s: string): string {\n  return s\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;')\n    .replace(/'/g, '&#39;');\n}\n\nexport interface EvaluateCtx {\n  path: string;\n  ua: string;\n  ip: string;\n  host?: string;\n  acceptLanguage?: string;\n  secFetchDest?: string;\n  secFetchMode?: string;\n  sgProof?: string;\n  sgOk?: string;\n  sgAuthorized?: string;\n  forwardedPrefix?: string;\n}\n\nexport interface BlockDecision {\n  block: true;\n  status: number;\n  contentType: string;\n  body: string;\n  headers?: Record<string, string>;\n}\n\nexport interface ShugoiCore {\n  csp: string;\n  cspEnabled: boolean;\n  ensureValidated(): Promise<void>;\n  isAllowlisted(path: string): boolean;\n  isWhitelistedBot(ua: string): boolean;\n  isTrustedBot(ua: string, ip: string): Promise<boolean>;\n  evaluate(ctx: EvaluateCtx): Promise<BlockDecision | null>;\n  isProofValid(proof: string): boolean;\n  sgOkCookie(proof: string, ip: string, ua: string): string | null;\n  log(...args: unknown[]): void;\n}\n\nfunction shieldPage(title: string, msg: string, badge: string, host: string, remainSecs: number, locale: Locale, userAgent: string): string {\n  const msgs = MESSAGES[locale]\n  const prefix = msg ? msg.replace(/Il reste \\d+ seconde?s?.*$/, '').replace(/Retry in \\d+s?.*$/, '').trim() : ''\n  const countdownScript = remainSecs > 0\n    ? '<script>var s=' + remainSecs + ';var i=setInterval(function(){s--;var e=document.getElementById(\"cd\");if(e){if(s<=0){e.innerHTML=\"0s\";clearInterval(i);setTimeout(function(){location.reload()},500)}else{e.innerHTML=s+\"s\"}}},1000)</script>'\n    : ''\n  const desc = remainSecs > 0\n    ? prefix + ' ' + msgs.retryInSeconds(remainSecs)\n    : (msg || '')\n  const htmlTitle = escapeHtml(title || msgs.blockedTitle)\n  const htmlBadge = escapeHtml(badge || msgs.blockedBadge)\n  const htmlHost = escapeHtml((host || 'shugoi.com').slice(0, 120))\n  const htmlDesc = escapeHtml(desc)\n  const htmlLang = locale === 'fr' ? 'fr' : 'en'\n  const palette = nativeBlockPalette(userAgent)\n  return `<!DOCTYPE html><html lang=\"${htmlLang}\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><meta name=\"color-scheme\" content=\"light dark\"><style>@font-face{font-family:'Reggae One';src:url(https://shugoi.com/reggae-one.woff2) format('woff2');font-display:swap}*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html,body{height:100%;background:${palette.light};color:#2b211d}body{font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;display:flex;align-items:center;justify-content:center;padding:1.2rem}#c{max-width:460px;width:100%;background:#fffdfa;border:1px solid rgba(43,33,29,.16);border-radius:16px 5px 16px 5px;box-shadow:0 10px 30px rgba(43,33,29,.08);padding:3rem 2.4rem 2.8rem;text-align:center}#c .l{width:80px;height:80px;pointer-events:none;filter:drop-shadow(2px 4px 8px rgba(231,112,144,.55));margin:0 auto .6rem;display:block}#c .b{display:block;margin:0 auto .2rem;pointer-events:none;max-width:100%;height:auto}#c .bdg{display:inline-block;background:#fdf0f4;border:1px solid rgba(194,84,111,.35);border-radius:10px 4px 10px 4px;padding:.3rem .9rem;font-size:.6rem;font-weight:600;text-transform:uppercase;letter-spacing:.16em;color:#a83d5a;margin-bottom:1.4rem}#c h2{font-family:'Reggae One',Georgia,'Times New Roman',serif;font-size:2.2rem;color:#a83d5a;font-weight:400;margin:0 auto .6rem}#c p.desc{font-size:.9rem;color:#7a6a62;line-height:1.8;max-width:380px;margin:0 auto}#c p.ft{font-size:.55rem;color:#a83d5a;margin-top:1.8rem}@media(prefers-color-scheme:dark){html,body{background:${palette.dark};color:#f1e8f5}#c{background:#241a30;border-color:rgba(241,232,245,.14);box-shadow:0 10px 30px rgba(0,0,0,.4)}#c .bdg{background:rgba(233,137,159,.16);border-color:rgba(233,137,159,.5);color:#e9899f}#c h2,#c p.ft{color:#e9899f}#c p.desc{color:#a795b4}}</style></head><body><div id=\"c\"><img src=\"https://shugoi.com/favicon-block.png\" alt=\"\" class=\"l\"><img src=\"https://shugoi.com/brand-block.png\" alt=\"Shugoi\" class=\"b\"><div class=\"bdg\">${htmlBadge}</div><h2>${htmlTitle}</h2><p class=\"desc\">${htmlDesc}</p><p class=\"ft\">${htmlHost} · Shugoi</p></div>${countdownScript}</body></html>`\n}\n\nexport function createCore(options: ShugoiCoreOptions): ShugoiCore {\n  const allowlist = options.allowlist ?? ['/api', '/legal']\n  const headlessPatterns = options.headlessPatterns ?? DEFAULT_HEADLESS_PATTERNS\n  const botWhitelist = options.botWhitelist ?? DEFAULT_BOT_WHITELIST\n  const baseUrl = options.baseUrl ?? 'https://shugoi.com/api/v1'\n  const debug = options.debug ?? false\n  const siteSecret = options.signingSecret || options.secret\n  const blockStatus = options.blockStatus ?? 403\n  const blockPage = options.blockPage ?? null\n  const cspEnabled = options.csp ?? true\n  const verifyBots = options.verifyBots !== false\n  let _validationValid = false\n  let _validationFailed = false\n  let _validationFailedReason = ''\n  let _validationWarnedAt = 0\n  const VALIDATION_WARN_INTERVAL = 3_600_000\n\n  const csp = buildCsp({ siteKey: options.siteKey, extraDirectives: options.extraDirectives || {}, splitRender: options.splitRender ?? true, apiOrigin: originOf(baseUrl) ?? undefined })\n\n  function log(...args: unknown[]) { if (debug) console.log('[shugoi]', ...args) }\n\n  // ═══ PoW anti-curl helpers (définis ici, utilisés par evaluate ET l'interface) ═══\n  // Audit #6 : difficulté configurable (SHUGOKI_POW_DIFF) au lieu d'une valeur en dur\n  // à 10. Le défaut remonte à 12 (4× plus dur que 10) tout en restant imperceptible à\n  // l'UX (~1-2k itérations crypto.subtle). Doit rester SYNCHRONE avec render.ts\n  // (generateSkeleton → __sg_pow.difficulty) et whitelist.ts côté site.\n  const POW_DIFF = (() => {\n    const raw = Number(process.env.SHUGOKI_POW_DIFF || \"14\");\n    return Number.isInteger(raw) && raw >= 8 && raw <= 24 ? raw : 12;\n  })();\n  const POW_OK_TTL_MS = 30 * 24 * 3600 * 1000; // cookie __sg_ok valable 30 jours\n  const powSecret = process.env.SHUGOKI_SIGNING_SECRET || process.env.SHUGOKI_SECRET || ''\n\n  // ═══ Fenêtre de validité d'une preuve PoW (audit 2026-08-03 #7 : rejeu) ═══\n  // Une preuve `sg_proof=ts:nonce` est acceptée si |now - ts| <= POW_TTL_MS. 120 s\n  // laissait une fenêtre de rejeu confortable ; 60 s suffit pour une navigation\n  // humaine (le solve est < 1 s) et réduit la durée de vie d'une preuve volée/rejouée.\n  const POW_TTL_MS = 60_000;\n\n  // ═══ Anti-scraping : rate-limit de l'émission du challenge (audit #6) ═══\n  // Le 307 anti-curl est STATELESS : sans limite, un script résout 20 challenges en\n  // série gratuitement (constaté par l'audit externe). On borne par IP :\n  //   - SHUGOKI_CHALLENGE_LIMIT = nombre max de challenges par fenêtre (défaut 60)\n  //   - SHUGOKI_CHALLENGE_WINDOW = fenêtre en secondes (défaut 60)\n  // Un SPA légitime déclenche 1-3 challenges par session (navigations full-page),\n  // 60/min ne le pénalise pas ; un scraper qui bourrine est freiné puis bloqué 429.\n  const CHALLENGE_LIMIT = (() => {\n    const raw = Number(process.env.SHUGOKI_CHALLENGE_LIMIT || \"60\");\n    return Number.isInteger(raw) && raw > 0 ? raw : 60;\n  })();\n  const CHALLENGE_WINDOW_MS = (() => {\n    const raw = Number(process.env.SHUGOKI_CHALLENGE_WINDOW || \"60\");\n    return Number.isInteger(raw) && raw > 0 ? raw * 1000 : 60_000;\n  })();\n  const CHALLENGE_MAX_BLOCK_MS = 15 * 60 * 1000; // plafond du blocage progressif\n  const _challengeLimits = new Map<string, { count: number; windowStart: number; blockedUntil: number }>();\n  setInterval(() => {\n    const now = Date.now();\n    for (const [k, e] of _challengeLimits) {\n      if (now > e.blockedUntil && now - e.windowStart > CHALLENGE_WINDOW_MS * 2) _challengeLimits.delete(k);\n    }\n  }, CHALLENGE_WINDOW_MS).unref();\n\n  // Retourne true si le challenge peut être émis pour cette IP. Au-delà du quota,\n  // un backoff exponentiel (2^n minutes, plafonné) s'applique : un scraper est\n  // ralenti à l'infini, un humain ne le ressent jamais (blocage ≥ 2^12 min).\n  function allowChallenge(ip: string): boolean {\n    if (!ip || ip === 'unknown') return true;\n    const now = Date.now();\n    let e = _challengeLimits.get(ip);\n    if (!e || now - e.windowStart >= CHALLENGE_WINDOW_MS) {\n      _challengeLimits.set(ip, { count: 1, windowStart: now, blockedUntil: 0 });\n      return true;\n    }\n    e.count++;\n    if (e.blockedUntil > now) return false;\n    if (e.count > CHALLENGE_LIMIT) {\n      const backoffMs = Math.min(60_000 * Math.pow(2, Math.min(e.count - CHALLENGE_LIMIT, 10)), CHALLENGE_MAX_BLOCK_MS);\n      e.blockedUntil = now + backoffMs;\n      e.count = 0;\n      return false;\n    }\n    return true;\n  }\n\n  // ═══ Preuve PoW single-use GLOBAL (round 17) ═══\n  // Une preuve `sg_proof=ts:nonce:solution` résolue ne doit servir QU'UNE fois, quel\n  // que soit l'IP : la preuve embarque un nonce aléatoire unique → clé = proof seule.\n  // (Round 16 : la clé `ip:proof` laissait un rejeu CROSS-IP → 200 : un script résolvait\n  // une fois et rejouait la preuve via botnet/rotation d'IP. Round 17 : la 1re requête\n  // qui présente la preuve la consomme, TOUTE autre requête (n'importe quel IP) → 307.\n  // Bonus NAT : deux visiteurs derrière le même IP ont des preuves distinctes → aucun\n  // cross-user). Entrées purgées après POW_TTL_MS (même fenêtre que la preuve).\n  const _usedProofs = new Map<string, number>();\n  setInterval(() => {\n    const now = Date.now();\n    for (const [k, t] of _usedProofs) {\n      if (now - t > POW_TTL_MS) _usedProofs.delete(k);\n    }\n  }, POW_TTL_MS).unref();\n  function consumeProof(proof: string): boolean {\n    if (_usedProofs.has(proof)) return false;\n    _usedProofs.set(proof, Date.now());\n    return true;\n  }\n\n  // ═══ Sanitisation du `path` du challenge (audit #5 : open redirect) ═══\n  // Le `path` reflété dans l'URL du challenge finit dans un `location.replace()` côté\n  // client. Un `//evil.com` (protocole-relatif) ou un backslash (`\\evil.com`, traité\n  // comme `/` par certains navigateurs) détournent la redirection vers un domaine\n  // externe. On n'accepte qu'un chemin relatif commençant par UN SEUL `/`.\n  function safeChallengePath(p: string): string {\n    if (!p) return '/';\n    if (p.charAt(0) !== '/' || p.charAt(1) === '/' || p.indexOf('\\\\') >= 0) return '/';\n    for (let i = 0; i < p.length; i++) {\n      const c = p.charCodeAt(i);\n      if (c < 0x20 || c === 0x7f) return '/';\n    }\n    return p;\n  }\n\n  function safeEqual(a: string, b: string): boolean {\n    if (a.length !== b.length) return false\n    const ba = Buffer.from(a, 'utf8')\n    const bb = Buffer.from(b, 'utf8')\n    return crypto.timingSafeEqual(ba, bb)\n  }\n\n  // ═══ Forteresse : sel RANDOM par challenge (nonce) + preuve ts:nonce:solution ═══\n  // L'ancien sel HMAC(secret, ts) était DÉTERMINISTE par seconde : le même sel pour\n  // tous les visiteurs de la seconde → un adversaire pouvait précalculer un lot de\n  // solutions et les rejouer via une botnet/rotation d'IP (la preuve single-use ne\n  // protège que d'un rejeu SAME-IP). Chaque challenge reçoit désormais un nonce\n  // aléatoire 64 bits : la preuve `ts:nonce:solution` est liée à SA challenge, la\n  // précomputation par lots devient impossible (chaque sollicitation = nouveau sel).\n  function sgNonce(): string {\n    return crypto.randomBytes(8).toString('hex')\n  }\n\n  // Bucket d'IP (sans ':' pour rester parseable dans le cookie). IPv4 → /24 (3 octets),\n  // IPv6 → 4 hextets. Une rotation d'IP dans le même sous-réseau garde le cookie valide.\n  function ipBucket(ip: string): string {\n    if (!ip || ip === 'unknown') return '0'\n    if (ip.includes('.')) {\n      const m = ip.match(/^(\\d+\\.\\d+\\.\\d+)(?:\\.\\d+)?$/)\n      if (m) return m[1]\n      return '0'\n    }\n    if (ip.includes(':')) {\n      const segs = ip.split(':').filter(Boolean)\n      return segs.slice(0, 4).join('.') || '0'\n    }\n    return '0'\n  }\n\n  // Empreinte UA (16 hex) — un changement de navigateur invalide le cookie → re-challenge.\n  function uaFp(ua: string): string {\n    return crypto.createHash('sha256').update(ua || '').digest('hex').slice(0, 16)\n  }\n\n  function isPowValid(proof: string): boolean {\n    if (!proof || !powSecret) return false\n    const parts = proof.split(':')\n    if (parts.length !== 3) return false\n    const [tsStr, nonce, sol] = parts\n    if (!tsStr || !nonce || !sol) return false\n    if (!/^[0-9a-f]{16}$/.test(nonce)) return false\n    const ts = parseInt(tsStr, 10)\n    if (isNaN(ts) || Math.abs(Date.now() - ts * 1000) > POW_TTL_MS) return false\n    const salt = crypto.createHmac('sha256', powSecret).update(tsStr + ':' + nonce).digest('hex')\n    const digest = crypto.createHash('sha256').update(salt + ':' + sol).digest('hex')\n    // Vérifie POW_DIFFICULTY bits à zéro en tête (en hex, chaque nibble = 4 bits).\n    // Audit 2026-08-03 : comptage CORRIGÉ — l'ancienne version sous-comptait les zéros\n    // internes du premier nibble non-nul (`3` → '11' → 0 au lieu de 2), ce qui rendait\n    // la difficulté effective ~2^12.5 au lieu de 2^14. Le comptage ci-dessous est exact\n    // et DOIT rester synchrone avec render.ts (challenge JS), whitelist.ts et le guard.\n    let leading = 0\n    for (let i = 0; i < digest.length; i++) {\n      const nib = parseInt(digest[i], 16)\n      if (nib === 0) { leading += 4; continue }\n      leading += (nib & 8) ? 0 : (nib & 4) ? 1 : (nib & 2) ? 2 : 3\n      break\n    }\n    return leading >= POW_DIFF\n  }\n\n  // Forteresse : le cookie __sg_ok est lié au bucket IP + empreinte UA. Un cookie\n  // volé/soustrait n'est plus rejouable depuis une autre IP (ou un autre navigateur) —\n  // la signature inclut ipBucket + uaFp. Format : ts:ipBucket:uaFp:sig.\n  function sgOkCookieValue(ip: string, ua: string): string {\n    const ts = Math.floor(Date.now() / 1000)\n    const bucket = ipBucket(ip)\n    const fp = uaFp(ua)\n    const sig = crypto.createHmac('sha256', powSecret).update('sg_ok:' + ts + ':' + bucket + ':' + fp).digest('hex')\n    return ts + ':' + bucket + ':' + fp + ':' + sig\n  }\n\n  function isSgOkValid(cookieVal: string, ip: string, ua: string): boolean {\n    if (!powSecret) return false\n    const parts = cookieVal.split(':')\n    if (parts.length !== 4) return false\n    const [tsStr, bucket, fp, sig] = parts\n    if (!tsStr || !bucket || !fp || !sig) return false\n    const ts = parseInt(tsStr, 10)\n    if (isNaN(ts) || Date.now() - ts * 1000 > POW_OK_TTL_MS || ts * 1000 > Date.now() + 60000) return false\n    // Lier au bucket IP + UA courants : un cookie d'une autre IP/UA → invalide.\n    if (bucket !== ipBucket(ip) || fp !== uaFp(ua)) return false\n    const expected = crypto.createHmac('sha256', powSecret).update('sg_ok:' + tsStr + ':' + bucket + ':' + fp).digest('hex')\n    return safeEqual(sig, expected)\n  }\n\n  // Cookie __sg_authorized posé par handleRender après un render réussi (grant valide).\n  // Protège les assets à contenu (/assets/*.js, *.css) : sans lui, le bundle JS est\n  // téléchargeable publiquement → extraction du contenu. TTL court (120s).\n  const SG_AUTHORIZED_TTL_MS = 120_000;\n  function isSgAuthorizedValid(cookieVal: string): boolean {\n    if (!powSecret) return false\n    const sep = cookieVal.indexOf(':')\n    if (sep <= 0) return false\n    const tsStr = cookieVal.slice(0, sep)\n    const sig = cookieVal.slice(sep + 1)\n    const ts = parseInt(tsStr, 10)\n    if (isNaN(ts) || Date.now() - ts * 1000 > SG_AUTHORIZED_TTL_MS || ts * 1000 > Date.now() + 60000) return false\n    const expected = crypto.createHmac('sha256', powSecret).update('sg_authorized:' + tsStr).digest('hex')\n    return safeEqual(sig, expected)\n  }\n\n  const validationPromise: Promise<void> = (async () => {\n    if (siteSecret && baseUrl) {\n      try {\n        const res = await fetch(baseUrl + '/validate-key', {\n          method: 'POST',\n          headers: { 'Content-Type': 'application/json' },\n          body: JSON.stringify({ siteKey: options.siteKey, secret: siteSecret }),\n          signal: AbortSignal.timeout(5000)\n        })\n        if (res.ok) {\n          const data = await res.json() as Record<string, unknown>\n          if (data.valid) {\n            _validationValid = true\n            if (debug) console.log('[shugoi] key validation OK')\n          } else {\n            _validationFailed = true\n            _validationFailedReason = String(data.reason || 'invalid')\n          }\n        } else {\n          _validationFailed = true\n          _validationFailedReason = 'HTTP ' + res.status\n        }\n      } catch (e) {\n        _validationFailed = true\n        _validationFailedReason = 'réseau: ' + (e instanceof Error ? e.message : String(e))\n      }\n    } else if (debug) {\n      console.log('[shugoi] no secret provided, skipping key validation')\n    }\n  })()\n\n  ensureGuardsReady(baseUrl, siteSecret, options.siteKey).catch(() => {})\n\n  async function ensureValidated(): Promise<void> {\n    if (!siteSecret) return\n    if (_validationValid || _validationFailed) return\n    await Promise.race([\n      validationPromise,\n      new Promise<void>((r) => setTimeout(r, 500)),\n    ])\n  }\n\n  function isAllowlisted(path: string): boolean {\n    return allowlist.some(p => path === p || path.startsWith(p + '/'))\n  }\n\n  function isWhitelistedBot(ua: string): boolean {\n    return botWhitelist.some(p => p.test(ua))\n  }\n  // Allowlist IP EMPIRIQUE (env SHUGOKI_BOT_IPS, virgules) : Discord fetch les embeds\n  // depuis des IP Google Cloud (reverse-DNS non Discord) → on autorise par IP observée\n  // dans les logs (\"[shugoi] bot_ua ip=…\"). Vérif : UA whitelisté + IP dans la liste.\n  const botIpList = new Set((process.env.SHUGOKI_BOT_IPS || '').split(',').map(s => s.trim()).filter(Boolean))\n  // Bypass bot SÉCURISÉ : les bots dont l'IP est VÉRIFIABLE (reverse-DNS : Google, Bing,\n  // Yandex, DuckDuckGo, Apple, Discord…) exigent UA + IP confirmée — un curl qui imite\n  // leur UA depuis une IP aléatoire est traité comme un visiteur normal (F1/challenge).\n  // Les bots sociaux SANS config de vérif d'IP (facebookexternalhit, Twitterbot…)\n  // restent UA-only (compromis assumé, à durcir au fur et à mesure).\n  async function botBypass(ua: string, ip: string): Promise<boolean> {\n    if (!isWhitelistedBot(ua)) return false\n    // Diagnostic : journaliser les IP des bots (Discord, Twitter…) pour construire une\n    // allowlist IP EMPIRIQUE (le reverse-DNS de Discord doit être confirmé — les embeds\n    // peuvent passer par un CDN qui fait varier l'IP). Grep: 'shugoi] bot_ua'.\n    if (options.logBotIps !== false) {\n      console.log('[shugoi] bot_ua ip=' + ip + ' ua=' + String(ua).slice(0, 50))\n    }\n    if (VERIFIABLE_BOTS.some(p => p.test(ua))) return await isTrustedBot(ua, ip)\n    return true\n  }\n\n  async function isTrustedBot(ua: string, ip: string): Promise<boolean> {\n    if (!isWhitelistedBot(ua)) return false\n    if (!verifyBots) return true\n    if (botIpList.has(ip)) return true\n    const verified = await verifyBotIp(ua, ip)\n    if (verified === null) return false\n    return verified\n  }\n\n  async function evaluate(ctx: EvaluateCtx): Promise<BlockDecision | null> {\n    // botBypass peut coûter un DNS reverse (verifyBotIp) : on le mémoïse par appel\n    // evaluate (jusqu'à 3 sites d'appel : fake-browser, proof, headless → 1 seul).\n    // Le cache DNS global (1h) couvre l'inter-requêtes ; ici c'est l'intra-requête.\n    let _bypassMemo: boolean | null = null;\n    const botBypassOnce = async (ua: string, ip: string): Promise<boolean> => {\n      if (_bypassMemo !== null) return _bypassMemo;\n      _bypassMemo = await botBypass(ua, ip);\n      return _bypassMemo;\n    };\n    await ensureValidated()\n    if (siteSecret && _validationFailed && Date.now() - _validationWarnedAt > VALIDATION_WARN_INTERVAL) {\n      _validationWarnedAt = Date.now()\n      console.warn(\n        '[shugoi] La validation de la clé a échoué (' + (_validationFailedReason || 'raison inconnue') + ').\\n' +\n        '[shugoi] La protection reste active, mais cette installation n\\'est pas authentifiée.\\n' +\n        '[shugoi] Vérifiez `siteKey` et `secret` : https://shugoi.com/docs#validation'\n      )\n      fetch(baseUrl + '/event', {\n        method: 'POST', headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify({ siteKey: options.siteKey, reason: 'validation_failed' }),\n        signal: AbortSignal.timeout(2000),\n      }).catch(() => {})\n    }\n\n    // ═══ Protection des assets à contenu (/assets/*.js, *.css) ═══\n    // Le bundle SPA contient les textes/structure de la page. Servi publiquement, il\n    // permet d'extraire tout le contenu sans passer la whitelist (audit). On exige donc\n    // le cookie __sg_authorized (posé par handleRender après un render réussi) pour le\n    // télécharger. Un curl direct / un non-validé reçoit le tableau BLOCKED.\n    // NB : on vérifie AVANT isAllowlisted (les assets sont allowlistés pour le split-render).\n    if (/\\/assets\\/[^?#]+\\.(js|css)(\\?|$)/.test(ctx.path)) {\n      const authOk = !!ctx.sgAuthorized && isSgAuthorizedValid(ctx.sgAuthorized)\n      if (!authOk) {\n        log('asset protégé refusé:', ctx.path.slice(0, 60))\n        return { block: true, status: 403, contentType: 'text/plain', body: BLOCK_PAGE, headers: {} }\n      }\n    }\n\n    if (isAllowlisted(ctx.path)) return null\n\n    // ═══ Route du challenge JS (suit le 307 anti-curl) ═══\n    // Le navigateur arrive ici après le 307. Body : le tableau ASCII dans un COMMENTAIRE\n    // HTML (<!-- -->) — le view-source le montre, mais le navigateur ne le peint PAS :\n    // pas de flash pendant la résolution PoW. Le JS de résolution est INLINE (économise\n    // un aller-retour réseau : pas de <script src> externe → chargement plus rapide).\n    if (ctx.path === '/__sg_challenge') {\n      // Anti-scraping (audit #6) : la page de challenge est aussi bornée par IP —\n      // un script peut la requêter directement sans passer par le 307.\n      if (!allowChallenge(ctx.ip)) {\n        const loc = resolveLocale(undefined, ctx.acceptLanguage)\n        const lmsgs = MESSAGES[loc]\n        return { block: true, status: 429, contentType: 'text/html', body: shieldPage(lmsgs.rateLimitTitle, lmsgs.rateLimitBody('1 min'), lmsgs.rateLimitBadge, ctx.host || '', 60, loc, ctx.ua || '') }\n      }\n      const js = `(function(){\nvar P=new URLSearchParams(location.search);\nvar salt=P.get('salt')||'', ts=P.get('ts')||'', nonce=P.get('nonce')||'', diff=parseInt(P.get('diff')||'14',10), path=P.get('path')||'/';\n// Open redirect (audit #5) : un //evil.com (protocole-relatif) ou un backslash\n// détourneraient le location.replace ci-dessous vers un domaine externe. On n'accepte\n// qu'un chemin relatif commençant par UN SEUL '/', sans backslash ni contrôle.\nif(path.charAt(0)!=='/'||path.charAt(1)==='/'||path.indexOf('\\\\\\\\')>=0)path='/';\nvar enc=new TextEncoder();\n// Audit 2026-08-03 : comptage de bits CORRIGÉ (zéros internes du premier nibble\n// non-nul comptés) — DOIT rester synchrone avec isPowValid serveur + guard + whitelist.\nfunction bits(d){var l=0;for(var i=0;i<d.length;i++){var b=parseInt(d[i],16);if(b===0){l+=4;continue}l+=(b&8)?0:(b&4)?1:(b&2)?2:3;break}return l}\nvar n=0;\nfunction step(){\n  crypto.subtle.digest('SHA-256',enc.encode(salt+':'+n.toString(16))).then(function(buf){\n    var h=Array.from(new Uint8Array(buf)).map(function(v){return v.toString(16).padStart(2,'0')}).join('');\n    if(bits(h)>=diff){var base=path;var q=(base.indexOf('?')>=0?'&':'?')+'sg_proof='+ts+':'+nonce+':'+n.toString(16);location.replace(base+q)}\n    else{n++;if(n<300000)step()}\n  }).catch(function(){location.reload()});\n}\nstep();\n})();`\n      const html = '<!--\\n' + BLOCK_PAGE + '-->\\n' + '<script>' + js + '</script>'\n      return { block: true, status: 200, contentType: 'text/html', body: html }\n    }\n\n    // ═══ Pre-flight PoW challenge (anti-curl/view-source) ═══\n    // Un 307 dont le corps est UNIQUEMENT le tableau ASCII : curl le voit en clair,\n    // le navigateur suit la redirection vers /__sg_challenge (le JS qui résout le PoW).\n    // Le challenge s'applique à TOUTE page sans sg_proof valide — même un navigateur avec\n    // cookie : le view-source (qui n'exécute pas le JS) voit donc toujours le tableau.\n    // Aucune exclusion d'extension : le catch-all SPA renvoie index.html pour TOUT chemin\n    // (y compris /index.js, /app.js, /__shugoi.js) → ils doivent être challengés aussi,\n    // sinon un curl les obtient sans PoW (fallback skeleton). Les vrais assets statiques\n    // (/assets/*, /robots.txt...) sont allowlisted et ne passent pas par ici.\n    const isPage = !ctx.path.includes('/__shugoi/') && !ctx.path.startsWith('/api/')\n\n    if (isPage && powSecret && ctx.ua) {\n      // Divulgation F1 : faux navigateur (UA navigateur mais ni Accept-Language ni\n      // Sec-Fetch-*) → 403 immédiat avec page dédiée, AVANT le challenge (sinon un curl\n      // reçoit le 307 avant ce check). Les navigateurs réels (Chrome/Firefox/Safari, y\n      // compris Tor Browser Firefox-based) envoient toujours ces headers. Le cas \"navigateur\n      // réel + Tor\" (IP = nœud de sortie, avec Sec-Fetch) reste géré par /wlc → card\n      // \"Tor détecté\".\n      // Le F1 vise les FAUX navigateurs (curl avec un UA navigateur). Un bot WHITELISTÉ\n      // (ex. Discordbot dont l'UA réel est \"Mozilla/5.0 (compatible; Discordbot/2.0; …)\")\n      // ne doit PAS être bloqué par ce check même sans IP vérifiée : il n'envoie pas de\n      // Sec-Fetch/Accept-Language. Exemption = isWhitelistedBot (même logique que le bypass\n      // challenge). Un curl qui imiterait Discordbot n'y gagne rien : il obtient la page\n      // publique, et l'anti-curl réel (rate-limit + grant + whitelist) reste en place.\n      if (/Mozilla/i.test(ctx.ua) && !(await botBypassOnce(ctx.ua, ctx.ip))) {\n        const sfd = ctx.secFetchDest ?? ''\n        const sfm = ctx.secFetchMode ?? ''\n        const al = ctx.acceptLanguage ?? ''\n        if (!al && !sfd && !sfm) {\n          log('fake browser (Accept-Language + Sec-Fetch absents) → 403 block page:', ctx.ua.slice(0, 40))\n          const bloc = resolveLocale(undefined, ctx.acceptLanguage)\n          const lmsgs = MESSAGES[bloc]\n          return { block: true, status: 403, contentType: 'text/html', body: shieldPage(lmsgs.fakeBrowserTitle, lmsgs.fakeBrowserBody, lmsgs.fakeBrowserBadge, ctx.host || '', 0, bloc, ctx.ua || '') }\n        }\n      }\n      const proof = ctx.sgProof || ''\n      const validProof = !!proof && isPowValid(proof)\n      // Re-audit (résidu #3) : un cookie __sg_ok valide (HMAC serveur, 30 j) saute le\n      // pre-flight PoW. Il est posé APRÈS une première résolution réussie (middleware).\n      // Bénéfices :\n      //   - UX : 1 PoW par navigateur/30 j au lieu d'un par chargement de page ;\n      //   - anti-DoS NAT : un IP partagé (entreprise/VPN) ne consomme plus le quota de\n      //     challenge à chaque utilisateur — seuls les visiteurs sans cookie challengent.\n      // Un script doit de toute façon résoudre le PoW une première fois pour obtenir le\n      // cookie, puis le vrai verrou reste le render-grant (wlc + raw + mid).\n      const validCookie = !!ctx.sgOk && isSgOkValid(ctx.sgOk, ctx.ip, ctx.ua)\n      // Round 16 (R2) : la preuve est SINGLE-USE (par IP). Une seule résolution ne doit\n      // pas permettre de mint des cookies sur plusieurs chemins/sessions. Une preuve\n      // valide est CONSOMMÉE à sa 1re utilisation ; un rejeu (sans cookie) → 307.\n      const proofFresh = validProof ? consumeProof(proof) : false\n      const canProceed = validCookie || proofFresh\n      if (!canProceed && !(await botBypassOnce(ctx.ua, ctx.ip))) {\n        // SEO / aperçus sociaux : un crawler légitime NE PEUT PAS exécuter le PoW JS.\n        // Les bots whitelistés (moteurs de recherche + bots de partage social :\n        // googlebot, bingbot, facebookexternalhit, twitterbot, linkedinbot, discordbot,\n        // whatsapp, telegram…) bypassent le challenge → la page HTML (og:image, contenu\n        // indexable) leur est servie. Le vrai verrou reste le render-grant + la whitelist\n        // (round 16 : la difficulté du PoW n'est pas la barrière — un script le résout\n        // en ~0,15 s ; il ne sert que de premier filtre anti-curl). `verifyBots` conserve\n        // la vérification DNS inverse pour les moteurs (checks F1 / headless).\n        // Anti-scraping (audit #6) : on refuse d'émettre le challenge à un IP qui\n        // bourrine (solve en série). 429 shield au lieu du 307 — un humain ne le\n        // ressent jamais (quota 60/fenêtre), un scraper est ralenti indéfiniment.\n        if (!allowChallenge(ctx.ip)) {\n          const loc = resolveLocale(undefined, ctx.acceptLanguage)\n          const lmsgs = MESSAGES[loc]\n          log('challenge rate-limited:', ctx.ip.slice(0, 24), ctx.ua.slice(0, 40))\n          return { block: true, status: 429, contentType: 'text/html', body: shieldPage(lmsgs.rateLimitTitle, lmsgs.rateLimitBody('1 min'), lmsgs.rateLimitBadge, ctx.host || '', 60, loc, ctx.ua || '') }\n        }\n        // 307 vers le challenge : body = tableau ASCII SEUL (curl le voit tel quel).\n        // Le navigateur suit la redirection → /__sg_challenge?ts=&salt=&diff=&path=\n        // Le prefix X-Forwarded-Prefix (ex. /express derrière un reverse proxy) est\n        // préfixé pour que la redirection reste dans le sous-chemin de la démo.\n        const tsNow = Math.floor(Date.now() / 1000)\n        // Forteresse : sel RANDOM par requête (nonce 64 bits) — plus de sel déterministe\n        // par seconde (précomputation par lots impossible). La preuve devient ts:nonce:n.\n        const nonce = sgNonce()\n        const salt = crypto.createHmac('sha256', powSecret).update(tsNow + ':' + nonce).digest('hex')\n        const prefix = ctx.forwardedPrefix && ctx.forwardedPrefix !== '/' ? ctx.forwardedPrefix.replace(/\\/$/, '') : ''\n        // Sanitisation open redirect (audit #5) : on ne reflète jamais un path brut.\n        const path = safeChallengePath(ctx.path.startsWith('/') ? ctx.path : '/' + ctx.path)\n        const chalUrl = prefix + '/__sg_challenge?ts=' + tsNow + '&salt=' + salt + '&nonce=' + nonce + '&diff=' + POW_DIFF + '&path=' + encodeURIComponent(prefix + path)\n        log('pow challenge (307):', ctx.ua.slice(0, 40))\n        return { block: true, status: 307, contentType: 'text/plain', body: BLOCK_PAGE, headers: { Location: chalUrl } }\n      }\n      // sgProof valide → on laisse passer (le middleware posera le cookie).\n    }\n\n    const flags = await fetchConfigForSiteKey(options.siteKey, baseUrl, options.signingSecret || options.secret)\n\n    // Le blocage headless est actif par défaut, y compris sans configuration chargée :\n    // c'est la protection minimale attendue du produit.\n    const headlessEnabled = flags.enableHeadlessCheck !== false\n\n    // Rate limit check — activé uniquement si le flag est explicitement vrai\n    if (flags.enableRateLimit === true) {\n      try {\n        const rlRes = await fetch(baseUrl + '/rate-limit-check', {\n          method: 'POST',\n          headers: { 'Content-Type': 'application/json' },\n          body: JSON.stringify({\n            siteKey: options.siteKey,\n            scope: 'edge_ip',\n            ip: ctx.ip,\n            metadata: { ip: ctx.ip, userAgent: ctx.ua || '', middleware: true },\n          }),\n          signal: AbortSignal.timeout(2000)\n        })\n        if (rlRes.ok) {\n          const rlData = await rlRes.json() as Record<string, unknown>\n          if (rlData.allowed === false) {\n            const remain = Math.max(0, Math.ceil(((rlData.resetAt as number) - Date.now()) / 1000))\n            const mins = Math.floor(remain / 60)\n            const secs = remain % 60\n            const timeStr = mins > 0 ? mins + ' min' + (mins > 1 ? 's' : '') + (secs > 0 ? ' ' + secs + ' s' : '') : secs + ' seconde' + (secs > 1 ? 's' : '')\n            const loc = options.locale ?? resolveLocale(undefined, ctx.acceptLanguage)\n            const msgs = MESSAGES[loc]\n            if (blockPage) {\n              return { block: true, status: 429, contentType: 'text/html', body: blockPage({ reason: 'rate_limit', title: msgs.rateLimitTitle, message: msgs.rateLimitBody(timeStr), badge: msgs.rateLimitBadge, host: ctx.host || '', remainingSeconds: remain, locale: loc }) }\n            }\n            return { block: true, status: 429, contentType: 'text/html', body: shieldPage(msgs.rateLimitTitle, msgs.rateLimitBody(timeStr), msgs.rateLimitBadge, ctx.host || '', remain, loc, ctx.ua || '') }\n          }\n        }\n      } catch {}\n    }\n\n    // Headless UA block\n    // Headless UA block — patterns d'abord (test regex synchrone pas cher) puis\n    // botBypass (peut coûter un DNS) : on évite le DNS pour les UA non-headless.\n    if (headlessEnabled && ctx.ua && headlessPatterns.some(p => p.test(ctx.ua)) && !(await botBypassOnce(ctx.ua, ctx.ip))) {\n      log('headless block:', ctx.ua.slice(0, 40))\n      fetch(baseUrl + '/event', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ siteKey: options.siteKey, reason: 'headless' }), signal: AbortSignal.timeout(2000) }).catch(() => {})\n      return { block: true, status: blockStatus, contentType: 'text/plain', body: BLOCK_PAGE }\n    }\n\n    return null\n  }\n\n  return { csp, cspEnabled, ensureValidated, isAllowlisted, isWhitelistedBot, isTrustedBot, evaluate, log,\n    isProofValid: isPowValid,\n    sgOkCookie(proof: string, ip: string, ua: string): string | null {\n      if (!proof || !isPowValid(proof)) return null\n      return '__sg_ok=' + sgOkCookieValue(ip, ua) + '; Path=/; HttpOnly; SameSite=Lax; Max-Age=' + Math.floor(POW_OK_TTL_MS / 1000) + (process.env.NODE_ENV === 'production' ? '; Secure' : '')\n    }\n  }\n}\n","import { promises as dns } from 'dns';\n\nconst BOT_DOMAINS: Array<{ pattern: RegExp; suffixes: string[] }> = [\n  { pattern: /Googlebot|Google-InspectionTool|Storebot-Google/i, suffixes: ['.googlebot.com', '.google.com'] },\n  { pattern: /Bingbot|adidxbot|BingPreview/i, suffixes: ['.search.msn.com'] },\n  { pattern: /Slurp/i, suffixes: ['.crawl.yahoo.net'] },\n  { pattern: /DuckDuckBot/i, suffixes: ['.duckduckgo.com'] },\n  { pattern: /YandexBot/i, suffixes: ['.yandex.ru', '.yandex.net', '.yandex.com'] },\n  { pattern: /Applebot/i, suffixes: ['.applebot.apple.com'] },\n  // Discord (embeds) : UA réel = \"Mozilla/5.0 (compatible; Discordbot/2.0; …)\". Le suffixe\n  // reverse-DNS doit être confirmé empiriquement (IP des serveurs Discord). Twitter à venir.\n  { pattern: /Discordbot/i, suffixes: ['.discord.gg', '.discord.com', '.discordapp.com'] },\n];\n\n/** Bots pour lesquels le bypass exige UA + IP vérifiée (reverse-DNS), pas l'UA seul. */\nexport const VERIFIABLE_BOTS: RegExp[] = BOT_DOMAINS.map((b) => b.pattern);\n\nconst VERIFY_TTL = 3_600_000;\nconst MAX_ENTRIES = 5_000;\nconst _cache = new Map<string, { ok: boolean; at: number }>();\n\nexport async function verifyBotIp(ua: string, ip: string): Promise<boolean | null> {\n  const entry = BOT_DOMAINS.find((b) => b.pattern.test(ua));\n  if (!entry) return null;\n  if (!ip || ip === 'unknown') return false;\n\n  const key = ip + '|' + entry.suffixes[0];\n  const hit = _cache.get(key);\n  if (hit && Date.now() - hit.at < VERIFY_TTL) return hit.ok;\n\n  let ok = false;\n  try {\n    const names = await dns.reverse(ip);\n    const name = names.find((n) => entry.suffixes.some((s) => n.toLowerCase().endsWith(s)));\n    if (name) {\n      const forward = await dns.resolve(name).catch(() => [] as string[]);\n      const forward6 = await dns.resolve6(name).catch(() => [] as string[]);\n      ok = forward.includes(ip) || forward6.includes(ip);\n    }\n  } catch { ok = false; }\n\n  // Éviction O(1) : Map en ordre d'insertion → on éjecte la plus ancienne clé\n  // au lieu de trier tout le cache en O(n log n) à chaque insert au-delà du max.\n  if (_cache.size >= MAX_ENTRIES) {\n    const oldest = _cache.keys().next();\n    if (!oldest.done) _cache.delete(oldest.value);\n  }\n  _cache.set(key, { ok, at: Date.now() });\n  return ok;\n}\n","import { ShugoiError } from './errors';\nimport type { CheckResponse } from './types';\n\n/**\n * Options for {@link checkLicense}.\n */\nexport interface CheckLicenseOptions {\n  /** Shugoi siteKey */\n  siteKey: string;\n  /** Action (e.g. \"signup\", \"login\", \"download\") */\n  action: string;\n  /** Machine ID (SHA-256 of browser fingerprint) */\n  machineId?: string;\n  /** Additional fingerprint signals */\n  signals?: Record<string, unknown>;\n  /** Captcha token (if required by API) */\n  captchaToken?: string;\n  /** Pass token */\n  passToken?: string;\n  /** Request metadata (IP, email) */\n  metadata?: { ip?: string; email?: string };\n  /** API base URL */\n  baseUrl?: string;\n  /** Request timeout in ms */\n  timeout?: number;\n  /** User-Agent for server-to-server requests */\n  serverUa?: string;\n}\n\n/**\n * Checks a license against the Shugoi API.\n *\n * Typed wrapper around `POST /api/v1/check` with error handling,\n * timeout, and normalized error codes.\n *\n * @param options - Check parameters\n * @returns Shugoi API response\n * @throws {ShugoiError} If the key is invalid, API unreachable, or timeout\n *\n * @example\n * ```ts\n * const result = await checkLicense({\n *   siteKey: 'sg_sk_live_xxx',\n *   action: 'signup',\n *   machineId: window.machineId,\n * });\n *\n * if (result.blocked) {\n *   return res.redirect(`https://shugoi.com/api/v1/block?reason=${result.blocked_reason}`);\n * }\n * ```\n */\nexport async function checkLicense(\n  options: CheckLicenseOptions\n): Promise<CheckResponse> {\n  const baseUrl = options.baseUrl ?? 'https://shugoi.com/api/v1';\n  const timeout = options.timeout ?? 5000;\n  const serverUa = options.serverUa ?? 'ShugoiNode/0.1.0';\n\n  const body = {\n    siteKey: options.siteKey,\n    action: options.action,\n    fingerprint: {\n      machineId: options.machineId,\n    },\n    signals: options.signals ?? {},\n    captchaToken: options.captchaToken,\n    passToken: options.passToken,\n    metadata: options.metadata,\n  };\n\n  try {\n    const controller = new AbortController();\n    const timer = setTimeout(() => controller.abort(), timeout);\n\n    const res = await fetch(`${baseUrl}/check`, {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        'User-Agent': serverUa,\n      },\n      body: JSON.stringify(body),\n      signal: controller.signal,\n    });\n    clearTimeout(timer);\n\n    const data = await res.json() as CheckResponse;\n\n    if (data.error === 'invalid_site_key') {\n      throw new ShugoiError('invalid_site_key', `Invalid siteKey: ${options.siteKey}`);\n    }\n    if (!res.ok) {\n      throw new ShugoiError(\n        'unexpected_api_response',\n        `API returned status ${res.status}: ${JSON.stringify(data)}`\n      );\n    }\n\n    return data;\n  } catch (err) {\n    if (err instanceof ShugoiError) throw err;\n    if (err instanceof Error && err.name === 'AbortError') {\n      throw new ShugoiError('api_timeout', 'Shugoi API request timed out', err);\n    }\n    throw new ShugoiError('api_unreachable', 'Shugoi API unreachable', err);\n  }\n}\n","import type { ScriptTagsOptions, ScriptTagsResult } from './types'\nimport { signToken, generateSkeleton } from './render'\n\n/** Generates script tags for manual Shugoi integration. */\nexport async function scriptTags(options: ScriptTagsOptions): Promise<ScriptTagsResult> {\n  const base = options.baseUrl ?? 'https://shugoi.com/api/v1';\n  const key = options.siteKey;\n  const ts = Date.now();\n  const signed = signToken(key, ts, options.signingSecret);\n  const skel = await generateSkeleton(key, signed.token, base, options.restrictedAccess ?? false, options.whitelist, undefined, undefined, undefined, undefined, options.signingSecret, options.timingLogs ?? false);\n  return { guardDetect: skel, guard: '', whitelistConfig: '', token: signed.token };\n}\n","import { ShugoiError } from './errors';\nimport type { CheckResponse } from './types';\n\n/**\n * Options for {@link validateSiteKey}.\n */\nexport interface ValidateSiteKeyOptions {\n  /** SiteKey to validate */\n  siteKey: string;\n  /** API base URL */\n  baseUrl?: string;\n  /** Timeout in ms */\n  timeout?: number;\n}\n\n/**\n * Validates a siteKey by calling the Shugoi API.\n *\n * Sends a lightweight request to `/api/v1/check` with the siteKey\n * and checks that the API does not return `invalid_site_key`.\n *\n * @param options - Validation options\n * @returns Validation result with validity and mode\n * @throws {ShugoiError} If the API is unreachable\n */\nexport async function validateSiteKey(\n  options: ValidateSiteKeyOptions\n): Promise<{ valid: boolean; mode?: 'live' | 'test'; error?: string }> {\n  const baseUrl = options.baseUrl ?? 'https://shugoi.com/api/v1';\n  const timeout = options.timeout ?? 5000;\n\n  try {\n    const controller = new AbortController();\n    const timer = setTimeout(() => controller.abort(), timeout);\n\n    const res = await fetch(`${baseUrl}/check`, {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        'User-Agent': 'ShugoiNode/0.1.0',\n      },\n      body: JSON.stringify({\n        siteKey: options.siteKey,\n        action: 'validate',\n        fingerprint: {},\n        signals: {},\n      }),\n      signal: controller.signal,\n    });\n    clearTimeout(timer);\n\n    const data = await res.json() as CheckResponse;\n\n    if (data.error === 'invalid_site_key') {\n      return { valid: false, error: 'invalid_site_key' };\n    }\n\n    const mode = options.siteKey.startsWith('sg_sk_test_') ? 'test' : 'live';\n    return { valid: true, mode };\n  } catch (err) {\n    if (err instanceof ShugoiError) throw err;\n    if (err instanceof Error && err.name === 'AbortError') {\n      throw new ShugoiError('api_timeout', 'API request timed out', err);\n    }\n    throw new ShugoiError('api_unreachable', 'Shugoi API unreachable', err);\n  }\n}\n"],"mappings":"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","names":["crypto","MAX_ENTRIES","dns","crypto","handleRender","ip","renderResponseData","injectReferrerPolicy","ct"]}