@waniwani/relay 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ var settings = {
17
17
  };
18
18
 
19
19
  // src/version.ts
20
- var RELAY_VERSION = true ? "0.4.1" : "0.0.0-dev";
20
+ var RELAY_VERSION = true ? "0.4.2" : "0.0.0-dev";
21
21
 
22
22
  // src/agent-page.ts
23
23
  var isObject = (value) => typeof value === "object" && value !== null;
@@ -177,7 +177,7 @@ async function fetchAgentPage(rules, hostname, path) {
177
177
  const controller = new AbortController();
178
178
  try {
179
179
  const response = await within(
180
- fetch(`${rules.pagesUrl}/${hostname}${path}`, {
180
+ fetch(`${rules.pagesUrl}/${hostname}${path === "/" ? "" : path}`, {
181
181
  headers: {
182
182
  accept: "text/markdown, text/html;q=0.9",
183
183
  "x-waniwani-relay": RELAY_VERSION
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/settings.ts","../src/version.ts","../src/agent-page.ts","../src/headers.ts","../src/http.ts","../src/ingest.ts","../src/key.ts","../src/logic/decide.ts","../src/logic/agents.generated.json","../src/logic/ip-ranges.generated.json","../src/match-key.ts","../src/refresh.ts","../src/wait-until.ts","../src/platform.ts","../src/request.ts","../src/index.ts"],"sourcesContent":["export const settings = {\n\tapiUrls: {\n\t\tus: \"https://app.waniwani.ai\",\n\t\teu: \"https://eu.app.waniwani.ai\",\n\t},\n\trequestTimeoutMs: 2000,\n\tcloudFrontTimeoutMs: 1000,\n\tcloudFrontMaxUriBytes: 8192,\n\tcloudFrontMaxBodyBytes: 1_000_000 - 16_384,\n\tretryDelayMs: 500,\n\trulesRefreshMs: 12 * 60 * 60 * 1000,\n\trulesRetryMs: 5 * 60 * 1000,\n\tsiteRulesTimeoutMs: 300,\n\tsiteRulesRefreshMs: 60_000,\n\tsiteRulesRetryMs: 5_000,\n};\n","declare const __RELAY_PACKAGE_VERSION__: string | undefined;\n\nexport const RELAY_VERSION: string =\n\ttypeof __RELAY_PACKAGE_VERSION__ === \"string\"\n\t\t? __RELAY_PACKAGE_VERSION__\n\t\t: \"0.0.0-dev\";\n","import { settings } from \"./settings\";\nimport { RELAY_VERSION } from \"./version\";\n\nexport type SiteRules = {\n\tversion: 1;\n\tpagesUrl: string;\n\tuserAgentTokens: string[];\n\tdeadlines: { headersMs: number; bodyMs: number };\n\tmaxBodyBytes: number;\n\tsites: Array<{\n\t\thostname: string;\n\t\tblockedUserAgentTokens: string[];\n\t\tagentPages: { enabled: boolean; paths: string[] };\n\t}>;\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null;\n\nconst isStringArray = (value: unknown): value is string[] =>\n\tArray.isArray(value) && value.every((item) => typeof item === \"string\");\n\nconst isPositive = (value: unknown): value is number =>\n\ttypeof value === \"number\" && Number.isFinite(value) && value > 0;\n\n/** Lowercase like the user agent they are matched against, with blank tokens dropped since they would match every request. */\nconst cleanTokens = (tokens: string[]): string[] =>\n\ttokens\n\t\t.map((token) => token.trim().toLowerCase())\n\t\t.filter((token) => token !== \"\");\n\n/** The site rules in a serve rules file from the app (the `data` of its answer), or null when it does not have the expected form. */\nexport function parseSiteRules(body: unknown): SiteRules | null {\n\tif (!isObject(body) || body.version !== 1) {\n\t\treturn null;\n\t}\n\tconst { pagesUrl, userAgentTokens, deadlines, maxBodyBytes, sites } = body;\n\tif (\n\t\ttypeof pagesUrl !== \"string\" ||\n\t\t!isStringArray(userAgentTokens) ||\n\t\t!isObject(deadlines) ||\n\t\t!isPositive(deadlines.headersMs) ||\n\t\t!isPositive(deadlines.bodyMs) ||\n\t\t!isPositive(maxBodyBytes) ||\n\t\t!Array.isArray(sites)\n\t) {\n\t\treturn null;\n\t}\n\tconst parsedSites: SiteRules[\"sites\"] = [];\n\tfor (const site of sites) {\n\t\tif (!isObject(site)) {\n\t\t\treturn null;\n\t\t}\n\t\tconst blocked = site.blockedUserAgentTokens ?? [];\n\t\tif (\n\t\t\ttypeof site.hostname !== \"string\" ||\n\t\t\t!isStringArray(blocked) ||\n\t\t\t!isObject(site.agentPages) ||\n\t\t\ttypeof site.agentPages.enabled !== \"boolean\" ||\n\t\t\t!isStringArray(site.agentPages.paths)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tparsedSites.push({\n\t\t\thostname: site.hostname.toLowerCase(),\n\t\t\tblockedUserAgentTokens: cleanTokens(blocked),\n\t\t\tagentPages: {\n\t\t\t\tenabled: site.agentPages.enabled,\n\t\t\t\tpaths: site.agentPages.paths,\n\t\t\t},\n\t\t});\n\t}\n\treturn {\n\t\tversion: 1,\n\t\tpagesUrl: pagesUrl.replace(/\\/+$/, \"\"),\n\t\tuserAgentTokens: cleanTokens(userAgentTokens),\n\t\tdeadlines: { headersMs: deadlines.headersMs, bodyMs: deadlines.bodyMs },\n\t\tmaxBodyBytes,\n\t\tsites: parsedSites,\n\t};\n}\n\ntype CacheEntry = {\n\trules: SiteRules | null;\n\tfetchedAt: number;\n\tlastAttemptAt: number;\n\tlastAttemptOk: boolean;\n\tinFlight: Promise<SiteRules | null> | null;\n};\n\nconst cache = new Map<string, CacheEntry>();\n\nasync function fetchSiteRules(\n\turl: string,\n\ttimeoutMs: number,\n): Promise<SiteRules | null> {\n\tconst response = await fetch(url, {\n\t\tsignal: AbortSignal.timeout(timeoutMs),\n\t});\n\tif (!response.ok) {\n\t\tawait response.body?.cancel().catch(() => {});\n\t\treturn null;\n\t}\n\tconst body: unknown = await response.json();\n\treturn parseSiteRules(isObject(body) ? body.data : null);\n}\n\nfunction startFetch(\n\tentry: CacheEntry,\n\turl: string,\n\ttimeoutMs: number,\n): Promise<SiteRules | null> {\n\tconst now = Date.now();\n\tentry.lastAttemptAt = now;\n\tentry.lastAttemptOk = false;\n\tconst work = fetchSiteRules(url, timeoutMs)\n\t\t.catch(() => null)\n\t\t.then((rules) => {\n\t\t\tif (rules) {\n\t\t\t\tentry.rules = rules;\n\t\t\t\tentry.fetchedAt = Date.now();\n\t\t\t\tentry.lastAttemptOk = true;\n\t\t\t}\n\t\t\tentry.inFlight = null;\n\t\t\treturn rules;\n\t\t});\n\tentry.inFlight = work;\n\treturn work;\n}\n\n/**\n * The rules for this org. Awaits the first fetch, then serves the cache and refreshes it in the background once it is stale.\n * At most one fetch per org is in flight.\n */\nexport async function loadSiteRules(\n\tapiUrl: string,\n\tingestToken: string,\n\tbackground: (work: Promise<unknown>) => void,\n): Promise<SiteRules | null> {\n\tconst url = `${apiUrl}/api/v2/agent-traffic/serve/${ingestToken}/rules`;\n\tlet entry = cache.get(url);\n\tif (!entry) {\n\t\tentry = {\n\t\t\trules: null,\n\t\t\tfetchedAt: 0,\n\t\t\tlastAttemptAt: Number.NEGATIVE_INFINITY,\n\t\t\tlastAttemptOk: false,\n\t\t\tinFlight: null,\n\t\t};\n\t\tcache.set(url, entry);\n\t}\n\tconst now = Date.now();\n\tif (entry.rules) {\n\t\tconst stale = now - entry.fetchedAt >= settings.siteRulesRefreshMs;\n\t\tconst retryDue =\n\t\t\tentry.lastAttemptOk ||\n\t\t\tnow - entry.lastAttemptAt >= settings.siteRulesRetryMs;\n\t\tif (stale && retryDue && !entry.inFlight) {\n\t\t\tbackground(startFetch(entry, url, settings.requestTimeoutMs));\n\t\t}\n\t\treturn entry.rules;\n\t}\n\tif (entry.inFlight) {\n\t\treturn entry.inFlight;\n\t}\n\tif (now - entry.lastAttemptAt < settings.siteRulesRetryMs) {\n\t\treturn null;\n\t}\n\treturn startFetch(entry, url, settings.siteRulesTimeoutMs);\n}\n\n/** The pathname without its trailing slashes, except for the root. */\nexport function normalizePath(pathname: string): string {\n\tconst trimmed = pathname.replace(/\\/+$/, \"\");\n\treturn trimmed === \"\" ? \"/\" : trimmed;\n}\n\n/** Whether a path matches an exact entry like `/pricing`, or a folder entry like `/products/*` that also covers `/products`. */\nexport function matchesPath(path: string, patterns: string[]): boolean {\n\treturn patterns.some((pattern) => {\n\t\tif (pattern === \"*\" || pattern === \"/*\") {\n\t\t\treturn true;\n\t\t}\n\t\tif (pattern.endsWith(\"/*\")) {\n\t\t\tconst folder = normalizePath(pattern.slice(0, -2));\n\t\t\treturn path === folder || path.startsWith(`${folder}/`);\n\t\t}\n\t\treturn path === normalizePath(pattern);\n\t});\n}\n\nconst TIMEOUT = Symbol(\"timeout\");\n\nfunction within<T>(\n\tpromise: Promise<T>,\n\tms: number,\n): Promise<T | typeof TIMEOUT> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tconst deadline = new Promise<typeof TIMEOUT>((resolve) => {\n\t\ttimer = setTimeout(() => resolve(TIMEOUT), ms);\n\t});\n\treturn Promise.race([promise, deadline]).finally(() => clearTimeout(timer));\n}\n\nasync function readCapped(\n\tbody: ReadableStream<Uint8Array>,\n\tdeadline: Promise<typeof TIMEOUT>,\n\tmaxBytes: number,\n): Promise<Uint8Array<ArrayBuffer> | null> {\n\tconst reader = body.getReader();\n\tconst chunks: Uint8Array[] = [];\n\tlet size = 0;\n\ttry {\n\t\twhile (true) {\n\t\t\tconst result = await Promise.race([reader.read(), deadline]);\n\t\t\tif (result === TIMEOUT) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (result.done) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tsize += result.value.byteLength;\n\t\t\tif (size > maxBytes) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tchunks.push(result.value);\n\t\t}\n\t} finally {\n\t\treader.cancel().catch(() => {});\n\t}\n\tconst out = new Uint8Array(size);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\tout.set(chunk, offset);\n\t\toffset += chunk.byteLength;\n\t}\n\treturn out;\n}\n\n/** Fetches the stored page within both deadlines and the size cap. Returns its body and content type, or null. */\nexport async function fetchAgentPage(\n\trules: SiteRules,\n\thostname: string,\n\tpath: string,\n): Promise<{ body: Uint8Array<ArrayBuffer>; contentType: string } | null> {\n\tconst controller = new AbortController();\n\ttry {\n\t\tconst response = await within(\n\t\t\tfetch(`${rules.pagesUrl}/${hostname}${path}`, {\n\t\t\t\theaders: {\n\t\t\t\t\taccept: \"text/markdown, text/html;q=0.9\",\n\t\t\t\t\t\"x-waniwani-relay\": RELAY_VERSION,\n\t\t\t\t},\n\t\t\t\tsignal: controller.signal,\n\t\t\t}),\n\t\t\trules.deadlines.headersMs,\n\t\t);\n\t\tif (response === TIMEOUT) {\n\t\t\treturn null;\n\t\t}\n\t\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\t\tif (\n\t\t\tresponse.status !== 200 ||\n\t\t\t!contentType.toLowerCase().startsWith(\"text/\") ||\n\t\t\t!response.body\n\t\t) {\n\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\treturn null;\n\t\t}\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst deadline = new Promise<typeof TIMEOUT>((resolve) => {\n\t\t\ttimer = setTimeout(() => resolve(TIMEOUT), rules.deadlines.bodyMs);\n\t\t});\n\t\ttry {\n\t\t\tconst body = await readCapped(\n\t\t\t\tresponse.body,\n\t\t\t\tdeadline,\n\t\t\t\trules.maxBodyBytes,\n\t\t\t);\n\t\t\treturn body ? { body, contentType } : null;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tcontroller.abort();\n\t}\n}\n","export const HEADER_CAP: readonly string[] = [\n\t\"user-agent\",\n\t\"referer\",\n\t\"accept\",\n\t\"accept-language\",\n\t\"from\",\n\t\"signature-agent\",\n\t\"signature-input\",\n\t\"signature\",\n\t\"dpop\",\n\t\"sec-ch-ua\",\n\t\"sec-ch-ua-platform\",\n\t\"sec-ch-ua-mobile\",\n\t\"authorization\",\n];\n\nconst KNOWN_SCHEMES = new Map(\n\t[\n\t\t\"Basic\",\n\t\t\"Bearer\",\n\t\t\"Digest\",\n\t\t\"DPoP\",\n\t\t\"HOBA\",\n\t\t\"Mutual\",\n\t\t\"Negotiate\",\n\t\t\"NTLM\",\n\t\t\"OAuth\",\n\t\t\"PrivateToken\",\n\t\t\"SCRAM-SHA-1\",\n\t\t\"SCRAM-SHA-256\",\n\t\t\"vapid\",\n\t\t\"AWS4-HMAC-SHA256\",\n\t].map((scheme) => [scheme.toLowerCase(), scheme]),\n);\n\n/** The canonical name of a known Authorization scheme, kept only when a credential follows it. */\nexport function authorizationScheme(value: string): string | null {\n\tconst parts = value.trim().split(/\\s+/);\n\tif (parts.length < 2) {\n\t\treturn null;\n\t}\n\treturn KNOWN_SCHEMES.get((parts[0] ?? \"\").toLowerCase()) ?? null;\n}\n\nexport function readCappedHeaders(headers: Headers): Record<string, string> {\n\tconst out: Record<string, string> = {};\n\tfor (const name of HEADER_CAP) {\n\t\tconst value = headers.get(name);\n\t\tif (value === null) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (name === \"authorization\") {\n\t\t\tconst scheme = authorizationScheme(value);\n\t\t\tif (scheme !== null) {\n\t\t\t\tout[name] = scheme;\n\t\t\t}\n\t\t} else {\n\t\t\tout[name] = value;\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Cloudflare appends the real IP to a client-sent `X-Forwarded-For`, so `CF-Connecting-IP` comes first. */\nexport function clientIp(headers: Headers): string | null {\n\tconst forwarded = headers.get(\"x-forwarded-for\");\n\tconst candidate =\n\t\theaders.get(\"cf-connecting-ip\") ??\n\t\t(forwarded !== null ? forwarded.split(\",\")[0] : headers.get(\"x-real-ip\"));\n\tconst ip = candidate?.trim().toLowerCase();\n\treturn ip ? ip : null;\n}\n","import { settings } from \"./settings\";\n\nexport type Outcome = { status: number } | { failure: \"timeout\" | \"network\" };\n\n/** One fetch bounded by the timeout. Never rejects. */\nexport async function fetchWithTimeout(\n\turl: string,\n\tinit: RequestInit,\n\ttimeoutMs: number = settings.requestTimeoutMs,\n): Promise<Outcome> {\n\tconst controller = new AbortController();\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\ttry {\n\t\tconst response = await fetch(url, { ...init, signal: controller.signal });\n\t\tawait response.body?.cancel().catch(() => {});\n\t\treturn { status: response.status };\n\t} catch {\n\t\treturn { failure: controller.signal.aborted ? \"timeout\" : \"network\" };\n\t} finally {\n\t\tclearTimeout(timer);\n\t}\n}\n\nexport const delay = (ms: number) =>\n\tnew Promise<void>((resolve) => setTimeout(resolve, ms));\n","import { delay, fetchWithTimeout, type Outcome } from \"./http\";\nimport { settings } from \"./settings\";\n\nconst isSuccess = (outcome: Outcome) =>\n\t\"status\" in outcome && outcome.status >= 200 && outcome.status < 300;\n\nconst isRetryable = (outcome: Outcome) =>\n\t\"failure\" in outcome || outcome.status === 429 || outcome.status >= 500;\n\nconst describe = (outcome: Outcome) =>\n\t\"failure\" in outcome ? outcome.failure : `status ${outcome.status}`;\n\nexport type PostPolicy = { timeoutMs: number; retry: boolean };\n\nconst post = (url: string, body: string, timeoutMs: number) =>\n\tfetchWithTimeout(\n\t\turl,\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\tbody,\n\t\t},\n\t\ttimeoutMs,\n\t);\n\n/** Posts once, retries once on a timeout, network error, 429 or 5xx when the policy allows it, and never rejects. */\nexport async function postEvent(\n\turl: string,\n\tbody: string,\n\tpolicy: PostPolicy = { timeoutMs: settings.requestTimeoutMs, retry: true },\n): Promise<boolean> {\n\tlet outcome = await post(url, body, policy.timeoutMs);\n\tif (policy.retry && !isSuccess(outcome) && isRetryable(outcome)) {\n\t\tawait delay(settings.retryDelayMs);\n\t\toutcome = await post(url, body, policy.timeoutMs);\n\t}\n\tif (isSuccess(outcome)) {\n\t\treturn true;\n\t}\n\tconsole.warn(\n\t\t`[@waniwani/relay] Could not record an agent visit (${describe(outcome)}). The event was dropped.`,\n\t);\n\treturn false;\n}\n","export type RelayKey = { ingestToken: string; orgKey: string };\n\nconst KEY = /^wwt_([A-Za-z0-9_-]+)\\.([A-Za-z0-9_-]+={0,2})$/;\n\nlet warnedMalformed = false;\n\nexport function readKey(explicit: string | undefined): RelayKey | null {\n\tconst raw = explicit ?? readEnv(\"WANIWANI_AGENT_TRAFFIC_KEY\");\n\tif (raw === undefined || raw.trim() === \"\") {\n\t\treturn null;\n\t}\n\tconst match = KEY.exec(raw.trim());\n\tif (!match?.[1] || !match[2]) {\n\t\tif (!warnedMalformed) {\n\t\t\twarnedMalformed = true;\n\t\t\tconsole.warn(\n\t\t\t\t\"[@waniwani/relay] WANIWANI_AGENT_TRAFFIC_KEY is malformed, so no agent visits are recorded. Copy the key again from the WaniWani setup screen.\",\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n\treturn { ingestToken: match[1], orgKey: match[2] };\n}\n\nfunction readEnv(name: string): string | undefined {\n\ttry {\n\t\treturn typeof process === \"undefined\" ? undefined : process.env[name];\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n","import { BlockList } from \"node:net\";\nimport agents from \"./agents.generated.json\";\nimport ranges from \"./ip-ranges.generated.json\";\n\nexport type DecideInput = {\n\theaders: Record<string, string>;\n\tip: string | null;\n\tuseBundled?: boolean;\n};\n\nexport type MatchRules = {\n\ttokens: string[];\n\tipRanges: Array<{ ipv4: string[]; ipv6: string[] }>;\n};\n\nconst MAX_SCANNED_USER_AGENT = 1024;\nconst PREFIX = 3;\n\ntype CompiledRules = {\n\ttokensByPrefix: Map<string, string[]>;\n\tagentRanges: BlockList;\n};\n\nfunction compile(rules: MatchRules): CompiledRules {\n\tconst tokensByPrefix = new Map<string, string[]>();\n\tfor (const token of rules.tokens) {\n\t\tconst key = token.slice(0, PREFIX);\n\t\tconst list = tokensByPrefix.get(key);\n\t\tif (list) {\n\t\t\tlist.push(token);\n\t\t} else {\n\t\t\ttokensByPrefix.set(key, [token]);\n\t\t}\n\t}\n\n\tconst agentRanges = new BlockList();\n\tconst addRanges = (cidrs: string[], type: \"ipv4\" | \"ipv6\") => {\n\t\tfor (const cidr of cidrs) {\n\t\t\tconst [address = \"\", prefix, extra] = cidr.split(\"/\");\n\t\t\tif (!prefix || extra !== undefined) {\n\t\t\t\tthrow new Error(`malformed range ${cidr}`);\n\t\t\t}\n\t\t\tagentRanges.addSubnet(address, Number(prefix), type);\n\t\t}\n\t};\n\tfor (const source of rules.ipRanges) {\n\t\taddRanges(source.ipv4, \"ipv4\");\n\t\taddRanges(source.ipv6, \"ipv6\");\n\t}\n\n\treturn { tokensByPrefix, agentRanges };\n}\n\nconst bundled = compile({ tokens: agents.tokens, ipRanges: ranges.sources });\nlet remote: CompiledRules | null = null;\n\n/** Sets the remote match rules. Throws, and keeps the current ones, when a range is malformed. */\nexport function applyRules(rules: MatchRules): void {\n\tremote = compile(rules);\n}\n\nconst pick = (useBundled: boolean): CompiledRules =>\n\tuseBundled ? bundled : (remote ?? bundled);\n\nexport const isAgentIp = (ip: string, useBundled = false): boolean =>\n\tpick(useBundled).agentRanges.check(ip, ip.includes(\":\") ? \"ipv6\" : \"ipv4\");\n\nexport function hasAgentToken(userAgent: string, useBundled = false): boolean {\n\tconst { tokensByPrefix } = pick(useBundled);\n\tconst text = userAgent.slice(0, MAX_SCANNED_USER_AGENT).toLowerCase();\n\tfor (let i = 0; i + PREFIX <= text.length; i++) {\n\t\tconst candidates = tokensByPrefix.get(text.slice(i, i + PREFIX));\n\t\tif (candidates) {\n\t\t\tfor (const token of candidates) {\n\t\t\t\tif (text.startsWith(token, i)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nexport function decide({\n\theaders,\n\tip,\n\tuseBundled = false,\n}: DecideInput): boolean {\n\tconst userAgent = headers[\"user-agent\"];\n\treturn (\n\t\t(userAgent !== undefined && hasAgentToken(userAgent, useBundled)) ||\n\t\theaders[\"signature-agent\"] !== undefined ||\n\t\theaders.dpop !== undefined ||\n\t\theaders.authorization?.toLowerCase() === \"dpop\" ||\n\t\t(ip !== null && isAgentIp(ip, useBundled))\n\t);\n}\n","{\n\t\"source\": \"Generated by scripts/build-rules.ts from the WaniWani rules file d9b242ef451056c0, do not edit. Each token is matched case-insensitively inside the User-Agent header.\",\n\t\"tokens\": [\n\t\t\"addsearchbot\",\n\t\t\"agentdatabot\",\n\t\t\"agenttimes\",\n\t\t\"ai2bot\",\n\t\t\"ai2bot-deepresearcheval\",\n\t\t\"ai2bot-dolma\",\n\t\t\"aihitbot\",\n\t\t\"aiwebindex\",\n\t\t\"allenai.org\",\n\t\t\"amazon-kendra\",\n\t\t\"amazon-qbusiness\",\n\t\t\"amazonbot\",\n\t\t\"amazonbuyforme\",\n\t\t\"amzn-searchbot\",\n\t\t\"amzn-user\",\n\t\t\"andibot\",\n\t\t\"anthropic-ai\",\n\t\t\"apifybot\",\n\t\t\"apifywebsitecontentcrawler\",\n\t\t\"applebot\",\n\t\t\"aranet-searchbot\",\n\t\t\"atlassian-bot\",\n\t\t\"awario\",\n\t\t\"azureai-searchbot\",\n\t\t\"bedrockbot\",\n\t\t\"bigsur.ai\",\n\t\t\"bixelbot\",\n\t\t\"bravebot\",\n\t\t\"brightbot\",\n\t\t\"buddybot\",\n\t\t\"bytespider\",\n\t\t\"ccbot\",\n\t\t\"channel3bot\",\n\t\t\"chatglm-spider\",\n\t\t\"chatgpt agent\",\n\t\t\"chatgpt-user\",\n\t\t\"claude-code\",\n\t\t\"claude-searchbot\",\n\t\t\"claude-user\",\n\t\t\"claude-web\",\n\t\t\"claudebot\",\n\t\t\"cloudflare-autorag\",\n\t\t\"cloudflarebrowserrenderingcrawler\",\n\t\t\"cloudvertexbot\",\n\t\t\"cohere-ai\",\n\t\t\"cohere-training-data-crawler\",\n\t\t\"cotoyogi\",\n\t\t\"coveobot\",\n\t\t\"cragcrawler\",\n\t\t\"crawl4ai\",\n\t\t\"crawlspace\",\n\t\t\"cursor/1.0.0\",\n\t\t\"datenbank crawler\",\n\t\t\"deepseekbot\",\n\t\t\"diffbot\",\n\t\t\"diffbot-user\",\n\t\t\"doubaobot\",\n\t\t\"duckassistbot\",\n\t\t\"echobot bot\",\n\t\t\"echoboxbot\",\n\t\t\"erniebot\",\n\t\t\"exabot\",\n\t\t\"exasearchbot\",\n\t\t\"exauser\",\n\t\t\"facebookbot\",\n\t\t\"factset_spyderbot\",\n\t\t\"firecrawlagent\",\n\t\t\"friendlycrawler\",\n\t\t\"geisthaus-pagefetcher\",\n\t\t\"gemini-deep-research\",\n\t\t\"google-ads-creatives-assistant\",\n\t\t\"google-agent\",\n\t\t\"google-cloudvertexbot\",\n\t\t\"google-firebase\",\n\t\t\"google-gemini-cli\",\n\t\t\"google-gemininotebook\",\n\t\t\"google-lens\",\n\t\t\"google-notebooklm\",\n\t\t\"googleagent-mariner\",\n\t\t\"googleagent-urlcontext\",\n\t\t\"googleother\",\n\t\t\"googleother-image\",\n\t\t\"googleother-video\",\n\t\t\"gptbot\",\n\t\t\"grove/1.0 rss feed service\",\n\t\t\"henkbot\",\n\t\t\"iaskbot\",\n\t\t\"iaskspider\",\n\t\t\"icc-crawler\",\n\t\t\"imagesiftbot\",\n\t\t\"imagespider\",\n\t\t\"img2dataset\",\n\t\t\"isscyberriskcrawler\",\n\t\t\"jackjillcareersbot\",\n\t\t\"kagi-fetcher\",\n\t\t\"kangaroo bot\",\n\t\t\"keenablebot\",\n\t\t\"kernel searchbot\",\n\t\t\"kimi-agent\",\n\t\t\"kimi-searchbot\",\n\t\t\"kimi-user\",\n\t\t\"kimibot\",\n\t\t\"klaviyoaibot\",\n\t\t\"kunatocrawler\",\n\t\t\"laion-huggingface-processor\",\n\t\t\"laiondownloader\",\n\t\t\"lightpanda\",\n\t\t\"linerbot\",\n\t\t\"linkupbot\",\n\t\t\"manus-user\",\n\t\t\"meta-externalagent\",\n\t\t\"meta-externalfetcher\",\n\t\t\"meta-webindexer\",\n\t\t\"mistralai-index\",\n\t\t\"mistralai-training\",\n\t\t\"mistralai-user\",\n\t\t\"momentic\",\n\t\t\"mozilla-tabstack\",\n\t\t\"mycentralaiscraperbot\",\n\t\t\"nagetbot\",\n\t\t\"netestate imprint crawler\",\n\t\t\"notebooklm\",\n\t\t\"novaact\",\n\t\t\"oai-adsbot\",\n\t\t\"oai-searchbot\",\n\t\t\"omgili\",\n\t\t\"omgilibot\",\n\t\t\"pangubot\",\n\t\t\"panscient\",\n\t\t\"perplexity-user\",\n\t\t\"perplexitybot\",\n\t\t\"petalbot\",\n\t\t\"phindbot\",\n\t\t\"poggio-citations\",\n\t\t\"poseidon research crawler\",\n\t\t\"qodercli\",\n\t\t\"qualifiedbot\",\n\t\t\"querit-searchbot\",\n\t\t\"queritbot\",\n\t\t\"quillbot\",\n\t\t\"qwenbot\",\n\t\t\"reflectionbot\",\n\t\t\"ryebot\",\n\t\t\"sbintuitionsbot\",\n\t\t\"seltzsitemapfetcher\",\n\t\t\"semanticscholarbot\",\n\t\t\"semrushbot-ocob\",\n\t\t\"semrushbot-swa\",\n\t\t\"shap-user\",\n\t\t\"shapbot\",\n\t\t\"sidetrade indexer bot\",\n\t\t\"tavilybot\",\n\t\t\"terra cotta\",\n\t\t\"terracotta\",\n\t\t\"thinkbot\",\n\t\t\"tiktokspider\",\n\t\t\"timpibot\",\n\t\t\"tongyibot\",\n\t\t\"twilioknowledge\",\n\t\t\"twinagent\",\n\t\t\"v0bot\",\n\t\t\"velenpublicwebcrawler\",\n\t\t\"wardbot\",\n\t\t\"wrtnbot\",\n\t\t\"yandexadditional\",\n\t\t\"yandexadditionalbot\",\n\t\t\"yiyanbot\",\n\t\t\"youbot\",\n\t\t\"zanistabot\"\n\t]\n}\n","{\n\t\"sources\": [\n\t\t{\n\t\t\t\"bot\": \"GPTBot\",\n\t\t\t\"url\": \"https://openai.com/gptbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.227.36.0/25\",\n\t\t\t\t\"20.125.66.80/28\",\n\t\t\t\t\"20.171.206.0/24\",\n\t\t\t\t\"20.171.207.0/24\",\n\t\t\t\t\"52.230.152.0/24\",\n\t\t\t\t\"74.7.175.128/25\",\n\t\t\t\t\"74.7.227.0/25\",\n\t\t\t\t\"74.7.227.128/25\",\n\t\t\t\t\"74.7.228.0/25\",\n\t\t\t\t\"74.7.230.0/25\",\n\t\t\t\t\"74.7.241.0/25\",\n\t\t\t\t\"74.7.241.128/25\",\n\t\t\t\t\"74.7.242.0/25\",\n\t\t\t\t\"74.7.243.128/25\",\n\t\t\t\t\"74.7.244.0/25\",\n\t\t\t\t\"132.196.86.0/24\",\n\t\t\t\t\"172.182.202.0/25\",\n\t\t\t\t\"172.182.207.0/25\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"OAI-SearchBot\",\n\t\t\t\"url\": \"https://openai.com/searchbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.227.36.0/25\",\n\t\t\t\t\"13.66.216.176/28\",\n\t\t\t\t\"20.14.99.96/28\",\n\t\t\t\t\"20.25.151.224/28\",\n\t\t\t\t\"20.42.10.176/28\",\n\t\t\t\t\"20.168.18.32/28\",\n\t\t\t\t\"20.169.6.224/28\",\n\t\t\t\t\"20.169.7.48/28\",\n\t\t\t\t\"20.169.77.0/25\",\n\t\t\t\t\"20.171.53.224/28\",\n\t\t\t\t\"20.171.123.64/28\",\n\t\t\t\t\"23.102.145.48/28\",\n\t\t\t\t\"40.67.175.0/25\",\n\t\t\t\t\"40.90.214.16/28\",\n\t\t\t\t\"51.8.102.0/24\",\n\t\t\t\t\"74.7.175.128/25\",\n\t\t\t\t\"74.7.228.0/25\",\n\t\t\t\t\"74.7.228.128/25\",\n\t\t\t\t\"74.7.229.0/25\",\n\t\t\t\t\"74.7.229.128/25\",\n\t\t\t\t\"74.7.230.0/25\",\n\t\t\t\t\"74.7.241.128/25\",\n\t\t\t\t\"74.7.242.128/25\",\n\t\t\t\t\"74.7.243.0/25\",\n\t\t\t\t\"74.7.244.0/25\",\n\t\t\t\t\"104.210.140.128/28\",\n\t\t\t\t\"135.234.64.0/24\",\n\t\t\t\t\"172.182.193.80/28\",\n\t\t\t\t\"172.182.193.224/28\",\n\t\t\t\t\"172.182.194.32/28\",\n\t\t\t\t\"172.182.194.144/28\",\n\t\t\t\t\"172.182.195.48/28\",\n\t\t\t\t\"172.182.209.208/28\",\n\t\t\t\t\"172.182.211.192/28\",\n\t\t\t\t\"172.182.213.192/28\",\n\t\t\t\t\"172.182.224.0/28\",\n\t\t\t\t\"172.203.190.80/28\",\n\t\t\t\t\"172.203.190.112/28\",\n\t\t\t\t\"172.203.190.128/28\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"ChatGPT-User\",\n\t\t\t\"url\": \"https://openai.com/chatgpt-user.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.151.71.176/28\",\n\t\t\t\t\"4.151.119.48/28\",\n\t\t\t\t\"4.189.118.208/28\",\n\t\t\t\t\"4.189.119.48/28\",\n\t\t\t\t\"4.197.22.112/28\",\n\t\t\t\t\"4.197.64.0/28\",\n\t\t\t\t\"4.197.64.16/28\",\n\t\t\t\t\"4.197.64.48/28\",\n\t\t\t\t\"4.197.64.64/28\",\n\t\t\t\t\"4.197.115.112/28\",\n\t\t\t\t\"4.201.232.64/28\",\n\t\t\t\t\"4.205.128.176/28\",\n\t\t\t\t\"4.218.24.64/28\",\n\t\t\t\t\"4.226.200.16/28\",\n\t\t\t\t\"4.226.226.32/28\",\n\t\t\t\t\"9.129.0.0/16\",\n\t\t\t\t\"9.160.34.144/28\",\n\t\t\t\t\"9.160.36.16/28\",\n\t\t\t\t\"9.160.96.16/28\",\n\t\t\t\t\"9.160.128.16/28\",\n\t\t\t\t\"9.160.128.64/28\",\n\t\t\t\t\"9.160.163.128/28\",\n\t\t\t\t\"9.163.101.48/28\",\n\t\t\t\t\"9.205.8.48/28\",\n\t\t\t\t\"9.205.8.64/28\",\n\t\t\t\t\"9.205.25.128/28\",\n\t\t\t\t\"9.205.30.128/28\",\n\t\t\t\t\"9.205.30.144/28\",\n\t\t\t\t\"9.205.30.176/28\",\n\t\t\t\t\"9.205.128.32/28\",\n\t\t\t\t\"9.205.128.48/28\",\n\t\t\t\t\"9.223.181.208/28\",\n\t\t\t\t\"9.234.96.192/28\",\n\t\t\t\t\"9.234.97.96/28\",\n\t\t\t\t\"13.65.138.112/28\",\n\t\t\t\t\"13.67.72.16/28\",\n\t\t\t\t\"13.71.2.208/28\",\n\t\t\t\t\"13.76.32.208/28\",\n\t\t\t\t\"13.76.116.80/28\",\n\t\t\t\t\"13.83.167.128/28\",\n\t\t\t\t\"13.83.237.176/28\",\n\t\t\t\t\"20.45.178.144/28\",\n\t\t\t\t\"20.55.229.144/28\",\n\t\t\t\t\"20.57.199.192/28\",\n\t\t\t\t\"20.63.180.96/28\",\n\t\t\t\t\"20.63.221.64/28\",\n\t\t\t\t\"20.78.130.48/28\",\n\t\t\t\t\"20.83.243.176/28\",\n\t\t\t\t\"20.98.18.80/28\",\n\t\t\t\t\"20.102.212.144/28\",\n\t\t\t\t\"20.113.211.112/28\",\n\t\t\t\t\"20.113.225.112/28\",\n\t\t\t\t\"20.125.112.224/28\",\n\t\t\t\t\"20.125.144.144/28\",\n\t\t\t\t\"20.161.75.208/28\",\n\t\t\t\t\"20.168.7.192/28\",\n\t\t\t\t\"20.169.73.32/28\",\n\t\t\t\t\"20.169.73.64/28\",\n\t\t\t\t\"20.169.78.48/28\",\n\t\t\t\t\"20.169.78.64/28\",\n\t\t\t\t\"20.169.78.128/28\",\n\t\t\t\t\"20.169.78.160/28\",\n\t\t\t\t\"20.169.78.192/28\",\n\t\t\t\t\"20.169.86.224/28\",\n\t\t\t\t\"20.169.86.240/28\",\n\t\t\t\t\"20.170.184.16/28\",\n\t\t\t\t\"20.170.184.32/28\",\n\t\t\t\t\"20.170.184.48/28\",\n\t\t\t\t\"20.170.184.64/28\",\n\t\t\t\t\"20.170.184.80/28\",\n\t\t\t\t\"20.172.29.32/28\",\n\t\t\t\t\"20.199.211.160/28\",\n\t\t\t\t\"20.199.242.0/28\",\n\t\t\t\t\"20.200.212.240/28\",\n\t\t\t\t\"20.206.101.192/28\",\n\t\t\t\t\"20.210.211.192/28\",\n\t\t\t\t\"20.212.62.208/28\",\n\t\t\t\t\"20.215.187.208/28\",\n\t\t\t\t\"20.215.219.128/28\",\n\t\t\t\t\"20.215.219.160/28\",\n\t\t\t\t\"20.215.219.208/28\",\n\t\t\t\t\"20.218.30.240/28\",\n\t\t\t\t\"20.219.71.192/28\",\n\t\t\t\t\"20.219.161.192/28\",\n\t\t\t\t\"20.219.184.96/28\",\n\t\t\t\t\"20.222.36.192/28\",\n\t\t\t\t\"20.227.140.32/28\",\n\t\t\t\t\"20.228.106.176/28\",\n\t\t\t\t\"20.235.87.224/28\",\n\t\t\t\t\"20.249.63.208/28\",\n\t\t\t\t\"20.250.6.128/28\",\n\t\t\t\t\"20.250.136.64/28\",\n\t\t\t\t\"20.254.201.208/28\",\n\t\t\t\t\"23.98.142.176/28\",\n\t\t\t\t\"23.98.186.64/28\",\n\t\t\t\t\"23.98.186.96/28\",\n\t\t\t\t\"23.98.186.176/28\",\n\t\t\t\t\"23.98.186.192/28\",\n\t\t\t\t\"23.101.217.176/28\",\n\t\t\t\t\"23.102.140.144/28\",\n\t\t\t\t\"23.102.141.32/28\",\n\t\t\t\t\"40.74.200.208/28\",\n\t\t\t\t\"40.81.67.96/28\",\n\t\t\t\t\"40.81.234.144/28\",\n\t\t\t\t\"40.84.221.208/28\",\n\t\t\t\t\"40.84.221.224/28\",\n\t\t\t\t\"40.116.73.208/28\",\n\t\t\t\t\"40.119.36.240/28\",\n\t\t\t\t\"40.124.161.0/28\",\n\t\t\t\t\"48.221.40.176/28\",\n\t\t\t\t\"48.221.184.80/28\",\n\t\t\t\t\"48.221.184.96/28\",\n\t\t\t\t\"51.8.155.48/28\",\n\t\t\t\t\"51.8.155.112/28\",\n\t\t\t\t\"51.57.0.96/28\",\n\t\t\t\t\"51.59.24.64/28\",\n\t\t\t\t\"51.59.24.80/28\",\n\t\t\t\t\"51.59.48.80/28\",\n\t\t\t\t\"51.116.2.80/28\",\n\t\t\t\t\"51.116.221.96/28\",\n\t\t\t\t\"52.148.129.32/28\",\n\t\t\t\t\"52.156.77.144/28\",\n\t\t\t\t\"52.159.227.32/28\",\n\t\t\t\t\"52.159.249.96/28\",\n\t\t\t\t\"52.161.49.96/28\",\n\t\t\t\t\"52.165.212.48/28\",\n\t\t\t\t\"52.172.129.160/28\",\n\t\t\t\t\"52.173.219.96/28\",\n\t\t\t\t\"52.173.219.112/28\",\n\t\t\t\t\"52.173.221.16/28\",\n\t\t\t\t\"52.173.221.176/28\",\n\t\t\t\t\"52.173.221.208/28\",\n\t\t\t\t\"52.173.234.16/28\",\n\t\t\t\t\"52.173.234.80/28\",\n\t\t\t\t\"52.183.217.240/28\",\n\t\t\t\t\"52.190.137.16/28\",\n\t\t\t\t\"52.190.137.144/28\",\n\t\t\t\t\"52.190.139.48/28\",\n\t\t\t\t\"52.190.142.64/28\",\n\t\t\t\t\"52.190.190.16/28\",\n\t\t\t\t\"52.190.251.112/28\",\n\t\t\t\t\"52.225.75.208/28\",\n\t\t\t\t\"52.231.30.48/28\",\n\t\t\t\t\"52.231.34.176/28\",\n\t\t\t\t\"52.231.39.144/28\",\n\t\t\t\t\"52.231.50.64/28\",\n\t\t\t\t\"52.236.94.144/28\",\n\t\t\t\t\"52.241.146.208/28\",\n\t\t\t\t\"52.242.132.224/28\",\n\t\t\t\t\"52.242.132.240/28\",\n\t\t\t\t\"52.255.109.80/28\",\n\t\t\t\t\"52.255.109.96/28\",\n\t\t\t\t\"52.255.109.144/28\",\n\t\t\t\t\"52.255.111.0/28\",\n\t\t\t\t\"52.255.111.80/28\",\n\t\t\t\t\"57.154.174.112/28\",\n\t\t\t\t\"57.154.175.0/28\",\n\t\t\t\t\"57.154.187.32/28\",\n\t\t\t\t\"68.154.28.96/28\",\n\t\t\t\t\"68.220.57.64/28\",\n\t\t\t\t\"70.153.32.16/28\",\n\t\t\t\t\"70.153.32.32/28\",\n\t\t\t\t\"70.153.87.224/28\",\n\t\t\t\t\"70.153.189.192/28\",\n\t\t\t\t\"70.156.152.96/28\",\n\t\t\t\t\"74.7.35.48/28\",\n\t\t\t\t\"74.7.35.112/28\",\n\t\t\t\t\"74.7.36.64/28\",\n\t\t\t\t\"74.7.36.80/28\",\n\t\t\t\t\"74.7.36.96/28\",\n\t\t\t\t\"74.161.200.96/28\",\n\t\t\t\t\"74.224.217.64/28\",\n\t\t\t\t\"74.226.253.160/28\",\n\t\t\t\t\"74.248.37.160/28\",\n\t\t\t\t\"85.211.128.16/28\",\n\t\t\t\t\"85.211.128.32/28\",\n\t\t\t\t\"104.208.184.192/28\",\n\t\t\t\t\"104.210.139.192/28\",\n\t\t\t\t\"104.210.139.224/28\",\n\t\t\t\t\"128.85.198.32/28\",\n\t\t\t\t\"132.196.82.48/28\",\n\t\t\t\t\"134.33.102.192/28\",\n\t\t\t\t\"134.138.52.16/28\",\n\t\t\t\t\"134.138.52.64/28\",\n\t\t\t\t\"134.138.52.96/28\",\n\t\t\t\t\"134.138.52.128/28\",\n\t\t\t\t\"134.138.57.64/28\",\n\t\t\t\t\"134.138.57.80/28\",\n\t\t\t\t\"134.138.57.176/28\",\n\t\t\t\t\"134.138.58.32/28\",\n\t\t\t\t\"134.149.233.80/28\",\n\t\t\t\t\"135.13.64.240/28\",\n\t\t\t\t\"135.116.136.160/28\",\n\t\t\t\t\"135.220.73.208/28\",\n\t\t\t\t\"135.220.73.240/28\",\n\t\t\t\t\"135.222.99.32/28\",\n\t\t\t\t\"135.237.131.208/28\",\n\t\t\t\t\"135.237.133.48/28\",\n\t\t\t\t\"137.135.191.176/28\",\n\t\t\t\t\"138.91.46.96/28\",\n\t\t\t\t\"145.132.136.96/28\",\n\t\t\t\t\"158.158.5.32/28\",\n\t\t\t\t\"168.63.252.240/28\",\n\t\t\t\t\"172.162.248.64/28\",\n\t\t\t\t\"172.170.1.80/28\",\n\t\t\t\t\"172.170.225.0/28\",\n\t\t\t\t\"172.170.241.80/28\",\n\t\t\t\t\"172.172.206.48/28\",\n\t\t\t\t\"172.175.152.224/28\",\n\t\t\t\t\"172.178.140.144/28\",\n\t\t\t\t\"172.178.141.112/28\",\n\t\t\t\t\"172.178.141.128/28\",\n\t\t\t\t\"172.183.143.224/28\",\n\t\t\t\t\"172.183.222.128/28\",\n\t\t\t\t\"172.192.112.208/28\",\n\t\t\t\t\"172.197.160.192/28\",\n\t\t\t\t\"172.197.203.16/28\",\n\t\t\t\t\"172.198.58.176/28\",\n\t\t\t\t\"172.198.78.192/28\",\n\t\t\t\t\"172.198.79.112/28\",\n\t\t\t\t\"172.198.161.128/28\",\n\t\t\t\t\"172.199.137.80/28\",\n\t\t\t\t\"172.204.28.224/28\",\n\t\t\t\t\"172.204.96.80/28\",\n\t\t\t\t\"172.205.189.192/28\",\n\t\t\t\t\"172.206.38.240/28\",\n\t\t\t\t\"172.207.1.32/28\",\n\t\t\t\t\"172.212.172.160/28\",\n\t\t\t\t\"172.215.215.32/28\",\n\t\t\t\t\"191.232.238.96/28\",\n\t\t\t\t\"191.233.197.0/28\",\n\t\t\t\t\"191.233.199.160/28\",\n\t\t\t\t\"191.234.167.144/28\",\n\t\t\t\t\"191.235.99.160/28\",\n\t\t\t\t\"191.237.249.64/28\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"ClaudeBot\",\n\t\t\t\"url\": \"https://claude.com/crawling/bots.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"34.152.124.140/32\",\n\t\t\t\t\"34.153.21.190/32\",\n\t\t\t\t\"34.162.27.34/32\",\n\t\t\t\t\"34.162.86.102/32\",\n\t\t\t\t\"34.162.90.102/32\",\n\t\t\t\t\"34.162.111.78/32\",\n\t\t\t\t\"34.162.153.71/32\",\n\t\t\t\t\"34.162.191.81/32\",\n\t\t\t\t\"34.162.230.222/32\",\n\t\t\t\t\"34.162.244.71/32\",\n\t\t\t\t\"34.186.252.4/32\",\n\t\t\t\t\"136.79.143.251/32\",\n\t\t\t\t\"136.83.20.215/32\",\n\t\t\t\t\"216.73.216.0/22\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"PerplexityBot\",\n\t\t\t\"url\": \"https://www.perplexity.com/perplexitybot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"3.222.232.239/32\",\n\t\t\t\t\"3.224.62.45/32\",\n\t\t\t\t\"18.97.1.228/32\",\n\t\t\t\t\"18.97.9.96/32\",\n\t\t\t\t\"18.97.9.97/32\",\n\t\t\t\t\"18.97.9.98/32\",\n\t\t\t\t\"18.97.9.99/32\",\n\t\t\t\t\"18.97.9.100/32\",\n\t\t\t\t\"18.97.9.101/32\",\n\t\t\t\t\"18.97.9.102/32\",\n\t\t\t\t\"18.97.9.103/32\",\n\t\t\t\t\"18.210.92.235/32\",\n\t\t\t\t\"107.20.236.150/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Perplexity-User\",\n\t\t\t\"url\": \"https://www.perplexity.com/perplexity-user.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"18.97.43.80/32\",\n\t\t\t\t\"18.97.43.81/32\",\n\t\t\t\t\"18.97.43.82/32\",\n\t\t\t\t\"18.97.43.83/32\",\n\t\t\t\t\"18.97.43.84/32\",\n\t\t\t\t\"18.97.43.85/32\",\n\t\t\t\t\"18.97.43.86/32\",\n\t\t\t\t\"18.97.43.87/32\",\n\t\t\t\t\"34.193.163.52/32\",\n\t\t\t\t\"44.208.221.197/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"MistralAI-User\",\n\t\t\t\"url\": \"https://mistral.ai/mistralai-user-ips.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"20.240.160.1/32\",\n\t\t\t\t\"20.240.160.161/32\",\n\t\t\t\t\"20.240.194.83/32\",\n\t\t\t\t\"51.12.243.114/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"CCBot\",\n\t\t\t\"url\": \"https://index.commoncrawl.org/ccbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"3.41.188.32/29\",\n\t\t\t\t\"18.97.9.168/29\",\n\t\t\t\t\"18.97.14.80/29\",\n\t\t\t\t\"18.97.14.88/30\"\n\t\t\t],\n\t\t\t\"ipv6\": [\n\t\t\t\t\"2600:1f28:365:8000::/56\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"DuckAssistBot\",\n\t\t\t\"url\": \"https://duckduckgo.com/duckassistbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.144.148.23/32\",\n\t\t\t\t\"4.144.182.50/32\",\n\t\t\t\t\"4.144.227.142/32\",\n\t\t\t\t\"4.144.251.39/32\",\n\t\t\t\t\"4.149.142.7/32\",\n\t\t\t\t\"4.149.159.71/32\",\n\t\t\t\t\"4.149.159.102/32\",\n\t\t\t\t\"4.149.216.220/32\",\n\t\t\t\t\"4.150.142.218/32\",\n\t\t\t\t\"4.154.193.167/32\",\n\t\t\t\t\"4.156.30.123/32\",\n\t\t\t\t\"4.156.136.79/32\",\n\t\t\t\t\"4.156.154.107/32\",\n\t\t\t\t\"4.157.77.76/32\",\n\t\t\t\t\"4.172.49.103/32\",\n\t\t\t\t\"4.177.16.196/32\",\n\t\t\t\t\"4.182.10.198/32\",\n\t\t\t\t\"4.182.131.108/32\",\n\t\t\t\t\"4.195.133.120/32\",\n\t\t\t\t\"4.201.125.59/32\",\n\t\t\t\t\"4.201.141.71/32\",\n\t\t\t\t\"4.201.197.203/32\",\n\t\t\t\t\"4.201.206.133/32\",\n\t\t\t\t\"4.201.220.8/32\",\n\t\t\t\t\"4.207.220.92/32\",\n\t\t\t\t\"4.209.224.56/32\",\n\t\t\t\t\"4.213.46.14/32\",\n\t\t\t\t\"4.226.40.135/32\",\n\t\t\t\t\"4.228.76.163/32\",\n\t\t\t\t\"4.237.155.54/32\",\n\t\t\t\t\"4.237.244.80/32\",\n\t\t\t\t\"4.242.119.13/32\",\n\t\t\t\t\"4.246.10.252/32\",\n\t\t\t\t\"4.248.234.26/32\",\n\t\t\t\t\"4.249.216.104/32\",\n\t\t\t\t\"4.249.223.51/32\",\n\t\t\t\t\"4.249.238.11/32\",\n\t\t\t\t\"4.254.123.56/32\",\n\t\t\t\t\"4.255.35.121/32\",\n\t\t\t\t\"4.255.39.80/32\",\n\t\t\t\t\"13.86.35.212/32\",\n\t\t\t\t\"13.89.106.77/32\",\n\t\t\t\t\"13.90.178.139/32\",\n\t\t\t\t\"20.3.1.178/32\",\n\t\t\t\t\"20.3.60.212/32\",\n\t\t\t\t\"20.8.252.26/32\",\n\t\t\t\t\"20.9.114.242/32\",\n\t\t\t\t\"20.12.141.99/32\",\n\t\t\t\t\"20.12.249.123/32\",\n\t\t\t\t\"20.12.252.21/32\",\n\t\t\t\t\"20.13.44.19/32\",\n\t\t\t\t\"20.15.142.178/32\",\n\t\t\t\t\"20.23.88.231/32\",\n\t\t\t\t\"20.29.149.44/32\",\n\t\t\t\t\"20.29.154.56/32\",\n\t\t\t\t\"20.40.133.240/32\",\n\t\t\t\t\"20.40.147.172/32\",\n\t\t\t\t\"20.43.150.85/32\",\n\t\t\t\t\"20.43.150.93/32\",\n\t\t\t\t\"20.43.152.28/32\",\n\t\t\t\t\"20.43.172.120/32\",\n\t\t\t\t\"20.44.222.1/32\",\n\t\t\t\t\"20.49.129.236/32\",\n\t\t\t\t\"20.49.136.28/32\",\n\t\t\t\t\"20.50.48.159/32\",\n\t\t\t\t\"20.50.48.192/32\",\n\t\t\t\t\"20.50.49.0/32\",\n\t\t\t\t\"20.50.49.25/32\",\n\t\t\t\t\"20.50.49.40/32\",\n\t\t\t\t\"20.50.49.55/32\",\n\t\t\t\t\"20.50.49.237/32\",\n\t\t\t\t\"20.50.50.46/32\",\n\t\t\t\t\"20.50.50.118/32\",\n\t\t\t\t\"20.50.50.121/32\",\n\t\t\t\t\"20.50.50.123/32\",\n\t\t\t\t\"20.50.50.130/32\",\n\t\t\t\t\"20.50.50.134/32\",\n\t\t\t\t\"20.50.50.145/32\",\n\t\t\t\t\"20.50.50.146/32\",\n\t\t\t\t\"20.50.50.163/32\",\n\t\t\t\t\"20.50.168.91/32\",\n\t\t\t\t\"20.53.78.106/32\",\n\t\t\t\t\"20.53.78.123/32\",\n\t\t\t\t\"20.53.78.138/32\",\n\t\t\t\t\"20.53.78.144/32\",\n\t\t\t\t\"20.53.78.236/32\",\n\t\t\t\t\"20.53.91.2/32\",\n\t\t\t\t\"20.53.92.211/32\",\n\t\t\t\t\"20.53.134.160/32\",\n\t\t\t\t\"20.54.224.39/32\",\n\t\t\t\t\"20.56.197.58/32\",\n\t\t\t\t\"20.56.197.63/32\",\n\t\t\t\t\"20.59.0.65/32\",\n\t\t\t\t\"20.61.34.40/32\",\n\t\t\t\t\"20.61.142.192/32\",\n\t\t\t\t\"20.62.224.44/32\",\n\t\t\t\t\"20.64.185.45/32\",\n\t\t\t\t\"20.69.74.250/32\",\n\t\t\t\t\"20.69.129.205/32\",\n\t\t\t\t\"20.69.131.45/32\",\n\t\t\t\t\"20.69.138.149/32\",\n\t\t\t\t\"20.69.138.189/32\",\n\t\t\t\t\"20.69.144.137/32\",\n\t\t\t\t\"20.69.146.171/32\",\n\t\t\t\t\"20.69.147.21/32\",\n\t\t\t\t\"20.69.171.204/32\",\n\t\t\t\t\"20.71.12.143/32\",\n\t\t\t\t\"20.71.69.210/32\",\n\t\t\t\t\"20.72.197.110/32\",\n\t\t\t\t\"20.72.203.250/32\",\n\t\t\t\t\"20.72.218.29/32\",\n\t\t\t\t\"20.72.221.33/32\",\n\t\t\t\t\"20.72.221.45/32\",\n\t\t\t\t\"20.72.225.13/32\",\n\t\t\t\t\"20.72.242.93/32\",\n\t\t\t\t\"20.72.242.209/32\",\n\t\t\t\t\"20.73.132.240/32\",\n\t\t\t\t\"20.73.202.147/32\",\n\t\t\t\t\"20.75.144.152/32\",\n\t\t\t\t\"20.75.254.61/32\",\n\t\t\t\t\"20.77.146.108/32\",\n\t\t\t\t\"20.79.226.26/32\",\n\t\t\t\t\"20.79.238.198/32\",\n\t\t\t\t\"20.79.239.66/32\",\n\t\t\t\t\"20.80.114.203/32\",\n\t\t\t\t\"20.80.120.41/32\",\n\t\t\t\t\"20.80.129.80/32\",\n\t\t\t\t\"20.80.135.7/32\",\n\t\t\t\t\"20.80.156.88/32\",\n\t\t\t\t\"20.81.115.13/32\",\n\t\t\t\t\"20.82.24.207/32\",\n\t\t\t\t\"20.84.138.61/32\",\n\t\t\t\t\"20.84.219.45/32\",\n\t\t\t\t\"20.84.251.223/32\",\n\t\t\t\t\"20.85.138.75/32\",\n\t\t\t\t\"20.85.167.56/32\",\n\t\t\t\t\"20.85.187.146/32\",\n\t\t\t\t\"20.88.168.65/32\",\n\t\t\t\t\"20.93.28.24/32\",\n\t\t\t\t\"20.93.49.96/32\",\n\t\t\t\t\"20.99.154.113/32\",\n\t\t\t\t\"20.99.255.235/32\",\n\t\t\t\t\"20.100.136.36/32\",\n\t\t\t\t\"20.100.140.155/32\",\n\t\t\t\t\"20.101.17.173/32\",\n\t\t\t\t\"20.102.34.205/32\",\n\t\t\t\t\"20.109.129.162/32\",\n\t\t\t\t\"20.109.130.190/32\",\n\t\t\t\t\"20.109.241.18/32\",\n\t\t\t\t\"20.112.58.44/32\",\n\t\t\t\t\"20.113.3.121/32\",\n\t\t\t\t\"20.113.14.159/32\",\n\t\t\t\t\"20.115.181.187/32\",\n\t\t\t\t\"20.118.11.251/32\",\n\t\t\t\t\"20.119.127.146/32\",\n\t\t\t\t\"20.121.75.32/32\",\n\t\t\t\t\"20.166.171.150/32\",\n\t\t\t\t\"20.170.75.54/32\",\n\t\t\t\t\"20.175.232.228/32\",\n\t\t\t\t\"20.185.79.15/32\",\n\t\t\t\t\"20.185.79.47/32\",\n\t\t\t\t\"20.190.21.172/32\",\n\t\t\t\t\"20.191.44.16/32\",\n\t\t\t\t\"20.191.44.22/32\",\n\t\t\t\t\"20.191.44.119/32\",\n\t\t\t\t\"20.191.44.234/32\",\n\t\t\t\t\"20.191.45.212/32\",\n\t\t\t\t\"20.193.12.126/32\",\n\t\t\t\t\"20.193.24.10/32\",\n\t\t\t\t\"20.193.24.251/32\",\n\t\t\t\t\"20.193.25.197/32\",\n\t\t\t\t\"20.193.27.215/32\",\n\t\t\t\t\"20.193.45.113/32\",\n\t\t\t\t\"20.195.58.189/32\",\n\t\t\t\t\"20.195.108.47/32\",\n\t\t\t\t\"20.197.209.11/32\",\n\t\t\t\t\"20.197.209.27/32\",\n\t\t\t\t\"20.198.132.126/32\",\n\t\t\t\t\"20.198.152.208/32\",\n\t\t\t\t\"20.201.15.208/32\",\n\t\t\t\t\"20.204.228.200/32\",\n\t\t\t\t\"20.204.240.172/32\",\n\t\t\t\t\"20.204.241.148/32\",\n\t\t\t\t\"20.204.242.19/32\",\n\t\t\t\t\"20.204.242.101/32\",\n\t\t\t\t\"20.204.243.55/32\",\n\t\t\t\t\"20.204.246.81/32\",\n\t\t\t\t\"20.204.246.254/32\",\n\t\t\t\t\"20.207.72.11/32\",\n\t\t\t\t\"20.207.72.21/32\",\n\t\t\t\t\"20.207.72.110/32\",\n\t\t\t\t\"20.207.72.113/32\",\n\t\t\t\t\"20.207.97.190/32\",\n\t\t\t\t\"20.207.99.197/32\",\n\t\t\t\t\"20.207.107.181/32\",\n\t\t\t\t\"20.207.117.66/32\",\n\t\t\t\t\"20.212.90.107/32\",\n\t\t\t\t\"20.212.139.34/32\",\n\t\t\t\t\"20.216.200.223/32\",\n\t\t\t\t\"20.219.43.246/32\",\n\t\t\t\t\"20.219.45.67/32\",\n\t\t\t\t\"20.219.45.190/32\",\n\t\t\t\t\"20.221.10.115/32\",\n\t\t\t\t\"20.221.84.185/32\",\n\t\t\t\t\"20.226.133.105/32\",\n\t\t\t\t\"20.231.254.179/32\",\n\t\t\t\t\"20.232.51.46/32\",\n\t\t\t\t\"20.232.249.95/32\",\n\t\t\t\t\"20.236.40.93/32\",\n\t\t\t\t\"20.236.195.196/32\",\n\t\t\t\t\"20.236.198.192/32\",\n\t\t\t\t\"20.236.227.113/32\",\n\t\t\t\t\"20.236.233.23/32\",\n\t\t\t\t\"20.236.247.180/32\",\n\t\t\t\t\"20.237.1.51/32\",\n\t\t\t\t\"20.241.135.19/32\",\n\t\t\t\t\"20.241.147.151/32\",\n\t\t\t\t\"20.241.153.203/32\",\n\t\t\t\t\"20.241.164.5/32\",\n\t\t\t\t\"20.241.241.100/32\",\n\t\t\t\t\"20.242.133.53/32\",\n\t\t\t\t\"20.242.135.165/32\",\n\t\t\t\t\"20.242.211.139/32\",\n\t\t\t\t\"20.242.215.53/32\",\n\t\t\t\t\"20.242.237.240/32\",\n\t\t\t\t\"20.246.132.168/32\",\n\t\t\t\t\"20.246.225.34/32\",\n\t\t\t\t\"20.247.235.11/32\",\n\t\t\t\t\"20.250.51.113/32\",\n\t\t\t\t\"20.252.9.6/32\",\n\t\t\t\t\"20.252.16.193/32\",\n\t\t\t\t\"20.252.41.18/32\",\n\t\t\t\t\"20.252.63.162/32\",\n\t\t\t\t\"20.252.88.10/32\",\n\t\t\t\t\"20.253.59.76/32\",\n\t\t\t\t\"20.253.96.199/32\",\n\t\t\t\t\"20.253.114.51/32\",\n\t\t\t\t\"40.64.67.220/32\",\n\t\t\t\t\"40.64.78.45/32\",\n\t\t\t\t\"40.64.88.145/32\",\n\t\t\t\t\"40.64.105.247/32\",\n\t\t\t\t\"40.64.106.11/32\",\n\t\t\t\t\"40.64.106.152/32\",\n\t\t\t\t\"40.65.78.86/32\",\n\t\t\t\t\"40.76.162.17/32\",\n\t\t\t\t\"40.76.162.191/32\",\n\t\t\t\t\"40.76.162.208/32\",\n\t\t\t\t\"40.76.162.247/32\",\n\t\t\t\t\"40.76.163.7/32\",\n\t\t\t\t\"40.76.163.23/32\",\n\t\t\t\t\"40.76.173.151/32\",\n\t\t\t\t\"40.81.250.205/32\",\n\t\t\t\t\"40.82.218.203/32\",\n\t\t\t\t\"40.88.21.235/32\",\n\t\t\t\t\"40.88.195.62/32\",\n\t\t\t\t\"40.89.243.175/32\",\n\t\t\t\t\"40.114.182.45/32\",\n\t\t\t\t\"40.114.182.153/32\",\n\t\t\t\t\"40.114.182.172/32\",\n\t\t\t\t\"40.114.183.88/32\",\n\t\t\t\t\"40.114.183.196/32\",\n\t\t\t\t\"40.114.183.251/32\",\n\t\t\t\t\"40.119.232.50/32\",\n\t\t\t\t\"40.119.232.146/32\",\n\t\t\t\t\"40.119.232.215/32\",\n\t\t\t\t\"40.119.232.218/32\",\n\t\t\t\t\"40.119.232.251/32\",\n\t\t\t\t\"40.121.121.54/32\",\n\t\t\t\t\"40.127.154.196/32\",\n\t\t\t\t\"48.192.16.86/32\",\n\t\t\t\t\"48.192.74.178/32\",\n\t\t\t\t\"48.200.33.227/32\",\n\t\t\t\t\"48.202.155.200/32\",\n\t\t\t\t\"48.202.171.124/32\",\n\t\t\t\t\"48.204.32.87/32\",\n\t\t\t\t\"48.206.18.152/32\",\n\t\t\t\t\"48.217.23.236/32\",\n\t\t\t\t\"48.217.129.210/32\",\n\t\t\t\t\"48.217.212.89/32\",\n\t\t\t\t\"48.223.209.110/32\",\n\t\t\t\t\"51.8.31.28/32\",\n\t\t\t\t\"51.8.71.117/32\",\n\t\t\t\t\"51.8.253.152/32\",\n\t\t\t\t\"51.104.144.31/32\",\n\t\t\t\t\"51.104.146.225/32\",\n\t\t\t\t\"51.104.146.235/32\",\n\t\t\t\t\"51.104.160.167/32\",\n\t\t\t\t\"51.104.160.177/32\",\n\t\t\t\t\"51.104.161.32/32\",\n\t\t\t\t\"51.104.162.149/32\",\n\t\t\t\t\"51.104.163.250/32\",\n\t\t\t\t\"51.104.164.109/32\",\n\t\t\t\t\"51.104.164.147/32\",\n\t\t\t\t\"51.104.164.189/32\",\n\t\t\t\t\"51.104.164.215/32\",\n\t\t\t\t\"51.104.166.111/32\",\n\t\t\t\t\"51.104.167.19/32\",\n\t\t\t\t\"51.104.167.52/32\",\n\t\t\t\t\"51.104.167.54/32\",\n\t\t\t\t\"51.104.167.61/32\",\n\t\t\t\t\"51.104.167.71/32\",\n\t\t\t\t\"51.104.167.87/32\",\n\t\t\t\t\"51.104.167.88/32\",\n\t\t\t\t\"51.104.167.95/32\",\n\t\t\t\t\"51.104.167.96/32\",\n\t\t\t\t\"51.104.167.104/32\",\n\t\t\t\t\"51.104.167.110/32\",\n\t\t\t\t\"51.104.180.26/32\",\n\t\t\t\t\"51.104.180.47/32\",\n\t\t\t\t\"51.104.180.53/32\",\n\t\t\t\t\"51.107.40.209/32\",\n\t\t\t\t\"51.116.131.221/32\",\n\t\t\t\t\"51.120.48.122/32\",\n\t\t\t\t\"51.138.90.161/32\",\n\t\t\t\t\"51.138.90.206/32\",\n\t\t\t\t\"51.138.90.233/32\",\n\t\t\t\t\"51.143.114.45/32\",\n\t\t\t\t\"51.145.185.16/32\",\n\t\t\t\t\"52.142.24.149/32\",\n\t\t\t\t\"52.142.26.175/32\",\n\t\t\t\t\"52.143.95.162/32\",\n\t\t\t\t\"52.143.95.204/32\",\n\t\t\t\t\"52.143.241.111/32\",\n\t\t\t\t\"52.143.242.6/32\",\n\t\t\t\t\"52.143.243.117/32\",\n\t\t\t\t\"52.143.244.81/32\",\n\t\t\t\t\"52.143.247.235/32\",\n\t\t\t\t\"52.146.57.51/32\",\n\t\t\t\t\"52.146.58.236/32\",\n\t\t\t\t\"52.146.59.12/32\",\n\t\t\t\t\"52.146.59.154/32\",\n\t\t\t\t\"52.146.59.156/32\",\n\t\t\t\t\"52.146.63.80/32\",\n\t\t\t\t\"52.148.161.87/32\",\n\t\t\t\t\"52.148.165.38/32\",\n\t\t\t\t\"52.149.25.43/32\",\n\t\t\t\t\"52.149.28.18/32\",\n\t\t\t\t\"52.149.28.83/32\",\n\t\t\t\t\"52.149.30.45/32\",\n\t\t\t\t\"52.149.56.151/32\",\n\t\t\t\t\"52.149.58.27/32\",\n\t\t\t\t\"52.149.58.69/32\",\n\t\t\t\t\"52.149.58.139/32\",\n\t\t\t\t\"52.149.58.173/32\",\n\t\t\t\t\"52.149.60.38/32\",\n\t\t\t\t\"52.149.61.51/32\",\n\t\t\t\t\"52.154.60.82/32\",\n\t\t\t\t\"52.154.169.50/32\",\n\t\t\t\t\"52.154.169.200/32\",\n\t\t\t\t\"52.154.170.26/32\",\n\t\t\t\t\"52.154.170.28/32\",\n\t\t\t\t\"52.154.170.88/32\",\n\t\t\t\t\"52.154.170.96/32\",\n\t\t\t\t\"52.154.170.113/32\",\n\t\t\t\t\"52.154.170.117/32\",\n\t\t\t\t\"52.154.170.122/32\",\n\t\t\t\t\"52.154.170.209/32\",\n\t\t\t\t\"52.154.170.229/32\",\n\t\t\t\t\"52.154.170.243/32\",\n\t\t\t\t\"52.154.171.0/32\",\n\t\t\t\t\"52.154.171.44/32\",\n\t\t\t\t\"52.154.171.70/32\",\n\t\t\t\t\"52.154.171.87/32\",\n\t\t\t\t\"52.154.171.150/32\",\n\t\t\t\t\"52.154.171.196/32\",\n\t\t\t\t\"52.154.171.205/32\",\n\t\t\t\t\"52.154.171.235/32\",\n\t\t\t\t\"52.154.171.250/32\",\n\t\t\t\t\"52.154.172.2/32\",\n\t\t\t\t\"52.154.204.93/32\",\n\t\t\t\t\"52.185.93.208/32\",\n\t\t\t\t\"52.186.37.211/32\",\n\t\t\t\t\"52.188.89.106/32\",\n\t\t\t\t\"52.189.70.103/32\",\n\t\t\t\t\"52.190.37.160/32\",\n\t\t\t\t\"52.191.82.242/32\",\n\t\t\t\t\"52.191.222.158/32\",\n\t\t\t\t\"52.224.16.221/32\",\n\t\t\t\t\"52.224.16.229/32\",\n\t\t\t\t\"52.224.19.152/32\",\n\t\t\t\t\"52.224.20.174/32\",\n\t\t\t\t\"52.224.20.181/32\",\n\t\t\t\t\"52.224.20.186/32\",\n\t\t\t\t\"52.224.20.190/32\",\n\t\t\t\t\"52.224.20.193/32\",\n\t\t\t\t\"52.224.20.203/32\",\n\t\t\t\t\"52.224.20.204/32\",\n\t\t\t\t\"52.224.20.223/32\",\n\t\t\t\t\"52.224.20.227/32\",\n\t\t\t\t\"52.224.20.249/32\",\n\t\t\t\t\"52.224.21.4/32\",\n\t\t\t\t\"52.224.21.19/32\",\n\t\t\t\t\"52.224.21.20/32\",\n\t\t\t\t\"52.224.21.23/32\",\n\t\t\t\t\"52.224.21.27/32\",\n\t\t\t\t\"52.224.21.49/32\",\n\t\t\t\t\"52.224.21.51/32\",\n\t\t\t\t\"52.224.21.53/32\",\n\t\t\t\t\"52.224.21.55/32\",\n\t\t\t\t\"52.224.21.61/32\",\n\t\t\t\t\"52.224.147.21/32\",\n\t\t\t\t\"52.224.191.245/32\",\n\t\t\t\t\"52.242.224.168/32\",\n\t\t\t\t\"52.250.46.221/32\",\n\t\t\t\t\"57.151.44.140/32\",\n\t\t\t\t\"57.152.17.237/32\",\n\t\t\t\t\"57.152.62.107/32\",\n\t\t\t\t\"57.152.72.128/32\",\n\t\t\t\t\"57.155.18.44/32\",\n\t\t\t\t\"57.165.205.172/32\",\n\t\t\t\t\"57.167.88.38/32\",\n\t\t\t\t\"64.236.15.33/32\",\n\t\t\t\t\"64.236.93.100/32\",\n\t\t\t\t\"64.236.118.43/32\",\n\t\t\t\t\"68.219.152.220/32\",\n\t\t\t\t\"72.144.95.185/32\",\n\t\t\t\t\"74.163.169.106/32\",\n\t\t\t\t\"74.163.208.206/32\",\n\t\t\t\t\"74.179.200.126/32\",\n\t\t\t\t\"74.179.232.116/32\",\n\t\t\t\t\"74.242.194.48/32\",\n\t\t\t\t\"98.70.219.37/32\",\n\t\t\t\t\"104.43.54.127/32\",\n\t\t\t\t\"104.43.55.116/32\",\n\t\t\t\t\"104.43.55.117/32\",\n\t\t\t\t\"104.43.55.166/32\",\n\t\t\t\t\"104.43.55.167/32\",\n\t\t\t\t\"108.141.83.74/32\",\n\t\t\t\t\"128.203.82.154/32\",\n\t\t\t\t\"128.203.132.152/32\",\n\t\t\t\t\"128.203.181.79/32\",\n\t\t\t\t\"128.203.214.241/32\",\n\t\t\t\t\"128.203.236.110/32\",\n\t\t\t\t\"130.107.228.224/32\",\n\t\t\t\t\"131.145.39.118/32\",\n\t\t\t\t\"131.189.183.241/32\",\n\t\t\t\t\"132.164.209.198/32\",\n\t\t\t\t\"132.196.106.146/32\",\n\t\t\t\t\"132.220.1.71/32\",\n\t\t\t\t\"134.33.207.189/32\",\n\t\t\t\t\"135.171.251.169/32\",\n\t\t\t\t\"135.233.63.237/32\",\n\t\t\t\t\"135.234.221.112/32\",\n\t\t\t\t\"135.235.183.95/32\",\n\t\t\t\t\"172.168.12.93/32\",\n\t\t\t\t\"172.168.43.43/32\",\n\t\t\t\t\"172.168.51.216/32\",\n\t\t\t\t\"172.168.53.53/32\",\n\t\t\t\t\"172.168.81.217/32\",\n\t\t\t\t\"172.168.94.160/32\",\n\t\t\t\t\"172.168.115.250/32\",\n\t\t\t\t\"172.168.133.76/32\",\n\t\t\t\t\"172.168.137.102/32\",\n\t\t\t\t\"172.168.152.88/32\",\n\t\t\t\t\"172.168.173.230/32\",\n\t\t\t\t\"172.168.190.186/32\",\n\t\t\t\t\"172.168.227.120/32\",\n\t\t\t\t\"172.168.254.119/32\",\n\t\t\t\t\"172.169.7.33/32\",\n\t\t\t\t\"172.169.17.165/32\",\n\t\t\t\t\"172.169.28.184/32\",\n\t\t\t\t\"172.169.36.43/32\",\n\t\t\t\t\"172.169.53.208/32\",\n\t\t\t\t\"172.169.60.134/32\",\n\t\t\t\t\"172.169.93.148/32\",\n\t\t\t\t\"172.169.164.109/32\",\n\t\t\t\t\"172.169.177.131/32\",\n\t\t\t\t\"172.169.181.237/32\",\n\t\t\t\t\"172.171.37.221/32\",\n\t\t\t\t\"172.179.105.87/32\",\n\t\t\t\t\"172.179.128.118/32\",\n\t\t\t\t\"172.188.250.220/32\",\n\t\t\t\t\"172.189.40.18/32\",\n\t\t\t\t\"172.193.184.42/32\",\n\t\t\t\t\"172.193.197.146/32\",\n\t\t\t\t\"172.193.245.229/32\",\n\t\t\t\t\"172.194.141.49/32\",\n\t\t\t\t\"172.199.55.212/32\",\n\t\t\t\t\"172.210.111.68/32\",\n\t\t\t\t\"191.233.3.197/32\",\n\t\t\t\t\"191.233.3.202/32\",\n\t\t\t\t\"191.234.216.4/32\",\n\t\t\t\t\"191.234.216.178/32\",\n\t\t\t\t\"191.235.201.214/32\",\n\t\t\t\t\"191.235.202.38/32\",\n\t\t\t\t\"191.235.202.48/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Applebot\",\n\t\t\t\"url\": \"https://search.developer.apple.com/applebot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"17.166.20.0/24\",\n\t\t\t\t\"17.166.21.0/24\",\n\t\t\t\t\"17.166.22.0/24\",\n\t\t\t\t\"17.166.23.0/24\",\n\t\t\t\t\"17.166.24.0/24\",\n\t\t\t\t\"17.166.25.0/24\",\n\t\t\t\t\"17.166.150.0/24\",\n\t\t\t\t\"17.166.151.0/24\",\n\t\t\t\t\"17.166.152.0/24\",\n\t\t\t\t\"17.166.153.0/24\",\n\t\t\t\t\"17.166.154.0/24\",\n\t\t\t\t\"17.166.155.0/24\",\n\t\t\t\t\"17.166.192.48/28\",\n\t\t\t\t\"17.166.232.0/24\",\n\t\t\t\t\"17.166.233.0/24\",\n\t\t\t\t\"17.166.234.0/24\",\n\t\t\t\t\"17.166.235.0/24\",\n\t\t\t\t\"17.166.236.0/24\",\n\t\t\t\t\"17.166.237.0/24\",\n\t\t\t\t\"17.166.240.48/28\",\n\t\t\t\t\"17.166.248.48/28\",\n\t\t\t\t\"17.241.193.160/27\",\n\t\t\t\t\"17.241.200.160/27\",\n\t\t\t\t\"17.241.208.160/27\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Claude API\",\n\t\t\t\"url\": \"https://platform.claude.com/docs/en/api/ip-addresses.md\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"153.61.208.0/20\",\n\t\t\t\t\"160.79.104.0/21\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t}\n\t]\n}\n","const encoder = new TextEncoder();\nconst keys = new Map<string, Promise<CryptoKey>>();\n\nfunction importOrgKey(orgKey: string): Promise<CryptoKey> {\n\tlet key = keys.get(orgKey);\n\tif (!key) {\n\t\tkey = crypto.subtle.importKey(\n\t\t\t\"raw\",\n\t\t\tencoder.encode(orgKey),\n\t\t\t{ name: \"HMAC\", hash: \"SHA-256\" },\n\t\t\tfalse,\n\t\t\t[\"sign\"],\n\t\t);\n\t\tkey.catch(() => keys.delete(orgKey));\n\t\tkeys.set(orgKey, key);\n\t}\n\treturn key;\n}\n\nconst toHex = (bytes: ArrayBuffer): string =>\n\tArray.from(new Uint8Array(bytes), (byte) =>\n\t\tbyte.toString(16).padStart(2, \"0\"),\n\t).join(\"\");\n\nexport async function deriveMatchKey(\n\torgKey: string,\n\tip: string,\n): Promise<string> {\n\tconst signature = await crypto.subtle.sign(\n\t\t\"HMAC\",\n\t\tawait importOrgKey(orgKey),\n\t\tencoder.encode(ip),\n\t);\n\treturn toHex(signature);\n}\n","import { applyRules, type MatchRules } from \"./logic/decide\";\nimport { settings } from \"./settings\";\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null;\n\nconst isStringArray = (value: unknown): value is string[] =>\n\tArray.isArray(value) && value.every((item) => typeof item === \"string\");\n\n/** The match rules in a rules file from the app (the `data` of its answer), or null when it does not have the expected form. A malformed range makes `applyRules` throw instead. */\nexport function parseRemoteRules(body: unknown): MatchRules | null {\n\tif (!isObject(body) || body.version !== 1) {\n\t\treturn null;\n\t}\n\tconst { tokens, ipRanges } = body;\n\tif (!isStringArray(tokens) || !Array.isArray(ipRanges)) {\n\t\treturn null;\n\t}\n\tconst ranges: MatchRules[\"ipRanges\"] = [];\n\tfor (const range of ipRanges) {\n\t\tif (\n\t\t\t!isObject(range) ||\n\t\t\t!isStringArray(range.ipv4) ||\n\t\t\t!isStringArray(range.ipv6)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tranges.push({ ipv4: range.ipv4, ipv6: range.ipv6 });\n\t}\n\treturn {\n\t\ttokens: tokens.map((token) => token.toLowerCase()),\n\t\tipRanges: ranges,\n\t};\n}\n\nlet lastAttemptOk = false;\nlet lastAttemptAt = Number.NEGATIVE_INFINITY;\n\nasync function fetchRules(apiUrl: string): Promise<boolean> {\n\tconst response = await fetch(`${apiUrl}/api/v2/agent-traffic/rules`, {\n\t\tsignal: AbortSignal.timeout(settings.requestTimeoutMs),\n\t});\n\tif (!response.ok) {\n\t\treturn false;\n\t}\n\tconst body: unknown = await response.json();\n\tconst rules = parseRemoteRules(isObject(body) ? body.data : null);\n\tif (!rules) {\n\t\treturn false;\n\t}\n\tapplyRules(rules);\n\treturn true;\n}\n\n/**\n * Starts one fetch of the rules file when the last attempt succeeded more than the refresh interval ago,\n * or failed more than the retry interval ago. Returns that work, or null.\n */\nexport function refreshRules(apiUrl: string): Promise<void> | null {\n\tconst now = Date.now();\n\tconst wait = lastAttemptOk ? settings.rulesRefreshMs : settings.rulesRetryMs;\n\tif (now - lastAttemptAt < wait) {\n\t\treturn null;\n\t}\n\tlastAttemptAt = now;\n\tlastAttemptOk = false;\n\treturn fetchRules(apiUrl).then(\n\t\t(ok) => {\n\t\t\tlastAttemptOk = ok;\n\t\t},\n\t\t() => undefined,\n\t);\n}\n","type WaitUntil = (promise: Promise<unknown>) => void;\ntype RequestContextStore = {\n\tget?: () => { waitUntil?: WaitUntil } | undefined;\n};\n\nconst REQUEST_CONTEXT = Symbol.for(\"@vercel/request-context\");\n\nexport function vercelRequestContext(): { waitUntil?: WaitUntil } | undefined {\n\tconst store: RequestContextStore | undefined = Reflect.get(\n\t\tglobalThis,\n\t\tREQUEST_CONTEXT,\n\t);\n\treturn store?.get?.();\n}\n\n/** The `waitUntil` that the Vercel runtime puts in the request context, the same one `@vercel/functions` reads. */\nexport function vercelWaitUntil(): WaitUntil | undefined {\n\tconst context = vercelRequestContext();\n\treturn context?.waitUntil\n\t\t? (promise) => context.waitUntil?.(promise)\n\t\t: undefined;\n}\n","import { vercelRequestContext } from \"./wait-until\";\n\nexport type Platform = \"vercel\" | \"cloudflare\" | \"cloudfront\";\n\n/** The platform this request runs on, or undefined on a runtime the relay does not know. Never throws. */\nexport function detectPlatform(): Platform | undefined {\n\ttry {\n\t\tconst context = vercelRequestContext();\n\t\tif (typeof context === \"object\" && context !== null) {\n\t\t\treturn \"vercel\";\n\t\t}\n\t\tif (globalThis.navigator?.userAgent === \"Cloudflare-Workers\") {\n\t\t\treturn \"cloudflare\";\n\t\t}\n\t} catch {}\n\treturn undefined;\n}\n","import { clientIp, HEADER_CAP } from \"./headers\";\nimport { detectPlatform, type Platform } from \"./platform\";\n\n/** `event.Records[0].cf.request` of a Lambda@Edge viewer-request event. */\nexport type CloudFrontRequest = {\n\tclientIp: string;\n\tmethod: string;\n\turi: string;\n\tquerystring: string;\n\theaders: Record<string, Array<{ key?: string; value: string }>>;\n};\n\nexport type RelayRequest = {\n\turl: string | null;\n\tmethod: string;\n\theaders: Headers;\n\tip: string | null;\n\tplatform: Platform | undefined;\n};\n\nexport function isCloudFrontRequest(\n\tinput: Request | CloudFrontRequest,\n): input is CloudFrontRequest {\n\treturn (\n\t\ttypeof Reflect.get(input, \"clientIp\") === \"string\" &&\n\t\ttypeof Reflect.get(input, \"uri\") === \"string\"\n\t);\n}\n\n/** The origin for a `host` header, or null when the value holds more than a host and an optional port. */\nfunction originOf(host: string | undefined): string | null {\n\tif (!host) {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst url = new URL(`https://${host}`);\n\t\tconst bare =\n\t\t\turl.pathname === \"/\" &&\n\t\t\turl.search === \"\" &&\n\t\t\turl.hash === \"\" &&\n\t\t\turl.username === \"\" &&\n\t\t\turl.password === \"\";\n\t\treturn bare ? url.origin : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** The hostname in a `host` header or a bare hostname, lowercased and without the port, or null when the value holds more. */\nexport function hostnameOf(host: string | undefined): string | null {\n\tconst origin = originOf(host);\n\treturn origin === null ? null : new URL(origin).hostname;\n}\n\nexport function cloudFrontHeader(\n\tinput: CloudFrontRequest,\n\tname: string,\n): string | null {\n\tconst value = input.headers?.[name]?.[0]?.value;\n\treturn typeof value === \"string\" ? value : null;\n}\n\n/** Copies only the headers the relay reads, so a cookie or a value `Headers` refuses never makes the request fail. */\nfunction cloudFrontHeaders(input: CloudFrontRequest[\"headers\"]): Headers {\n\tconst headers = new Headers();\n\tfor (const name of HEADER_CAP) {\n\t\tfor (const entry of input?.[name] ?? []) {\n\t\t\tif (typeof entry?.value !== \"string\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\theaders.append(name, entry.value);\n\t\t\t} catch {}\n\t\t}\n\t}\n\treturn headers;\n}\n\n/** The client can set `X-Forwarded-For` at viewer-request, so a CloudFront request takes its IP from `clientIp` alone. `url` is null when the host header is missing or malformed. */\nexport function toRelayRequest(\n\tinput: Request | CloudFrontRequest,\n): RelayRequest {\n\tif (!isCloudFrontRequest(input)) {\n\t\treturn {\n\t\t\turl: input.url,\n\t\t\tmethod: input.method,\n\t\t\theaders: input.headers,\n\t\t\tip: clientIp(input.headers),\n\t\t\tplatform: detectPlatform(),\n\t\t};\n\t}\n\tconst origin = originOf(input.headers?.host?.[0]?.value);\n\tconst query = input.querystring ? `?${input.querystring}` : \"\";\n\tconst ip = input.clientIp.trim().toLowerCase();\n\treturn {\n\t\turl: origin === null ? null : `${origin}${input.uri}${query}`,\n\t\tmethod: input.method,\n\t\theaders: cloudFrontHeaders(input.headers),\n\t\tip: ip ? ip : null,\n\t\tplatform: \"cloudfront\",\n\t};\n}\n","import {\n\tfetchAgentPage,\n\tloadSiteRules,\n\tmatchesPath,\n\tnormalizePath,\n\ttype SiteRules,\n} from \"./agent-page\";\nimport { readCappedHeaders } from \"./headers\";\nimport { type PostPolicy, postEvent } from \"./ingest\";\nimport { readKey } from \"./key\";\nimport { decide } from \"./logic\";\nimport { deriveMatchKey } from \"./match-key\";\nimport type { Platform } from \"./platform\";\nimport { refreshRules } from \"./refresh\";\nimport {\n\ttype CloudFrontRequest,\n\tcloudFrontHeader,\n\thostnameOf,\n\tisCloudFrontRequest,\n\ttoRelayRequest,\n} from \"./request\";\nimport { settings } from \"./settings\";\nimport { RELAY_VERSION } from \"./version\";\nimport { vercelWaitUntil } from \"./wait-until\";\n\nexport type { CloudFrontRequest };\n\n/** The response a Lambda@Edge handler returns in place of the request. */\nexport type CloudFrontResultResponse = {\n\tstatus: string;\n\tstatusDescription?: string;\n\theaders: Record<string, Array<{ key: string; value: string }>>;\n\tbody?: string;\n};\n\nexport type Region = keyof typeof settings.apiUrls;\n\nexport type RelayOptions = {\n\t/** The region of your WaniWani organization. Defaults to `\"us\"`. */\n\tregion?: Region;\n\t/** Overrides the host that `region` picks. */\n\tapiUrl?: string;\n\t/** Overrides `process.env.WANIWANI_AGENT_TRAFFIC_KEY`. */\n\tkey?: string;\n\t/** Keeps the background post alive after the response. Defaults to the `waitUntil` of the Vercel request context. */\n\twaitUntil?: (promise: Promise<unknown>) => void;\n\t/** Header names, and the fields `matchKey`, `userAgent` or `referer`, to leave out of every event. */\n\tomit?: Array<string>;\n\t/** `\"remote\"` loads the newest AI bot list from WaniWani every 12 hours. `\"bundled\"` keeps the list shipped in this version. Defaults to `\"remote\"`. */\n\trules?: \"remote\" | \"bundled\";\n};\n\nexport type IsAgentOptions = Pick<\n\tRelayOptions,\n\t\"region\" | \"apiUrl\" | \"waitUntil\" | \"rules\"\n>;\n\nexport type ServeAgentPageOptions = Pick<\n\tRelayOptions,\n\t\"region\" | \"apiUrl\" | \"key\"\n>;\n\nexport type AgentVisitRecord = {\n\toccurredAt: string;\n\tmethod: string;\n\thost: string;\n\tpath: string;\n\tuserAgent?: string;\n\treferer?: string;\n\theaders: Record<string, string>;\n\tmatchKey?: string;\n\tsourceEventId: string;\n\trelayVersion: string;\n\tplatform?: Platform;\n};\n\n/**\n * Tells whether a request, or the `cf.request` of a Lambda@Edge event, comes from an AI agent. Reads headers only and never waits on the network.\n * Every 12 hours it starts one background fetch of the newest AI bot list, unless `rules` is `\"bundled\"`.\n */\nexport function isAgent(\n\trequest: Request | CloudFrontRequest,\n\toptions: IsAgentOptions = {},\n): boolean {\n\tstartRulesRefresh(options);\n\ttry {\n\t\tconst { headers, ip } = toRelayRequest(request);\n\t\treturn decide({\n\t\t\theaders: readCappedHeaders(headers),\n\t\t\tip,\n\t\t\tuseBundled: options.rules === \"bundled\",\n\t\t});\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction startRulesRefresh(options: IsAgentOptions): void {\n\ttry {\n\t\tif (options.rules === \"bundled\") {\n\t\t\treturn;\n\t\t}\n\t\tconst apiUrl = resolveApiUrl(options);\n\t\tconst work = apiUrl ? refreshRules(apiUrl) : null;\n\t\tif (work) {\n\t\t\tschedule(work, options.waitUntil);\n\t\t}\n\t} catch {}\n}\n\n/**\n * Posts one event for this request to WaniWani after the response.\n * Sends nothing when no key is set. Never throws, never rejects and never touches the request.\n * The promise settles when the post is done, so Lambda@Edge, which has no `waitUntil`, awaits it.\n */\nexport function recordAgentVisit(\n\trequest: Request | CloudFrontRequest,\n\toptions: RelayOptions = {},\n): Promise<void> {\n\ttry {\n\t\tconst key = readKey(options.key);\n\t\tif (!key) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst apiUrl = resolveApiUrl(options);\n\t\tif (!apiUrl) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst omitted = omitSet(options.omit);\n\t\tconst {\n\t\t\turl: requestUrl,\n\t\t\tmethod,\n\t\t\theaders: requestHeaders,\n\t\t\tip,\n\t\t\tplatform,\n\t\t} = toRelayRequest(request);\n\t\tif (requestUrl === null) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst capped = readCappedHeaders(requestHeaders);\n\n\t\tconst headers: Record<string, string> = {};\n\t\tfor (const [name, value] of Object.entries(capped)) {\n\t\t\tif (!omitted.has(name)) {\n\t\t\t\theaders[name] = value;\n\t\t\t}\n\t\t}\n\t\tconst url = new URL(requestUrl);\n\t\tconst record: AgentVisitRecord = {\n\t\t\toccurredAt: new Date().toISOString(),\n\t\t\tmethod,\n\t\t\thost: url.hostname,\n\t\t\tpath: url.pathname + url.search,\n\t\t\theaders,\n\t\t\tsourceEventId: crypto.randomUUID(),\n\t\t\trelayVersion: RELAY_VERSION,\n\t\t};\n\t\tif (capped[\"user-agent\"] !== undefined && !omitted.has(\"user-agent\")) {\n\t\t\trecord.userAgent = capped[\"user-agent\"];\n\t\t}\n\t\tif (capped.referer !== undefined && !omitted.has(\"referer\")) {\n\t\t\trecord.referer = capped.referer;\n\t\t}\n\t\tif (platform !== undefined) {\n\t\t\trecord.platform = platform;\n\t\t}\n\n\t\tconst policy: PostPolicy =\n\t\t\tplatform === \"cloudfront\"\n\t\t\t\t? { timeoutMs: settings.cloudFrontTimeoutMs, retry: false }\n\t\t\t\t: { timeoutMs: settings.requestTimeoutMs, retry: true };\n\t\tconst ingestUrl = `${apiUrl}/api/v2/agent-traffic/ingest/middleware/${key.ingestToken}`;\n\t\tconst withMatchKey = async () => {\n\t\t\tif (ip !== null && !omitted.has(\"matchkey\")) {\n\t\t\t\trecord.matchKey = await deriveMatchKey(key.orgKey, ip).catch(\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait postEvent(\n\t\t\t\tingestUrl,\n\t\t\t\tJSON.stringify({ requests: [record] }),\n\t\t\t\tpolicy,\n\t\t\t);\n\t\t};\n\t\treturn schedule(withMatchKey(), options.waitUntil);\n\t} catch {\n\t\treturn Promise.resolve();\n\t}\n}\n\ntype PageRequest = {\n\tmethod: string;\n\thostname: string;\n\tpathname: string;\n\tuserAgent: string;\n\trendering: boolean;\n};\n\ntype PageAnswer = {\n\tstatus: 200 | 403;\n\theaders: Record<string, string>;\n\tbody: Uint8Array<ArrayBuffer> | string | null;\n};\n\ntype SiteMatch = {\n\trules: SiteRules;\n\tsite: SiteRules[\"sites\"][number];\n};\n\nconst BLOCKED: Omit<PageAnswer, \"body\"> = {\n\tstatus: 403,\n\theaders: {\n\t\t\"content-type\": \"text/plain; charset=utf-8\",\n\t\t\"x-waniwani-blocked\": \"1\",\n\t\t\"cache-control\": \"private, no-store\",\n\t\tvary: \"user-agent\",\n\t},\n};\n\n/**\n * The stored agent page for this request, when its site lists the path and the user agent acts for a user. A 403 when the site blocks the user agent. Otherwise null, and the caller serves the origin.\n * A `CloudFrontRequest` comes from the origin-request handler. Only a URI that `handleViewerRequest` marked gets the page, read for the host in the mark. The function restores `request.uri` to the original path before anything else, and it never returns a 403 on this path.\n * Never throws and never rejects.\n */\nexport function serveAgentPage(\n\trequest: Request,\n\toptions?: ServeAgentPageOptions,\n): Promise<Response | null>;\nexport function serveAgentPage(\n\trequest: CloudFrontRequest,\n\toptions?: ServeAgentPageOptions,\n): Promise<CloudFrontResultResponse | null>;\nexport async function serveAgentPage(\n\trequest: Request | CloudFrontRequest,\n\toptions: ServeAgentPageOptions = {},\n): Promise<Response | CloudFrontResultResponse | null> {\n\ttry {\n\t\treturn isCloudFrontRequest(request)\n\t\t\t? await serveCloudFront(request, options)\n\t\t\t: await serveFetch(request, options);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function serveFetch(\n\trequest: Request,\n\toptions: ServeAgentPageOptions,\n): Promise<Response | null> {\n\tconst url = new URL(request.url);\n\tconst answer = await answerAgentPage(\n\t\t{\n\t\t\tmethod: request.method.toUpperCase(),\n\t\t\thostname: url.hostname,\n\t\t\tpathname: url.pathname,\n\t\t\tuserAgent: request.headers.get(\"user-agent\") ?? \"\",\n\t\t\trendering: request.headers.has(\"x-waniwani-render\"),\n\t\t},\n\t\toptions,\n\t);\n\tif (!answer) {\n\t\treturn null;\n\t}\n\treturn new Response(answer.body, {\n\t\tstatus: answer.status,\n\t\theaders: answer.headers,\n\t});\n}\n\nasync function serveCloudFront(\n\trequest: CloudFrontRequest,\n\toptions: ServeAgentPageOptions,\n): Promise<CloudFrontResultResponse | null> {\n\tconst hostname = takeMarkFromUri(request);\n\tif (hostname === null) {\n\t\treturn null;\n\t}\n\tconst method = request.method.toUpperCase();\n\tif (!isPageMethod(method)) {\n\t\treturn null;\n\t}\n\tconst match = await findSite(hostname, options);\n\tif (!match) {\n\t\treturn null;\n\t}\n\tconst answer = await pageAnswer(\n\t\tmatch.rules,\n\t\thostname,\n\t\tnormalizePath(request.uri),\n\t\tmethod,\n\t);\n\tif (!answer) {\n\t\treturn null;\n\t}\n\tconst result = toCloudFrontResponse(answer);\n\tif (\n\t\tresult.body !== undefined &&\n\t\tnew TextEncoder().encode(result.body).byteLength >\n\t\t\tsettings.cloudFrontMaxBodyBytes\n\t) {\n\t\treturn null;\n\t}\n\treturn result;\n}\n\nfunction toCloudFrontResponse(answer: PageAnswer): CloudFrontResultResponse {\n\tconst headers: CloudFrontResultResponse[\"headers\"] = {};\n\tfor (const [name, value] of Object.entries(answer.headers)) {\n\t\theaders[name] = [{ key: headerKey(name), value }];\n\t}\n\tconst result: CloudFrontResultResponse = {\n\t\tstatus: String(answer.status),\n\t\tstatusDescription: answer.status === 200 ? \"OK\" : \"Forbidden\",\n\t\theaders,\n\t};\n\tif (answer.body !== null) {\n\t\tresult.body =\n\t\t\ttypeof answer.body === \"string\"\n\t\t\t\t? answer.body\n\t\t\t\t: new TextDecoder().decode(answer.body);\n\t}\n\treturn result;\n}\n\nfunction headerKey(name: string): string {\n\treturn name\n\t\t.split(\"-\")\n\t\t.map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n\t\t.join(\"-\");\n}\n\nconst isPageMethod = (method: string): boolean =>\n\tmethod === \"GET\" || method === \"HEAD\";\n\nasync function findSite(\n\thostname: string,\n\toptions: ServeAgentPageOptions,\n): Promise<SiteMatch | null> {\n\tconst key = readKey(options.key);\n\tif (!key) {\n\t\treturn null;\n\t}\n\tconst apiUrl = resolveApiUrl(options);\n\tif (!apiUrl) {\n\t\treturn null;\n\t}\n\tconst rules = await loadSiteRules(apiUrl, key.ingestToken, (work) =>\n\t\tschedule(work, undefined),\n\t);\n\tif (!rules) {\n\t\treturn null;\n\t}\n\tconst site = rules.sites.find((entry) => entry.hostname === hostname);\n\treturn site ? { rules, site } : null;\n}\n\nfunction blockedAnswer(\n\tsite: SiteMatch[\"site\"],\n\tuserAgent: string,\n\tmethod: string,\n): PageAnswer | null {\n\treturn site.blockedUserAgentTokens.some((token) => userAgent.includes(token))\n\t\t? { ...BLOCKED, body: method === \"HEAD\" ? null : \"Forbidden\" }\n\t\t: null;\n}\n\nasync function answerAgentPage(\n\trequest: PageRequest,\n\toptions: ServeAgentPageOptions,\n): Promise<PageAnswer | null> {\n\tconst { method, hostname } = request;\n\tif (!isPageMethod(method)) {\n\t\treturn null;\n\t}\n\tconst match = await findSite(hostname, options);\n\tif (!match) {\n\t\treturn null;\n\t}\n\tconst { rules, site } = match;\n\tconst userAgent = request.userAgent.toLowerCase();\n\tconst blocked = blockedAnswer(site, userAgent, method);\n\tif (blocked) {\n\t\treturn blocked;\n\t}\n\tif (request.rendering || !site.agentPages.enabled) {\n\t\treturn null;\n\t}\n\tconst path = normalizePath(request.pathname);\n\tif (!matchesPath(path, site.agentPages.paths)) {\n\t\treturn null;\n\t}\n\tif (!rules.userAgentTokens.some((token) => userAgent.includes(token))) {\n\t\treturn null;\n\t}\n\treturn pageAnswer(rules, hostname, path, method);\n}\n\nasync function pageAnswer(\n\trules: SiteRules,\n\thostname: string,\n\tpath: string,\n\tmethod: string,\n): Promise<PageAnswer | null> {\n\tconst page = await fetchAgentPage(rules, hostname, path);\n\tif (!page) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"content-type\": page.contentType,\n\t\t\t\"x-waniwani-agent-page\": \"hit\",\n\t\t\t\"cache-control\": \"private, no-store\",\n\t\t\tvary: \"user-agent, accept\",\n\t\t},\n\t\tbody: method === \"HEAD\" ? null : page.body,\n\t};\n}\n\nconst AGENT_URI_PREFIX = \"/__waniwani-agent/\";\n\nfunction splitAgentUri(\n\turi: string,\n): { encodedHost: string; originalUri: string } | null {\n\tif (!uri.startsWith(AGENT_URI_PREFIX)) {\n\t\treturn null;\n\t}\n\tconst rest = uri.slice(AGENT_URI_PREFIX.length);\n\tconst slash = rest.indexOf(\"/\");\n\treturn slash === -1\n\t\t? { encodedHost: rest, originalUri: \"/\" }\n\t\t: { encodedHost: rest.slice(0, slash), originalUri: rest.slice(slash) };\n}\n\nfunction stripForgedMark(request: CloudFrontRequest): void {\n\tlet forged = splitAgentUri(request.uri);\n\twhile (forged) {\n\t\trequest.uri = forged.originalUri;\n\t\tforged = splitAgentUri(request.uri);\n\t}\n}\n\nfunction takeMarkFromUri(request: CloudFrontRequest): string | null {\n\tconst parts = splitAgentUri(request.uri);\n\tif (!parts) {\n\t\treturn null;\n\t}\n\trequest.uri = parts.originalUri;\n\ttry {\n\t\treturn hostnameOf(decodeURIComponent(parts.encodedHost));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * For the viewer-request handler on CloudFront. Strips a `/__waniwani-agent/` prefix the client sent, then, for a GET or HEAD from an AI agent, loads the rules of the site of the `host` header.\n * Returns the 403 response when the site blocks the user agent. CloudFront never caches a response made at viewer-request, so a block takes effect at once.\n * When the site serves an agent page for the path and the user agent acts for a user, rewrites `request.uri` to `/__waniwani-agent/<host><original uri>` so the origin-request handler serves the page under its own cache key. The request stays unmarked when the new URI would pass the CloudFront URI limit.\n * Call `recordAgentVisit` before it, so the event holds the original path. Never throws and never rejects.\n */\nexport async function handleViewerRequest(\n\trequest: CloudFrontRequest,\n\toptions: ServeAgentPageOptions = {},\n): Promise<CloudFrontResultResponse | null> {\n\ttry {\n\t\tstripForgedMark(request);\n\t\tconst method = request.method.toUpperCase();\n\t\tif (!isPageMethod(method) || !isAgent(request, options)) {\n\t\t\treturn null;\n\t\t}\n\t\tconst hostname = hostnameOf(cloudFrontHeader(request, \"host\") ?? undefined);\n\t\tif (hostname === null) {\n\t\t\treturn null;\n\t\t}\n\t\tconst match = await findSite(hostname, options);\n\t\tif (!match) {\n\t\t\treturn null;\n\t\t}\n\t\tconst { rules, site } = match;\n\t\tconst userAgent = (\n\t\t\tcloudFrontHeader(request, \"user-agent\") ?? \"\"\n\t\t).toLowerCase();\n\t\tconst blocked = blockedAnswer(site, userAgent, method);\n\t\tif (blocked) {\n\t\t\treturn toCloudFrontResponse(blocked);\n\t\t}\n\t\tif (\n\t\t\tcloudFrontHeader(request, \"x-waniwani-render\") !== null ||\n\t\t\t!site.agentPages.enabled ||\n\t\t\t!matchesPath(normalizePath(request.uri), site.agentPages.paths) ||\n\t\t\t!rules.userAgentTokens.some((token) => userAgent.includes(token))\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tconst uri = `${AGENT_URI_PREFIX}${encodeURIComponent(hostname)}${request.uri}`;\n\t\tif (\n\t\t\tnew TextEncoder().encode(uri).byteLength <= settings.cloudFrontMaxUriBytes\n\t\t) {\n\t\t\trequest.uri = uri;\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nlet warnedRegion = false;\n\nfunction resolveApiUrl(options: IsAgentOptions): string | null {\n\tif (options.apiUrl !== undefined) {\n\t\treturn options.apiUrl.replace(/\\/+$/, \"\");\n\t}\n\tconst region = options.region ?? \"us\";\n\tif (Object.hasOwn(settings.apiUrls, region)) {\n\t\treturn settings.apiUrls[region];\n\t}\n\tif (!warnedRegion) {\n\t\twarnedRegion = true;\n\t\tconsole.warn(\n\t\t\t`[@waniwani/relay] Unknown region ${JSON.stringify(region)}, so no agent visits are recorded. Use \"us\" or \"eu\".`,\n\t\t);\n\t}\n\treturn null;\n}\n\nfunction omitSet(omit: Array<string> | undefined): Set<string> {\n\tconst names = new Set((omit ?? []).map((name) => name.toLowerCase()));\n\tif (names.has(\"useragent\")) {\n\t\tnames.add(\"user-agent\");\n\t}\n\treturn names;\n}\n\nfunction schedule(\n\twork: Promise<unknown>,\n\twaitUntil: RelayOptions[\"waitUntil\"],\n): Promise<void> {\n\tconst settled = work.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\ttry {\n\t\t(waitUntil ?? vercelWaitUntil())?.(settled);\n\t} catch {}\n\treturn settled;\n}\n"],"mappings":"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AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;;;AC7KA;AAAA,EACC,SAAW;AAAA,IACV;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QA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AA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AA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","names":["isObject","isStringArray"]}
1
+ {"version":3,"sources":["../src/settings.ts","../src/version.ts","../src/agent-page.ts","../src/headers.ts","../src/http.ts","../src/ingest.ts","../src/key.ts","../src/logic/decide.ts","../src/logic/agents.generated.json","../src/logic/ip-ranges.generated.json","../src/match-key.ts","../src/refresh.ts","../src/wait-until.ts","../src/platform.ts","../src/request.ts","../src/index.ts"],"sourcesContent":["export const settings = {\n\tapiUrls: {\n\t\tus: \"https://app.waniwani.ai\",\n\t\teu: \"https://eu.app.waniwani.ai\",\n\t},\n\trequestTimeoutMs: 2000,\n\tcloudFrontTimeoutMs: 1000,\n\tcloudFrontMaxUriBytes: 8192,\n\tcloudFrontMaxBodyBytes: 1_000_000 - 16_384,\n\tretryDelayMs: 500,\n\trulesRefreshMs: 12 * 60 * 60 * 1000,\n\trulesRetryMs: 5 * 60 * 1000,\n\tsiteRulesTimeoutMs: 300,\n\tsiteRulesRefreshMs: 60_000,\n\tsiteRulesRetryMs: 5_000,\n};\n","declare const __RELAY_PACKAGE_VERSION__: string | undefined;\n\nexport const RELAY_VERSION: string =\n\ttypeof __RELAY_PACKAGE_VERSION__ === \"string\"\n\t\t? __RELAY_PACKAGE_VERSION__\n\t\t: \"0.0.0-dev\";\n","import { settings } from \"./settings\";\nimport { RELAY_VERSION } from \"./version\";\n\nexport type SiteRules = {\n\tversion: 1;\n\tpagesUrl: string;\n\tuserAgentTokens: string[];\n\tdeadlines: { headersMs: number; bodyMs: number };\n\tmaxBodyBytes: number;\n\tsites: Array<{\n\t\thostname: string;\n\t\tblockedUserAgentTokens: string[];\n\t\tagentPages: { enabled: boolean; paths: string[] };\n\t}>;\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null;\n\nconst isStringArray = (value: unknown): value is string[] =>\n\tArray.isArray(value) && value.every((item) => typeof item === \"string\");\n\nconst isPositive = (value: unknown): value is number =>\n\ttypeof value === \"number\" && Number.isFinite(value) && value > 0;\n\n/** Lowercase like the user agent they are matched against, with blank tokens dropped since they would match every request. */\nconst cleanTokens = (tokens: string[]): string[] =>\n\ttokens\n\t\t.map((token) => token.trim().toLowerCase())\n\t\t.filter((token) => token !== \"\");\n\n/** The site rules in a serve rules file from the app (the `data` of its answer), or null when it does not have the expected form. */\nexport function parseSiteRules(body: unknown): SiteRules | null {\n\tif (!isObject(body) || body.version !== 1) {\n\t\treturn null;\n\t}\n\tconst { pagesUrl, userAgentTokens, deadlines, maxBodyBytes, sites } = body;\n\tif (\n\t\ttypeof pagesUrl !== \"string\" ||\n\t\t!isStringArray(userAgentTokens) ||\n\t\t!isObject(deadlines) ||\n\t\t!isPositive(deadlines.headersMs) ||\n\t\t!isPositive(deadlines.bodyMs) ||\n\t\t!isPositive(maxBodyBytes) ||\n\t\t!Array.isArray(sites)\n\t) {\n\t\treturn null;\n\t}\n\tconst parsedSites: SiteRules[\"sites\"] = [];\n\tfor (const site of sites) {\n\t\tif (!isObject(site)) {\n\t\t\treturn null;\n\t\t}\n\t\tconst blocked = site.blockedUserAgentTokens ?? [];\n\t\tif (\n\t\t\ttypeof site.hostname !== \"string\" ||\n\t\t\t!isStringArray(blocked) ||\n\t\t\t!isObject(site.agentPages) ||\n\t\t\ttypeof site.agentPages.enabled !== \"boolean\" ||\n\t\t\t!isStringArray(site.agentPages.paths)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tparsedSites.push({\n\t\t\thostname: site.hostname.toLowerCase(),\n\t\t\tblockedUserAgentTokens: cleanTokens(blocked),\n\t\t\tagentPages: {\n\t\t\t\tenabled: site.agentPages.enabled,\n\t\t\t\tpaths: site.agentPages.paths,\n\t\t\t},\n\t\t});\n\t}\n\treturn {\n\t\tversion: 1,\n\t\tpagesUrl: pagesUrl.replace(/\\/+$/, \"\"),\n\t\tuserAgentTokens: cleanTokens(userAgentTokens),\n\t\tdeadlines: { headersMs: deadlines.headersMs, bodyMs: deadlines.bodyMs },\n\t\tmaxBodyBytes,\n\t\tsites: parsedSites,\n\t};\n}\n\ntype CacheEntry = {\n\trules: SiteRules | null;\n\tfetchedAt: number;\n\tlastAttemptAt: number;\n\tlastAttemptOk: boolean;\n\tinFlight: Promise<SiteRules | null> | null;\n};\n\nconst cache = new Map<string, CacheEntry>();\n\nasync function fetchSiteRules(\n\turl: string,\n\ttimeoutMs: number,\n): Promise<SiteRules | null> {\n\tconst response = await fetch(url, {\n\t\tsignal: AbortSignal.timeout(timeoutMs),\n\t});\n\tif (!response.ok) {\n\t\tawait response.body?.cancel().catch(() => {});\n\t\treturn null;\n\t}\n\tconst body: unknown = await response.json();\n\treturn parseSiteRules(isObject(body) ? body.data : null);\n}\n\nfunction startFetch(\n\tentry: CacheEntry,\n\turl: string,\n\ttimeoutMs: number,\n): Promise<SiteRules | null> {\n\tconst now = Date.now();\n\tentry.lastAttemptAt = now;\n\tentry.lastAttemptOk = false;\n\tconst work = fetchSiteRules(url, timeoutMs)\n\t\t.catch(() => null)\n\t\t.then((rules) => {\n\t\t\tif (rules) {\n\t\t\t\tentry.rules = rules;\n\t\t\t\tentry.fetchedAt = Date.now();\n\t\t\t\tentry.lastAttemptOk = true;\n\t\t\t}\n\t\t\tentry.inFlight = null;\n\t\t\treturn rules;\n\t\t});\n\tentry.inFlight = work;\n\treturn work;\n}\n\n/**\n * The rules for this org. Awaits the first fetch, then serves the cache and refreshes it in the background once it is stale.\n * At most one fetch per org is in flight.\n */\nexport async function loadSiteRules(\n\tapiUrl: string,\n\tingestToken: string,\n\tbackground: (work: Promise<unknown>) => void,\n): Promise<SiteRules | null> {\n\tconst url = `${apiUrl}/api/v2/agent-traffic/serve/${ingestToken}/rules`;\n\tlet entry = cache.get(url);\n\tif (!entry) {\n\t\tentry = {\n\t\t\trules: null,\n\t\t\tfetchedAt: 0,\n\t\t\tlastAttemptAt: Number.NEGATIVE_INFINITY,\n\t\t\tlastAttemptOk: false,\n\t\t\tinFlight: null,\n\t\t};\n\t\tcache.set(url, entry);\n\t}\n\tconst now = Date.now();\n\tif (entry.rules) {\n\t\tconst stale = now - entry.fetchedAt >= settings.siteRulesRefreshMs;\n\t\tconst retryDue =\n\t\t\tentry.lastAttemptOk ||\n\t\t\tnow - entry.lastAttemptAt >= settings.siteRulesRetryMs;\n\t\tif (stale && retryDue && !entry.inFlight) {\n\t\t\tbackground(startFetch(entry, url, settings.requestTimeoutMs));\n\t\t}\n\t\treturn entry.rules;\n\t}\n\tif (entry.inFlight) {\n\t\treturn entry.inFlight;\n\t}\n\tif (now - entry.lastAttemptAt < settings.siteRulesRetryMs) {\n\t\treturn null;\n\t}\n\treturn startFetch(entry, url, settings.siteRulesTimeoutMs);\n}\n\n/** The pathname without its trailing slashes, except for the root. */\nexport function normalizePath(pathname: string): string {\n\tconst trimmed = pathname.replace(/\\/+$/, \"\");\n\treturn trimmed === \"\" ? \"/\" : trimmed;\n}\n\n/** Whether a path matches an exact entry like `/pricing`, or a folder entry like `/products/*` that also covers `/products`. */\nexport function matchesPath(path: string, patterns: string[]): boolean {\n\treturn patterns.some((pattern) => {\n\t\tif (pattern === \"*\" || pattern === \"/*\") {\n\t\t\treturn true;\n\t\t}\n\t\tif (pattern.endsWith(\"/*\")) {\n\t\t\tconst folder = normalizePath(pattern.slice(0, -2));\n\t\t\treturn path === folder || path.startsWith(`${folder}/`);\n\t\t}\n\t\treturn path === normalizePath(pattern);\n\t});\n}\n\nconst TIMEOUT = Symbol(\"timeout\");\n\nfunction within<T>(\n\tpromise: Promise<T>,\n\tms: number,\n): Promise<T | typeof TIMEOUT> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tconst deadline = new Promise<typeof TIMEOUT>((resolve) => {\n\t\ttimer = setTimeout(() => resolve(TIMEOUT), ms);\n\t});\n\treturn Promise.race([promise, deadline]).finally(() => clearTimeout(timer));\n}\n\nasync function readCapped(\n\tbody: ReadableStream<Uint8Array>,\n\tdeadline: Promise<typeof TIMEOUT>,\n\tmaxBytes: number,\n): Promise<Uint8Array<ArrayBuffer> | null> {\n\tconst reader = body.getReader();\n\tconst chunks: Uint8Array[] = [];\n\tlet size = 0;\n\ttry {\n\t\twhile (true) {\n\t\t\tconst result = await Promise.race([reader.read(), deadline]);\n\t\t\tif (result === TIMEOUT) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (result.done) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tsize += result.value.byteLength;\n\t\t\tif (size > maxBytes) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tchunks.push(result.value);\n\t\t}\n\t} finally {\n\t\treader.cancel().catch(() => {});\n\t}\n\tconst out = new Uint8Array(size);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\tout.set(chunk, offset);\n\t\toffset += chunk.byteLength;\n\t}\n\treturn out;\n}\n\n/** Fetches the stored page within both deadlines and the size cap. Returns its body and content type, or null. The home page URL has no trailing slash, which the app would answer with a redirect. */\nexport async function fetchAgentPage(\n\trules: SiteRules,\n\thostname: string,\n\tpath: string,\n): Promise<{ body: Uint8Array<ArrayBuffer>; contentType: string } | null> {\n\tconst controller = new AbortController();\n\ttry {\n\t\tconst response = await within(\n\t\t\tfetch(`${rules.pagesUrl}/${hostname}${path === \"/\" ? \"\" : path}`, {\n\t\t\t\theaders: {\n\t\t\t\t\taccept: \"text/markdown, text/html;q=0.9\",\n\t\t\t\t\t\"x-waniwani-relay\": RELAY_VERSION,\n\t\t\t\t},\n\t\t\t\tsignal: controller.signal,\n\t\t\t}),\n\t\t\trules.deadlines.headersMs,\n\t\t);\n\t\tif (response === TIMEOUT) {\n\t\t\treturn null;\n\t\t}\n\t\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\t\tif (\n\t\t\tresponse.status !== 200 ||\n\t\t\t!contentType.toLowerCase().startsWith(\"text/\") ||\n\t\t\t!response.body\n\t\t) {\n\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\treturn null;\n\t\t}\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst deadline = new Promise<typeof TIMEOUT>((resolve) => {\n\t\t\ttimer = setTimeout(() => resolve(TIMEOUT), rules.deadlines.bodyMs);\n\t\t});\n\t\ttry {\n\t\t\tconst body = await readCapped(\n\t\t\t\tresponse.body,\n\t\t\t\tdeadline,\n\t\t\t\trules.maxBodyBytes,\n\t\t\t);\n\t\t\treturn body ? { body, contentType } : null;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tcontroller.abort();\n\t}\n}\n","export const HEADER_CAP: readonly string[] = [\n\t\"user-agent\",\n\t\"referer\",\n\t\"accept\",\n\t\"accept-language\",\n\t\"from\",\n\t\"signature-agent\",\n\t\"signature-input\",\n\t\"signature\",\n\t\"dpop\",\n\t\"sec-ch-ua\",\n\t\"sec-ch-ua-platform\",\n\t\"sec-ch-ua-mobile\",\n\t\"authorization\",\n];\n\nconst KNOWN_SCHEMES = new Map(\n\t[\n\t\t\"Basic\",\n\t\t\"Bearer\",\n\t\t\"Digest\",\n\t\t\"DPoP\",\n\t\t\"HOBA\",\n\t\t\"Mutual\",\n\t\t\"Negotiate\",\n\t\t\"NTLM\",\n\t\t\"OAuth\",\n\t\t\"PrivateToken\",\n\t\t\"SCRAM-SHA-1\",\n\t\t\"SCRAM-SHA-256\",\n\t\t\"vapid\",\n\t\t\"AWS4-HMAC-SHA256\",\n\t].map((scheme) => [scheme.toLowerCase(), scheme]),\n);\n\n/** The canonical name of a known Authorization scheme, kept only when a credential follows it. */\nexport function authorizationScheme(value: string): string | null {\n\tconst parts = value.trim().split(/\\s+/);\n\tif (parts.length < 2) {\n\t\treturn null;\n\t}\n\treturn KNOWN_SCHEMES.get((parts[0] ?? \"\").toLowerCase()) ?? null;\n}\n\nexport function readCappedHeaders(headers: Headers): Record<string, string> {\n\tconst out: Record<string, string> = {};\n\tfor (const name of HEADER_CAP) {\n\t\tconst value = headers.get(name);\n\t\tif (value === null) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (name === \"authorization\") {\n\t\t\tconst scheme = authorizationScheme(value);\n\t\t\tif (scheme !== null) {\n\t\t\t\tout[name] = scheme;\n\t\t\t}\n\t\t} else {\n\t\t\tout[name] = value;\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Cloudflare appends the real IP to a client-sent `X-Forwarded-For`, so `CF-Connecting-IP` comes first. */\nexport function clientIp(headers: Headers): string | null {\n\tconst forwarded = headers.get(\"x-forwarded-for\");\n\tconst candidate =\n\t\theaders.get(\"cf-connecting-ip\") ??\n\t\t(forwarded !== null ? forwarded.split(\",\")[0] : headers.get(\"x-real-ip\"));\n\tconst ip = candidate?.trim().toLowerCase();\n\treturn ip ? ip : null;\n}\n","import { settings } from \"./settings\";\n\nexport type Outcome = { status: number } | { failure: \"timeout\" | \"network\" };\n\n/** One fetch bounded by the timeout. Never rejects. */\nexport async function fetchWithTimeout(\n\turl: string,\n\tinit: RequestInit,\n\ttimeoutMs: number = settings.requestTimeoutMs,\n): Promise<Outcome> {\n\tconst controller = new AbortController();\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\ttry {\n\t\tconst response = await fetch(url, { ...init, signal: controller.signal });\n\t\tawait response.body?.cancel().catch(() => {});\n\t\treturn { status: response.status };\n\t} catch {\n\t\treturn { failure: controller.signal.aborted ? \"timeout\" : \"network\" };\n\t} finally {\n\t\tclearTimeout(timer);\n\t}\n}\n\nexport const delay = (ms: number) =>\n\tnew Promise<void>((resolve) => setTimeout(resolve, ms));\n","import { delay, fetchWithTimeout, type Outcome } from \"./http\";\nimport { settings } from \"./settings\";\n\nconst isSuccess = (outcome: Outcome) =>\n\t\"status\" in outcome && outcome.status >= 200 && outcome.status < 300;\n\nconst isRetryable = (outcome: Outcome) =>\n\t\"failure\" in outcome || outcome.status === 429 || outcome.status >= 500;\n\nconst describe = (outcome: Outcome) =>\n\t\"failure\" in outcome ? outcome.failure : `status ${outcome.status}`;\n\nexport type PostPolicy = { timeoutMs: number; retry: boolean };\n\nconst post = (url: string, body: string, timeoutMs: number) =>\n\tfetchWithTimeout(\n\t\turl,\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\tbody,\n\t\t},\n\t\ttimeoutMs,\n\t);\n\n/** Posts once, retries once on a timeout, network error, 429 or 5xx when the policy allows it, and never rejects. */\nexport async function postEvent(\n\turl: string,\n\tbody: string,\n\tpolicy: PostPolicy = { timeoutMs: settings.requestTimeoutMs, retry: true },\n): Promise<boolean> {\n\tlet outcome = await post(url, body, policy.timeoutMs);\n\tif (policy.retry && !isSuccess(outcome) && isRetryable(outcome)) {\n\t\tawait delay(settings.retryDelayMs);\n\t\toutcome = await post(url, body, policy.timeoutMs);\n\t}\n\tif (isSuccess(outcome)) {\n\t\treturn true;\n\t}\n\tconsole.warn(\n\t\t`[@waniwani/relay] Could not record an agent visit (${describe(outcome)}). The event was dropped.`,\n\t);\n\treturn false;\n}\n","export type RelayKey = { ingestToken: string; orgKey: string };\n\nconst KEY = /^wwt_([A-Za-z0-9_-]+)\\.([A-Za-z0-9_-]+={0,2})$/;\n\nlet warnedMalformed = false;\n\nexport function readKey(explicit: string | undefined): RelayKey | null {\n\tconst raw = explicit ?? readEnv(\"WANIWANI_AGENT_TRAFFIC_KEY\");\n\tif (raw === undefined || raw.trim() === \"\") {\n\t\treturn null;\n\t}\n\tconst match = KEY.exec(raw.trim());\n\tif (!match?.[1] || !match[2]) {\n\t\tif (!warnedMalformed) {\n\t\t\twarnedMalformed = true;\n\t\t\tconsole.warn(\n\t\t\t\t\"[@waniwani/relay] WANIWANI_AGENT_TRAFFIC_KEY is malformed, so no agent visits are recorded. Copy the key again from the WaniWani setup screen.\",\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n\treturn { ingestToken: match[1], orgKey: match[2] };\n}\n\nfunction readEnv(name: string): string | undefined {\n\ttry {\n\t\treturn typeof process === \"undefined\" ? undefined : process.env[name];\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n","import { BlockList } from \"node:net\";\nimport agents from \"./agents.generated.json\";\nimport ranges from \"./ip-ranges.generated.json\";\n\nexport type DecideInput = {\n\theaders: Record<string, string>;\n\tip: string | null;\n\tuseBundled?: boolean;\n};\n\nexport type MatchRules = {\n\ttokens: string[];\n\tipRanges: Array<{ ipv4: string[]; ipv6: string[] }>;\n};\n\nconst MAX_SCANNED_USER_AGENT = 1024;\nconst PREFIX = 3;\n\ntype CompiledRules = {\n\ttokensByPrefix: Map<string, string[]>;\n\tagentRanges: BlockList;\n};\n\nfunction compile(rules: MatchRules): CompiledRules {\n\tconst tokensByPrefix = new Map<string, string[]>();\n\tfor (const token of rules.tokens) {\n\t\tconst key = token.slice(0, PREFIX);\n\t\tconst list = tokensByPrefix.get(key);\n\t\tif (list) {\n\t\t\tlist.push(token);\n\t\t} else {\n\t\t\ttokensByPrefix.set(key, [token]);\n\t\t}\n\t}\n\n\tconst agentRanges = new BlockList();\n\tconst addRanges = (cidrs: string[], type: \"ipv4\" | \"ipv6\") => {\n\t\tfor (const cidr of cidrs) {\n\t\t\tconst [address = \"\", prefix, extra] = cidr.split(\"/\");\n\t\t\tif (!prefix || extra !== undefined) {\n\t\t\t\tthrow new Error(`malformed range ${cidr}`);\n\t\t\t}\n\t\t\tagentRanges.addSubnet(address, Number(prefix), type);\n\t\t}\n\t};\n\tfor (const source of rules.ipRanges) {\n\t\taddRanges(source.ipv4, \"ipv4\");\n\t\taddRanges(source.ipv6, \"ipv6\");\n\t}\n\n\treturn { tokensByPrefix, agentRanges };\n}\n\nconst bundled = compile({ tokens: agents.tokens, ipRanges: ranges.sources });\nlet remote: CompiledRules | null = null;\n\n/** Sets the remote match rules. Throws, and keeps the current ones, when a range is malformed. */\nexport function applyRules(rules: MatchRules): void {\n\tremote = compile(rules);\n}\n\nconst pick = (useBundled: boolean): CompiledRules =>\n\tuseBundled ? bundled : (remote ?? bundled);\n\nexport const isAgentIp = (ip: string, useBundled = false): boolean =>\n\tpick(useBundled).agentRanges.check(ip, ip.includes(\":\") ? \"ipv6\" : \"ipv4\");\n\nexport function hasAgentToken(userAgent: string, useBundled = false): boolean {\n\tconst { tokensByPrefix } = pick(useBundled);\n\tconst text = userAgent.slice(0, MAX_SCANNED_USER_AGENT).toLowerCase();\n\tfor (let i = 0; i + PREFIX <= text.length; i++) {\n\t\tconst candidates = tokensByPrefix.get(text.slice(i, i + PREFIX));\n\t\tif (candidates) {\n\t\t\tfor (const token of candidates) {\n\t\t\t\tif (text.startsWith(token, i)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nexport function decide({\n\theaders,\n\tip,\n\tuseBundled = false,\n}: DecideInput): boolean {\n\tconst userAgent = headers[\"user-agent\"];\n\treturn (\n\t\t(userAgent !== undefined && hasAgentToken(userAgent, useBundled)) ||\n\t\theaders[\"signature-agent\"] !== undefined ||\n\t\theaders.dpop !== undefined ||\n\t\theaders.authorization?.toLowerCase() === \"dpop\" ||\n\t\t(ip !== null && isAgentIp(ip, useBundled))\n\t);\n}\n","{\n\t\"source\": \"Generated by scripts/build-rules.ts from the WaniWani rules file d9b242ef451056c0, do not edit. Each token is matched case-insensitively inside the User-Agent header.\",\n\t\"tokens\": [\n\t\t\"addsearchbot\",\n\t\t\"agentdatabot\",\n\t\t\"agenttimes\",\n\t\t\"ai2bot\",\n\t\t\"ai2bot-deepresearcheval\",\n\t\t\"ai2bot-dolma\",\n\t\t\"aihitbot\",\n\t\t\"aiwebindex\",\n\t\t\"allenai.org\",\n\t\t\"amazon-kendra\",\n\t\t\"amazon-qbusiness\",\n\t\t\"amazonbot\",\n\t\t\"amazonbuyforme\",\n\t\t\"amzn-searchbot\",\n\t\t\"amzn-user\",\n\t\t\"andibot\",\n\t\t\"anthropic-ai\",\n\t\t\"apifybot\",\n\t\t\"apifywebsitecontentcrawler\",\n\t\t\"applebot\",\n\t\t\"aranet-searchbot\",\n\t\t\"atlassian-bot\",\n\t\t\"awario\",\n\t\t\"azureai-searchbot\",\n\t\t\"bedrockbot\",\n\t\t\"bigsur.ai\",\n\t\t\"bixelbot\",\n\t\t\"bravebot\",\n\t\t\"brightbot\",\n\t\t\"buddybot\",\n\t\t\"bytespider\",\n\t\t\"ccbot\",\n\t\t\"channel3bot\",\n\t\t\"chatglm-spider\",\n\t\t\"chatgpt agent\",\n\t\t\"chatgpt-user\",\n\t\t\"claude-code\",\n\t\t\"claude-searchbot\",\n\t\t\"claude-user\",\n\t\t\"claude-web\",\n\t\t\"claudebot\",\n\t\t\"cloudflare-autorag\",\n\t\t\"cloudflarebrowserrenderingcrawler\",\n\t\t\"cloudvertexbot\",\n\t\t\"cohere-ai\",\n\t\t\"cohere-training-data-crawler\",\n\t\t\"cotoyogi\",\n\t\t\"coveobot\",\n\t\t\"cragcrawler\",\n\t\t\"crawl4ai\",\n\t\t\"crawlspace\",\n\t\t\"cursor/1.0.0\",\n\t\t\"datenbank crawler\",\n\t\t\"deepseekbot\",\n\t\t\"diffbot\",\n\t\t\"diffbot-user\",\n\t\t\"doubaobot\",\n\t\t\"duckassistbot\",\n\t\t\"echobot bot\",\n\t\t\"echoboxbot\",\n\t\t\"erniebot\",\n\t\t\"exabot\",\n\t\t\"exasearchbot\",\n\t\t\"exauser\",\n\t\t\"facebookbot\",\n\t\t\"factset_spyderbot\",\n\t\t\"firecrawlagent\",\n\t\t\"friendlycrawler\",\n\t\t\"geisthaus-pagefetcher\",\n\t\t\"gemini-deep-research\",\n\t\t\"google-ads-creatives-assistant\",\n\t\t\"google-agent\",\n\t\t\"google-cloudvertexbot\",\n\t\t\"google-firebase\",\n\t\t\"google-gemini-cli\",\n\t\t\"google-gemininotebook\",\n\t\t\"google-lens\",\n\t\t\"google-notebooklm\",\n\t\t\"googleagent-mariner\",\n\t\t\"googleagent-urlcontext\",\n\t\t\"googleother\",\n\t\t\"googleother-image\",\n\t\t\"googleother-video\",\n\t\t\"gptbot\",\n\t\t\"grove/1.0 rss feed service\",\n\t\t\"henkbot\",\n\t\t\"iaskbot\",\n\t\t\"iaskspider\",\n\t\t\"icc-crawler\",\n\t\t\"imagesiftbot\",\n\t\t\"imagespider\",\n\t\t\"img2dataset\",\n\t\t\"isscyberriskcrawler\",\n\t\t\"jackjillcareersbot\",\n\t\t\"kagi-fetcher\",\n\t\t\"kangaroo bot\",\n\t\t\"keenablebot\",\n\t\t\"kernel searchbot\",\n\t\t\"kimi-agent\",\n\t\t\"kimi-searchbot\",\n\t\t\"kimi-user\",\n\t\t\"kimibot\",\n\t\t\"klaviyoaibot\",\n\t\t\"kunatocrawler\",\n\t\t\"laion-huggingface-processor\",\n\t\t\"laiondownloader\",\n\t\t\"lightpanda\",\n\t\t\"linerbot\",\n\t\t\"linkupbot\",\n\t\t\"manus-user\",\n\t\t\"meta-externalagent\",\n\t\t\"meta-externalfetcher\",\n\t\t\"meta-webindexer\",\n\t\t\"mistralai-index\",\n\t\t\"mistralai-training\",\n\t\t\"mistralai-user\",\n\t\t\"momentic\",\n\t\t\"mozilla-tabstack\",\n\t\t\"mycentralaiscraperbot\",\n\t\t\"nagetbot\",\n\t\t\"netestate imprint crawler\",\n\t\t\"notebooklm\",\n\t\t\"novaact\",\n\t\t\"oai-adsbot\",\n\t\t\"oai-searchbot\",\n\t\t\"omgili\",\n\t\t\"omgilibot\",\n\t\t\"pangubot\",\n\t\t\"panscient\",\n\t\t\"perplexity-user\",\n\t\t\"perplexitybot\",\n\t\t\"petalbot\",\n\t\t\"phindbot\",\n\t\t\"poggio-citations\",\n\t\t\"poseidon research crawler\",\n\t\t\"qodercli\",\n\t\t\"qualifiedbot\",\n\t\t\"querit-searchbot\",\n\t\t\"queritbot\",\n\t\t\"quillbot\",\n\t\t\"qwenbot\",\n\t\t\"reflectionbot\",\n\t\t\"ryebot\",\n\t\t\"sbintuitionsbot\",\n\t\t\"seltzsitemapfetcher\",\n\t\t\"semanticscholarbot\",\n\t\t\"semrushbot-ocob\",\n\t\t\"semrushbot-swa\",\n\t\t\"shap-user\",\n\t\t\"shapbot\",\n\t\t\"sidetrade indexer bot\",\n\t\t\"tavilybot\",\n\t\t\"terra cotta\",\n\t\t\"terracotta\",\n\t\t\"thinkbot\",\n\t\t\"tiktokspider\",\n\t\t\"timpibot\",\n\t\t\"tongyibot\",\n\t\t\"twilioknowledge\",\n\t\t\"twinagent\",\n\t\t\"v0bot\",\n\t\t\"velenpublicwebcrawler\",\n\t\t\"wardbot\",\n\t\t\"wrtnbot\",\n\t\t\"yandexadditional\",\n\t\t\"yandexadditionalbot\",\n\t\t\"yiyanbot\",\n\t\t\"youbot\",\n\t\t\"zanistabot\"\n\t]\n}\n","{\n\t\"sources\": [\n\t\t{\n\t\t\t\"bot\": \"GPTBot\",\n\t\t\t\"url\": \"https://openai.com/gptbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.227.36.0/25\",\n\t\t\t\t\"20.125.66.80/28\",\n\t\t\t\t\"20.171.206.0/24\",\n\t\t\t\t\"20.171.207.0/24\",\n\t\t\t\t\"52.230.152.0/24\",\n\t\t\t\t\"74.7.175.128/25\",\n\t\t\t\t\"74.7.227.0/25\",\n\t\t\t\t\"74.7.227.128/25\",\n\t\t\t\t\"74.7.228.0/25\",\n\t\t\t\t\"74.7.230.0/25\",\n\t\t\t\t\"74.7.241.0/25\",\n\t\t\t\t\"74.7.241.128/25\",\n\t\t\t\t\"74.7.242.0/25\",\n\t\t\t\t\"74.7.243.128/25\",\n\t\t\t\t\"74.7.244.0/25\",\n\t\t\t\t\"132.196.86.0/24\",\n\t\t\t\t\"172.182.202.0/25\",\n\t\t\t\t\"172.182.207.0/25\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"OAI-SearchBot\",\n\t\t\t\"url\": \"https://openai.com/searchbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.227.36.0/25\",\n\t\t\t\t\"13.66.216.176/28\",\n\t\t\t\t\"20.14.99.96/28\",\n\t\t\t\t\"20.25.151.224/28\",\n\t\t\t\t\"20.42.10.176/28\",\n\t\t\t\t\"20.168.18.32/28\",\n\t\t\t\t\"20.169.6.224/28\",\n\t\t\t\t\"20.169.7.48/28\",\n\t\t\t\t\"20.169.77.0/25\",\n\t\t\t\t\"20.171.53.224/28\",\n\t\t\t\t\"20.171.123.64/28\",\n\t\t\t\t\"23.102.145.48/28\",\n\t\t\t\t\"40.67.175.0/25\",\n\t\t\t\t\"40.90.214.16/28\",\n\t\t\t\t\"51.8.102.0/24\",\n\t\t\t\t\"74.7.175.128/25\",\n\t\t\t\t\"74.7.228.0/25\",\n\t\t\t\t\"74.7.228.128/25\",\n\t\t\t\t\"74.7.229.0/25\",\n\t\t\t\t\"74.7.229.128/25\",\n\t\t\t\t\"74.7.230.0/25\",\n\t\t\t\t\"74.7.241.128/25\",\n\t\t\t\t\"74.7.242.128/25\",\n\t\t\t\t\"74.7.243.0/25\",\n\t\t\t\t\"74.7.244.0/25\",\n\t\t\t\t\"104.210.140.128/28\",\n\t\t\t\t\"135.234.64.0/24\",\n\t\t\t\t\"172.182.193.80/28\",\n\t\t\t\t\"172.182.193.224/28\",\n\t\t\t\t\"172.182.194.32/28\",\n\t\t\t\t\"172.182.194.144/28\",\n\t\t\t\t\"172.182.195.48/28\",\n\t\t\t\t\"172.182.209.208/28\",\n\t\t\t\t\"172.182.211.192/28\",\n\t\t\t\t\"172.182.213.192/28\",\n\t\t\t\t\"172.182.224.0/28\",\n\t\t\t\t\"172.203.190.80/28\",\n\t\t\t\t\"172.203.190.112/28\",\n\t\t\t\t\"172.203.190.128/28\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"ChatGPT-User\",\n\t\t\t\"url\": \"https://openai.com/chatgpt-user.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.151.71.176/28\",\n\t\t\t\t\"4.151.119.48/28\",\n\t\t\t\t\"4.189.118.208/28\",\n\t\t\t\t\"4.189.119.48/28\",\n\t\t\t\t\"4.197.22.112/28\",\n\t\t\t\t\"4.197.64.0/28\",\n\t\t\t\t\"4.197.64.16/28\",\n\t\t\t\t\"4.197.64.48/28\",\n\t\t\t\t\"4.197.64.64/28\",\n\t\t\t\t\"4.197.115.112/28\",\n\t\t\t\t\"4.201.232.64/28\",\n\t\t\t\t\"4.205.128.176/28\",\n\t\t\t\t\"4.218.24.64/28\",\n\t\t\t\t\"4.226.200.16/28\",\n\t\t\t\t\"4.226.226.32/28\",\n\t\t\t\t\"9.129.0.0/16\",\n\t\t\t\t\"9.160.34.144/28\",\n\t\t\t\t\"9.160.36.16/28\",\n\t\t\t\t\"9.160.96.16/28\",\n\t\t\t\t\"9.160.128.16/28\",\n\t\t\t\t\"9.160.128.64/28\",\n\t\t\t\t\"9.160.163.128/28\",\n\t\t\t\t\"9.163.101.48/28\",\n\t\t\t\t\"9.205.8.48/28\",\n\t\t\t\t\"9.205.8.64/28\",\n\t\t\t\t\"9.205.25.128/28\",\n\t\t\t\t\"9.205.30.128/28\",\n\t\t\t\t\"9.205.30.144/28\",\n\t\t\t\t\"9.205.30.176/28\",\n\t\t\t\t\"9.205.128.32/28\",\n\t\t\t\t\"9.205.128.48/28\",\n\t\t\t\t\"9.223.181.208/28\",\n\t\t\t\t\"9.234.96.192/28\",\n\t\t\t\t\"9.234.97.96/28\",\n\t\t\t\t\"13.65.138.112/28\",\n\t\t\t\t\"13.67.72.16/28\",\n\t\t\t\t\"13.71.2.208/28\",\n\t\t\t\t\"13.76.32.208/28\",\n\t\t\t\t\"13.76.116.80/28\",\n\t\t\t\t\"13.83.167.128/28\",\n\t\t\t\t\"13.83.237.176/28\",\n\t\t\t\t\"20.45.178.144/28\",\n\t\t\t\t\"20.55.229.144/28\",\n\t\t\t\t\"20.57.199.192/28\",\n\t\t\t\t\"20.63.180.96/28\",\n\t\t\t\t\"20.63.221.64/28\",\n\t\t\t\t\"20.78.130.48/28\",\n\t\t\t\t\"20.83.243.176/28\",\n\t\t\t\t\"20.98.18.80/28\",\n\t\t\t\t\"20.102.212.144/28\",\n\t\t\t\t\"20.113.211.112/28\",\n\t\t\t\t\"20.113.225.112/28\",\n\t\t\t\t\"20.125.112.224/28\",\n\t\t\t\t\"20.125.144.144/28\",\n\t\t\t\t\"20.161.75.208/28\",\n\t\t\t\t\"20.168.7.192/28\",\n\t\t\t\t\"20.169.73.32/28\",\n\t\t\t\t\"20.169.73.64/28\",\n\t\t\t\t\"20.169.78.48/28\",\n\t\t\t\t\"20.169.78.64/28\",\n\t\t\t\t\"20.169.78.128/28\",\n\t\t\t\t\"20.169.78.160/28\",\n\t\t\t\t\"20.169.78.192/28\",\n\t\t\t\t\"20.169.86.224/28\",\n\t\t\t\t\"20.169.86.240/28\",\n\t\t\t\t\"20.170.184.16/28\",\n\t\t\t\t\"20.170.184.32/28\",\n\t\t\t\t\"20.170.184.48/28\",\n\t\t\t\t\"20.170.184.64/28\",\n\t\t\t\t\"20.170.184.80/28\",\n\t\t\t\t\"20.172.29.32/28\",\n\t\t\t\t\"20.199.211.160/28\",\n\t\t\t\t\"20.199.242.0/28\",\n\t\t\t\t\"20.200.212.240/28\",\n\t\t\t\t\"20.206.101.192/28\",\n\t\t\t\t\"20.210.211.192/28\",\n\t\t\t\t\"20.212.62.208/28\",\n\t\t\t\t\"20.215.187.208/28\",\n\t\t\t\t\"20.215.219.128/28\",\n\t\t\t\t\"20.215.219.160/28\",\n\t\t\t\t\"20.215.219.208/28\",\n\t\t\t\t\"20.218.30.240/28\",\n\t\t\t\t\"20.219.71.192/28\",\n\t\t\t\t\"20.219.161.192/28\",\n\t\t\t\t\"20.219.184.96/28\",\n\t\t\t\t\"20.222.36.192/28\",\n\t\t\t\t\"20.227.140.32/28\",\n\t\t\t\t\"20.228.106.176/28\",\n\t\t\t\t\"20.235.87.224/28\",\n\t\t\t\t\"20.249.63.208/28\",\n\t\t\t\t\"20.250.6.128/28\",\n\t\t\t\t\"20.250.136.64/28\",\n\t\t\t\t\"20.254.201.208/28\",\n\t\t\t\t\"23.98.142.176/28\",\n\t\t\t\t\"23.98.186.64/28\",\n\t\t\t\t\"23.98.186.96/28\",\n\t\t\t\t\"23.98.186.176/28\",\n\t\t\t\t\"23.98.186.192/28\",\n\t\t\t\t\"23.101.217.176/28\",\n\t\t\t\t\"23.102.140.144/28\",\n\t\t\t\t\"23.102.141.32/28\",\n\t\t\t\t\"40.74.200.208/28\",\n\t\t\t\t\"40.81.67.96/28\",\n\t\t\t\t\"40.81.234.144/28\",\n\t\t\t\t\"40.84.221.208/28\",\n\t\t\t\t\"40.84.221.224/28\",\n\t\t\t\t\"40.116.73.208/28\",\n\t\t\t\t\"40.119.36.240/28\",\n\t\t\t\t\"40.124.161.0/28\",\n\t\t\t\t\"48.221.40.176/28\",\n\t\t\t\t\"48.221.184.80/28\",\n\t\t\t\t\"48.221.184.96/28\",\n\t\t\t\t\"51.8.155.48/28\",\n\t\t\t\t\"51.8.155.112/28\",\n\t\t\t\t\"51.57.0.96/28\",\n\t\t\t\t\"51.59.24.64/28\",\n\t\t\t\t\"51.59.24.80/28\",\n\t\t\t\t\"51.59.48.80/28\",\n\t\t\t\t\"51.116.2.80/28\",\n\t\t\t\t\"51.116.221.96/28\",\n\t\t\t\t\"52.148.129.32/28\",\n\t\t\t\t\"52.156.77.144/28\",\n\t\t\t\t\"52.159.227.32/28\",\n\t\t\t\t\"52.159.249.96/28\",\n\t\t\t\t\"52.161.49.96/28\",\n\t\t\t\t\"52.165.212.48/28\",\n\t\t\t\t\"52.172.129.160/28\",\n\t\t\t\t\"52.173.219.96/28\",\n\t\t\t\t\"52.173.219.112/28\",\n\t\t\t\t\"52.173.221.16/28\",\n\t\t\t\t\"52.173.221.176/28\",\n\t\t\t\t\"52.173.221.208/28\",\n\t\t\t\t\"52.173.234.16/28\",\n\t\t\t\t\"52.173.234.80/28\",\n\t\t\t\t\"52.183.217.240/28\",\n\t\t\t\t\"52.190.137.16/28\",\n\t\t\t\t\"52.190.137.144/28\",\n\t\t\t\t\"52.190.139.48/28\",\n\t\t\t\t\"52.190.142.64/28\",\n\t\t\t\t\"52.190.190.16/28\",\n\t\t\t\t\"52.190.251.112/28\",\n\t\t\t\t\"52.225.75.208/28\",\n\t\t\t\t\"52.231.30.48/28\",\n\t\t\t\t\"52.231.34.176/28\",\n\t\t\t\t\"52.231.39.144/28\",\n\t\t\t\t\"52.231.50.64/28\",\n\t\t\t\t\"52.236.94.144/28\",\n\t\t\t\t\"52.241.146.208/28\",\n\t\t\t\t\"52.242.132.224/28\",\n\t\t\t\t\"52.242.132.240/28\",\n\t\t\t\t\"52.255.109.80/28\",\n\t\t\t\t\"52.255.109.96/28\",\n\t\t\t\t\"52.255.109.144/28\",\n\t\t\t\t\"52.255.111.0/28\",\n\t\t\t\t\"52.255.111.80/28\",\n\t\t\t\t\"57.154.174.112/28\",\n\t\t\t\t\"57.154.175.0/28\",\n\t\t\t\t\"57.154.187.32/28\",\n\t\t\t\t\"68.154.28.96/28\",\n\t\t\t\t\"68.220.57.64/28\",\n\t\t\t\t\"70.153.32.16/28\",\n\t\t\t\t\"70.153.32.32/28\",\n\t\t\t\t\"70.153.87.224/28\",\n\t\t\t\t\"70.153.189.192/28\",\n\t\t\t\t\"70.156.152.96/28\",\n\t\t\t\t\"74.7.35.48/28\",\n\t\t\t\t\"74.7.35.112/28\",\n\t\t\t\t\"74.7.36.64/28\",\n\t\t\t\t\"74.7.36.80/28\",\n\t\t\t\t\"74.7.36.96/28\",\n\t\t\t\t\"74.161.200.96/28\",\n\t\t\t\t\"74.224.217.64/28\",\n\t\t\t\t\"74.226.253.160/28\",\n\t\t\t\t\"74.248.37.160/28\",\n\t\t\t\t\"85.211.128.16/28\",\n\t\t\t\t\"85.211.128.32/28\",\n\t\t\t\t\"104.208.184.192/28\",\n\t\t\t\t\"104.210.139.192/28\",\n\t\t\t\t\"104.210.139.224/28\",\n\t\t\t\t\"128.85.198.32/28\",\n\t\t\t\t\"132.196.82.48/28\",\n\t\t\t\t\"134.33.102.192/28\",\n\t\t\t\t\"134.138.52.16/28\",\n\t\t\t\t\"134.138.52.64/28\",\n\t\t\t\t\"134.138.52.96/28\",\n\t\t\t\t\"134.138.52.128/28\",\n\t\t\t\t\"134.138.57.64/28\",\n\t\t\t\t\"134.138.57.80/28\",\n\t\t\t\t\"134.138.57.176/28\",\n\t\t\t\t\"134.138.58.32/28\",\n\t\t\t\t\"134.149.233.80/28\",\n\t\t\t\t\"135.13.64.240/28\",\n\t\t\t\t\"135.116.136.160/28\",\n\t\t\t\t\"135.220.73.208/28\",\n\t\t\t\t\"135.220.73.240/28\",\n\t\t\t\t\"135.222.99.32/28\",\n\t\t\t\t\"135.237.131.208/28\",\n\t\t\t\t\"135.237.133.48/28\",\n\t\t\t\t\"137.135.191.176/28\",\n\t\t\t\t\"138.91.46.96/28\",\n\t\t\t\t\"145.132.136.96/28\",\n\t\t\t\t\"158.158.5.32/28\",\n\t\t\t\t\"168.63.252.240/28\",\n\t\t\t\t\"172.162.248.64/28\",\n\t\t\t\t\"172.170.1.80/28\",\n\t\t\t\t\"172.170.225.0/28\",\n\t\t\t\t\"172.170.241.80/28\",\n\t\t\t\t\"172.172.206.48/28\",\n\t\t\t\t\"172.175.152.224/28\",\n\t\t\t\t\"172.178.140.144/28\",\n\t\t\t\t\"172.178.141.112/28\",\n\t\t\t\t\"172.178.141.128/28\",\n\t\t\t\t\"172.183.143.224/28\",\n\t\t\t\t\"172.183.222.128/28\",\n\t\t\t\t\"172.192.112.208/28\",\n\t\t\t\t\"172.197.160.192/28\",\n\t\t\t\t\"172.197.203.16/28\",\n\t\t\t\t\"172.198.58.176/28\",\n\t\t\t\t\"172.198.78.192/28\",\n\t\t\t\t\"172.198.79.112/28\",\n\t\t\t\t\"172.198.161.128/28\",\n\t\t\t\t\"172.199.137.80/28\",\n\t\t\t\t\"172.204.28.224/28\",\n\t\t\t\t\"172.204.96.80/28\",\n\t\t\t\t\"172.205.189.192/28\",\n\t\t\t\t\"172.206.38.240/28\",\n\t\t\t\t\"172.207.1.32/28\",\n\t\t\t\t\"172.212.172.160/28\",\n\t\t\t\t\"172.215.215.32/28\",\n\t\t\t\t\"191.232.238.96/28\",\n\t\t\t\t\"191.233.197.0/28\",\n\t\t\t\t\"191.233.199.160/28\",\n\t\t\t\t\"191.234.167.144/28\",\n\t\t\t\t\"191.235.99.160/28\",\n\t\t\t\t\"191.237.249.64/28\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"ClaudeBot\",\n\t\t\t\"url\": \"https://claude.com/crawling/bots.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"34.152.124.140/32\",\n\t\t\t\t\"34.153.21.190/32\",\n\t\t\t\t\"34.162.27.34/32\",\n\t\t\t\t\"34.162.86.102/32\",\n\t\t\t\t\"34.162.90.102/32\",\n\t\t\t\t\"34.162.111.78/32\",\n\t\t\t\t\"34.162.153.71/32\",\n\t\t\t\t\"34.162.191.81/32\",\n\t\t\t\t\"34.162.230.222/32\",\n\t\t\t\t\"34.162.244.71/32\",\n\t\t\t\t\"34.186.252.4/32\",\n\t\t\t\t\"136.79.143.251/32\",\n\t\t\t\t\"136.83.20.215/32\",\n\t\t\t\t\"216.73.216.0/22\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"PerplexityBot\",\n\t\t\t\"url\": \"https://www.perplexity.com/perplexitybot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"3.222.232.239/32\",\n\t\t\t\t\"3.224.62.45/32\",\n\t\t\t\t\"18.97.1.228/32\",\n\t\t\t\t\"18.97.9.96/32\",\n\t\t\t\t\"18.97.9.97/32\",\n\t\t\t\t\"18.97.9.98/32\",\n\t\t\t\t\"18.97.9.99/32\",\n\t\t\t\t\"18.97.9.100/32\",\n\t\t\t\t\"18.97.9.101/32\",\n\t\t\t\t\"18.97.9.102/32\",\n\t\t\t\t\"18.97.9.103/32\",\n\t\t\t\t\"18.210.92.235/32\",\n\t\t\t\t\"107.20.236.150/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Perplexity-User\",\n\t\t\t\"url\": \"https://www.perplexity.com/perplexity-user.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"18.97.43.80/32\",\n\t\t\t\t\"18.97.43.81/32\",\n\t\t\t\t\"18.97.43.82/32\",\n\t\t\t\t\"18.97.43.83/32\",\n\t\t\t\t\"18.97.43.84/32\",\n\t\t\t\t\"18.97.43.85/32\",\n\t\t\t\t\"18.97.43.86/32\",\n\t\t\t\t\"18.97.43.87/32\",\n\t\t\t\t\"34.193.163.52/32\",\n\t\t\t\t\"44.208.221.197/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"MistralAI-User\",\n\t\t\t\"url\": \"https://mistral.ai/mistralai-user-ips.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"20.240.160.1/32\",\n\t\t\t\t\"20.240.160.161/32\",\n\t\t\t\t\"20.240.194.83/32\",\n\t\t\t\t\"51.12.243.114/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"CCBot\",\n\t\t\t\"url\": \"https://index.commoncrawl.org/ccbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"3.41.188.32/29\",\n\t\t\t\t\"18.97.9.168/29\",\n\t\t\t\t\"18.97.14.80/29\",\n\t\t\t\t\"18.97.14.88/30\"\n\t\t\t],\n\t\t\t\"ipv6\": [\n\t\t\t\t\"2600:1f28:365:8000::/56\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"DuckAssistBot\",\n\t\t\t\"url\": \"https://duckduckgo.com/duckassistbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.144.148.23/32\",\n\t\t\t\t\"4.144.182.50/32\",\n\t\t\t\t\"4.144.227.142/32\",\n\t\t\t\t\"4.144.251.39/32\",\n\t\t\t\t\"4.149.142.7/32\",\n\t\t\t\t\"4.149.159.71/32\",\n\t\t\t\t\"4.149.159.102/32\",\n\t\t\t\t\"4.149.216.220/32\",\n\t\t\t\t\"4.150.142.218/32\",\n\t\t\t\t\"4.154.193.167/32\",\n\t\t\t\t\"4.156.30.123/32\",\n\t\t\t\t\"4.156.136.79/32\",\n\t\t\t\t\"4.156.154.107/32\",\n\t\t\t\t\"4.157.77.76/32\",\n\t\t\t\t\"4.172.49.103/32\",\n\t\t\t\t\"4.177.16.196/32\",\n\t\t\t\t\"4.182.10.198/32\",\n\t\t\t\t\"4.182.131.108/32\",\n\t\t\t\t\"4.195.133.120/32\",\n\t\t\t\t\"4.201.125.59/32\",\n\t\t\t\t\"4.201.141.71/32\",\n\t\t\t\t\"4.201.197.203/32\",\n\t\t\t\t\"4.201.206.133/32\",\n\t\t\t\t\"4.201.220.8/32\",\n\t\t\t\t\"4.207.220.92/32\",\n\t\t\t\t\"4.209.224.56/32\",\n\t\t\t\t\"4.213.46.14/32\",\n\t\t\t\t\"4.226.40.135/32\",\n\t\t\t\t\"4.228.76.163/32\",\n\t\t\t\t\"4.237.155.54/32\",\n\t\t\t\t\"4.237.244.80/32\",\n\t\t\t\t\"4.242.119.13/32\",\n\t\t\t\t\"4.246.10.252/32\",\n\t\t\t\t\"4.248.234.26/32\",\n\t\t\t\t\"4.249.216.104/32\",\n\t\t\t\t\"4.249.223.51/32\",\n\t\t\t\t\"4.249.238.11/32\",\n\t\t\t\t\"4.254.123.56/32\",\n\t\t\t\t\"4.255.35.121/32\",\n\t\t\t\t\"4.255.39.80/32\",\n\t\t\t\t\"13.86.35.212/32\",\n\t\t\t\t\"13.89.106.77/32\",\n\t\t\t\t\"13.90.178.139/32\",\n\t\t\t\t\"20.3.1.178/32\",\n\t\t\t\t\"20.3.60.212/32\",\n\t\t\t\t\"20.8.252.26/32\",\n\t\t\t\t\"20.9.114.242/32\",\n\t\t\t\t\"20.12.141.99/32\",\n\t\t\t\t\"20.12.249.123/32\",\n\t\t\t\t\"20.12.252.21/32\",\n\t\t\t\t\"20.13.44.19/32\",\n\t\t\t\t\"20.15.142.178/32\",\n\t\t\t\t\"20.23.88.231/32\",\n\t\t\t\t\"20.29.149.44/32\",\n\t\t\t\t\"20.29.154.56/32\",\n\t\t\t\t\"20.40.133.240/32\",\n\t\t\t\t\"20.40.147.172/32\",\n\t\t\t\t\"20.43.150.85/32\",\n\t\t\t\t\"20.43.150.93/32\",\n\t\t\t\t\"20.43.152.28/32\",\n\t\t\t\t\"20.43.172.120/32\",\n\t\t\t\t\"20.44.222.1/32\",\n\t\t\t\t\"20.49.129.236/32\",\n\t\t\t\t\"20.49.136.28/32\",\n\t\t\t\t\"20.50.48.159/32\",\n\t\t\t\t\"20.50.48.192/32\",\n\t\t\t\t\"20.50.49.0/32\",\n\t\t\t\t\"20.50.49.25/32\",\n\t\t\t\t\"20.50.49.40/32\",\n\t\t\t\t\"20.50.49.55/32\",\n\t\t\t\t\"20.50.49.237/32\",\n\t\t\t\t\"20.50.50.46/32\",\n\t\t\t\t\"20.50.50.118/32\",\n\t\t\t\t\"20.50.50.121/32\",\n\t\t\t\t\"20.50.50.123/32\",\n\t\t\t\t\"20.50.50.130/32\",\n\t\t\t\t\"20.50.50.134/32\",\n\t\t\t\t\"20.50.50.145/32\",\n\t\t\t\t\"20.50.50.146/32\",\n\t\t\t\t\"20.50.50.163/32\",\n\t\t\t\t\"20.50.168.91/32\",\n\t\t\t\t\"20.53.78.106/32\",\n\t\t\t\t\"20.53.78.123/32\",\n\t\t\t\t\"20.53.78.138/32\",\n\t\t\t\t\"20.53.78.144/32\",\n\t\t\t\t\"20.53.78.236/32\",\n\t\t\t\t\"20.53.91.2/32\",\n\t\t\t\t\"20.53.92.211/32\",\n\t\t\t\t\"20.53.134.160/32\",\n\t\t\t\t\"20.54.224.39/32\",\n\t\t\t\t\"20.56.197.58/32\",\n\t\t\t\t\"20.56.197.63/32\",\n\t\t\t\t\"20.59.0.65/32\",\n\t\t\t\t\"20.61.34.40/32\",\n\t\t\t\t\"20.61.142.192/32\",\n\t\t\t\t\"20.62.224.44/32\",\n\t\t\t\t\"20.64.185.45/32\",\n\t\t\t\t\"20.69.74.250/32\",\n\t\t\t\t\"20.69.129.205/32\",\n\t\t\t\t\"20.69.131.45/32\",\n\t\t\t\t\"20.69.138.149/32\",\n\t\t\t\t\"20.69.138.189/32\",\n\t\t\t\t\"20.69.144.137/32\",\n\t\t\t\t\"20.69.146.171/32\",\n\t\t\t\t\"20.69.147.21/32\",\n\t\t\t\t\"20.69.171.204/32\",\n\t\t\t\t\"20.71.12.143/32\",\n\t\t\t\t\"20.71.69.210/32\",\n\t\t\t\t\"20.72.197.110/32\",\n\t\t\t\t\"20.72.203.250/32\",\n\t\t\t\t\"20.72.218.29/32\",\n\t\t\t\t\"20.72.221.33/32\",\n\t\t\t\t\"20.72.221.45/32\",\n\t\t\t\t\"20.72.225.13/32\",\n\t\t\t\t\"20.72.242.93/32\",\n\t\t\t\t\"20.72.242.209/32\",\n\t\t\t\t\"20.73.132.240/32\",\n\t\t\t\t\"20.73.202.147/32\",\n\t\t\t\t\"20.75.144.152/32\",\n\t\t\t\t\"20.75.254.61/32\",\n\t\t\t\t\"20.77.146.108/32\",\n\t\t\t\t\"20.79.226.26/32\",\n\t\t\t\t\"20.79.238.198/32\",\n\t\t\t\t\"20.79.239.66/32\",\n\t\t\t\t\"20.80.114.203/32\",\n\t\t\t\t\"20.80.120.41/32\",\n\t\t\t\t\"20.80.129.80/32\",\n\t\t\t\t\"20.80.135.7/32\",\n\t\t\t\t\"20.80.156.88/32\",\n\t\t\t\t\"20.81.115.13/32\",\n\t\t\t\t\"20.82.24.207/32\",\n\t\t\t\t\"20.84.138.61/32\",\n\t\t\t\t\"20.84.219.45/32\",\n\t\t\t\t\"20.84.251.223/32\",\n\t\t\t\t\"20.85.138.75/32\",\n\t\t\t\t\"20.85.167.56/32\",\n\t\t\t\t\"20.85.187.146/32\",\n\t\t\t\t\"20.88.168.65/32\",\n\t\t\t\t\"20.93.28.24/32\",\n\t\t\t\t\"20.93.49.96/32\",\n\t\t\t\t\"20.99.154.113/32\",\n\t\t\t\t\"20.99.255.235/32\",\n\t\t\t\t\"20.100.136.36/32\",\n\t\t\t\t\"20.100.140.155/32\",\n\t\t\t\t\"20.101.17.173/32\",\n\t\t\t\t\"20.102.34.205/32\",\n\t\t\t\t\"20.109.129.162/32\",\n\t\t\t\t\"20.109.130.190/32\",\n\t\t\t\t\"20.109.241.18/32\",\n\t\t\t\t\"20.112.58.44/32\",\n\t\t\t\t\"20.113.3.121/32\",\n\t\t\t\t\"20.113.14.159/32\",\n\t\t\t\t\"20.115.181.187/32\",\n\t\t\t\t\"20.118.11.251/32\",\n\t\t\t\t\"20.119.127.146/32\",\n\t\t\t\t\"20.121.75.32/32\",\n\t\t\t\t\"20.166.171.150/32\",\n\t\t\t\t\"20.170.75.54/32\",\n\t\t\t\t\"20.175.232.228/32\",\n\t\t\t\t\"20.185.79.15/32\",\n\t\t\t\t\"20.185.79.47/32\",\n\t\t\t\t\"20.190.21.172/32\",\n\t\t\t\t\"20.191.44.16/32\",\n\t\t\t\t\"20.191.44.22/32\",\n\t\t\t\t\"20.191.44.119/32\",\n\t\t\t\t\"20.191.44.234/32\",\n\t\t\t\t\"20.191.45.212/32\",\n\t\t\t\t\"20.193.12.126/32\",\n\t\t\t\t\"20.193.24.10/32\",\n\t\t\t\t\"20.193.24.251/32\",\n\t\t\t\t\"20.193.25.197/32\",\n\t\t\t\t\"20.193.27.215/32\",\n\t\t\t\t\"20.193.45.113/32\",\n\t\t\t\t\"20.195.58.189/32\",\n\t\t\t\t\"20.195.108.47/32\",\n\t\t\t\t\"20.197.209.11/32\",\n\t\t\t\t\"20.197.209.27/32\",\n\t\t\t\t\"20.198.132.126/32\",\n\t\t\t\t\"20.198.152.208/32\",\n\t\t\t\t\"20.201.15.208/32\",\n\t\t\t\t\"20.204.228.200/32\",\n\t\t\t\t\"20.204.240.172/32\",\n\t\t\t\t\"20.204.241.148/32\",\n\t\t\t\t\"20.204.242.19/32\",\n\t\t\t\t\"20.204.242.101/32\",\n\t\t\t\t\"20.204.243.55/32\",\n\t\t\t\t\"20.204.246.81/32\",\n\t\t\t\t\"20.204.246.254/32\",\n\t\t\t\t\"20.207.72.11/32\",\n\t\t\t\t\"20.207.72.21/32\",\n\t\t\t\t\"20.207.72.110/32\",\n\t\t\t\t\"20.207.72.113/32\",\n\t\t\t\t\"20.207.97.190/32\",\n\t\t\t\t\"20.207.99.197/32\",\n\t\t\t\t\"20.207.107.181/32\",\n\t\t\t\t\"20.207.117.66/32\",\n\t\t\t\t\"20.212.90.107/32\",\n\t\t\t\t\"20.212.139.34/32\",\n\t\t\t\t\"20.216.200.223/32\",\n\t\t\t\t\"20.219.43.246/32\",\n\t\t\t\t\"20.219.45.67/32\",\n\t\t\t\t\"20.219.45.190/32\",\n\t\t\t\t\"20.221.10.115/32\",\n\t\t\t\t\"20.221.84.185/32\",\n\t\t\t\t\"20.226.133.105/32\",\n\t\t\t\t\"20.231.254.179/32\",\n\t\t\t\t\"20.232.51.46/32\",\n\t\t\t\t\"20.232.249.95/32\",\n\t\t\t\t\"20.236.40.93/32\",\n\t\t\t\t\"20.236.195.196/32\",\n\t\t\t\t\"20.236.198.192/32\",\n\t\t\t\t\"20.236.227.113/32\",\n\t\t\t\t\"20.236.233.23/32\",\n\t\t\t\t\"20.236.247.180/32\",\n\t\t\t\t\"20.237.1.51/32\",\n\t\t\t\t\"20.241.135.19/32\",\n\t\t\t\t\"20.241.147.151/32\",\n\t\t\t\t\"20.241.153.203/32\",\n\t\t\t\t\"20.241.164.5/32\",\n\t\t\t\t\"20.241.241.100/32\",\n\t\t\t\t\"20.242.133.53/32\",\n\t\t\t\t\"20.242.135.165/32\",\n\t\t\t\t\"20.242.211.139/32\",\n\t\t\t\t\"20.242.215.53/32\",\n\t\t\t\t\"20.242.237.240/32\",\n\t\t\t\t\"20.246.132.168/32\",\n\t\t\t\t\"20.246.225.34/32\",\n\t\t\t\t\"20.247.235.11/32\",\n\t\t\t\t\"20.250.51.113/32\",\n\t\t\t\t\"20.252.9.6/32\",\n\t\t\t\t\"20.252.16.193/32\",\n\t\t\t\t\"20.252.41.18/32\",\n\t\t\t\t\"20.252.63.162/32\",\n\t\t\t\t\"20.252.88.10/32\",\n\t\t\t\t\"20.253.59.76/32\",\n\t\t\t\t\"20.253.96.199/32\",\n\t\t\t\t\"20.253.114.51/32\",\n\t\t\t\t\"40.64.67.220/32\",\n\t\t\t\t\"40.64.78.45/32\",\n\t\t\t\t\"40.64.88.145/32\",\n\t\t\t\t\"40.64.105.247/32\",\n\t\t\t\t\"40.64.106.11/32\",\n\t\t\t\t\"40.64.106.152/32\",\n\t\t\t\t\"40.65.78.86/32\",\n\t\t\t\t\"40.76.162.17/32\",\n\t\t\t\t\"40.76.162.191/32\",\n\t\t\t\t\"40.76.162.208/32\",\n\t\t\t\t\"40.76.162.247/32\",\n\t\t\t\t\"40.76.163.7/32\",\n\t\t\t\t\"40.76.163.23/32\",\n\t\t\t\t\"40.76.173.151/32\",\n\t\t\t\t\"40.81.250.205/32\",\n\t\t\t\t\"40.82.218.203/32\",\n\t\t\t\t\"40.88.21.235/32\",\n\t\t\t\t\"40.88.195.62/32\",\n\t\t\t\t\"40.89.243.175/32\",\n\t\t\t\t\"40.114.182.45/32\",\n\t\t\t\t\"40.114.182.153/32\",\n\t\t\t\t\"40.114.182.172/32\",\n\t\t\t\t\"40.114.183.88/32\",\n\t\t\t\t\"40.114.183.196/32\",\n\t\t\t\t\"40.114.183.251/32\",\n\t\t\t\t\"40.119.232.50/32\",\n\t\t\t\t\"40.119.232.146/32\",\n\t\t\t\t\"40.119.232.215/32\",\n\t\t\t\t\"40.119.232.218/32\",\n\t\t\t\t\"40.119.232.251/32\",\n\t\t\t\t\"40.121.121.54/32\",\n\t\t\t\t\"40.127.154.196/32\",\n\t\t\t\t\"48.192.16.86/32\",\n\t\t\t\t\"48.192.74.178/32\",\n\t\t\t\t\"48.200.33.227/32\",\n\t\t\t\t\"48.202.155.200/32\",\n\t\t\t\t\"48.202.171.124/32\",\n\t\t\t\t\"48.204.32.87/32\",\n\t\t\t\t\"48.206.18.152/32\",\n\t\t\t\t\"48.217.23.236/32\",\n\t\t\t\t\"48.217.129.210/32\",\n\t\t\t\t\"48.217.212.89/32\",\n\t\t\t\t\"48.223.209.110/32\",\n\t\t\t\t\"51.8.31.28/32\",\n\t\t\t\t\"51.8.71.117/32\",\n\t\t\t\t\"51.8.253.152/32\",\n\t\t\t\t\"51.104.144.31/32\",\n\t\t\t\t\"51.104.146.225/32\",\n\t\t\t\t\"51.104.146.235/32\",\n\t\t\t\t\"51.104.160.167/32\",\n\t\t\t\t\"51.104.160.177/32\",\n\t\t\t\t\"51.104.161.32/32\",\n\t\t\t\t\"51.104.162.149/32\",\n\t\t\t\t\"51.104.163.250/32\",\n\t\t\t\t\"51.104.164.109/32\",\n\t\t\t\t\"51.104.164.147/32\",\n\t\t\t\t\"51.104.164.189/32\",\n\t\t\t\t\"51.104.164.215/32\",\n\t\t\t\t\"51.104.166.111/32\",\n\t\t\t\t\"51.104.167.19/32\",\n\t\t\t\t\"51.104.167.52/32\",\n\t\t\t\t\"51.104.167.54/32\",\n\t\t\t\t\"51.104.167.61/32\",\n\t\t\t\t\"51.104.167.71/32\",\n\t\t\t\t\"51.104.167.87/32\",\n\t\t\t\t\"51.104.167.88/32\",\n\t\t\t\t\"51.104.167.95/32\",\n\t\t\t\t\"51.104.167.96/32\",\n\t\t\t\t\"51.104.167.104/32\",\n\t\t\t\t\"51.104.167.110/32\",\n\t\t\t\t\"51.104.180.26/32\",\n\t\t\t\t\"51.104.180.47/32\",\n\t\t\t\t\"51.104.180.53/32\",\n\t\t\t\t\"51.107.40.209/32\",\n\t\t\t\t\"51.116.131.221/32\",\n\t\t\t\t\"51.120.48.122/32\",\n\t\t\t\t\"51.138.90.161/32\",\n\t\t\t\t\"51.138.90.206/32\",\n\t\t\t\t\"51.138.90.233/32\",\n\t\t\t\t\"51.143.114.45/32\",\n\t\t\t\t\"51.145.185.16/32\",\n\t\t\t\t\"52.142.24.149/32\",\n\t\t\t\t\"52.142.26.175/32\",\n\t\t\t\t\"52.143.95.162/32\",\n\t\t\t\t\"52.143.95.204/32\",\n\t\t\t\t\"52.143.241.111/32\",\n\t\t\t\t\"52.143.242.6/32\",\n\t\t\t\t\"52.143.243.117/32\",\n\t\t\t\t\"52.143.244.81/32\",\n\t\t\t\t\"52.143.247.235/32\",\n\t\t\t\t\"52.146.57.51/32\",\n\t\t\t\t\"52.146.58.236/32\",\n\t\t\t\t\"52.146.59.12/32\",\n\t\t\t\t\"52.146.59.154/32\",\n\t\t\t\t\"52.146.59.156/32\",\n\t\t\t\t\"52.146.63.80/32\",\n\t\t\t\t\"52.148.161.87/32\",\n\t\t\t\t\"52.148.165.38/32\",\n\t\t\t\t\"52.149.25.43/32\",\n\t\t\t\t\"52.149.28.18/32\",\n\t\t\t\t\"52.149.28.83/32\",\n\t\t\t\t\"52.149.30.45/32\",\n\t\t\t\t\"52.149.56.151/32\",\n\t\t\t\t\"52.149.58.27/32\",\n\t\t\t\t\"52.149.58.69/32\",\n\t\t\t\t\"52.149.58.139/32\",\n\t\t\t\t\"52.149.58.173/32\",\n\t\t\t\t\"52.149.60.38/32\",\n\t\t\t\t\"52.149.61.51/32\",\n\t\t\t\t\"52.154.60.82/32\",\n\t\t\t\t\"52.154.169.50/32\",\n\t\t\t\t\"52.154.169.200/32\",\n\t\t\t\t\"52.154.170.26/32\",\n\t\t\t\t\"52.154.170.28/32\",\n\t\t\t\t\"52.154.170.88/32\",\n\t\t\t\t\"52.154.170.96/32\",\n\t\t\t\t\"52.154.170.113/32\",\n\t\t\t\t\"52.154.170.117/32\",\n\t\t\t\t\"52.154.170.122/32\",\n\t\t\t\t\"52.154.170.209/32\",\n\t\t\t\t\"52.154.170.229/32\",\n\t\t\t\t\"52.154.170.243/32\",\n\t\t\t\t\"52.154.171.0/32\",\n\t\t\t\t\"52.154.171.44/32\",\n\t\t\t\t\"52.154.171.70/32\",\n\t\t\t\t\"52.154.171.87/32\",\n\t\t\t\t\"52.154.171.150/32\",\n\t\t\t\t\"52.154.171.196/32\",\n\t\t\t\t\"52.154.171.205/32\",\n\t\t\t\t\"52.154.171.235/32\",\n\t\t\t\t\"52.154.171.250/32\",\n\t\t\t\t\"52.154.172.2/32\",\n\t\t\t\t\"52.154.204.93/32\",\n\t\t\t\t\"52.185.93.208/32\",\n\t\t\t\t\"52.186.37.211/32\",\n\t\t\t\t\"52.188.89.106/32\",\n\t\t\t\t\"52.189.70.103/32\",\n\t\t\t\t\"52.190.37.160/32\",\n\t\t\t\t\"52.191.82.242/32\",\n\t\t\t\t\"52.191.222.158/32\",\n\t\t\t\t\"52.224.16.221/32\",\n\t\t\t\t\"52.224.16.229/32\",\n\t\t\t\t\"52.224.19.152/32\",\n\t\t\t\t\"52.224.20.174/32\",\n\t\t\t\t\"52.224.20.181/32\",\n\t\t\t\t\"52.224.20.186/32\",\n\t\t\t\t\"52.224.20.190/32\",\n\t\t\t\t\"52.224.20.193/32\",\n\t\t\t\t\"52.224.20.203/32\",\n\t\t\t\t\"52.224.20.204/32\",\n\t\t\t\t\"52.224.20.223/32\",\n\t\t\t\t\"52.224.20.227/32\",\n\t\t\t\t\"52.224.20.249/32\",\n\t\t\t\t\"52.224.21.4/32\",\n\t\t\t\t\"52.224.21.19/32\",\n\t\t\t\t\"52.224.21.20/32\",\n\t\t\t\t\"52.224.21.23/32\",\n\t\t\t\t\"52.224.21.27/32\",\n\t\t\t\t\"52.224.21.49/32\",\n\t\t\t\t\"52.224.21.51/32\",\n\t\t\t\t\"52.224.21.53/32\",\n\t\t\t\t\"52.224.21.55/32\",\n\t\t\t\t\"52.224.21.61/32\",\n\t\t\t\t\"52.224.147.21/32\",\n\t\t\t\t\"52.224.191.245/32\",\n\t\t\t\t\"52.242.224.168/32\",\n\t\t\t\t\"52.250.46.221/32\",\n\t\t\t\t\"57.151.44.140/32\",\n\t\t\t\t\"57.152.17.237/32\",\n\t\t\t\t\"57.152.62.107/32\",\n\t\t\t\t\"57.152.72.128/32\",\n\t\t\t\t\"57.155.18.44/32\",\n\t\t\t\t\"57.165.205.172/32\",\n\t\t\t\t\"57.167.88.38/32\",\n\t\t\t\t\"64.236.15.33/32\",\n\t\t\t\t\"64.236.93.100/32\",\n\t\t\t\t\"64.236.118.43/32\",\n\t\t\t\t\"68.219.152.220/32\",\n\t\t\t\t\"72.144.95.185/32\",\n\t\t\t\t\"74.163.169.106/32\",\n\t\t\t\t\"74.163.208.206/32\",\n\t\t\t\t\"74.179.200.126/32\",\n\t\t\t\t\"74.179.232.116/32\",\n\t\t\t\t\"74.242.194.48/32\",\n\t\t\t\t\"98.70.219.37/32\",\n\t\t\t\t\"104.43.54.127/32\",\n\t\t\t\t\"104.43.55.116/32\",\n\t\t\t\t\"104.43.55.117/32\",\n\t\t\t\t\"104.43.55.166/32\",\n\t\t\t\t\"104.43.55.167/32\",\n\t\t\t\t\"108.141.83.74/32\",\n\t\t\t\t\"128.203.82.154/32\",\n\t\t\t\t\"128.203.132.152/32\",\n\t\t\t\t\"128.203.181.79/32\",\n\t\t\t\t\"128.203.214.241/32\",\n\t\t\t\t\"128.203.236.110/32\",\n\t\t\t\t\"130.107.228.224/32\",\n\t\t\t\t\"131.145.39.118/32\",\n\t\t\t\t\"131.189.183.241/32\",\n\t\t\t\t\"132.164.209.198/32\",\n\t\t\t\t\"132.196.106.146/32\",\n\t\t\t\t\"132.220.1.71/32\",\n\t\t\t\t\"134.33.207.189/32\",\n\t\t\t\t\"135.171.251.169/32\",\n\t\t\t\t\"135.233.63.237/32\",\n\t\t\t\t\"135.234.221.112/32\",\n\t\t\t\t\"135.235.183.95/32\",\n\t\t\t\t\"172.168.12.93/32\",\n\t\t\t\t\"172.168.43.43/32\",\n\t\t\t\t\"172.168.51.216/32\",\n\t\t\t\t\"172.168.53.53/32\",\n\t\t\t\t\"172.168.81.217/32\",\n\t\t\t\t\"172.168.94.160/32\",\n\t\t\t\t\"172.168.115.250/32\",\n\t\t\t\t\"172.168.133.76/32\",\n\t\t\t\t\"172.168.137.102/32\",\n\t\t\t\t\"172.168.152.88/32\",\n\t\t\t\t\"172.168.173.230/32\",\n\t\t\t\t\"172.168.190.186/32\",\n\t\t\t\t\"172.168.227.120/32\",\n\t\t\t\t\"172.168.254.119/32\",\n\t\t\t\t\"172.169.7.33/32\",\n\t\t\t\t\"172.169.17.165/32\",\n\t\t\t\t\"172.169.28.184/32\",\n\t\t\t\t\"172.169.36.43/32\",\n\t\t\t\t\"172.169.53.208/32\",\n\t\t\t\t\"172.169.60.134/32\",\n\t\t\t\t\"172.169.93.148/32\",\n\t\t\t\t\"172.169.164.109/32\",\n\t\t\t\t\"172.169.177.131/32\",\n\t\t\t\t\"172.169.181.237/32\",\n\t\t\t\t\"172.171.37.221/32\",\n\t\t\t\t\"172.179.105.87/32\",\n\t\t\t\t\"172.179.128.118/32\",\n\t\t\t\t\"172.188.250.220/32\",\n\t\t\t\t\"172.189.40.18/32\",\n\t\t\t\t\"172.193.184.42/32\",\n\t\t\t\t\"172.193.197.146/32\",\n\t\t\t\t\"172.193.245.229/32\",\n\t\t\t\t\"172.194.141.49/32\",\n\t\t\t\t\"172.199.55.212/32\",\n\t\t\t\t\"172.210.111.68/32\",\n\t\t\t\t\"191.233.3.197/32\",\n\t\t\t\t\"191.233.3.202/32\",\n\t\t\t\t\"191.234.216.4/32\",\n\t\t\t\t\"191.234.216.178/32\",\n\t\t\t\t\"191.235.201.214/32\",\n\t\t\t\t\"191.235.202.38/32\",\n\t\t\t\t\"191.235.202.48/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Applebot\",\n\t\t\t\"url\": \"https://search.developer.apple.com/applebot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"17.166.20.0/24\",\n\t\t\t\t\"17.166.21.0/24\",\n\t\t\t\t\"17.166.22.0/24\",\n\t\t\t\t\"17.166.23.0/24\",\n\t\t\t\t\"17.166.24.0/24\",\n\t\t\t\t\"17.166.25.0/24\",\n\t\t\t\t\"17.166.150.0/24\",\n\t\t\t\t\"17.166.151.0/24\",\n\t\t\t\t\"17.166.152.0/24\",\n\t\t\t\t\"17.166.153.0/24\",\n\t\t\t\t\"17.166.154.0/24\",\n\t\t\t\t\"17.166.155.0/24\",\n\t\t\t\t\"17.166.192.48/28\",\n\t\t\t\t\"17.166.232.0/24\",\n\t\t\t\t\"17.166.233.0/24\",\n\t\t\t\t\"17.166.234.0/24\",\n\t\t\t\t\"17.166.235.0/24\",\n\t\t\t\t\"17.166.236.0/24\",\n\t\t\t\t\"17.166.237.0/24\",\n\t\t\t\t\"17.166.240.48/28\",\n\t\t\t\t\"17.166.248.48/28\",\n\t\t\t\t\"17.241.193.160/27\",\n\t\t\t\t\"17.241.200.160/27\",\n\t\t\t\t\"17.241.208.160/27\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Claude API\",\n\t\t\t\"url\": \"https://platform.claude.com/docs/en/api/ip-addresses.md\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"153.61.208.0/20\",\n\t\t\t\t\"160.79.104.0/21\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t}\n\t]\n}\n","const encoder = new TextEncoder();\nconst keys = new Map<string, Promise<CryptoKey>>();\n\nfunction importOrgKey(orgKey: string): Promise<CryptoKey> {\n\tlet key = keys.get(orgKey);\n\tif (!key) {\n\t\tkey = crypto.subtle.importKey(\n\t\t\t\"raw\",\n\t\t\tencoder.encode(orgKey),\n\t\t\t{ name: \"HMAC\", hash: \"SHA-256\" },\n\t\t\tfalse,\n\t\t\t[\"sign\"],\n\t\t);\n\t\tkey.catch(() => keys.delete(orgKey));\n\t\tkeys.set(orgKey, key);\n\t}\n\treturn key;\n}\n\nconst toHex = (bytes: ArrayBuffer): string =>\n\tArray.from(new Uint8Array(bytes), (byte) =>\n\t\tbyte.toString(16).padStart(2, \"0\"),\n\t).join(\"\");\n\nexport async function deriveMatchKey(\n\torgKey: string,\n\tip: string,\n): Promise<string> {\n\tconst signature = await crypto.subtle.sign(\n\t\t\"HMAC\",\n\t\tawait importOrgKey(orgKey),\n\t\tencoder.encode(ip),\n\t);\n\treturn toHex(signature);\n}\n","import { applyRules, type MatchRules } from \"./logic/decide\";\nimport { settings } from \"./settings\";\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null;\n\nconst isStringArray = (value: unknown): value is string[] =>\n\tArray.isArray(value) && value.every((item) => typeof item === \"string\");\n\n/** The match rules in a rules file from the app (the `data` of its answer), or null when it does not have the expected form. A malformed range makes `applyRules` throw instead. */\nexport function parseRemoteRules(body: unknown): MatchRules | null {\n\tif (!isObject(body) || body.version !== 1) {\n\t\treturn null;\n\t}\n\tconst { tokens, ipRanges } = body;\n\tif (!isStringArray(tokens) || !Array.isArray(ipRanges)) {\n\t\treturn null;\n\t}\n\tconst ranges: MatchRules[\"ipRanges\"] = [];\n\tfor (const range of ipRanges) {\n\t\tif (\n\t\t\t!isObject(range) ||\n\t\t\t!isStringArray(range.ipv4) ||\n\t\t\t!isStringArray(range.ipv6)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tranges.push({ ipv4: range.ipv4, ipv6: range.ipv6 });\n\t}\n\treturn {\n\t\ttokens: tokens.map((token) => token.toLowerCase()),\n\t\tipRanges: ranges,\n\t};\n}\n\nlet lastAttemptOk = false;\nlet lastAttemptAt = Number.NEGATIVE_INFINITY;\n\nasync function fetchRules(apiUrl: string): Promise<boolean> {\n\tconst response = await fetch(`${apiUrl}/api/v2/agent-traffic/rules`, {\n\t\tsignal: AbortSignal.timeout(settings.requestTimeoutMs),\n\t});\n\tif (!response.ok) {\n\t\treturn false;\n\t}\n\tconst body: unknown = await response.json();\n\tconst rules = parseRemoteRules(isObject(body) ? body.data : null);\n\tif (!rules) {\n\t\treturn false;\n\t}\n\tapplyRules(rules);\n\treturn true;\n}\n\n/**\n * Starts one fetch of the rules file when the last attempt succeeded more than the refresh interval ago,\n * or failed more than the retry interval ago. Returns that work, or null.\n */\nexport function refreshRules(apiUrl: string): Promise<void> | null {\n\tconst now = Date.now();\n\tconst wait = lastAttemptOk ? settings.rulesRefreshMs : settings.rulesRetryMs;\n\tif (now - lastAttemptAt < wait) {\n\t\treturn null;\n\t}\n\tlastAttemptAt = now;\n\tlastAttemptOk = false;\n\treturn fetchRules(apiUrl).then(\n\t\t(ok) => {\n\t\t\tlastAttemptOk = ok;\n\t\t},\n\t\t() => undefined,\n\t);\n}\n","type WaitUntil = (promise: Promise<unknown>) => void;\ntype RequestContextStore = {\n\tget?: () => { waitUntil?: WaitUntil } | undefined;\n};\n\nconst REQUEST_CONTEXT = Symbol.for(\"@vercel/request-context\");\n\nexport function vercelRequestContext(): { waitUntil?: WaitUntil } | undefined {\n\tconst store: RequestContextStore | undefined = Reflect.get(\n\t\tglobalThis,\n\t\tREQUEST_CONTEXT,\n\t);\n\treturn store?.get?.();\n}\n\n/** The `waitUntil` that the Vercel runtime puts in the request context, the same one `@vercel/functions` reads. */\nexport function vercelWaitUntil(): WaitUntil | undefined {\n\tconst context = vercelRequestContext();\n\treturn context?.waitUntil\n\t\t? (promise) => context.waitUntil?.(promise)\n\t\t: undefined;\n}\n","import { vercelRequestContext } from \"./wait-until\";\n\nexport type Platform = \"vercel\" | \"cloudflare\" | \"cloudfront\";\n\n/** The platform this request runs on, or undefined on a runtime the relay does not know. Never throws. */\nexport function detectPlatform(): Platform | undefined {\n\ttry {\n\t\tconst context = vercelRequestContext();\n\t\tif (typeof context === \"object\" && context !== null) {\n\t\t\treturn \"vercel\";\n\t\t}\n\t\tif (globalThis.navigator?.userAgent === \"Cloudflare-Workers\") {\n\t\t\treturn \"cloudflare\";\n\t\t}\n\t} catch {}\n\treturn undefined;\n}\n","import { clientIp, HEADER_CAP } from \"./headers\";\nimport { detectPlatform, type Platform } from \"./platform\";\n\n/** `event.Records[0].cf.request` of a Lambda@Edge viewer-request event. */\nexport type CloudFrontRequest = {\n\tclientIp: string;\n\tmethod: string;\n\turi: string;\n\tquerystring: string;\n\theaders: Record<string, Array<{ key?: string; value: string }>>;\n};\n\nexport type RelayRequest = {\n\turl: string | null;\n\tmethod: string;\n\theaders: Headers;\n\tip: string | null;\n\tplatform: Platform | undefined;\n};\n\nexport function isCloudFrontRequest(\n\tinput: Request | CloudFrontRequest,\n): input is CloudFrontRequest {\n\treturn (\n\t\ttypeof Reflect.get(input, \"clientIp\") === \"string\" &&\n\t\ttypeof Reflect.get(input, \"uri\") === \"string\"\n\t);\n}\n\n/** The origin for a `host` header, or null when the value holds more than a host and an optional port. */\nfunction originOf(host: string | undefined): string | null {\n\tif (!host) {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst url = new URL(`https://${host}`);\n\t\tconst bare =\n\t\t\turl.pathname === \"/\" &&\n\t\t\turl.search === \"\" &&\n\t\t\turl.hash === \"\" &&\n\t\t\turl.username === \"\" &&\n\t\t\turl.password === \"\";\n\t\treturn bare ? url.origin : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** The hostname in a `host` header or a bare hostname, lowercased and without the port, or null when the value holds more. */\nexport function hostnameOf(host: string | undefined): string | null {\n\tconst origin = originOf(host);\n\treturn origin === null ? null : new URL(origin).hostname;\n}\n\nexport function cloudFrontHeader(\n\tinput: CloudFrontRequest,\n\tname: string,\n): string | null {\n\tconst value = input.headers?.[name]?.[0]?.value;\n\treturn typeof value === \"string\" ? value : null;\n}\n\n/** Copies only the headers the relay reads, so a cookie or a value `Headers` refuses never makes the request fail. */\nfunction cloudFrontHeaders(input: CloudFrontRequest[\"headers\"]): Headers {\n\tconst headers = new Headers();\n\tfor (const name of HEADER_CAP) {\n\t\tfor (const entry of input?.[name] ?? []) {\n\t\t\tif (typeof entry?.value !== \"string\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\theaders.append(name, entry.value);\n\t\t\t} catch {}\n\t\t}\n\t}\n\treturn headers;\n}\n\n/** The client can set `X-Forwarded-For` at viewer-request, so a CloudFront request takes its IP from `clientIp` alone. `url` is null when the host header is missing or malformed. */\nexport function toRelayRequest(\n\tinput: Request | CloudFrontRequest,\n): RelayRequest {\n\tif (!isCloudFrontRequest(input)) {\n\t\treturn {\n\t\t\turl: input.url,\n\t\t\tmethod: input.method,\n\t\t\theaders: input.headers,\n\t\t\tip: clientIp(input.headers),\n\t\t\tplatform: detectPlatform(),\n\t\t};\n\t}\n\tconst origin = originOf(input.headers?.host?.[0]?.value);\n\tconst query = input.querystring ? `?${input.querystring}` : \"\";\n\tconst ip = input.clientIp.trim().toLowerCase();\n\treturn {\n\t\turl: origin === null ? null : `${origin}${input.uri}${query}`,\n\t\tmethod: input.method,\n\t\theaders: cloudFrontHeaders(input.headers),\n\t\tip: ip ? ip : null,\n\t\tplatform: \"cloudfront\",\n\t};\n}\n","import {\n\tfetchAgentPage,\n\tloadSiteRules,\n\tmatchesPath,\n\tnormalizePath,\n\ttype SiteRules,\n} from \"./agent-page\";\nimport { readCappedHeaders } from \"./headers\";\nimport { type PostPolicy, postEvent } from \"./ingest\";\nimport { readKey } from \"./key\";\nimport { decide } from \"./logic\";\nimport { deriveMatchKey } from \"./match-key\";\nimport type { Platform } from \"./platform\";\nimport { refreshRules } from \"./refresh\";\nimport {\n\ttype CloudFrontRequest,\n\tcloudFrontHeader,\n\thostnameOf,\n\tisCloudFrontRequest,\n\ttoRelayRequest,\n} from \"./request\";\nimport { settings } from \"./settings\";\nimport { RELAY_VERSION } from \"./version\";\nimport { vercelWaitUntil } from \"./wait-until\";\n\nexport type { CloudFrontRequest };\n\n/** The response a Lambda@Edge handler returns in place of the request. */\nexport type CloudFrontResultResponse = {\n\tstatus: string;\n\tstatusDescription?: string;\n\theaders: Record<string, Array<{ key: string; value: string }>>;\n\tbody?: string;\n};\n\nexport type Region = keyof typeof settings.apiUrls;\n\nexport type RelayOptions = {\n\t/** The region of your WaniWani organization. Defaults to `\"us\"`. */\n\tregion?: Region;\n\t/** Overrides the host that `region` picks. */\n\tapiUrl?: string;\n\t/** Overrides `process.env.WANIWANI_AGENT_TRAFFIC_KEY`. */\n\tkey?: string;\n\t/** Keeps the background post alive after the response. Defaults to the `waitUntil` of the Vercel request context. */\n\twaitUntil?: (promise: Promise<unknown>) => void;\n\t/** Header names, and the fields `matchKey`, `userAgent` or `referer`, to leave out of every event. */\n\tomit?: Array<string>;\n\t/** `\"remote\"` loads the newest AI bot list from WaniWani every 12 hours. `\"bundled\"` keeps the list shipped in this version. Defaults to `\"remote\"`. */\n\trules?: \"remote\" | \"bundled\";\n};\n\nexport type IsAgentOptions = Pick<\n\tRelayOptions,\n\t\"region\" | \"apiUrl\" | \"waitUntil\" | \"rules\"\n>;\n\nexport type ServeAgentPageOptions = Pick<\n\tRelayOptions,\n\t\"region\" | \"apiUrl\" | \"key\"\n>;\n\nexport type AgentVisitRecord = {\n\toccurredAt: string;\n\tmethod: string;\n\thost: string;\n\tpath: string;\n\tuserAgent?: string;\n\treferer?: string;\n\theaders: Record<string, string>;\n\tmatchKey?: string;\n\tsourceEventId: string;\n\trelayVersion: string;\n\tplatform?: Platform;\n};\n\n/**\n * Tells whether a request, or the `cf.request` of a Lambda@Edge event, comes from an AI agent. Reads headers only and never waits on the network.\n * Every 12 hours it starts one background fetch of the newest AI bot list, unless `rules` is `\"bundled\"`.\n */\nexport function isAgent(\n\trequest: Request | CloudFrontRequest,\n\toptions: IsAgentOptions = {},\n): boolean {\n\tstartRulesRefresh(options);\n\ttry {\n\t\tconst { headers, ip } = toRelayRequest(request);\n\t\treturn decide({\n\t\t\theaders: readCappedHeaders(headers),\n\t\t\tip,\n\t\t\tuseBundled: options.rules === \"bundled\",\n\t\t});\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction startRulesRefresh(options: IsAgentOptions): void {\n\ttry {\n\t\tif (options.rules === \"bundled\") {\n\t\t\treturn;\n\t\t}\n\t\tconst apiUrl = resolveApiUrl(options);\n\t\tconst work = apiUrl ? refreshRules(apiUrl) : null;\n\t\tif (work) {\n\t\t\tschedule(work, options.waitUntil);\n\t\t}\n\t} catch {}\n}\n\n/**\n * Posts one event for this request to WaniWani after the response.\n * Sends nothing when no key is set. Never throws, never rejects and never touches the request.\n * The promise settles when the post is done, so Lambda@Edge, which has no `waitUntil`, awaits it.\n */\nexport function recordAgentVisit(\n\trequest: Request | CloudFrontRequest,\n\toptions: RelayOptions = {},\n): Promise<void> {\n\ttry {\n\t\tconst key = readKey(options.key);\n\t\tif (!key) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst apiUrl = resolveApiUrl(options);\n\t\tif (!apiUrl) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst omitted = omitSet(options.omit);\n\t\tconst {\n\t\t\turl: requestUrl,\n\t\t\tmethod,\n\t\t\theaders: requestHeaders,\n\t\t\tip,\n\t\t\tplatform,\n\t\t} = toRelayRequest(request);\n\t\tif (requestUrl === null) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst capped = readCappedHeaders(requestHeaders);\n\n\t\tconst headers: Record<string, string> = {};\n\t\tfor (const [name, value] of Object.entries(capped)) {\n\t\t\tif (!omitted.has(name)) {\n\t\t\t\theaders[name] = value;\n\t\t\t}\n\t\t}\n\t\tconst url = new URL(requestUrl);\n\t\tconst record: AgentVisitRecord = {\n\t\t\toccurredAt: new Date().toISOString(),\n\t\t\tmethod,\n\t\t\thost: url.hostname,\n\t\t\tpath: url.pathname + url.search,\n\t\t\theaders,\n\t\t\tsourceEventId: crypto.randomUUID(),\n\t\t\trelayVersion: RELAY_VERSION,\n\t\t};\n\t\tif (capped[\"user-agent\"] !== undefined && !omitted.has(\"user-agent\")) {\n\t\t\trecord.userAgent = capped[\"user-agent\"];\n\t\t}\n\t\tif (capped.referer !== undefined && !omitted.has(\"referer\")) {\n\t\t\trecord.referer = capped.referer;\n\t\t}\n\t\tif (platform !== undefined) {\n\t\t\trecord.platform = platform;\n\t\t}\n\n\t\tconst policy: PostPolicy =\n\t\t\tplatform === \"cloudfront\"\n\t\t\t\t? { timeoutMs: settings.cloudFrontTimeoutMs, retry: false }\n\t\t\t\t: { timeoutMs: settings.requestTimeoutMs, retry: true };\n\t\tconst ingestUrl = `${apiUrl}/api/v2/agent-traffic/ingest/middleware/${key.ingestToken}`;\n\t\tconst withMatchKey = async () => {\n\t\t\tif (ip !== null && !omitted.has(\"matchkey\")) {\n\t\t\t\trecord.matchKey = await deriveMatchKey(key.orgKey, ip).catch(\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait postEvent(\n\t\t\t\tingestUrl,\n\t\t\t\tJSON.stringify({ requests: [record] }),\n\t\t\t\tpolicy,\n\t\t\t);\n\t\t};\n\t\treturn schedule(withMatchKey(), options.waitUntil);\n\t} catch {\n\t\treturn Promise.resolve();\n\t}\n}\n\ntype PageRequest = {\n\tmethod: string;\n\thostname: string;\n\tpathname: string;\n\tuserAgent: string;\n\trendering: boolean;\n};\n\ntype PageAnswer = {\n\tstatus: 200 | 403;\n\theaders: Record<string, string>;\n\tbody: Uint8Array<ArrayBuffer> | string | null;\n};\n\ntype SiteMatch = {\n\trules: SiteRules;\n\tsite: SiteRules[\"sites\"][number];\n};\n\nconst BLOCKED: Omit<PageAnswer, \"body\"> = {\n\tstatus: 403,\n\theaders: {\n\t\t\"content-type\": \"text/plain; charset=utf-8\",\n\t\t\"x-waniwani-blocked\": \"1\",\n\t\t\"cache-control\": \"private, no-store\",\n\t\tvary: \"user-agent\",\n\t},\n};\n\n/**\n * The stored agent page for this request, when its site lists the path and the user agent acts for a user. A 403 when the site blocks the user agent. Otherwise null, and the caller serves the origin.\n * A `CloudFrontRequest` comes from the origin-request handler. Only a URI that `handleViewerRequest` marked gets the page, read for the host in the mark. The function restores `request.uri` to the original path before anything else, and it never returns a 403 on this path.\n * Never throws and never rejects.\n */\nexport function serveAgentPage(\n\trequest: Request,\n\toptions?: ServeAgentPageOptions,\n): Promise<Response | null>;\nexport function serveAgentPage(\n\trequest: CloudFrontRequest,\n\toptions?: ServeAgentPageOptions,\n): Promise<CloudFrontResultResponse | null>;\nexport async function serveAgentPage(\n\trequest: Request | CloudFrontRequest,\n\toptions: ServeAgentPageOptions = {},\n): Promise<Response | CloudFrontResultResponse | null> {\n\ttry {\n\t\treturn isCloudFrontRequest(request)\n\t\t\t? await serveCloudFront(request, options)\n\t\t\t: await serveFetch(request, options);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function serveFetch(\n\trequest: Request,\n\toptions: ServeAgentPageOptions,\n): Promise<Response | null> {\n\tconst url = new URL(request.url);\n\tconst answer = await answerAgentPage(\n\t\t{\n\t\t\tmethod: request.method.toUpperCase(),\n\t\t\thostname: url.hostname,\n\t\t\tpathname: url.pathname,\n\t\t\tuserAgent: request.headers.get(\"user-agent\") ?? \"\",\n\t\t\trendering: request.headers.has(\"x-waniwani-render\"),\n\t\t},\n\t\toptions,\n\t);\n\tif (!answer) {\n\t\treturn null;\n\t}\n\treturn new Response(answer.body, {\n\t\tstatus: answer.status,\n\t\theaders: answer.headers,\n\t});\n}\n\nasync function serveCloudFront(\n\trequest: CloudFrontRequest,\n\toptions: ServeAgentPageOptions,\n): Promise<CloudFrontResultResponse | null> {\n\tconst hostname = takeMarkFromUri(request);\n\tif (hostname === null) {\n\t\treturn null;\n\t}\n\tconst method = request.method.toUpperCase();\n\tif (!isPageMethod(method)) {\n\t\treturn null;\n\t}\n\tconst match = await findSite(hostname, options);\n\tif (!match) {\n\t\treturn null;\n\t}\n\tconst answer = await pageAnswer(\n\t\tmatch.rules,\n\t\thostname,\n\t\tnormalizePath(request.uri),\n\t\tmethod,\n\t);\n\tif (!answer) {\n\t\treturn null;\n\t}\n\tconst result = toCloudFrontResponse(answer);\n\tif (\n\t\tresult.body !== undefined &&\n\t\tnew TextEncoder().encode(result.body).byteLength >\n\t\t\tsettings.cloudFrontMaxBodyBytes\n\t) {\n\t\treturn null;\n\t}\n\treturn result;\n}\n\nfunction toCloudFrontResponse(answer: PageAnswer): CloudFrontResultResponse {\n\tconst headers: CloudFrontResultResponse[\"headers\"] = {};\n\tfor (const [name, value] of Object.entries(answer.headers)) {\n\t\theaders[name] = [{ key: headerKey(name), value }];\n\t}\n\tconst result: CloudFrontResultResponse = {\n\t\tstatus: String(answer.status),\n\t\tstatusDescription: answer.status === 200 ? \"OK\" : \"Forbidden\",\n\t\theaders,\n\t};\n\tif (answer.body !== null) {\n\t\tresult.body =\n\t\t\ttypeof answer.body === \"string\"\n\t\t\t\t? answer.body\n\t\t\t\t: new TextDecoder().decode(answer.body);\n\t}\n\treturn result;\n}\n\nfunction headerKey(name: string): string {\n\treturn name\n\t\t.split(\"-\")\n\t\t.map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n\t\t.join(\"-\");\n}\n\nconst isPageMethod = (method: string): boolean =>\n\tmethod === \"GET\" || method === \"HEAD\";\n\nasync function findSite(\n\thostname: string,\n\toptions: ServeAgentPageOptions,\n): Promise<SiteMatch | null> {\n\tconst key = readKey(options.key);\n\tif (!key) {\n\t\treturn null;\n\t}\n\tconst apiUrl = resolveApiUrl(options);\n\tif (!apiUrl) {\n\t\treturn null;\n\t}\n\tconst rules = await loadSiteRules(apiUrl, key.ingestToken, (work) =>\n\t\tschedule(work, undefined),\n\t);\n\tif (!rules) {\n\t\treturn null;\n\t}\n\tconst site = rules.sites.find((entry) => entry.hostname === hostname);\n\treturn site ? { rules, site } : null;\n}\n\nfunction blockedAnswer(\n\tsite: SiteMatch[\"site\"],\n\tuserAgent: string,\n\tmethod: string,\n): PageAnswer | null {\n\treturn site.blockedUserAgentTokens.some((token) => userAgent.includes(token))\n\t\t? { ...BLOCKED, body: method === \"HEAD\" ? null : \"Forbidden\" }\n\t\t: null;\n}\n\nasync function answerAgentPage(\n\trequest: PageRequest,\n\toptions: ServeAgentPageOptions,\n): Promise<PageAnswer | null> {\n\tconst { method, hostname } = request;\n\tif (!isPageMethod(method)) {\n\t\treturn null;\n\t}\n\tconst match = await findSite(hostname, options);\n\tif (!match) {\n\t\treturn null;\n\t}\n\tconst { rules, site } = match;\n\tconst userAgent = request.userAgent.toLowerCase();\n\tconst blocked = blockedAnswer(site, userAgent, method);\n\tif (blocked) {\n\t\treturn blocked;\n\t}\n\tif (request.rendering || !site.agentPages.enabled) {\n\t\treturn null;\n\t}\n\tconst path = normalizePath(request.pathname);\n\tif (!matchesPath(path, site.agentPages.paths)) {\n\t\treturn null;\n\t}\n\tif (!rules.userAgentTokens.some((token) => userAgent.includes(token))) {\n\t\treturn null;\n\t}\n\treturn pageAnswer(rules, hostname, path, method);\n}\n\nasync function pageAnswer(\n\trules: SiteRules,\n\thostname: string,\n\tpath: string,\n\tmethod: string,\n): Promise<PageAnswer | null> {\n\tconst page = await fetchAgentPage(rules, hostname, path);\n\tif (!page) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"content-type\": page.contentType,\n\t\t\t\"x-waniwani-agent-page\": \"hit\",\n\t\t\t\"cache-control\": \"private, no-store\",\n\t\t\tvary: \"user-agent, accept\",\n\t\t},\n\t\tbody: method === \"HEAD\" ? null : page.body,\n\t};\n}\n\nconst AGENT_URI_PREFIX = \"/__waniwani-agent/\";\n\nfunction splitAgentUri(\n\turi: string,\n): { encodedHost: string; originalUri: string } | null {\n\tif (!uri.startsWith(AGENT_URI_PREFIX)) {\n\t\treturn null;\n\t}\n\tconst rest = uri.slice(AGENT_URI_PREFIX.length);\n\tconst slash = rest.indexOf(\"/\");\n\treturn slash === -1\n\t\t? { encodedHost: rest, originalUri: \"/\" }\n\t\t: { encodedHost: rest.slice(0, slash), originalUri: rest.slice(slash) };\n}\n\nfunction stripForgedMark(request: CloudFrontRequest): void {\n\tlet forged = splitAgentUri(request.uri);\n\twhile (forged) {\n\t\trequest.uri = forged.originalUri;\n\t\tforged = splitAgentUri(request.uri);\n\t}\n}\n\nfunction takeMarkFromUri(request: CloudFrontRequest): string | null {\n\tconst parts = splitAgentUri(request.uri);\n\tif (!parts) {\n\t\treturn null;\n\t}\n\trequest.uri = parts.originalUri;\n\ttry {\n\t\treturn hostnameOf(decodeURIComponent(parts.encodedHost));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * For the viewer-request handler on CloudFront. Strips a `/__waniwani-agent/` prefix the client sent, then, for a GET or HEAD from an AI agent, loads the rules of the site of the `host` header.\n * Returns the 403 response when the site blocks the user agent. CloudFront never caches a response made at viewer-request, so a block takes effect at once.\n * When the site serves an agent page for the path and the user agent acts for a user, rewrites `request.uri` to `/__waniwani-agent/<host><original uri>` so the origin-request handler serves the page under its own cache key. The request stays unmarked when the new URI would pass the CloudFront URI limit.\n * Call `recordAgentVisit` before it, so the event holds the original path. Never throws and never rejects.\n */\nexport async function handleViewerRequest(\n\trequest: CloudFrontRequest,\n\toptions: ServeAgentPageOptions = {},\n): Promise<CloudFrontResultResponse | null> {\n\ttry {\n\t\tstripForgedMark(request);\n\t\tconst method = request.method.toUpperCase();\n\t\tif (!isPageMethod(method) || !isAgent(request, options)) {\n\t\t\treturn null;\n\t\t}\n\t\tconst hostname = hostnameOf(cloudFrontHeader(request, \"host\") ?? undefined);\n\t\tif (hostname === null) {\n\t\t\treturn null;\n\t\t}\n\t\tconst match = await findSite(hostname, options);\n\t\tif (!match) {\n\t\t\treturn null;\n\t\t}\n\t\tconst { rules, site } = match;\n\t\tconst userAgent = (\n\t\t\tcloudFrontHeader(request, \"user-agent\") ?? \"\"\n\t\t).toLowerCase();\n\t\tconst blocked = blockedAnswer(site, userAgent, method);\n\t\tif (blocked) {\n\t\t\treturn toCloudFrontResponse(blocked);\n\t\t}\n\t\tif (\n\t\t\tcloudFrontHeader(request, \"x-waniwani-render\") !== null ||\n\t\t\t!site.agentPages.enabled ||\n\t\t\t!matchesPath(normalizePath(request.uri), site.agentPages.paths) ||\n\t\t\t!rules.userAgentTokens.some((token) => userAgent.includes(token))\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tconst uri = `${AGENT_URI_PREFIX}${encodeURIComponent(hostname)}${request.uri}`;\n\t\tif (\n\t\t\tnew TextEncoder().encode(uri).byteLength <= settings.cloudFrontMaxUriBytes\n\t\t) {\n\t\t\trequest.uri = uri;\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nlet warnedRegion = false;\n\nfunction resolveApiUrl(options: IsAgentOptions): string | null {\n\tif (options.apiUrl !== undefined) {\n\t\treturn options.apiUrl.replace(/\\/+$/, \"\");\n\t}\n\tconst region = options.region ?? \"us\";\n\tif (Object.hasOwn(settings.apiUrls, region)) {\n\t\treturn settings.apiUrls[region];\n\t}\n\tif (!warnedRegion) {\n\t\twarnedRegion = true;\n\t\tconsole.warn(\n\t\t\t`[@waniwani/relay] Unknown region ${JSON.stringify(region)}, so no agent visits are recorded. Use \"us\" or \"eu\".`,\n\t\t);\n\t}\n\treturn null;\n}\n\nfunction omitSet(omit: Array<string> | undefined): Set<string> {\n\tconst names = new Set((omit ?? []).map((name) => name.toLowerCase()));\n\tif (names.has(\"useragent\")) {\n\t\tnames.add(\"user-agent\");\n\t}\n\treturn names;\n}\n\nfunction schedule(\n\twork: Promise<unknown>,\n\twaitUntil: RelayOptions[\"waitUntil\"],\n): Promise<void> {\n\tconst settled = work.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\ttry {\n\t\t(waitUntil ?? vercelWaitUntil())?.(settled);\n\t} catch {}\n\treturn settled;\n}\n"],"mappings":"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IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;;;AC7KA;AAAA,EACC,SAAW;AAAA,IACV;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AA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CA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QA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","names":["isObject","isStringArray"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waniwani/relay",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Records AI agent visits to your website from your middleware and posts them to WaniWani. Open source, so your security team can read every line.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",