{"version":3,"sources":["../../src/next/index.ts","../../src/csp.ts","../../src/next/with-shugoi.ts","../../src/next/proxy.ts","../../src/render.ts","../../src/locales.ts","../../src/block-palette.ts","../../src/next/middleware.ts","../../src/next/guard-component.ts"],"sourcesContent":["export { withShugoi } from './with-shugoi';\nexport { createShugoiProxy } from './proxy';\nexport { createShugoiNextMiddleware } from './middleware';\nexport { generateGuardHtml } from './guard-component';\n\nexport const SHUGOI_MATCHER = '/((?!_next/static|_next/image|favicon.ico).*)';\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","import { buildCsp } from '../csp';\n\n/**\n * Options for the Next.js Shugoi plugin.\n */\nexport interface WithShugoiOptions {\n  /** Shugoi siteKey */\n  siteKey: string;\n  /**\n   * Local whitelist of machine IDs.\n   * Empty array `[]` allows all machines.\n   */\n  whitelist?: string[];\n  /**\n   * Additional paths for the proxy allowlist.\n   * @default [\"/legal\"]\n   */\n  allowlist?: string[];\n  /** API base URL */\n  baseUrl?: string;\n}\n\n/**\n * Next.js config wrapper that adds Shugoi CSP headers.\n *\n * ```ts\n * // next.config.ts\n * import { withShugoi } from 'shugoi/next';\n * export default withShugoi({ siteKey: 'sg_sk_live_xxx' });\n * ```\n *\n * Add guard scripts in your layout:\n * ```tsx\n * // app/layout.tsx\n * import { scriptTags } from 'shugoi';\n * const { whitelistConfig, guardDetect, guard } = scriptTags({\n *   siteKey: 'sg_sk_live_xxx',\n *   whitelist: [],\n * });\n * // head: whitelistConfig + guardDetect\n * // body: guard\n * ```\n */\nexport function withShugoi(\n  opts: WithShugoiOptions,\n  nextConfig: Record<string, unknown> = {},\n): Record<string, unknown> {\n  const csp = buildCsp({ siteKey: opts.siteKey });\n\n  return {\n    ...nextConfig,\n    async headers() {\n      const existingHeaders = typeof nextConfig.headers === 'function'\n        ? await (nextConfig as any).headers()\n        : Array.isArray(nextConfig.headers)\n          ? nextConfig.headers\n          : [];\n\n      return [\n        ...existingHeaders,\n        {\n          source: '/(.*)',\n          headers: [\n            {\n              key: 'Content-Security-Policy',\n              value: csp,\n            },\n          ],\n        },\n      ];\n    },\n  };\n}\n","import { NextResponse } from \"next/server.js\";\nimport type { NextRequest } from \"next/server.js\";\nimport { injectGuardScripts } from \"../render\";\n\nexport interface ShugoiProxyOptions {\n  siteKey: string;\n  allowlist?: string[];\n  whitelist?: string[];\n  headlessPatterns?: RegExp[];\n  /** Internal URL to fetch page content (e.g. \"http://127.0.0.1:3009\") */\n  target?: string;\n  /** Affiche les mesures de performance `[SHUGOI-TIMING]` dans le navigateur. Défaut : false */\n  timingLogs?: boolean;\n}\n\nconst DEFAULT_HEADLESS = [\n  /^curl/i, /^wget/i, /^python/i, /^Go-http-client/i,\n  /^Java\\//, /HTTPie/i, /^node-fetch/i, /axios/i,\n  /^okhttp/i, /^scrapy/i, /PowerShell/i, /WinHttp/i,\n];\nconst BASE_URL = \"https://shugoi.com/api/v1\";\n\nexport function createShugoiProxy(options: ShugoiProxyOptions) {\n  const { siteKey, target } = options;\n  const allowlist = options.allowlist ?? [\"/legal\"];\n  const headless = options.headlessPatterns ?? DEFAULT_HEADLESS;\n\n  return async function proxy(request: NextRequest) {\n    const path = request.nextUrl.pathname;\n    const accept = request.headers.get(\"accept\") || \"\";\n\n    // Prevent recursion on internal sub-requests\n    if (request.headers.get(\"x-shugoi-internal\") === \"1\")\n      return NextResponse.next();\n\n    if (path.startsWith(\"/_next/\") || path.startsWith(\"/api/\"))\n      return NextResponse.next();\n    if (allowlist.some((p) => path === p || path.startsWith(p + \"/\")))\n      return NextResponse.next();\n\n    const ua = request.headers.get(\"user-agent\") || \"\";\n    if (headless.some((p) => p.test(ua))) {\n      const block = [\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        \"|  - contact: support@shugoi.com -            |\",\n        \"+---------------------------------------------+\",\n      ].join('\\n') + '\\n';\n      return new NextResponse(block, { status: 403 });\n    }\n\n    // Split-render: inject guard skeleton for HTML pages\n    if (!accept.includes(\"text/html\")) return NextResponse.next();\n\n    try {\n      const fetchUrl = target ? target + path : new URL(path, request.url).toString();\n      const pageRes = await fetch(fetchUrl, {\n        headers: {\n          accept: \"text/html\",\n          \"user-agent\": \"Shugoi\",\n          cookie: request.headers.get(\"cookie\") || \"\",\n          \"x-shugoi-internal\": \"1\",\n        },\n        signal: AbortSignal.timeout(10000),\n      });\n\n      if (!pageRes.ok) return NextResponse.next();\n\n      const html = await pageRes.text();\n\n      const skeleton = await injectGuardScripts(\n        html, siteKey, BASE_URL, undefined, false, undefined,\n        { url: path }, undefined, undefined, undefined, options.timingLogs ?? false\n      );\n\n      return new NextResponse(skeleton, {\n        status: 200,\n        headers: {\n          \"content-type\": \"text/html; charset=utf-8\",\n          \"cache-control\": \"private, no-cache, no-store, max-age=0, must-revalidate\",\n        },\n      });\n    } catch {\n      return NextResponse.next();\n    }\n  };\n}\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 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","import { NextResponse } from \"next/server.js\";\nimport type { NextRequest } from \"next/server.js\";\nimport { readFileSync, existsSync, writeFileSync, mkdirSync, readdirSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport crypto from \"node:crypto\";\nimport { verifyRenderGrant } from \"../render\";\n\n// ── Config ──\nconst TOKEN_DIR = join(tmpdir(), \"shugoi-render\");\nconst TOKEN_TTL = 120000;\nif (!existsSync(TOKEN_DIR)) try { mkdirSync(TOKEN_DIR, { recursive: true }); } catch {}\nsetInterval(() => {\n  try { for (const f of readdirSync(TOKEN_DIR)) {\n    const p = join(TOKEN_DIR, f);\n    if (Date.now() - parseInt(f.split(\"_\")[0] || \"0\") > TOKEN_TTL) try { unlinkSync(p); } catch {}\n  }} catch {}\n}, 30000).unref();\n\nexport interface ShugoiNextOptions {\n  siteKey: string;\n  baseUrl?: string;\n  allowlist?: string[];\n  whitelist?: string[];\n  signingSecret?: string;\n}\n\nconst DEFAULT_HEADLESS = [\n  /^curl/i, /^wget/i, /^python/i, /^Go-http-client/i,\n  /^Java\\//, /HTTPie/i, /^node-fetch/i, /axios/i,\n  /^okhttp/i, /^scrapy/i, /PowerShell/i, /WinHttp/i,\n];\n\nconst DEFAULT_BOT_WHITELIST = [\n  /Googlebot/i, /Bingbot/i, /Slurp/i, /DuckDuckBot/i, /YandexBot/i,\n  /FacebookExternalHit/i, /Twitterbot/i, /LinkedInBot/i, /Applebot/i,\n  /AhrefsBot/i, /SemrushBot/i,\n];\n\nconst 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  \"|  - contact: support@shugoi.com -            |\",\n  \"+---------------------------------------------+\",\n].join('\\n') + '\\n';\n\nfunction signToken(siteKey: string, timestamp: number, secretOverride?: 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\nfunction loadAssets(root: string): Record<string, string> {\n  try {\n    return JSON.parse(readFileSync(join(root, \"lib\", \"guard-assets.json\"), \"utf-8\"));\n  } catch { return {}; }\n}\n\nfunction loadGuardSource(root: string, name: string, assets: Record<string, string>): string {\n  try {\n    const p = join(root, \"scripts\", name);\n    if (!existsSync(p)) return \"\";\n    let code = readFileSync(p, \"utf-8\");\n    if (assets.favicon) code = code.replaceAll(\"__SG_FAVICON__\", assets.favicon);\n    if (assets.brand) code = code.replaceAll(\"__SG_BRAND_IMG__\", assets.brand);\n    if (assets.title_tor) code = code.replaceAll(\"__SG_TITLE_TOR__\", assets.title_tor);\n    return code;\n  } catch { return \"\"; }\n}\n\nlet _httpGuardCache: { detect: string | null; guard: string | null; fetchedAt: number } = { detect: null, guard: null, fetchedAt: 0 };\n\nasync function fetchGuardsHttp(baseUrl: string, siteKey: string, signingSecret?: string): Promise<{ detect: string; guard: string } | null> {\n  const GUARD_CACHE_TTL = 300_000;\n  if (_httpGuardCache.detect && _httpGuardCache.guard && Date.now() - _httpGuardCache.fetchedAt < GUARD_CACHE_TTL) {\n    return { detect: _httpGuardCache.detect, guard: _httpGuardCache.guard };\n  }\n  try {\n    const cb = Date.now();\n    const sk = siteKey || 'cache';\n    const secret = signingSecret || process.env.SHUGOKI_SIGNING_SECRET || process.env.SHUGOKI_SECRET;\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    if (!dRes.ok || !gRes.ok) return null;\n    _httpGuardCache.detect = await dRes.text();\n    _httpGuardCache.guard = await gRes.text();\n    _httpGuardCache.fetchedAt = Date.now();\n    return { detect: _httpGuardCache.detect, guard: _httpGuardCache.guard };\n  } catch {\n    return null;\n  }\n}\n\nfunction generateBootcode(siteKey: string, config: string, detectCode: string, guardCode: string): string {\n  const combined = 'window.__sg_siteKey=' + JSON.stringify(siteKey) +\n    ';window.__sg_config=' + config +\n    ';try{' + detectCode + '}catch(e){window.__sg_blocked=true}' +\n    ';try{' + guardCode + '}catch(e){window.__sg_blocked=true}';\n  // NOTE bench : += plus rapide que tableau + join sous V8 (ropes). Inchangé.\n  let enc = \"\";\n  for (let i = 0; i < combined.length; i++) {\n    enc += String.fromCodePoint(917504 + combined.charCodeAt(i));\n  }\n  return '<script>eval([...\\'' + enc + '\\'].map(function(x){return String.fromCodePoint(x.codePointAt(0)-917504)}).join(\\'\\'))</script>';\n}\n\nexport function renderResponseData(token: string, mid?: string, grant?: string, ip?: string, expectedSiteKey?: string): { html?: string; blocked?: boolean; error?: string } {\n  if (!token || token.length < 16 || token.length > 300) return { error: \"not_found\" };\n  // CRITIQUE 1 (§7bis) : le token appartient à ce site uniquement (anti cross-site grant).\n  if (expectedSiteKey && token.split(':')[0] !== expectedSiteKey) return { error: \"not_found\" };\n  // Parité avec l'adapter Express (NP-01) : sans grant valide (lié au siteKey), pas de HTML.\n  if (!verifyRenderGrant(mid, grant, token, ip, expectedSiteKey)) return { error: \"not_found\" };\n  const suffix = token.slice(-16);\n  try {\n    for (const f of readdirSync(TOKEN_DIR)) {\n      if (f.endsWith(suffix)) {\n        const html = readFileSync(join(TOKEN_DIR, f), \"utf-8\");\n        return { html };\n      }\n    }\n  } catch {}\n  return { blocked: true };\n}\n\nexport function createShugoiNextMiddleware(options: ShugoiNextOptions) {\n  const { siteKey, baseUrl, allowlist, signingSecret } = options;\n  const headless = DEFAULT_HEADLESS;\n  const root = process.cwd();\n  const BASE_URL = baseUrl ?? 'https://shugoi.com/api/v1';\n\n  return async function shugoiMiddleware(request: NextRequest) {\n    if (request.headers.get(\"x-shugoi-internal\") === \"1\")\n      return NextResponse.next();\n\n    const path = request.nextUrl.pathname;\n    const accept = request.headers.get(\"accept\") || \"\";\n\n    if (path.endsWith(\"/__shugoi/render\")) {\n      const token = request.nextUrl.searchParams.get(\"token\") || \"\";\n      const mid = request.nextUrl.searchParams.get(\"mid\") || \"\";\n      const grant = request.nextUrl.searchParams.get(\"grant\") || \"\";\n      const xff = request.headers.get(\"x-forwarded-for\") || \"\";\n      const ip = xff.split(\",\")[0]?.trim() || \"unknown\";\n      return NextResponse.json(renderResponseData(token, mid, grant, ip, options.siteKey));\n    }\n\n    if (path.startsWith(\"/_next/\") || path.startsWith(\"/api/\")) return NextResponse.next();\n    if (allowlist?.some((p) => path === p || path.startsWith(p + \"/\"))) return NextResponse.next();\n\n    const ua = request.headers.get(\"user-agent\") || \"\";\n\n    if (headless.some((p) => p.test(ua))) {\n      return new NextResponse(BLOCK_PAGE, { status: 403 });\n    }\n\n    if (!accept.includes(\"text/html\")) return NextResponse.next();\n\n    const isBot = DEFAULT_BOT_WHITELIST.some(p => p.test(ua));\n    if (isBot) return NextResponse.next();\n\n    try {\n      let detectCode = \"\";\n      let guardCode = \"\";\n\n      // 1. Try disk first\n      const assets = loadAssets(root);\n      detectCode = loadGuardSource(root, \"guard-detect.src.js\", assets);\n      guardCode = loadGuardSource(root, \"guard.src.js\", assets);\n\n      // 2. Fallback: HTTP\n      if (!detectCode || !guardCode) {\n        const httpGuards = await fetchGuardsHttp(BASE_URL, siteKey, signingSecret);\n        if (httpGuards) {\n          detectCode = httpGuards.detect;\n          guardCode = httpGuards.guard;\n        }\n      }\n\n      if (!detectCode) {\n        console.error(\"[shugoi] WARNING: unable to load guard scripts — protection inactive\");\n        return NextResponse.next();\n      }\n\n      const ts = Date.now();\n      const signed = signToken(siteKey, ts, signingSecret);\n      const cfg = JSON.stringify({\n        enableWhitelist: true, enableVmCheck: true, enableTorCheck: true,\n        enableHeadlessCheck: true, enableAntiDetectCheck: true,\n        enableContentReplacementCheck: false,\n      });\n\n      const internalFetch = await fetch(request.url, {\n        headers: { accept: \"text/html\", \"user-agent\": \"Shugoi\", \"x-shugoi-internal\": \"1\", cookie: request.headers.get(\"cookie\") || \"\" },\n        signal: AbortSignal.timeout(5000),\n      });\n\n      if (internalFetch.ok) {\n        const originalHtml = await internalFetch.text();\n        writeFileSync(join(TOKEN_DIR, ts + \"_\" + signed.token.slice(-16)), originalHtml, \"utf-8\");\n      }\n\n      const skeleton = generateBootcode(siteKey, cfg, detectCode, guardCode);\n      const fullPage = '<!DOCTYPE html><html lang=\"fr\"><head><meta charset=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>' +\n        skeleton +\n        '</head><body><div id=\"__sg_root\"></div></body></html>';\n\n      return new NextResponse(fullPage, {\n        status: 200,\n        headers: { \"content-type\": \"text/html; charset=utf-8\" },\n      });\n    } catch {\n      return NextResponse.next();\n    }\n  };\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface ShugoiGuardProps {\n  siteKey: string;\n  enableWhitelist?: boolean;\n  enableVmCheck?: boolean;\n}\n\n/** Generate the Shugoi guard script tag HTML (eval bootcode) */\nexport async function generateGuardHtml({ siteKey, enableWhitelist = true, enableVmCheck = true }: ShugoiGuardProps): Promise<string> {\n  try {\n    const root = process.cwd();\n    const assets: Record<string, string> = {};\n    try {\n      const a = JSON.parse(readFileSync(join(root, \"lib\", \"guard-assets.json\"), \"utf-8\"));\n      if (a.favicon) assets.favicon = a.favicon;\n      if (a.brand) assets.brand = a.brand;\n      if (a.title_tor) assets.title_tor = a.title_tor;\n    } catch {}\n\n    const detectPath = join(root, \"scripts\", \"guard-detect.src.js\");\n    const guardPath = join(root, \"scripts\", \"guard.src.js\");\n    if (!existsSync(detectPath)) return '<script>console.warn(\"Shugoi guards not found\")<\\/script>';\n\n    let detect = readFileSync(detectPath, \"utf-8\");\n    let guard = readFileSync(guardPath, \"utf-8\");\n\n    if (assets.favicon) { detect = detect.replaceAll(\"__SG_FAVICON__\", assets.favicon); guard = guard.replaceAll(\"__SG_FAVICON__\", assets.favicon); }\n    if (assets.brand) { detect = detect.replaceAll(\"__SG_BRAND_IMG__\", assets.brand); guard = guard.replaceAll(\"__SG_BRAND_IMG__\", assets.brand); }\n    if (assets.title_tor) detect = detect.replaceAll(\"__SG_TITLE_TOR__\", assets.title_tor);\n\n    const cfg = JSON.stringify({ enableWhitelist, enableVmCheck, enableTorCheck: true, enableHeadlessCheck: true, enableAntiDetectCheck: true, enableContentReplacementCheck: false });\n    const combined = 'window.__sg_siteKey=' + JSON.stringify(siteKey) + ';window.__sg_config=' + cfg + ';try{' + detect + '}catch(e){window.__sg_blocked=true};try{' + guard + '}catch(e){window.__sg_blocked=true}';\n\n    // NOTE bench : += plus rapide que tableau + join sous V8 (ropes). Inchangé.\n    let enc = \"\";\n    for (let i = 0; i < combined.length; i++) {\n      enc += String.fromCodePoint(917504 + combined.charCodeAt(i));\n    }\n    return '<script>eval([...\\'' + enc + '\\'].map(function(x){return String.fromCodePoint(x.codePointAt(0)-917504)}).join(\\'\\'))<\\/script>';\n  } catch {\n    return '<script>console.warn(\"Shugoi guard generation failed\")<\\/script>';\n  }\n}\n"],"mappings":"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","names":["crypto","crypto","crypto","import_server","TOKEN_DIR","TOKEN_TTL","DEFAULT_HEADLESS","signToken","crypto","GUARD_CACHE_TTL","BASE_URL","import_node_fs","import_node_path"]}