403Webshell
Server IP : 159.203.156.69  /  Your IP : 216.73.217.172
Web Server : nginx/1.24.0
System : Linux main-ubuntu 6.8.0-71-generic #71-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 22 16:52:38 UTC 2025 x86_64
User : root ( 0)
PHP Version : 8.3.6
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/tanviranik.com/node_modules/vinext/dist/cloudflare/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/tanviranik.com/node_modules/vinext/dist/cloudflare/tpr.js.map
{"version":3,"file":"tpr.js","names":[],"sources":["../../src/cloudflare/tpr.ts"],"sourcesContent":["/**\n * TPR: Traffic-aware Pre-Rendering\n *\n * Uses Cloudflare zone analytics to determine which pages actually get\n * traffic, and pre-renders only those during deploy. The pre-rendered\n * HTML is uploaded to KV in the same format ISR uses at runtime — no\n * runtime changes needed.\n *\n * Flow:\n *   1. Parse wrangler config to find custom domain and KV namespace\n *   2. Resolve the Cloudflare zone for the custom domain\n *   3. Query zone analytics (GraphQL) for top pages by request count\n *   4. Walk ranked list until coverage threshold is met\n *   5. Start the built production server locally\n *   6. Fetch each hot route to produce HTML\n *   7. Upload pre-rendered HTML to KV (same KVCacheEntry format ISR reads)\n *\n * TPR is an experimental feature enabled via --experimental-tpr. It\n * gracefully skips when no custom domain, no API token, no traffic data,\n * or no KV namespace is configured.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { VINEXT_REVALIDATE_HEADER } from \"../server/headers.js\";\nimport { isrCacheKey } from \"../server/isr-cache.js\";\nimport { ENTRY_PREFIX } from \"./kv-cache-handler.js\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type TPROptions = {\n  /** Project root directory. */\n  root: string;\n  /** Traffic coverage percentage (0–100). Default: 90. */\n  coverage: number;\n  /** Hard cap on number of pages to pre-render. Default: 1000. */\n  limit: number;\n  /** Analytics lookback window in hours. Default: 24. */\n  window: number;\n};\n\nexport type TPRResult = {\n  /** Total unique page paths found in analytics. */\n  totalPaths: number;\n  /** Number of pages successfully pre-rendered and uploaded. */\n  prerenderedCount: number;\n  /** Actual traffic coverage achieved (percentage). */\n  coverageAchieved: number;\n  /** Wall-clock duration of the TPR step in milliseconds. */\n  durationMs: number;\n  /** If TPR was skipped, the reason. */\n  skipped?: string;\n};\n\ntype TrafficEntry = {\n  path: string;\n  requests: number;\n};\n\ntype SelectedRoutes = {\n  routes: TrafficEntry[];\n  totalRequests: number;\n  coveredRequests: number;\n  coveragePercent: number;\n};\n\ntype PrerenderResult = {\n  html: string;\n  status: number;\n  headers: Record<string, string>;\n};\n\ntype WranglerConfig = {\n  accountId?: string;\n  kvNamespaceId?: string;\n  customDomain?: string;\n};\n\n// ─── Wrangler Config Parsing ─────────────────────────────────────────────────\n\n/**\n * Parse wrangler config (JSONC or TOML) to extract the fields TPR needs:\n * account_id, VINEXT_CACHE KV namespace ID, and custom domain.\n */\nexport function parseWranglerConfig(root: string): WranglerConfig | null {\n  // Try JSONC / JSON first\n  for (const filename of [\"wrangler.jsonc\", \"wrangler.json\"]) {\n    const filepath = path.join(root, filename);\n    if (fs.existsSync(filepath)) {\n      const content = fs.readFileSync(filepath, \"utf-8\");\n      try {\n        const json = JSON.parse(stripJsonComments(content));\n        return extractFromJSON(json);\n      } catch {\n        continue;\n      }\n    }\n  }\n\n  // Try TOML\n  const tomlPath = path.join(root, \"wrangler.toml\");\n  if (fs.existsSync(tomlPath)) {\n    const content = fs.readFileSync(tomlPath, \"utf-8\");\n    return extractFromTOML(content);\n  }\n\n  return null;\n}\n\n/**\n * Strip single-line (//) and multi-line comments from JSONC while\n * preserving strings that contain slashes.\n */\nfunction stripJsonComments(str: string): string {\n  let result = \"\";\n  let inString = false;\n  let inSingleLine = false;\n  let inMultiLine = false;\n  let escapeNext = false;\n\n  for (let i = 0; i < str.length; i++) {\n    const ch = str[i];\n    const next = str[i + 1];\n\n    if (escapeNext) {\n      if (!inSingleLine && !inMultiLine) result += ch;\n      escapeNext = false;\n      continue;\n    }\n\n    if (ch === \"\\\\\" && inString) {\n      result += ch;\n      escapeNext = true;\n      continue;\n    }\n\n    if (inSingleLine) {\n      if (ch === \"\\n\") {\n        inSingleLine = false;\n        result += ch;\n      }\n      continue;\n    }\n\n    if (inMultiLine) {\n      if (ch === \"*\" && next === \"/\") {\n        inMultiLine = false;\n        i++;\n      }\n      continue;\n    }\n\n    if (ch === '\"' && !inString) {\n      inString = true;\n      result += ch;\n      continue;\n    }\n\n    if (ch === '\"' && inString) {\n      inString = false;\n      result += ch;\n      continue;\n    }\n\n    if (!inString && ch === \"/\" && next === \"/\") {\n      inSingleLine = true;\n      i++;\n      continue;\n    }\n\n    if (!inString && ch === \"/\" && next === \"*\") {\n      inMultiLine = true;\n      i++;\n      continue;\n    }\n\n    result += ch;\n  }\n\n  return result;\n}\n\nfunction extractFromJSON(config: Record<string, unknown>): WranglerConfig {\n  const result: WranglerConfig = {};\n\n  // account_id\n  if (typeof config.account_id === \"string\") {\n    result.accountId = config.account_id;\n  }\n\n  // KV namespace ID for VINEXT_CACHE\n  if (Array.isArray(config.kv_namespaces)) {\n    const vinextKV = config.kv_namespaces.find(\n      (ns: Record<string, unknown>) =>\n        ns && typeof ns === \"object\" && ns.binding === \"VINEXT_CACHE\",\n    );\n    if (vinextKV && typeof vinextKV.id === \"string\" && vinextKV.id !== \"<your-kv-namespace-id>\") {\n      result.kvNamespaceId = vinextKV.id;\n    }\n  }\n\n  // Custom domain — check routes[] and custom_domains[]\n  const domain = extractDomainFromRoutes(config.routes) ?? extractDomainFromCustomDomains(config);\n  if (domain) result.customDomain = domain;\n\n  return result;\n}\n\nfunction extractDomainFromRoutes(routes: unknown): string | null {\n  if (!Array.isArray(routes)) return null;\n\n  for (const route of routes) {\n    if (typeof route === \"string\") {\n      const domain = cleanDomain(route);\n      if (domain && !domain.includes(\"workers.dev\")) return domain;\n    } else if (route && typeof route === \"object\") {\n      const r = route as Record<string, unknown>;\n      const pattern =\n        typeof r.zone_name === \"string\"\n          ? r.zone_name\n          : typeof r.pattern === \"string\"\n            ? r.pattern\n            : null;\n      if (pattern) {\n        const domain = cleanDomain(pattern);\n        if (domain && !domain.includes(\"workers.dev\")) return domain;\n      }\n    }\n  }\n  return null;\n}\n\nfunction extractDomainFromCustomDomains(config: Record<string, unknown>): string | null {\n  // Workers Custom Domains: \"custom_domains\": [\"example.com\"]\n  if (Array.isArray(config.custom_domains)) {\n    for (const d of config.custom_domains) {\n      if (typeof d === \"string\" && !d.includes(\"workers.dev\")) {\n        return cleanDomain(d);\n      }\n    }\n  }\n  return null;\n}\n\n/** Strip protocol and trailing wildcards from a route pattern to get a bare domain. */\nfunction cleanDomain(raw: string): string | null {\n  const cleaned = raw\n    .replace(/^https?:\\/\\//, \"\")\n    .replace(/\\/\\*$/, \"\")\n    .replace(/\\/+$/, \"\")\n    .split(\"/\")[0]; // Take only the host part\n  return cleaned || null;\n}\n\n/**\n * Simple extraction of specific fields from wrangler.toml content.\n * Not a full TOML parser — just enough for the fields we need.\n */\nfunction extractFromTOML(content: string): WranglerConfig {\n  const result: WranglerConfig = {};\n\n  // account_id = \"...\"\n  const accountMatch = content.match(/^account_id\\s*=\\s*\"([^\"]+)\"/m);\n  if (accountMatch) result.accountId = accountMatch[1];\n\n  // KV namespace with binding = \"VINEXT_CACHE\"\n  // Look for [[kv_namespaces]] blocks\n  const kvBlocks = content.split(/\\[\\[kv_namespaces\\]\\]/);\n  for (let i = 1; i < kvBlocks.length; i++) {\n    const block = kvBlocks[i].split(/\\[\\[/)[0]; // Take until next section\n    const bindingMatch = block.match(/binding\\s*=\\s*\"([^\"]+)\"/);\n    const idMatch = block.match(/\\bid\\s*=\\s*\"([^\"]+)\"/);\n    if (\n      bindingMatch?.[1] === \"VINEXT_CACHE\" &&\n      idMatch?.[1] &&\n      idMatch[1] !== \"<your-kv-namespace-id>\"\n    ) {\n      result.kvNamespaceId = idMatch[1];\n    }\n  }\n\n  // routes — both string and table forms\n  // route = \"example.com/*\"\n  const routeMatch = content.match(/^route\\s*=\\s*\"([^\"]+)\"/m);\n  if (routeMatch) {\n    const domain = cleanDomain(routeMatch[1]);\n    if (domain && !domain.includes(\"workers.dev\")) {\n      result.customDomain = domain;\n    }\n  }\n\n  // [[routes]] blocks\n  if (!result.customDomain) {\n    const routeBlocks = content.split(/\\[\\[routes\\]\\]/);\n    for (let i = 1; i < routeBlocks.length; i++) {\n      const block = routeBlocks[i].split(/\\[\\[/)[0];\n      const patternMatch = block.match(/pattern\\s*=\\s*\"([^\"]+)\"/);\n      if (patternMatch) {\n        const domain = cleanDomain(patternMatch[1]);\n        if (domain && !domain.includes(\"workers.dev\")) {\n          result.customDomain = domain;\n          break;\n        }\n      }\n    }\n  }\n\n  return result;\n}\n\n// ─── Cloudflare API ──────────────────────────────────────────────────────────\n\n/**\n * Generate zone lookup candidates from shortest (2-part) to longest.\n * Tries the most common case first (e.g., \"example.com\") and progressively\n * adds labels for multi-part TLDs (e.g., \"co.uk\" → \"example.co.uk\").\n *\n * \"shop.example.com\"    → [\"example.com\", \"shop.example.com\"]\n * \"shop.example.co.uk\"  → [\"co.uk\", \"example.co.uk\", \"shop.example.co.uk\"]\n * \"example.com\"         → [\"example.com\"]\n */\nexport function domainCandidates(domain: string): string[] {\n  const parts = domain.split(\".\");\n  const candidates: string[] = [];\n  for (let i = parts.length - 2; i >= 0; i--) {\n    candidates.push(parts.slice(i).join(\".\"));\n  }\n  return candidates;\n}\n\n/** Resolve zone ID from a domain name via the Cloudflare API. */\nasync function resolveZoneId(domain: string, apiToken: string): Promise<string | null> {\n  // Try progressively longer domain candidates until one matches a zone.\n  // This handles all public suffixes without a hardcoded TLD list —\n  // for simple TLDs (.com, .io) the 2-part candidate hits on the first try;\n  // for multi-part TLDs (.co.uk, .com.au) it takes one extra call.\n  for (const candidate of domainCandidates(domain)) {\n    const response = await fetch(\n      `https://api.cloudflare.com/client/v4/zones?name=${encodeURIComponent(candidate)}`,\n      {\n        headers: {\n          Authorization: `Bearer ${apiToken}`,\n          \"Content-Type\": \"application/json\",\n        },\n      },\n    );\n\n    if (!response.ok) continue;\n\n    const data = (await response.json()) as {\n      success: boolean;\n      result?: Array<{ id: string }>;\n    };\n    if (data.success && data.result?.length) {\n      return data.result[0].id;\n    }\n  }\n\n  return null;\n}\n\n/** Resolve the account ID associated with the API token. */\nasync function resolveAccountId(apiToken: string): Promise<string | null> {\n  const response = await fetch(\"https://api.cloudflare.com/client/v4/accounts?per_page=1\", {\n    headers: {\n      Authorization: `Bearer ${apiToken}`,\n      \"Content-Type\": \"application/json\",\n    },\n  });\n\n  if (!response.ok) return null;\n\n  const data = (await response.json()) as {\n    success: boolean;\n    result?: Array<{ id: string }>;\n  };\n  if (!data.success || !data.result?.length) return null;\n\n  return data.result[0].id;\n}\n\n// ─── Traffic Querying ────────────────────────────────────────────────────────\n\n/**\n * Query Cloudflare zone analytics for top page paths by request count\n * over the given time window.\n */\nasync function queryTraffic(\n  zoneTag: string,\n  apiToken: string,\n  windowHours: number,\n): Promise<TrafficEntry[]> {\n  const now = new Date();\n  const start = new Date(now.getTime() - windowHours * 60 * 60 * 1000);\n\n  const query = `{\n    viewer {\n      zones(filter: { zoneTag: \"${zoneTag}\" }) {\n        httpRequestsAdaptiveGroups(\n          limit: 10000\n          orderBy: [sum_requests_DESC]\n          filter: {\n            datetime_geq: \"${start.toISOString()}\"\n            datetime_lt: \"${now.toISOString()}\"\n            requestSource: \"eyeball\"\n          }\n        ) {\n          sum { requests }\n          dimensions { clientRequestPath }\n        }\n      }\n    }\n  }`;\n\n  const response = await fetch(\"https://api.cloudflare.com/client/v4/graphql\", {\n    method: \"POST\",\n    headers: {\n      Authorization: `Bearer ${apiToken}`,\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({ query }),\n  });\n\n  if (!response.ok) {\n    throw new Error(`Zone analytics query failed: ${response.status} ${response.statusText}`);\n  }\n\n  const data = (await response.json()) as {\n    errors?: Array<{ message: string }>;\n    data?: {\n      viewer?: {\n        zones?: Array<{\n          httpRequestsAdaptiveGroups?: Array<{\n            sum: { requests: number };\n            dimensions: { clientRequestPath: string };\n          }>;\n        }>;\n      };\n    };\n  };\n\n  if (data.errors?.length) {\n    throw new Error(`Zone analytics error: ${data.errors[0].message}`);\n  }\n\n  const groups = data.data?.viewer?.zones?.[0]?.httpRequestsAdaptiveGroups;\n  if (!groups || groups.length === 0) return [];\n\n  return filterTrafficPaths(\n    groups.map((g) => ({\n      path: g.dimensions.clientRequestPath,\n      requests: g.sum.requests,\n    })),\n  );\n}\n\n/** Filter out non-page requests (static assets, API routes, internal routes). */\nfunction filterTrafficPaths(entries: TrafficEntry[]): TrafficEntry[] {\n  return entries.filter((e) => {\n    if (!e.path.startsWith(\"/\")) return false;\n    // Static assets\n    if (/\\.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|map|webp|avif)$/i.test(e.path))\n      return false;\n    // API routes\n    if (e.path.startsWith(\"/api/\")) return false;\n    // Internal routes\n    if (e.path.startsWith(\"/_vinext/\") || e.path.startsWith(\"/_next/\")) return false;\n    // RSC requests\n    if (e.path.endsWith(\".rsc\")) return false;\n    return true;\n  });\n}\n\n// ─── Route Selection ─────────────────────────────────────────────────────────\n\n/**\n * Walk the ranked traffic list, accumulating request counts until the\n * coverage target is met or the hard cap is reached.\n */\nfunction selectRoutes(\n  traffic: TrafficEntry[],\n  coverageTarget: number,\n  limit: number,\n): SelectedRoutes {\n  const totalRequests = traffic.reduce((sum, e) => sum + e.requests, 0);\n  if (totalRequests === 0) {\n    return { routes: [], totalRequests: 0, coveredRequests: 0, coveragePercent: 0 };\n  }\n\n  const target = totalRequests * (coverageTarget / 100);\n  const selected: TrafficEntry[] = [];\n  let accumulated = 0;\n\n  // Traffic is already sorted DESC by requests from the GraphQL query\n  for (const entry of traffic) {\n    if (accumulated >= target || selected.length >= limit) break;\n    selected.push(entry);\n    accumulated += entry.requests;\n  }\n\n  return {\n    routes: selected,\n    totalRequests,\n    coveredRequests: accumulated,\n    coveragePercent: (accumulated / totalRequests) * 100,\n  };\n}\n\n// ─── Pre-rendering ───────────────────────────────────────────────────────────\n\n/** Pre-render port — high number to avoid collisions with dev servers. */\nconst PRERENDER_PORT = 19384;\n\n/** Max time to wait for the local server to start (ms). */\nconst SERVER_STARTUP_TIMEOUT = 30_000;\n\n/** Max concurrent fetch requests during pre-rendering. */\nconst FETCH_CONCURRENCY = 10;\n\n/**\n * Start a local production server, fetch each route to produce HTML,\n * and return the results. Pages that fail to render are skipped.\n */\nasync function prerenderRoutes(\n  routes: string[],\n  root: string,\n  hostDomain?: string,\n): Promise<Map<string, PrerenderResult>> {\n  const results = new Map<string, PrerenderResult>();\n  let failedCount = 0;\n  const port = PRERENDER_PORT;\n\n  // Verify dist/ exists\n  const distDir = path.join(root, \"dist\");\n  if (!fs.existsSync(distDir)) {\n    console.log(\"  TPR: Skipping pre-render — dist/ directory not found\");\n    return results;\n  }\n\n  // Start the local production server as a subprocess\n  const serverProcess = startLocalServer(root, port);\n\n  try {\n    await waitForServer(port, SERVER_STARTUP_TIMEOUT);\n\n    // Fetch routes in batches to limit concurrency\n    for (let i = 0; i < routes.length; i += FETCH_CONCURRENCY) {\n      const batch = routes.slice(i, i + FETCH_CONCURRENCY);\n      const promises = batch.map(async (routePath) => {\n        try {\n          const response = await fetch(`http://127.0.0.1:${port}${routePath}`, {\n            headers: {\n              \"User-Agent\": \"vinext-tpr/1.0\",\n              ...(hostDomain ? { Host: hostDomain } : {}),\n            },\n            redirect: \"manual\", // Don't follow redirects — cache the redirect itself\n          });\n\n          // Only cache successful responses (2xx and 3xx)\n          if (response.status < 400) {\n            const html = await response.text();\n            const headers: Record<string, string> = {};\n            response.headers.forEach((value, key) => {\n              // Only keep relevant headers\n              if (\n                key === \"content-type\" ||\n                key === \"cache-control\" ||\n                key === VINEXT_REVALIDATE_HEADER ||\n                key === \"location\"\n              ) {\n                headers[key] = value;\n              }\n            });\n            results.set(routePath, {\n              html,\n              status: response.status,\n              headers,\n            });\n          }\n        } catch {\n          // Skip pages that fail to render — they may depend on\n          // request-specific data (cookies, headers, auth) that\n          // isn't available during pre-rendering.\n          failedCount++;\n        }\n      });\n\n      await Promise.all(promises);\n    }\n\n    if (failedCount > 0) {\n      console.log(`  TPR: ${failedCount} page(s) failed to pre-render (skipped)`);\n    }\n  } finally {\n    serverProcess.kill(\"SIGTERM\");\n    // Give it a moment to clean up\n    await new Promise<void>((resolve) => {\n      serverProcess.on(\"exit\", resolve);\n      setTimeout(resolve, 2000);\n    });\n  }\n\n  return results;\n}\n\n/**\n * Spawn a subprocess running the vinext production server.\n * Uses the same Node.js binary and resolves prod-server.js relative\n * to the current module (works whether vinext is installed or linked).\n */\nfunction startLocalServer(root: string, port: number): ChildProcess {\n  const prodServerPath = path.resolve(import.meta.dirname, \"..\", \"server\", \"prod-server.js\");\n  const outDir = path.join(root, \"dist\");\n\n  // Escape backslashes for Windows paths inside the JS string\n  const escapedProdServer = prodServerPath.replace(/\\\\/g, \"\\\\\\\\\");\n  const escapedOutDir = outDir.replace(/\\\\/g, \"\\\\\\\\\");\n\n  const script = [\n    `import(\"file://${escapedProdServer}\")`,\n    `.then(m => m.startProdServer({ port: ${port}, host: \"127.0.0.1\", outDir: \"${escapedOutDir}\" }))`,\n    `.catch(e => { console.error(\"[vinext-tpr] Server failed to start:\", e); process.exit(1); });`,\n  ].join(\"\");\n\n  const proc = spawn(process.execPath, [\"--input-type=module\", \"-e\", script], {\n    cwd: root,\n    stdio: \"pipe\",\n    env: { ...process.env, NODE_ENV: \"production\" },\n  });\n\n  // Forward server errors to the parent's stderr for debugging\n  proc.stderr?.on(\"data\", (chunk: Buffer) => {\n    const msg = chunk.toString().trim();\n    if (msg) console.error(`  [tpr-server] ${msg}`);\n  });\n\n  return proc;\n}\n\n/** Poll the local server until it responds or the timeout is reached. */\nasync function waitForServer(port: number, timeoutMs: number): Promise<void> {\n  const start = Date.now();\n  while (Date.now() - start < timeoutMs) {\n    try {\n      const controller = new AbortController();\n      const timer = setTimeout(() => controller.abort(), 2000);\n      const response = await fetch(`http://127.0.0.1:${port}/`, {\n        redirect: \"manual\",\n        signal: controller.signal,\n      });\n      clearTimeout(timer);\n      // Any response means the server is up\n      await response.text(); // consume body\n      return;\n    } catch {\n      await new Promise<void>((r) => setTimeout(r, 300));\n    }\n  }\n  throw new Error(`Local production server failed to start within ${timeoutMs / 1000}s`);\n}\n\n// ─── KV Upload ───────────────────────────────────────────────────────────────\n\n/** KV bulk API accepts up to 10,000 pairs per request */\nconst KV_BATCH_SIZE = 10_000;\n\n/** Maximum KV expiration TTL: 30 days */\nconst MAX_KV_TTL_SECONDS = 30 * 24 * 3600;\n\n/**\n * Build KV bulk API pairs from pre-rendered entries.\n *\n * Key format matches the runtime KVCacheHandler exactly:\n *   ENTRY_PREFIX + isrCacheKey(\"app\", pathname, buildId) + \":html\"\n *   → \"cache:app:<buildId>:<pathname>:html\"\n */\nexport function buildTprKVPairs(\n  entries: Map<string, PrerenderResult>,\n  buildId: string | undefined,\n  defaultRevalidateSeconds: number,\n): Array<{ key: string; value: string; expiration_ttl: number }> {\n  const now = Date.now();\n  const pairs: Array<{ key: string; value: string; expiration_ttl: number }> = [];\n\n  for (const [routePath, result] of entries) {\n    const revalidateHeader = result.headers[VINEXT_REVALIDATE_HEADER];\n    const revalidateSeconds =\n      revalidateHeader && !isNaN(Number(revalidateHeader))\n        ? Number(revalidateHeader)\n        : defaultRevalidateSeconds;\n\n    const revalidateAt = revalidateSeconds > 0 ? now + revalidateSeconds * 1000 : null;\n\n    // For revalidating entries: 30-day TTL matches runtime KVCacheHandler.set().\n    // For non-revalidating entries: runtime uses no TTL (entries persist indefinitely),\n    // but TPR uses a 24h fallback so pre-warmed entries don't accumulate forever.\n    const kvTtl = revalidateSeconds > 0 ? MAX_KV_TTL_SECONDS : 24 * 3600;\n\n    const entry = {\n      value: {\n        kind: \"APP_PAGE\" as const,\n        html: result.html,\n        headers: result.headers,\n        status: result.status,\n      },\n      tags: [] as string[],\n      lastModified: now,\n      revalidateAt,\n    };\n\n    const cacheKey = ENTRY_PREFIX + isrCacheKey(\"app\", routePath, buildId) + \":html\";\n\n    pairs.push({\n      key: cacheKey,\n      value: JSON.stringify(entry),\n      expiration_ttl: kvTtl,\n    });\n  }\n\n  return pairs;\n}\n\n/**\n * Upload pre-rendered pages to KV using the Cloudflare REST API.\n * Writes in the same KVCacheEntry format that KVCacheHandler reads\n * at runtime, so ISR serves these entries without any code changes.\n */\nasync function uploadToKV(\n  entries: Map<string, PrerenderResult>,\n  namespaceId: string,\n  accountId: string,\n  apiToken: string,\n  defaultRevalidateSeconds: number,\n  buildId?: string,\n): Promise<void> {\n  const pairs = buildTprKVPairs(entries, buildId, defaultRevalidateSeconds);\n  for (let i = 0; i < pairs.length; i += KV_BATCH_SIZE) {\n    const batch = pairs.slice(i, i + KV_BATCH_SIZE);\n    const response = await fetch(\n      `https://api.cloudflare.com/client/v4/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,\n      {\n        method: \"PUT\",\n        headers: {\n          Authorization: `Bearer ${apiToken}`,\n          \"Content-Type\": \"application/json\",\n        },\n        body: JSON.stringify(batch),\n      },\n    );\n\n    if (!response.ok) {\n      const text = await response.text();\n      throw new Error(\n        `KV bulk upload failed (batch ${Math.floor(i / KV_BATCH_SIZE) + 1}): ${response.status} — ${text}`,\n      );\n    }\n  }\n}\n\n// ─── Main Entry ──────────────────────────────────────────────────────────────\n\n/** Default revalidation TTL for pre-rendered pages (1 hour). */\nconst DEFAULT_REVALIDATE_SECONDS = 3600;\n\n/**\n * Run the TPR pipeline: query traffic, select routes, pre-render, upload.\n *\n * Designed to be called between the build step and wrangler deploy in\n * the `vinext deploy` pipeline. Gracefully skips (never errors) when\n * the prerequisites aren't met.\n */\nexport async function runTPR(options: TPROptions): Promise<TPRResult> {\n  const startTime = Date.now();\n  const { root, coverage, limit, window: windowHours } = options;\n\n  const skip = (reason: string): TPRResult => ({\n    totalPaths: 0,\n    prerenderedCount: 0,\n    coverageAchieved: 0,\n    durationMs: Date.now() - startTime,\n    skipped: reason,\n  });\n\n  // ── 1. Check for API token ────────────────────────────────────\n  const apiToken = process.env.CLOUDFLARE_API_TOKEN;\n  if (!apiToken) {\n    return skip(\"no CLOUDFLARE_API_TOKEN set\");\n  }\n\n  // ── 2. Parse wrangler config ──────────────────────────────────\n  const wranglerConfig = parseWranglerConfig(root);\n  if (!wranglerConfig) {\n    return skip(\"could not parse wrangler config\");\n  }\n\n  // ── 3. Check for custom domain ────────────────────────────────\n  if (!wranglerConfig.customDomain) {\n    return skip(\"no custom domain — zone analytics unavailable\");\n  }\n\n  // ── 4. Check for KV namespace ─────────────────────────────────\n  if (!wranglerConfig.kvNamespaceId) {\n    return skip(\"no VINEXT_CACHE KV namespace configured\");\n  }\n\n  // ── 5. Resolve account ID ─────────────────────────────────────\n  const accountId = wranglerConfig.accountId ?? (await resolveAccountId(apiToken));\n  if (!accountId) {\n    return skip(\"could not resolve Cloudflare account ID\");\n  }\n\n  // ── 6. Resolve zone ID ────────────────────────────────────────\n  console.log(`  TPR: Analyzing traffic for ${wranglerConfig.customDomain} (last ${windowHours}h)`);\n\n  const zoneId = await resolveZoneId(wranglerConfig.customDomain, apiToken);\n  if (!zoneId) {\n    return skip(`could not resolve zone for ${wranglerConfig.customDomain}`);\n  }\n\n  // ── 7. Query traffic data ─────────────────────────────────────\n  let traffic: TrafficEntry[];\n  try {\n    traffic = await queryTraffic(zoneId, apiToken, windowHours);\n  } catch (err) {\n    return skip(`analytics query failed: ${err instanceof Error ? err.message : String(err)}`);\n  }\n\n  if (traffic.length === 0) {\n    return skip(\"no traffic data available (first deploy?)\");\n  }\n\n  // ── 8. Select routes by coverage ──────────────────────────────\n  const selection = selectRoutes(traffic, coverage, limit);\n\n  console.log(\n    `  TPR: ${traffic.length.toLocaleString()} unique paths — ` +\n      `${selection.routes.length} pages cover ${Math.round(selection.coveragePercent)}% of traffic`,\n  );\n\n  if (selection.routes.length === 0) {\n    return {\n      totalPaths: traffic.length,\n      prerenderedCount: 0,\n      coverageAchieved: 0,\n      durationMs: Date.now() - startTime,\n      skipped: \"no pre-renderable routes after filtering\",\n    };\n  }\n\n  // ── 9. Pre-render selected routes ─────────────────────────────\n  console.log(`  TPR: Pre-rendering ${selection.routes.length} pages...`);\n\n  const routePaths = selection.routes.map((r) => r.path);\n  let rendered: Map<string, PrerenderResult>;\n  try {\n    rendered = await prerenderRoutes(routePaths, root, wranglerConfig.customDomain);\n  } catch (err) {\n    return skip(`pre-rendering failed: ${err instanceof Error ? err.message : String(err)}`);\n  }\n\n  if (rendered.size === 0) {\n    return {\n      totalPaths: traffic.length,\n      prerenderedCount: 0,\n      coverageAchieved: selection.coveragePercent,\n      durationMs: Date.now() - startTime,\n      skipped: \"all pages failed to pre-render (request-dependent?)\",\n    };\n  }\n\n  // ── 10. Upload to KV ──────────────────────────────────────────\n  // Read buildId from the BUILD_ID file written by vinext:build-id plugin.\n  let buildId: string;\n  try {\n    buildId = fs.readFileSync(path.join(root, \"dist\", \"server\", \"BUILD_ID\"), \"utf-8\").trim();\n  } catch {\n    // BUILD_ID is written by vinext:build-id during every production build.\n    // If missing, the build output is likely corrupted or incomplete.\n    // Proceeding without buildId would write keys that never match runtime.\n    console.warn(\n      \"  TPR: Could not read BUILD_ID from dist/server/ — KV keys will not match runtime. Skipping KV upload.\",\n    );\n    return skip(\"BUILD_ID not found in dist/server/ — build output may be incomplete\");\n  }\n\n  try {\n    await uploadToKV(\n      rendered,\n      wranglerConfig.kvNamespaceId,\n      accountId,\n      apiToken,\n      DEFAULT_REVALIDATE_SECONDS,\n      buildId,\n    );\n  } catch (err) {\n    return skip(`KV upload failed: ${err instanceof Error ? err.message : String(err)}`);\n  }\n\n  const durationMs = Date.now() - startTime;\n  console.log(\n    `  TPR: Pre-rendered ${rendered.size} pages in ${(durationMs / 1000).toFixed(1)}s → KV cache`,\n  );\n\n  return {\n    totalPaths: traffic.length,\n    prerenderedCount: rendered.size,\n    coverageAchieved: selection.coveragePercent,\n    durationMs,\n  };\n}\n"],"mappings":"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"}

Youez - 2016 - github.com/yon3zu
LinuXploit