@patchstack/connect 0.3.29 → 0.3.31

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.
Files changed (40) hide show
  1. package/AGENT-INSTALL.md +42 -23
  2. package/README.md +40 -11
  3. package/dist/{chunk-MJOTFUDE.js → chunk-LLKP5EJS.js} +1 -1
  4. package/dist/chunk-LLKP5EJS.js.map +1 -0
  5. package/dist/cli.js +1265 -287
  6. package/dist/cli.js.map +1 -1
  7. package/dist/index.cjs +456 -111
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +76 -10
  10. package/dist/index.d.ts +76 -10
  11. package/dist/index.js +449 -104
  12. package/dist/index.js.map +1 -1
  13. package/dist/protect/templates/astro-middleware.ts +33 -13
  14. package/dist/protect/templates/demo-rules.json +2 -3
  15. package/dist/protect/templates/express-guard.cjs +26 -15
  16. package/dist/protect/templates/express-guard.js +26 -15
  17. package/dist/protect/templates/express-guard.ts +33 -16
  18. package/dist/protect/templates/fastify-plugin.cjs +33 -11
  19. package/dist/protect/templates/fastify-plugin.js +33 -11
  20. package/dist/protect/templates/fastify-plugin.ts +40 -12
  21. package/dist/protect/templates/generic-guard.cjs +28 -12
  22. package/dist/protect/templates/generic-guard.js +28 -13
  23. package/dist/protect/templates/generic-guard.ts +41 -20
  24. package/dist/protect/templates/guard.ts +47 -27
  25. package/dist/protect/templates/next-middleware.ts +29 -12
  26. package/dist/protect/templates/nuxt-middleware.ts +29 -12
  27. package/dist/protect/templates/rules.json +2 -2
  28. package/dist/protect/templates/sveltekit-hooks.ts +33 -13
  29. package/dist/protect.cjs +1204 -252
  30. package/dist/protect.cjs.map +1 -1
  31. package/dist/protect.d.ts +39 -11
  32. package/dist/protect.edge.js +792 -143
  33. package/dist/protect.edge.js.map +4 -4
  34. package/dist/protect.js +794 -145
  35. package/dist/protect.js.map +1 -1
  36. package/dist/{refresh-manifest-VRBE6RH6.js → refresh-manifest-KNZQBC4V.js} +387 -100
  37. package/dist/refresh-manifest-KNZQBC4V.js.map +1 -0
  38. package/package.json +5 -2
  39. package/dist/chunk-MJOTFUDE.js.map +0 -1
  40. package/dist/refresh-manifest-VRBE6RH6.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/protect/safe-origin.js","../src/protect/engine/client.js","../src/protect/engine/request.js","../src/protect/notify.js","../src/protect/engine/normalizer.js","../src/protect/engine/engine.js","../src/protect/engine/pulse-client.js","../src/protect/engine/fetch.js","../src/protect/engine/node.js","../src/protect/egress.js","../src/protect/defaults.js","../src/protect/block-page.js","../src/protect/rules/store.js","../src/protect/rules/validate.js","../src/protect/rules/source.js","../src/protect/rules/refresh.js","../src/protect/detections.js","../src/protect/firewall-log.js","../src/protect/supabase-guard.js","../src/protect/runtime.js"],"sourcesContent":["// Which origins this guard is willing to talk to. Both of its remote conversations are security\n// sensitive: the RULE endpoint delivers policy the engine then executes on every request (an\n// attacker-controlled endpoint could remove protection wholesale or serve a CPU-expensive ruleset), and\n// the telemetry endpoint receives the site api_key. Env/CI injection is the realistic threat, so a\n// non-default override must be https — with localhost permitted so local development and tests work.\nexport function isSafeOrigin(value) {\n try {\n const u = new URL(value);\n if (u.protocol === 'https:') return true;\n return u.protocol === 'http:' && (u.hostname === 'localhost' || u.hostname === '127.0.0.1' || u.hostname === '[::1]' || u.hostname === '::1');\n } catch {\n return false;\n }\n}\n\n/**\n * Accept `candidate` only if it is a safe origin; otherwise warn once and fall back to `fallback`.\n * @param {string|undefined} candidate @param {string} fallback @param {string} label\n */\nexport function safeBaseUrl(candidate, fallback, label) {\n if (typeof candidate !== 'string' || candidate === '') return fallback;\n if (isSafeOrigin(candidate)) return candidate;\n warnOnce(label, `[patchstack] ignoring unsafe ${label} override (${candidate}): must be https (or localhost). Using the default.`);\n return fallback;\n}\n\nconst warned = new Set();\nfunction warnOnce(key, message) {\n if (warned.has(key)) return;\n warned.add(key);\n // eslint-disable-next-line no-console\n console.warn(message);\n}\n","import { safeBaseUrl } from '../safe-origin.js';\n\nconst DEFAULT_BASE_URL = 'https://api.patchstack.com';\nconst DEFAULT_CACHE_TTL = 300_000;\n// Randomly shorten the effective TTL by up to this fraction so many long-lived clients don't all\n// revalidate on the same tick (spreads load against the rules API).\nconst JITTER_FRACTION = 0.1;\n\n// Token-authenticated rules client. Conditional fetch (If-None-Match / 304) and the persisted\n// `etag` mirror PulseRuleClient; both are a no-op until the API emits an ETag and honors the\n// conditional request — with no ETag in the response the client behaves as before (full fetch).\nexport class PatchstackRuleClient {\n #token;\n #baseUrl;\n #cacheTtl;\n #cache = null;\n #cacheTime = null;\n #ttlEffective = 0;\n #etag;\n #timeoutMs;\n\n constructor({ token, baseUrl, cacheTtl, etag, timeoutMs } = {}) {\n // Bounded so app STARTUP can't hang on a slow API (see the boot budget in rules/source.js).\n this.#timeoutMs = Number(timeoutMs) > 0 ? Number(timeoutMs) : 30_000;\n this.#token = token ?? process.env.PATCHSTACK_WAF_TOKEN;\n this.#baseUrl = safeBaseUrl(baseUrl ?? process.env.PATCHSTACK_WAF_API_URL, DEFAULT_BASE_URL, 'rule endpoint');\n this.#cacheTtl = Number.isFinite(cacheTtl) && cacheTtl > 0 ? cacheTtl : DEFAULT_CACHE_TTL;\n this.#etag = etag ?? null;\n\n if (!this.#token) {\n throw new Error('Patchstack WAF token is required. Pass { token } or set PATCHSTACK_WAF_TOKEN env var.');\n }\n }\n\n async getRules() {\n const now = Date.now();\n\n if (this.#cache && this.#cacheTime !== null && (now - this.#cacheTime) < this.#ttlEffective) {\n return this.#cache;\n }\n\n const url = `${this.#baseUrl}/api/get-rules/3`;\n\n try {\n const headers = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.#token}`,\n 'LicenseID': '1' // Hard-coded, is never actually used but needed by the API\n };\n if (this.#etag) headers['If-None-Match'] = this.#etag;\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n signal: AbortSignal.timeout(this.#timeoutMs)\n });\n\n if (response.status === 304) {\n this.#touch(now); // revalidated — reset the clock (fresh jitter)\n return this.#cache ?? { success: true, notModified: true, etag: this.#etag, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n\n if (!response.ok) {\n return {\n success: false,\n error: `API returned ${response.status}: ${response.statusText}`,\n firewall: [],\n whitelists: [],\n whitelist_keys: {}\n };\n }\n\n const data = await response.json();\n\n // A 200 that isn't a genuine rule envelope must not be treated as \"no rules\". Report failure\n // so the caller falls back to the cache / bundled rules rather than caching an empty bundle.\n if (!data || !Array.isArray(data.firewall)) {\n return { success: false, error: 'unexpected response shape (no firewall array)', firewall: [], whitelists: [], whitelist_keys: {} };\n }\n\n const result = {\n success: true,\n notModified: false,\n etag: response.headers?.get?.('etag') ?? null,\n firewall: data.firewall,\n whitelists: Array.isArray(data.whitelists) ? data.whitelists : [],\n whitelist_keys: data.whitelist_keys ?? {}\n };\n\n this.#cache = result;\n this.#etag = result.etag;\n this.#touch(now);\n\n return result;\n } catch (err) {\n return {\n success: false,\n error: err.name === 'TimeoutError'\n ? 'Request timed out'\n : err.message,\n firewall: [],\n whitelists: [],\n whitelist_keys: {}\n };\n }\n }\n\n #touch(now) {\n this.#cacheTime = now;\n this.#ttlEffective = this.#cacheTtl - Math.floor(Math.random() * this.#cacheTtl * JITTER_FRACTION);\n }\n\n clearCache() {\n this.#cache = null;\n this.#cacheTime = null;\n this.#etag = null;\n }\n}\n","// Resolvable DATA attributes of an uploaded file part (files.<name>.<attr>). The engine only exposes\n// the raw data — WHAT counts as a malicious upload (signatures, type-vs-content mismatch) is expressed\n// in rules (see the triage-vpatch-npm skill), not hardcoded here.\nconst FILE_ATTRS = new Set(['content', 'filename', 'type']);\n\n// A captured file part is { filename, type, content }; tolerate the legacy bare-filename string.\nconst fileFilename = (f) => (f && typeof f === 'object' ? f.filename : f);\nconst fileAttribute = (f, attr) => (f && typeof f === 'object' ? f[attr] : attr === 'filename' ? f : undefined);\n\n// WinterCG-safe base64 decode: use Buffer on Node, fall back to atob/TextDecoder on\n// edge runtimes (Cloudflare Workers, Deno, Bun) where Buffer may be absent. Keeps the\n// engine hot path free of Node-only APIs (per the ADR engine-language decision).\nfunction base64DecodeUtf8(value) {\n const str = String(value);\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'base64').toString('utf-8');\n }\n const binary = atob(str);\n const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));\n return new TextDecoder().decode(bytes);\n}\n\nexport class RequestResolver {\n #req;\n #cookies;\n\n constructor(req) {\n this.#req = req;\n this.#cookies = null;\n }\n\n resolve(parameter) {\n if (!parameter || parameter === 'false') {\n return [null];\n }\n\n if (parameter === 'rules') {\n return [null];\n }\n\n if (parameter === 'raw') {\n return this.#resolveRaw();\n }\n\n if (parameter === 'all') {\n return this.#resolveAll();\n }\n\n const dotIndex = parameter.indexOf('.');\n if (dotIndex === -1) {\n return [];\n }\n\n const source = parameter.substring(0, dotIndex);\n const key = parameter.substring(dotIndex + 1);\n\n switch (source) {\n case 'get':\n return this.#resolveGet(key);\n case 'post':\n return this.#resolvePost(key);\n case 'request':\n return this.#resolveRequest(key);\n case 'cookie':\n return this.#resolveCookie(key);\n case 'server':\n return this.#resolveServer(key);\n case 'files':\n return this.#resolveFiles(key);\n case 'response':\n return this.#resolveResponse(key);\n case 'egress':\n return this.#resolveEgress(key);\n default:\n return [];\n }\n }\n\n // Response-phase sources (req._response = { status, headers, body }). Lets rules inspect\n // what the app is about to SEND — e.g. a leaked secret in the body — regardless of route.\n #resolveResponse(key) {\n const resp = this.#req._response;\n if (!resp) {\n return [];\n }\n if (key === 'status') {\n return resp.status !== undefined ? [String(resp.status)] : [];\n }\n if (key === 'body') {\n return resp.body != null && resp.body !== '' ? [resp.body] : [];\n }\n if (key === 'headers') {\n const headers = resp.headers ?? {};\n return [Object.entries(headers).map(([k, v]) => `${k}: ${v}`).join('\\n')];\n }\n if (key.startsWith('header.')) {\n const name = key.slice('header.'.length).toLowerCase();\n const value = (resp.headers ?? {})[name];\n return value !== undefined ? [value] : [];\n }\n return [];\n }\n\n // Egress-phase sources (req._egress = { url, host, method }). Lets rules inspect an\n // OUTBOUND request the app is about to make — SSRF at the egress boundary.\n #resolveEgress(key) {\n const eg = this.#req._egress;\n if (!eg) {\n return [];\n }\n if (key === 'url') return eg.url ? [eg.url] : [];\n if (key === 'host') return eg.host ? [eg.host] : [];\n if (key === 'method') return eg.method ? [eg.method] : [];\n return [];\n }\n\n applyMutations(mutations, value) {\n if (!mutations || !Array.isArray(mutations)) {\n return value;\n }\n\n let result = value;\n\n for (const mutation of mutations) {\n result = this.#applyMutation(mutation, result);\n }\n\n return result;\n }\n\n #applyMutation(mutation, value) {\n if (value === null || value === undefined) {\n return value;\n }\n\n switch (mutation) {\n case 'base64_decode':\n try {\n return base64DecodeUtf8(value);\n } catch {\n return value;\n }\n\n case 'json_decode':\n try {\n return JSON.parse(String(value));\n } catch {\n return value;\n }\n\n case 'json_encode':\n try {\n return JSON.stringify(value);\n } catch {\n return value;\n }\n\n case 'urldecode':\n try {\n return decodeURIComponent(String(value));\n } catch {\n return value;\n }\n\n case 'intval':\n return parseInt(String(value), 10) || 0;\n\n case 'getArrayValues':\n if (typeof value === 'object' && value !== null) {\n return Object.values(value);\n }\n return value;\n\n default:\n return value;\n }\n }\n\n #resolveGet(key) {\n const query = this.#req.query ?? {};\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(query, key);\n }\n\n const value = this.#getNestedValue(query, key);\n return value !== undefined ? [value] : [];\n }\n\n #resolvePost(key) {\n const body = this.#req.body ?? {};\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(body, key);\n }\n\n const value = this.#getNestedValue(body, key);\n return value !== undefined ? [value] : [];\n }\n\n #resolveRequest(key) {\n const query = this.#req.query ?? {};\n const body = this.#req.body ?? {};\n const cookies = this.#parseCookies();\n\n if (key.endsWith('*')) {\n return [\n ...this.#resolveWildcard(query, key),\n ...this.#resolveWildcard(body, key),\n ...this.#resolveWildcard(cookies, key)\n ];\n }\n\n const value = this.#getNestedValue(query, key)\n ?? this.#getNestedValue(body, key)\n ?? cookies[key];\n\n return value !== undefined ? [value] : [];\n }\n\n #resolveCookie(key) {\n const cookies = this.#parseCookies();\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(cookies, key);\n }\n\n const value = cookies[key];\n return value !== undefined ? [value] : [];\n }\n\n #resolveServer(key) {\n const req = this.#req;\n\n switch (key) {\n case 'REQUEST_URI':\n return [req.originalUrl ?? req.url ?? '/'];\n case 'REQUEST_METHOD':\n return [req.method ?? 'GET'];\n case 'HTTP_USER_AGENT':\n return req.headers?.['user-agent'] ? [req.headers['user-agent']] : [];\n case 'HTTP_REFERER':\n return req.headers?.referer ? [req.headers.referer] : [];\n case 'HTTP_HOST':\n return req.headers?.host ? [req.headers.host] : [];\n case 'REMOTE_ADDR':\n case 'ip':\n return [req.ip ?? req.socket?.remoteAddress ?? ''];\n case 'CONTENT_TYPE':\n return req.headers?.['content-type'] ? [req.headers['content-type']] : [];\n case 'CONTENT_LENGTH':\n return req.headers?.['content-length'] ? [req.headers['content-length']] : [];\n default: {\n if (key.startsWith('HTTP_')) {\n const headerName = key.substring(5).toLowerCase().replace(/_/g, '-');\n const headers = req.headers;\n // Presence, not truthiness. A header sent with an empty value IS present, and an `isset`\n // rule authored against it must see it — some bypasses are carried by the header existing\n // at all, so treating `Header:` as absent would make the rule quietly miss the shape it\n // was written for. (The named cases above keep value semantics: for host/origin/referer an\n // empty string and an absent header mean the same thing to the matchers that read them.)\n if (headers === null || typeof headers !== 'object') return [];\n return Object.prototype.hasOwnProperty.call(headers, headerName) ? [headers[headerName]] : [];\n }\n return [];\n }\n }\n }\n\n #resolveFiles(key) {\n const files = this.#req.files;\n if (!files || typeof files !== 'object') {\n return [];\n }\n\n // files.<name>.<attr> — content | filename | type. Fans out over multiple files uploaded under\n // the same field name.\n const dot = key.lastIndexOf('.');\n if (dot !== -1 && FILE_ATTRS.has(key.slice(dot + 1)) && Object.prototype.hasOwnProperty.call(files, key.slice(0, dot))) {\n const attr = key.slice(dot + 1);\n const entry = files[key.slice(0, dot)];\n const list = Array.isArray(entry) ? entry : [entry];\n const out = [];\n for (const f of list) {\n const v = fileAttribute(f, attr);\n if (v !== undefined && v !== '') out.push(v);\n }\n return out;\n }\n\n // Bare files.<name> (or wildcard) → the filename(s), preserving the legacy behavior that\n // filename-scoped rules rely on (the parser now stores a { filename, type, content } object).\n const filenamesOf = (entry) => (Array.isArray(entry) ? entry.map(fileFilename) : [fileFilename(entry)]);\n if (key.endsWith('*')) {\n const prefix = key.slice(0, -1);\n const out = [];\n for (const [k, entry] of Object.entries(files)) {\n if (k.startsWith(prefix)) out.push(...filenamesOf(entry));\n }\n return out.filter((v) => v !== undefined);\n }\n if (!Object.prototype.hasOwnProperty.call(files, key)) return [];\n return filenamesOf(files[key]).filter((v) => v !== undefined);\n }\n\n #resolveRaw() {\n // Use pre-captured raw body if available (set by normalizeRequest).\n // For string bodies, the original text is preserved verbatim.\n // For pre-parsed objects, serializeForRawDetection uses Object.getOwnPropertyNames()\n // to include __proto__ own-property keys that JSON.stringify() would silently drop.\n if (typeof this.#req._rawBody === 'string') {\n return this.#req._rawBody ? [this.#req._rawBody] : [];\n }\n\n const body = this.#req.body;\n\n if (body === undefined || body === null) {\n return [];\n }\n\n if (typeof body === 'string') {\n return [body];\n }\n\n try {\n return [JSON.stringify(body)];\n } catch {\n return [String(body)];\n }\n }\n\n #resolveAll() {\n const parts = [];\n\n const uri = this.#req.originalUrl ?? this.#req.url ?? '/';\n parts.push(uri);\n\n const queryString = uri.includes('?') ? uri.split('?')[1] : '';\n if (queryString) {\n parts.push(queryString);\n }\n\n const body = this.#req.body;\n if (body) {\n parts.push(typeof body === 'string' ? body : JSON.stringify(body));\n }\n // Also fold in the verbatim body: a body the adapter couldn't structurally parse (an unusual\n // content-type, a non-JSON payload) leaves `body` empty, but the raw text must still be matchable\n // by an `all` rule — otherwise it's only visible via `raw`.\n if (typeof this.#req._rawBody === 'string' && this.#req._rawBody) {\n parts.push(this.#req._rawBody);\n }\n\n const headers = this.#req.headers ?? {};\n const excludedHeaders = new Set([\n 'host', 'connection', 'cache-control', 'accept', 'accept-encoding',\n 'accept-language', 'priority', 'sec-ch-ua', 'sec-ch-ua-mobile',\n 'sec-ch-ua-platform', 'sec-fetch-dest', 'sec-fetch-mode',\n 'sec-fetch-site', 'sec-fetch-user', 'upgrade-insecure-requests'\n ]);\n\n for (const [name, value] of Object.entries(headers)) {\n if (!excludedHeaders.has(name)) {\n parts.push(`${name}: ${value}`);\n }\n }\n\n const cookies = this.#parseCookies();\n const cookieStr = Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ');\n if (cookieStr) {\n parts.push(cookieStr);\n }\n\n return [parts.join(' ')];\n }\n\n #resolveWildcard(obj, pattern) {\n if (typeof obj !== 'object' || obj === null) {\n return [];\n }\n\n const prefix = pattern.slice(0, -1);\n const values = [];\n\n for (const [key, value] of Object.entries(obj)) {\n if (key.startsWith(prefix)) {\n values.push(value);\n }\n }\n\n return values;\n }\n\n #getNestedValue(obj, key) {\n if (typeof obj !== 'object' || obj === null) {\n return undefined;\n }\n\n // Own-property only: `key in obj` would resolve `__proto__`/`constructor`/`toString` to the\n // prototype chain, so a rule like `post.__proto__` (detecting a literal `__proto__` field) would\n // read Object.prototype instead of request data and never match. Use hasOwnProperty.\n const own = (o, k) => o !== null && typeof o === 'object' && Object.prototype.hasOwnProperty.call(o, k);\n\n if (own(obj, key)) {\n return obj[key];\n }\n\n const parts = key.split('.');\n let current = obj;\n\n for (const part of parts) {\n if (!own(current, part)) {\n return undefined;\n }\n current = current[part];\n }\n\n return current;\n }\n\n #parseCookies() {\n if (this.#cookies !== null) {\n return this.#cookies;\n }\n\n if (this.#req.cookies) {\n this.#cookies = this.#req.cookies;\n return this.#cookies;\n }\n\n const header = this.#req.headers?.cookie;\n if (!header) {\n this.#cookies = {};\n return this.#cookies;\n }\n\n const cookies = {};\n\n for (const pair of header.split(';')) {\n const eqIndex = pair.indexOf('=');\n if (eqIndex === -1) {\n continue;\n }\n const name = pair.substring(0, eqIndex).trim();\n const value = pair.substring(eqIndex + 1).trim();\n cookies[name] = value;\n }\n\n this.#cookies = cookies;\n return this.#cookies;\n }\n}\n","/**\n * Deliver a value to a caller-supplied callback without letting it break anything.\n *\n * `onError`, `onDetect` and `onSkip` are hooks a host passes in, so their code is not ours and its\n * failure is not ours to inherit. This package's one promise is that it never takes down the app it\n * protects — an engine error fails open, a malformed rule is skipped, a slow API boots from cache. A\n * reporting hook that throws has to fail open for the same reason, and for a sharper one: it fires\n * exactly when something interesting happened, so an unguarded throw converts \"we noticed something\"\n * into \"the request died\", and does it only on the requests that mattered.\n *\n * `onSkip` was already wrapped this way, with the reason written next to it. This is that same rule,\n * applied to the hooks that were missed rather than restated for one of them.\n *\n * Not silent, though. A hook that throws is a bug in the host's code and swallowing it entirely would\n * hide it forever, so the first failure per hook is reported — once, because these run per request and a\n * persistently broken hook would otherwise print on every one. Same reasoning as the engine's\n * report-once for a persistently broken rule.\n *\n * @param {unknown} fn the callback, or anything that is not a function (then this is a no-op)\n * @param {unknown} arg the single argument to hand it\n * @param {string} label which hook, for the one-time warning\n * @returns {boolean} whether the callback ran to completion, so a caller can fall back to its own\n * reporting rather than losing the report entirely. For an ASYNC callback this can only mean it\n * started: a rejection arrives after we return, and is contained and warned about, but by then a\n * caller has already decided not to fall back. Synchronous handlers get the stronger answer.\n */\n\n/** Hooks already reported as broken. Module-scoped: one warning per hook per process, not per guard. */\nconst reported = new Set();\n\nexport function notify(fn, arg, label) {\n if (typeof fn !== 'function') return false;\n\n try {\n const result = fn(arg);\n\n // An ASYNC callback fails after this function has already returned. `async () => { throw ... }` does\n // not throw — it hands back a rejected promise, and an unhandled rejection terminates the process by\n // default on Node. So a try/catch alone would contain the synchronous hosts and leave the async ones\n // able to kill the app, which is a worse outcome than the throw we set out to contain.\n if (result !== null && typeof result === 'object' && typeof result.then === 'function') {\n try {\n result.then(undefined, (err) => warnOnce(label, err));\n } catch {\n // A `then` that throws on access. Nothing more to attach to; the value is not a usable promise.\n }\n }\n\n return true;\n } catch (err) {\n warnOnce(label, err);\n\n return false;\n }\n}\n\n/**\n * Report a broken callback once per process.\n *\n * Must not throw: it runs inside the containment, so its own failure would be the thing that breaks the\n * guarantee it exists to report on.\n */\nfunction warnOnce(label, err) {\n if (reported.has(label)) return;\n reported.add(label);\n try {\n // Named as the host's callback, not as a Patchstack failure: pointing at ourselves for someone\n // else's throw sends them reading the wrong code.\n console.warn(\n `Patchstack: the ${label} callback passed to createProtection failed and was ignored. ` +\n `Protection is unaffected; this is reported once per process. ` +\n `Cause: ${err && err.message ? err.message : String(err)}`,\n );\n } catch {\n /* no console on this runtime */\n }\n}\n\n/** Test seam: forget which hooks have been reported, so warn-once is assertable more than once. */\nexport function resetNotifyWarnings() {\n reported.clear();\n}\n","/**\n * Request Normalization Pipeline\n *\n * Normalizes request data to prevent encoding-based bypass attacks.\n * This module handles URL encoding, HTML entities, SQL comments, and other\n * obfuscation techniques that attackers use to evade pattern matching.\n */\n\nconst HTML_ENTITIES = {\n '&amp;': '&',\n '&lt;': '<',\n '&gt;': '>',\n '&quot;': '\"',\n '&#39;': \"'\",\n '&apos;': \"'\",\n '&nbsp;': ' ',\n '&#x27;': \"'\",\n '&#x22;': '\"',\n '&#x3C;': '<',\n '&#x3E;': '>',\n '&#x26;': '&',\n '&#34;': '\"',\n '&#60;': '<',\n '&#62;': '>',\n '&#38;': '&'\n};\n\nconst MAX_DECODE_ITERATIONS = 5;\n\nexport function normalize(value, options = {}) {\n if (typeof value !== 'string') {\n return value;\n }\n\n const opts = {\n urlDecode: true,\n htmlDecode: true,\n sqlComments: true,\n nullBytes: true,\n whitespace: true,\n ...options\n };\n\n let result = value;\n\n if (opts.nullBytes) {\n result = removeNullBytes(result);\n }\n\n if (opts.urlDecode) {\n result = urlDecode(result);\n }\n\n if (opts.htmlDecode) {\n result = htmlEntityDecode(result);\n }\n\n if (opts.sqlComments) {\n result = removeSqlComments(result);\n }\n\n if (opts.whitespace) {\n result = normalizeWhitespace(result);\n }\n\n return result;\n}\n\nexport function urlDecode(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value;\n let previous = '';\n let iterations = 0;\n\n while (result !== previous && iterations < MAX_DECODE_ITERATIONS) {\n previous = result;\n iterations++;\n\n try {\n result = decodeURIComponent(result);\n } catch {\n result = safeUrlDecode(result);\n break;\n }\n }\n\n return result;\n}\n\nfunction safeUrlDecode(value) {\n return value.replace(/%([0-9A-Fa-f]{2})/g, (match, hex) => {\n try {\n return String.fromCharCode(parseInt(hex, 16));\n } catch {\n return match;\n }\n });\n}\n\nexport function htmlEntityDecode(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value;\n\n for (const [entity, char] of Object.entries(HTML_ENTITIES)) {\n result = result.split(entity).join(char);\n }\n\n result = result.replace(/&#(\\d+);/g, (match, code) => {\n const num = parseInt(code, 10);\n return num > 0 && num < 65536 ? String.fromCharCode(num) : match;\n });\n\n result = result.replace(/&#x([0-9A-Fa-f]+);/g, (match, hex) => {\n const num = parseInt(hex, 16);\n return num > 0 && num < 65536 ? String.fromCharCode(num) : match;\n });\n\n return result;\n}\n\nexport function removeSqlComments(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n // Collapse inline block comments to a space (the anti-obfuscation goal). We must NOT strip the\n // line-comment forms (`--…`, `#…`) to end-of-line: on the WAF inspection path that DELETES\n // attacker-controlled spans from the value the engine sees while the app still processes the\n // original — e.g. `#<script>…` becomes empty and evades an XSS rule, though the browser still\n // runs it. Keeping the content only ever ADDS matches (more of the value is inspected), never\n // hides one. SQLi keyword detection is unaffected — the keywords remain visible.\n let result = value;\n\n result = result.replace(/\\/\\*[\\s\\S]*?\\*\\//g, ' ');\n result = result.replace(/\\/\\*![\\s\\S]*?\\*\\//g, ' ');\n\n return result;\n}\n\nexport function removeNullBytes(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value.replace(/\\x00/g, '');\n\n result = result.replace(/[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F]/g, '');\n\n return result;\n}\n\nexport function normalizeWhitespace(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value.replace(/[\\t\\r\\n\\f\\v]+/g, ' ');\n\n result = result.replace(/ {2,}/g, ' ');\n\n return result;\n}\n\nfunction serializeForRawDetection(body, visited = new Set(), isRoot = true) {\n if (body === null || body === undefined) {\n return isRoot ? '' : 'null';\n }\n\n if (typeof body === 'string') {\n return isRoot ? body : JSON.stringify(body);\n }\n\n if (typeof body !== 'object') {\n return String(body);\n }\n\n if (visited.has(body)) {\n return '[Circular]';\n }\n visited.add(body);\n\n if (Array.isArray(body)) {\n const items = body.map(item => serializeForRawDetection(item, visited, false));\n return '[' + items.join(',') + ']';\n }\n\n // Object.getOwnPropertyNames includes non-enumerable own properties, so a __proto__ key\n // set via Object.defineProperty (as modern JSON.parse may do) is included in the output.\n const keys = Object.getOwnPropertyNames(body);\n const parts = keys.map(key => {\n const val = serializeForRawDetection(body[key], visited, false);\n return JSON.stringify(key) + ':' + val;\n });\n\n return '{' + parts.join(',') + '}';\n}\n\nexport function normalizeRequest(req, options = {}) {\n // Prefer a caller-provided verbatim body string (set by the fetch/node adapters):\n // it preserves literal keys like `__proto__` that JSON.stringify drops, which is\n // what makes prototype-pollution rules on `raw` robust. Fall back to a\n // reconstruction from the parsed body (the Express path, which has no raw text).\n const rawBody = typeof req._rawBody === 'string'\n ? req._rawBody\n : serializeForRawDetection(req.body ?? null);\n\n return {\n query: normalizeObject(req.query || {}, options),\n body: normalizeObject(req.body || {}, options),\n headers: normalizeObject(req.headers || {}, options),\n url: normalize(req.url || '', options),\n originalUrl: normalize(req.originalUrl || req.url || '', options),\n _rawBody: rawBody\n };\n}\n\n// Depth bound for the recursive walk: a pathologically deep object would otherwise overflow the\n// stack, and the engine's per-rule catch would swallow that into a fail-open. Beyond the bound the\n// sub-value is left un-normalized (still matched, just in its raw form) rather than crashing.\nconst MAX_NORMALIZE_DEPTH = 200;\n\nexport function normalizeObject(value, options = {}, depth = 0) {\n if (typeof value === 'string') {\n return normalize(value, options);\n }\n\n if (depth >= MAX_NORMALIZE_DEPTH) {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map(item => normalizeObject(item, options, depth + 1));\n }\n\n if (typeof value === 'object' && value !== null) {\n const result = {};\n\n for (const [key, val] of Object.entries(value)) {\n result[key] = normalizeObject(val, options, depth + 1);\n }\n\n return result;\n }\n\n return value;\n}\n\nexport function createMatchVariants(value) {\n if (typeof value !== 'string') {\n return [value];\n }\n\n const variants = new Set();\n\n variants.add(value);\n\n const urlDecoded = urlDecode(value);\n variants.add(urlDecoded);\n\n const htmlDecoded = htmlEntityDecode(value);\n variants.add(htmlDecoded);\n\n const bothDecoded = htmlEntityDecode(urlDecoded);\n variants.add(bothDecoded);\n\n const normalized = normalize(value);\n variants.add(normalized);\n\n variants.add(value.toLowerCase());\n variants.add(normalized.toLowerCase());\n\n return Array.from(variants);\n}\n\nexport const _testExports = {\n safeUrlDecode,\n serializeForRawDetection,\n HTML_ENTITIES,\n MAX_DECODE_ITERATIONS\n};\n","import { RequestResolver } from './request.js';\nimport { notify } from '../notify.js';\nimport { normalizeRequest } from './normalizer.js';\n\n// Catastrophic-backtracking shapes. Broad on purpose: a group whose inner content is quantified\n// (+, *, or {n,}) and is itself quantified — (a+)+, (\\w+)+, (.*)*, ([a-z]+)*, (ab+)+ — or an\n// alternation under an outer quantifier — (a|a)*, (x|y)+. A rule matching one of these is skipped\n// (safer than hanging the event loop). The `NEST` variants allow ONE level of nested parentheses in\n// the outer group so a quantified SUBGROUP is caught too (`((ab)+)+`, `((a|b)+)*`) — the earlier\n// `[^)]*` forms stopped at the first inner `)` and missed those. `.test()` scans every start offset,\n// so deeper nestings match at an inner window too.\nconst GRP = '(?:[^()]|\\\\([^()]*\\\\))*'; // group body allowing one level of nested parens\nconst REDOS_PATTERNS = [\n /\\([^)]*[+*}][^)]*\\)\\s*[+*]/,\n /\\([^)]*\\|[^)]*\\)\\s*[+*]/,\n new RegExp('\\\\(' + GRP + '[+*}]' + GRP + '\\\\)\\\\s*[+*]'), // nested quantified subgroup\n new RegExp('\\\\(' + GRP + '\\\\|' + GRP + '\\\\)\\\\s*[+*]') // nested alternation under a quantifier\n];\n\n// Report once when a rule's regex is rejected (ReDoS-shaped or unparseable). Unlike an unknown match\n// type, a rejected regex used to fail silently — so a delivered rule protected nothing and nobody knew.\nconst warnedRejectedPatterns = new Set();\nfunction warnRejectedPatternOnce(pattern) {\n const key = String(pattern);\n if (warnedRejectedPatterns.has(key)) return;\n warnedRejectedPatterns.add(key);\n console.warn(\n `[patchstack] rule_v2 regex pattern rejected (unsafe or invalid) — condition treated as no-match. ` +\n `The rule relying on it is NOT enforced: ${key}`\n );\n}\n\n// An absurdly long pattern is either a mistake or an attack on our own matcher; compiling and running\n// it on every request is unbounded work. The rule-bundle validator rejects these upstream — this is the\n// backstop for a caller-supplied bundle that never went through it.\nconst MAX_PATTERN_LENGTH = 1000;\n\nexport function safeRegExp(pattern) {\n if (!pattern) {\n return null;\n }\n if (typeof pattern !== 'string' || pattern.length > MAX_PATTERN_LENGTH) {\n return null;\n }\n\n for (const dangerous of REDOS_PATTERNS) {\n if (dangerous.test(pattern)) {\n return null;\n }\n }\n\n const match = pattern.match(/^\\/(.+?)\\/([gimsuy]*)$/s);\n if (!match) {\n return null;\n }\n\n try {\n return new RegExp(match[1], match[2]);\n } catch {\n return null;\n }\n}\n\n// ctype_*/is_numeric compare their character-class result to the rule's expected\n// `value`. Rules are almost always written `{ \"type\": \"ctype_digit\", \"value\": false }`\n// meaning \"flag when NOT of this class\" — so the result must be compared to matchVal,\n// not returned raw. Empty/absent values never match (mirrors engine-php's `$value != ''`\n// guard), otherwise every missing parameter false-positives a `value:false` rule.\n// When `value` is not a boolean (null/omitted) the legacy default of `true` is used, so\n// existing rules that relied on the raw class check are unaffected.\nfunction ctypeResult(strValue, isClass, matchVal) {\n if (strValue === '') {\n return false;\n }\n const expected = typeof matchVal === 'boolean' ? matchVal : true;\n return isClass === expected;\n}\n\n// Walk a dot-path into a decoded object and invoke `cb({ parent, key, value })` for every leaf it\n// reaches, fanning out over arrays at EVERY segment (not just the last) — so `orders.customers.email`\n// visits the email of each customer of each order, for arbitrary-length / arbitrarily-nested arrays.\n// `cb` gets the container + key so callers can either read (match) or set (redact) the leaf.\nexport function walkLeaves(node, segments, cb) {\n if (node === null || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n for (const el of node) walkLeaves(el, segments, cb);\n return;\n }\n const [head, ...rest] = segments;\n if (head === undefined || !Object.prototype.hasOwnProperty.call(node, head)) return;\n if (rest.length === 0) {\n const leaf = node[head];\n if (Array.isArray(leaf)) {\n for (let i = 0; i < leaf.length; i++) cb({ parent: leaf, key: i, value: leaf[i] });\n } else {\n cb({ parent: node, key: head, value: leaf });\n }\n } else {\n walkLeaves(node[head], rest, cb);\n }\n}\n\n// array_key_value: navigate `match.key` (a dot-path, or an array of paths) inside the decoded value\n// and run the nested `match.match` against every leaf it finds (fanning out over arrays at every\n// segment). Mirrors engine-php's recursive array_key_value handling.\nfunction arrayKeyValue(value, matchObj) {\n if (!matchObj || !matchObj.match || value === null || typeof value !== 'object') {\n return false;\n }\n const keys = Array.isArray(matchObj.key) ? matchObj.key : [matchObj.key];\n const sub = matchObj.match;\n\n for (const key of keys) {\n let matched = false;\n walkLeaves(value, String(key).split('.'), ({ value: leaf }) => {\n if (!matched && matchValue(sub.type, leaf, sub.value, sub)) matched = true;\n });\n if (matched) return true;\n }\n return false;\n}\n\n// Match types that operate on the whole container (not per-leaf): `isset` (presence) and\n// `array_in_array` / `array_key_value` (structural). Everything else is a scalar matcher that must\n// fan out over the leaves of an object/array value.\nconst WHOLE_VALUE_MATCH_TYPES = new Set(['isset', 'array_in_array', 'array_key_value']);\n\n// Iteratively collect every scalar (non-object) leaf of a structured value. Iterative + bounded\n// (depth and node caps) so a pathologically deep/large attacker payload STOPS at the bound rather\n// than throwing a RangeError that the per-rule catch would swallow into a fail-open bypass.\nfunction collectLeafValues(root, nodeCap = 20000, maxDepth = 1000) {\n const out = [];\n const stack = [[root, 0]];\n let visited = 0;\n while (stack.length) {\n const [node, depth] = stack.pop();\n if (node === null || node === undefined) continue;\n if (typeof node !== 'object') {\n out.push(node);\n continue;\n }\n if (depth >= maxDepth || visited >= nodeCap) continue;\n visited++;\n if (Array.isArray(node)) {\n for (let i = node.length - 1; i >= 0; i--) stack.push([node[i], depth + 1]);\n } else {\n for (const k of Object.keys(node)) stack.push([node[k], depth + 1]);\n }\n }\n return out;\n}\n\n// Emit a warning at most once per distinct key (keeps a persistent misconfiguration from spamming).\nconst warnedKeys = new Set();\nfunction warnOnce(key, message) {\n if (warnedKeys.has(key)) return;\n warnedKeys.add(key);\n console.warn(message);\n}\n\n// Report an unknown/removed match type once, so a rule referencing it is not silently\n// unenforced (ADR: \"unknown match type → skipped and logged, never silently passed\").\nconst warnedMatchTypes = new Set();\nfunction warnUnsupportedMatchType(type) {\n if (type == null || warnedMatchTypes.has(type)) {\n return;\n }\n warnedMatchTypes.add(type);\n console.warn(\n `[patchstack] Unsupported rule_v2 match type \"${type}\" — condition treated as no-match. ` +\n `Rules relying on it are not enforced by this engine.`\n );\n}\n\n// Internal / private / loopback / link-local / cloud-metadata host check behind the `internal_host`\n// match type. It CANONICALIZES the host before classifying —\n// a textual/prefix check is bypassable by alternate encodings (decimal/hex/octal IPv4, expanded or\n// IPv4-mapped IPv6), which is a classic SSRF evasion. Handles localhost / *.local / GCP metadata\n// names, every IPv4 spelling inet_aton accepts, and IPv6 loopback/link-local/unique-local/mapped.\n/**\n * The host to classify out of a rule parameter's value.\n *\n * `internal_host` was written for the egress phase, where the value IS the destination host. On the\n * request phase the same question arrives as an application parameter, and there the value is almost\n * always a full URL (`?url=http://169.254.169.254/latest/meta-data/`) or a `host:port` pair — neither of\n * which is a hostname, so classifying the raw string answered \"not internal\" for every one of them. A\n * request-phase SSRF rule was therefore expressible, servable and permanently inert: the exact failure\n * this engine has been repeatedly hardened against, in the one match type meant to prevent it.\n *\n * Only the host is extracted; the classification itself is unchanged, so every canonicalization defence\n * (decimal/hex IPv4, expanded and IPv4-mapped IPv6, trailing dots) still applies to what comes out. A\n * value that is already a bare host passes through untouched, which is what keeps the egress path and\n * the built-in default rule behaving exactly as before.\n */\nfunction hostFromValue(value) {\n const raw = String(value ?? '').trim();\n if (raw === '') return '';\n\n // A scheme (`http://`, and deliberately any other) or a protocol-relative URL. Parsing rather than\n // string-slicing is what makes the userinfo evasion (`http://trusted@169.254.169.254/`) resolve to the\n // host actually contacted, and keeps `http://evil.com#@127.0.0.1` resolving to evil.com.\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw) || raw.startsWith('//')) {\n try {\n return new URL(raw.startsWith('//') ? `http:${raw}` : raw).hostname;\n } catch {\n // Unparseable: hand the raw value on, where the host check rejects it rather than guessing.\n return raw;\n }\n }\n\n // `[::1]:8080` — bracketed IPv6 with or without a port.\n if (raw.startsWith('[')) {\n const end = raw.indexOf(']');\n if (end > 0) return raw.slice(1, end);\n }\n\n // `169.254.169.254:80`. Only a single colon followed by digits: a bare IPv6 address has several, and\n // must not have its last group mistaken for a port.\n const colon = raw.indexOf(':');\n if (colon > 0 && raw.indexOf(':', colon + 1) === -1 && /^\\d+$/.test(raw.slice(colon + 1))) {\n return raw.slice(0, colon);\n }\n\n return raw;\n}\n\nfunction isInternalHost(hostname) {\n if (!hostname) return false;\n let host = String(hostname).toLowerCase().replace(/^\\[|\\]$/g, '');\n host = host.replace(/%[^\\]]*$/, ''); // strip an IPv6 zone id (fe80::1%eth0)\n host = host.replace(/\\.$/, ''); // strip a single trailing dot (127.0.0.1.)\n\n if (host === 'localhost' || host.endsWith('.localhost') || host.endsWith('.local')) return true;\n if (host === 'metadata.google.internal') return true;\n\n // IPv6 (contains a colon): expand to 8 groups, then classify on the canonical form.\n if (host.includes(':')) {\n const g = expandIPv6(host);\n if (!g) return false;\n const allZeroHi = g[0] === 0 && g[1] === 0 && g[2] === 0 && g[3] === 0 && g[4] === 0;\n if (allZeroHi && g[5] === 0 && g[6] === 0 && (g[7] === 0 || g[7] === 1)) return true; // ::, ::1 loopback\n if ((g[0] & 0xffc0) === 0xfe80) return true; // link-local fe80::/10\n if ((g[0] & 0xfe00) === 0xfc00) return true; // unique-local fc00::/7\n if (allZeroHi && (g[5] === 0xffff || g[5] === 0)) {\n // IPv4-mapped (::ffff:a.b.c.d) / IPv4-compatible (::a.b.c.d) — classify the embedded v4.\n return isPrivateV4Int((((g[6] << 16) >>> 0) | g[7]) >>> 0);\n }\n return false;\n }\n\n // IPv4 in any inet_aton spelling (dotted quad, shorthand, decimal, hex, octal).\n const v4 = parseIPv4ToInt(host);\n if (v4 !== null) return isPrivateV4Int(v4);\n return false;\n}\n\n// Private / loopback / link-local / this-host / CGNAT test on a 32-bit IPv4 integer.\nfunction isPrivateV4Int(n) {\n const a = (n >>> 24) & 0xff;\n const b = (n >>> 16) & 0xff;\n if (a === 127 || a === 10 || a === 0) return true; // loopback / private / this-host\n if (a === 169 && b === 254) return true; // link-local incl. 169.254.169.254 metadata\n if (a === 172 && b >= 16 && b <= 31) return true; // 172.16.0.0/12\n if (a === 192 && b === 168) return true; // 192.168.0.0/16\n if (a === 100 && b >= 64 && b <= 127) return true; // 100.64.0.0/10 CGNAT (Alibaba metadata 100.100.100.200)\n return false;\n}\n\n// inet_aton-style parse: 1–4 dot-separated parts, each decimal / 0x-hex / 0-octal; the final part\n// fills the remaining low bytes. Returns a uint32, or null if the host isn't a numeric IPv4 form\n// (so ordinary hostnames — which contain letters outside [0-9a-fx] or other chars — return null).\nfunction parseIPv4ToInt(host) {\n if (!/^[0-9a-fx]+(\\.[0-9a-fx]+)*$/i.test(host)) return null;\n const parts = host.split('.');\n if (parts.length > 4) return null;\n const nums = [];\n for (const p of parts) {\n let n;\n if (/^0x[0-9a-f]+$/i.test(p)) n = parseInt(p, 16);\n else if (/^0[0-7]+$/.test(p)) n = parseInt(p, 8);\n else if (/^[0-9]+$/.test(p)) n = parseInt(p, 10);\n else return null; // e.g. a bare hex like \"7f\" (not inet_aton-valid without 0x)\n if (!Number.isInteger(n) || n < 0) return null;\n nums.push(n);\n }\n const last = nums.length - 1;\n let value = 0;\n for (let i = 0; i < last; i++) {\n if (nums[i] > 0xff) return null;\n value += nums[i] * 2 ** (8 * (3 - i));\n }\n const maxLast = 2 ** (8 * (5 - nums.length)) - 1; // the final part fills the remaining low bytes\n if (nums[last] > maxLast) return null;\n value += nums[last];\n if (value < 0 || value > 0xffffffff) return null;\n return value >>> 0;\n}\n\n// Expand an IPv6 string (any `::` compression, optional embedded IPv4 tail) to 8 numeric groups,\n// or null if it isn't valid IPv6. Lets the classifier compare the canonical form, not a string prefix.\nfunction expandIPv6(host) {\n if (!host.includes(':')) return null;\n let s = host;\n // Embedded IPv4 tail (::ffff:127.0.0.1) → convert the dotted part to two hex groups.\n const lastColon = s.lastIndexOf(':');\n const tail = s.slice(lastColon + 1);\n if (tail.includes('.')) {\n const v4 = parseIPv4ToInt(tail);\n if (v4 === null) return null;\n s = s.slice(0, lastColon + 1) + ((v4 >>> 16) & 0xffff).toString(16) + ':' + (v4 & 0xffff).toString(16);\n }\n const halves = s.split('::');\n if (halves.length > 2) return null;\n const head = halves[0] ? halves[0].split(':') : [];\n const back = halves.length === 2 ? (halves[1] ? halves[1].split(':') : []) : null;\n let groups;\n if (back === null) {\n groups = head;\n } else {\n const fill = 8 - head.length - back.length;\n if (fill < 0) return null;\n groups = [...head, ...Array(fill).fill('0'), ...back];\n }\n if (groups.length !== 8) return null;\n const out = [];\n for (const g of groups) {\n if (!/^[0-9a-f]{1,4}$/i.test(g)) return null;\n out.push(parseInt(g, 16));\n }\n return out;\n}\n\n// Report a `when` block that names nothing this engine understands. Fail-open is correct for a scope that\n// cannot be EVALUATED, but a scope that cannot be UNDERSTOOD is an authoring mistake with the opposite\n// consequence: the rule silently applies to every request instead of one route, which for a blocking rule\n// is a false-positive surface across the whole app. Warned once so it is discoverable in a log.\nconst warnedScopes = new Set();\nfunction warnUnrecognisedScope(when) {\n const key = Object.keys(when).sort().join(',');\n if (warnedScopes.has(key)) return;\n warnedScopes.add(key);\n console.warn(\n `[patchstack] Rule scope \\`when: { ${key} }\\` names no supported key — the engine understands ` +\n `\\`method\\` and \\`path\\`. The scope is IGNORED and the rule applies to every request.`\n );\n}\n\n// Route/method scope for a rule's optional `when: { method, path }`. Fail-open: if the scope can't\n// be evaluated, the rule still applies (never silently suppress a rule).\nfunction ruleAppliesTo(when, resolver) {\n try {\n if (when.method === undefined && when.path === undefined && Object.keys(when).length > 0) {\n warnUnrecognisedScope(when);\n }\n if (when.method) {\n const methods = (Array.isArray(when.method) ? when.method : [when.method]).map((m) => String(m).toUpperCase());\n const actual = String(resolver.resolve('server.REQUEST_METHOD')[0] ?? 'GET').toUpperCase();\n if (!methods.includes(actual)) return false;\n }\n if (when.path) {\n const uri = String(resolver.resolve('server.REQUEST_URI')[0] ?? '/');\n if (!pathMatches(String(when.path), uri.split('?')[0])) return false;\n }\n return true;\n } catch {\n return true;\n }\n}\n\n// `when.path`: an exact path, a glob with `*`, or an explicit `/regex/` (starts & ends with `/`).\nfunction pathMatches(pattern, path) {\n if (pattern.length > 2 && pattern.startsWith('/') && pattern.endsWith('/')) {\n const re = safeRegExp(pattern);\n return re ? re.test(path) : false;\n }\n if (pattern.includes('*')) {\n const re = safeRegExp('/^' + pattern.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '[^?]*') + '$/');\n return re ? re.test(path) : false;\n }\n return path === pattern;\n}\n\n// Drop a default port so `app.com:443` and `app.com` compare equal (a real proxy shape), without\n// dropping a non-default port (so genuine cross-port stays distinguishable).\nfunction normalizeDefaultPort(host) {\n return String(host).toLowerCase().replace(/:(?:80|443)$/, '');\n}\n\n// CSRF primitive: does the request come from a different origin than its own Host? Lenient only when\n// the Origin AND Referer are TRULY ABSENT (a non-browser client). A present-but-opaque `Origin: null`\n// / empty / unparseable value is NOT same-origin — it's the sandboxed-iframe / opaque-origin signal a\n// CSRF attacker supplies, so it is treated as cross-origin.\nfunction isCrossOrigin(resolver) {\n try {\n const host = normalizeDefaultPort(String(resolver.resolve('server.HTTP_HOST')[0] ?? ''));\n if (!host) return false;\n const originRaw = resolver.resolve('server.HTTP_ORIGIN')[0];\n const hasOrigin = originRaw !== undefined && originRaw !== null;\n const src = hasOrigin ? originRaw : resolver.resolve('server.HTTP_REFERER')[0];\n if (src === undefined || src === null) return false; // both absent → lenient\n const s = String(src).trim();\n if (hasOrigin && (s === '' || s.toLowerCase() === 'null')) return true; // present but opaque → cross-origin\n const srcHost = hostFromUrl(s);\n if (srcHost === null) return hasOrigin; // present-but-unparseable Origin → treat as cross-origin\n return normalizeDefaultPort(srcHost) !== host;\n } catch {\n return false;\n }\n}\n\nfunction hostFromUrl(value) {\n try {\n return new URL(value).host.toLowerCase();\n } catch {\n return null;\n }\n}\n\n// Open-redirect primitive (response phase): a 3xx whose Location header points to a DIFFERENT origin\n// than the request's own Host. A relative Location (same-origin) never matches. Needs the request\n// Host, which the response phase threads in via reqCtx. Lenient: no Location, no request Host, or a\n// same-origin / relative target → not flagged (so it can't false-positive without the signal).\nfunction isOffOriginRedirect(resolver) {\n try {\n const status = Number(resolver.resolve('response.status')[0] ?? 0);\n if (status < 300 || status >= 400) return false;\n const location = resolver.resolve('response.header.location')[0];\n if (!location) return false;\n const host = String(resolver.resolve('server.HTTP_HOST')[0] ?? '').toLowerCase();\n if (!host) return false;\n // Resolve the Location the way a browser would before comparing hosts: strip TAB/CR/LF and\n // normalize backslashes to forward slashes (browsers do), then resolve against the request origin\n // as a base. A relative `/path` resolves to our own host (not flagged); a protocol-relative\n // `//evil.com` or backslash `/\\evil.com` resolves off-origin (flagged) — the canonical\n // open-redirect payloads that a base-less `new URL()` used to treat as \"relative & safe\".\n const loc = String(location).replace(/[\\t\\r\\n]/g, '').replace(/\\\\/g, '/');\n let target;\n try {\n target = new URL(loc, 'http://' + host).host.toLowerCase();\n } catch {\n return false; // unresolvable even with a base → not a redirect we can judge\n }\n return normalizeDefaultPort(target) !== normalizeDefaultPort(host);\n } catch {\n return false;\n }\n}\n\n// CORS-reflection primitive (response phase): the response allows credentials AND lets any origin\n// read it — either `Access-Control-Allow-Origin: *`, or it reflects the caller's own Origin (so\n// every origin is allowed) — rather than a fixed allowlisted origin. That combination lets any\n// malicious site read the authenticated response. Needs the request Origin (threaded via reqCtx).\n// Lenient: credentials not allowed, no ACAO, or a fixed (non-reflected, non-*) ACAO → not flagged.\nfunction isReflectedCorsWithCredentials(resolver) {\n try {\n const acac = String(resolver.resolve('response.header.access-control-allow-credentials')[0] ?? '').toLowerCase();\n if (acac !== 'true') return false; // only dangerous when credentials are allowed\n const acao = String(resolver.resolve('response.header.access-control-allow-origin')[0] ?? '');\n if (!acao) return false;\n if (acao === '*') return true; // wildcard + credentials\n if (acao.toLowerCase() === 'null') return true; // `null` + credentials is readable from a sandboxed iframe (Origin: null)\n const origin = String(resolver.resolve('server.HTTP_ORIGIN')[0] ?? '');\n if (!origin) return false;\n return acao === origin; // ACAO echoes the caller's Origin → any origin is allowed\n } catch {\n return false;\n }\n}\n\n// `matchObj` is the full match object; needed by types that read sibling fields\n// (array_key_value reads `key`/`match`). Optional so direct callers/tests can keep\n// using the (type, value, matchVal) signature.\nexport function matchValue(type, value, matchVal, matchObj) {\n if (value === null || value === undefined) {\n if (type === 'isset') {\n return false;\n }\n return false;\n }\n\n // Guard the coercion: String() on a pathologically deep array/object can throw RangeError\n // (stack overflow). Catching it here keeps a hostile nested value from failing the rule open.\n let strValue;\n if (typeof value === 'string') {\n strValue = value;\n } else {\n try {\n strValue = String(value);\n } catch {\n strValue = '';\n }\n }\n\n switch (type) {\n case 'equals':\n return strValue == matchVal;\n\n case 'equals_strict':\n // Type-strict by design (`'1' !== 1`). A rule that wants loose matching should use `equals`.\n return strValue === matchVal;\n\n case 'contains':\n // engine-php exposes `stripos` as an alias of `contains`.\n case 'stripos': {\n const lower = strValue.toLowerCase();\n const target = String(matchVal).toLowerCase();\n return lower.includes(target);\n }\n\n case 'not_contains': {\n const lower = strValue.toLowerCase();\n const target = String(matchVal).toLowerCase();\n return !lower.includes(target);\n }\n\n case 'regex': {\n const regex = safeRegExp(matchVal);\n if (!regex) {\n warnRejectedPatternOnce(matchVal);\n return false;\n }\n return regex.test(strValue);\n }\n\n case 'more_than':\n return Number(strValue) > Number(matchVal);\n\n case 'less_than':\n return Number(strValue) < Number(matchVal);\n\n case 'ctype_digit':\n return ctypeResult(strValue, /^\\d+$/.test(strValue), matchVal);\n\n case 'ctype_alnum':\n return ctypeResult(strValue, /^[\\w$\\u0080-\\uFFFF]+$/.test(strValue), matchVal);\n\n case 'is_numeric':\n return ctypeResult(strValue, !isNaN(strValue) && strValue.trim() !== '', matchVal);\n\n case 'isset':\n return true;\n\n case 'in_array': {\n const arr = (Array.isArray(matchVal) ? matchVal : [matchVal]).map((x) => String(x));\n return arr.includes(strValue);\n }\n\n case 'not_in_array': {\n const arr = (Array.isArray(matchVal) ? matchVal : [matchVal]).map((x) => String(x));\n return !arr.includes(strValue);\n }\n\n case 'array_in_array': {\n if (!Array.isArray(value) || !Array.isArray(matchVal)) {\n return false;\n }\n const target = matchVal.map((x) => String(x));\n return value.some((v) => target.includes(String(v)));\n }\n\n case 'hostname': {\n try {\n const url = new URL(strValue);\n return url.hostname === matchVal;\n } catch {\n return false;\n }\n }\n\n case 'internal_host':\n // SSRF: private / loopback / link-local / cloud-metadata destinations. The value may be a bare\n // host (egress) or a URL / host:port in an application parameter (request) — see `hostFromValue`.\n return isInternalHost(hostFromValue(strValue));\n\n case 'quotes':\n // engine-php exposes `inline_js_xss` as an alias of `quotes`.\n case 'inline_js_xss':\n return /['\"]/.test(strValue);\n\n case 'inline_xss':\n // Attribute-breakout heuristic: a quote AND a `>` or `=` (matches engine-php).\n return /['\"]/.test(strValue) && /[>=]/.test(strValue);\n\n case 'ctype_special': {\n // engine-php strips space, `_`, `-`, `,` then applies the alnum/unicode check,\n // and compares the result to `value` (usually false = \"flag when NOT clean\").\n const stripped = strValue.replace(/[ _\\-,]/g, '');\n return ctypeResult(strValue, /^[\\w$€-￿]*$/.test(stripped), matchVal);\n }\n\n case 'array_key_value':\n // Navigate `match.key` (dot-path, or array of paths) inside the decoded value and\n // run the nested `match.match` against it — e.g. a json_decode'd body where a\n // specific key must hold a specific value.\n return arrayKeyValue(value, matchObj);\n\n // file_contains (uploaded-file content scanning) is NOT WordPress-specific, but is\n // not yet implemented in the JS engine (needs multipart body access). Kept as an\n // explicit, documented no-match rather than silently hitting `default`.\n case 'file_contains':\n return false;\n\n // WordPress-only match types (current_user_cannot, general_xss, getShortcodeAtts,\n // getBlockAtts) have been removed: they depend on WP capabilities, wp_kses_post, and\n // the shortcode/block parsers, and have no meaning in a JS app. They now fall through\n // here and are reported once instead of pretending to be a no-match.\n default:\n warnUnsupportedMatchType(type);\n return false;\n }\n}\n\nexport class RuleEngine {\n #rules;\n #whitelists;\n #whitelistKeys;\n #onError;\n #reportedErrors = new Set();\n\n constructor({ firewall = [], whitelists = [], whitelist_keys = {}, onError } = {}) {\n this.#rules = firewall;\n this.#whitelists = whitelists;\n this.#whitelistKeys = whitelist_keys;\n this.#onError = onError;\n // A whitelist with no `rule_id` suppresses EVERY rule when its (attacker-reachable) condition\n // trips — almost never intended. And `whitelist_keys` is accepted but not implemented. Warn once\n // for each so a misconfiguration that silently weakens the firewall is visible to the operator.\n if (Array.isArray(whitelists) && whitelists.some((w) => w && Array.isArray(w.rule_v2) && !w.rule_id)) {\n warnOnce(\n 'whitelist-no-rule-id',\n '[patchstack] a whitelist has no `rule_id` — it suppresses ALL rules when it matches. ' +\n 'Scope each whitelist to a specific rule_id, and key it only on values an attacker cannot set.'\n );\n }\n if (whitelist_keys && typeof whitelist_keys === 'object' && Object.keys(whitelist_keys).length > 0) {\n warnOnce('whitelist-keys-unimplemented', '[patchstack] `whitelist_keys` is not implemented and has no effect.');\n }\n }\n\n // A mitigation engine must never take down the app it protects: any error while\n // evaluating a rule is reported and the request is allowed through (fail open). A\n // malformed rule is skipped without aborting the rest of the ruleset.\n #reportError(err) {\n // A host handler that runs takes over reporting. One that THROWS does not: fall through to the\n // built-in logging below, or a rule error would disappear into a broken reporter.\n if (notify(this.#onError, err, 'onError')) {\n return;\n }\n // Default: log once per distinct message so a persistently-broken rule doesn't\n // spam per request (never silently swallow — hosts can pass `onError` for structured logging).\n const key = err && err.message ? err.message : String(err);\n if (this.#reportedErrors.has(key)) {\n return;\n }\n this.#reportedErrors.add(key);\n console.error('[patchstack] rule evaluation error (rule skipped, request allowed):', err);\n }\n\n evaluate(req) {\n let normalizedReq;\n let resolver;\n try {\n normalizedReq = { ...req, ...normalizeRequest(req) };\n resolver = new RequestResolver(normalizedReq);\n } catch (err) {\n this.#reportError(err);\n return { blocked: false, rule: null, message: null }; // fail open\n }\n\n for (const rule of this.#rules) {\n try {\n // Route/method scope: a rule may declare `when: { method, path }` to apply only on matching\n // requests (so an auto-generated per-endpoint rule doesn't fire elsewhere).\n if (rule.when && !ruleAppliesTo(rule.when, resolver)) {\n continue;\n }\n\n const conditions = rule.rule_v2;\n\n if (!Array.isArray(conditions) || conditions.length === 0) {\n continue;\n }\n\n if (this.#evaluateRule(conditions, resolver)) {\n if (this.#isWhitelisted(normalizedReq, rule)) {\n continue;\n }\n\n return {\n blocked: true,\n rule,\n message: rule.message ?? `Blocked by Patchstack WAF rule: ${rule.title ?? rule.id}`\n };\n }\n } catch (err) {\n // Skip this rule and keep evaluating the rest — one bad rule never blocks a\n // request nor shadows the rules after it.\n this.#reportError(err);\n }\n }\n\n return { blocked: false, rule: null, message: null };\n }\n\n #evaluateRule(conditions, resolver) {\n const inclusiveConditions = conditions.filter(c => c.inclusive);\n const nonInclusiveConditions = conditions.filter(c => !c.inclusive);\n\n if (inclusiveConditions.length > 0) {\n let inclusiveHits = 0;\n\n for (const condition of inclusiveConditions) {\n if (this.#evaluateCondition(condition, resolver)) {\n inclusiveHits++;\n }\n }\n\n if (inclusiveHits === inclusiveConditions.length) {\n return true;\n }\n }\n\n for (const condition of nonInclusiveConditions) {\n if (this.#evaluateCondition(condition, resolver)) {\n return true;\n }\n }\n\n return false;\n }\n\n #evaluateCondition(condition, resolver) {\n const { parameter, match, mutations } = condition;\n\n if (parameter === 'rules' && Array.isArray(condition.rules)) {\n return this.#evaluateRule(condition.rules, resolver);\n }\n\n // `cross_origin` needs the request as a whole (Origin/Referer vs Host), not a single resolved\n // parameter — it's the CSRF primitive: true (→ block) when the request comes from another origin.\n if (match && match.type === 'cross_origin') {\n return isCrossOrigin(resolver);\n }\n\n // `off_origin` (response phase): true (→ block) when a 3xx redirects to a different origin than\n // the request Host — the open-redirect primitive. Like cross_origin, it needs the whole resolver.\n if (match && match.type === 'off_origin') {\n return isOffOriginRedirect(resolver);\n }\n\n // `cors_reflected` (response phase): true (→ block) when the response allows credentials and\n // reflects the caller's Origin (or uses `*`) — the CORS-misconfiguration primitive. Needs the\n // whole resolver (request Origin vs response ACAO/ACAC).\n if (match && match.type === 'cors_reflected') {\n return isReflectedCorsWithCredentials(resolver);\n }\n\n // `parameter` may be an array (e.g. [\"get.action\",\"post.action\"]) — the rule_v2 format\n // uses these pervasively to mean \"any of these sources\". Resolve each and OR the\n // candidate values together. (A bare string resolves as a single source.)\n const params = Array.isArray(parameter) ? parameter : [parameter];\n const values = params.flatMap((p) => resolver.resolve(p));\n\n if (values.length === 0) {\n return false;\n }\n\n for (let value of values) {\n if (mutations) {\n value = resolver.applyMutations(mutations, value);\n }\n\n // array_key_value inspects the object as a whole (it navigates a key path),\n // so it must not go through the per-value object iteration below.\n if (match.type === 'array_key_value') {\n if (matchValue(match.type, value, match.value, match)) {\n return true;\n }\n continue;\n }\n\n // A structured (object / array-of-object) value must be inspected at every leaf: a payload\n // nested deeper than a scalar rule expects would otherwise stringify to \"[object Object]\" and\n // evade the match, while the app still reads the live value. Whole-value match types\n // (isset / array_in_array) see the container; scalar matchers fan out over all leaves.\n if (typeof value === 'object' && value !== null) {\n if (WHOLE_VALUE_MATCH_TYPES.has(match.type)) {\n if (matchValue(match.type, value, match.value, match)) return true;\n } else {\n for (const leaf of collectLeafValues(value)) {\n if (matchValue(match.type, leaf, match.value, match)) return true;\n }\n }\n continue;\n }\n\n if (matchValue(match.type, value, match.value, match)) {\n return true;\n }\n }\n\n return false;\n }\n\n #isWhitelisted(req, rule) {\n if (!this.#whitelists || this.#whitelists.length === 0) {\n return false;\n }\n\n for (const whitelist of this.#whitelists) {\n if (!Array.isArray(whitelist.rule_v2)) {\n continue;\n }\n\n if (whitelist.rule_id && whitelist.rule_id !== rule.id) {\n continue;\n }\n\n const resolver = new RequestResolver(req);\n if (this.#evaluateRule(whitelist.rule_v2, resolver)) {\n return true;\n }\n }\n\n return false;\n }\n}\n\nexport const _testExports = { matchValue, safeRegExp, isInternalHost };\n","import { safeBaseUrl } from '../safe-origin.js';\nimport { pulseAuthHeader } from '../../pulse-token.js';\n\nconst DEFAULT_BASE_URL = 'https://api.patchstack.com/monitor/pulse';\nconst DEFAULT_CACHE_TTL = 300_000;\n// Randomly shorten the effective TTL by up to this fraction so many long-lived clients don't all\n// revalidate on the same tick (spreads load / avoids a thundering herd against the rules API).\nconst JITTER_FRACTION = 0.1;\n\n// Per-site rules client for Pulse (npm/JS) apps. Public endpoint — the site UUID is the only\n// credential, passed in the path. Fail-open: any error returns success:false + empty rules so\n// createProtection falls back to the disk cache or the bundled rules.\n//\n// Conditional fetch: pass a prior `etag` (persisted with the last cached bundle) and the client\n// sends `If-None-Match`; a 304 returns `{ notModified: true }` so the caller reuses its cache\n// without re-downloading, and a 200 returns the new bundle plus its `etag` to persist. This is a\n// no-op until the rules API emits an ETag and honors If-None-Match — with no ETag in the response\n// the client simply behaves as before (a full fetch every refresh).\nexport class PulseRuleClient {\n #siteUuid;\n #timeoutMs;\n #baseUrl;\n #cacheTtl;\n #cache = null;\n #cacheTime = null;\n #ttlEffective = 0;\n #etag;\n #pulseAuth;\n\n #reportsDetections;\n\n constructor({ siteUuid, baseUrl, cacheTtl, etag, timeoutMs, pulseAuth, reportsDetections } = {}) {\n // Bounded so app STARTUP can't hang on a slow API: hosted platforms fail a deploy whose health\n // check is slow, and we always have a cache/bundled fallback to boot from.\n this.#timeoutMs = Number(timeoutMs) > 0 ? Number(timeoutMs) : 30_000;\n this.#siteUuid = siteUuid ?? process.env.PATCHSTACK_SITE_UUID;\n // Rules are executed policy — refuse a plaintext remote override (see safe-origin.js).\n this.#baseUrl = safeBaseUrl(baseUrl ?? process.env.PATCHSTACK_PULSE_RULES_URL, DEFAULT_BASE_URL, 'rule endpoint');\n this.#cacheTtl = Number.isFinite(cacheTtl) && cacheTtl > 0 ? cacheTtl : DEFAULT_CACHE_TTL;\n this.#etag = etag ?? null;\n this.#pulseAuth = pulseAuth ?? null;\n // Whether this guard reports detections, declared on a request it already makes.\n //\n // Detections are only sent when a rule fires, so silence at the server means one of three things —\n // nothing matched, reporting is off, or reports are not arriving — and nothing distinguishes them.\n // Saying \"reporting is on\" on the rules fetch does, without a new outbound path or any request data:\n // the fetch is already periodic, already authenticated, and already carries this site's identity.\n //\n // A capability, not a timestamp: the server records when IT saw this, because a client clock is a\n // value from outside and \"alive as of\" is exactly the claim a stale or wrong clock would fake.\n this.#reportsDetections = reportsDetections === true;\n if (!this.#siteUuid) {\n throw new Error('Patchstack site UUID is required. Pass { siteUuid } or set PATCHSTACK_SITE_UUID.');\n }\n }\n\n async getRules() {\n const now = Date.now();\n if (this.#cache && this.#cacheTime !== null && now - this.#cacheTime < this.#ttlEffective) {\n return this.#cache;\n }\n const url = `${this.#baseUrl}/rules/${encodeURIComponent(this.#siteUuid)}`;\n try {\n // Unauthenticated when no credential resolved, or when the exchange\n // fails — the server still accepts the UUID, and protection must never\n // hinge on getting a token.\n const auth = await pulseAuthHeader(\n { pulseAuth: this.#pulseAuth, endpoint: this.#baseUrl, timeoutMs: this.#timeoutMs },\n fetch,\n );\n const headers = { Accept: 'application/json', ...auth };\n // Claimed only on an authenticated request. The rules endpoint still accepts a bare UUID, so on that\n // path this header would be an assertion anyone holding the UUID could make — and it asserts the\n // reassuring thing: that reporting is on. A dashboard would then say a site is covered because a\n // stranger said so.\n //\n // Fetching rules must never hinge on getting a token (protection comes first), but CLAIMING a\n // capability may: an unauthenticated request is one whose statements about this site carry no weight.\n // This check only removes the ACCIDENTAL case. The forgeable one is not the client's to prevent, so\n // anything acting on this header has to require a verified token itself before believing it — a\n // client-side gate is a courtesy, never the guarantee.\n if (this.#reportsDetections && typeof auth.Authorization === 'string') {\n headers['X-Patchstack-Detections'] = 'enabled';\n }\n if (this.#etag) headers['If-None-Match'] = this.#etag;\n const response = await fetch(url, { method: 'GET', headers, signal: AbortSignal.timeout(this.#timeoutMs) });\n\n if (response.status === 304) {\n this.#touch(now); // revalidated — reset the clock (fresh jitter)\n return this.#cache ?? { success: true, notModified: true, etag: this.#etag, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n if (!response.ok) {\n return { success: false, error: `API returned ${response.status}`, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n const data = await response.json();\n // A 200 that isn't a genuine rule envelope (schema drift, a proxy/interstitial page, an\n // {error} body) must not be treated as \"no rules\". Report failure so the caller falls back to\n // the cache / bundled rules rather than caching an empty bundle.\n if (!data || !Array.isArray(data.firewall)) {\n return { success: false, error: 'unexpected response shape (no firewall array)', firewall: [], whitelists: [], whitelist_keys: {} };\n }\n const result = {\n success: true,\n notModified: false,\n etag: response.headers?.get?.('etag') ?? null,\n firewall: data.firewall,\n whitelists: Array.isArray(data.whitelists) ? data.whitelists : [],\n whitelist_keys: data.whitelist_keys ?? {},\n ...enforcementField(data),\n };\n this.#cache = result;\n this.#etag = result.etag;\n this.#touch(now);\n return result;\n } catch (err) {\n return {\n success: false,\n error: err.name === 'TimeoutError' ? 'Request timed out' : err.message,\n firewall: [], whitelists: [], whitelist_keys: {},\n };\n }\n }\n\n #touch(now) {\n this.#cacheTime = now;\n this.#ttlEffective = this.#cacheTtl - Math.floor(Math.random() * this.#cacheTtl * JITTER_FRACTION);\n }\n\n clearCache() {\n this.#cache = null;\n this.#cacheTime = null;\n this.#etag = null;\n }\n}\n\n/** @param {unknown} data @returns {{ enforcement?: 'block'|'dry-run' }} */\nfunction enforcementField(data) {\n if (!data || typeof data !== 'object') return {};\n const v = data.enforcement ?? data.mode;\n return v === 'block' || v === 'dry-run' ? { enforcement: v } : {};\n}\n","// Web Fetch adapter — runs the node-waf engine on any runtime where a request is a\n// WHATWG `Request` and a handler returns a `Response`: Cloudflare Workers, Bun, Deno,\n// Hono, Next.js edge, and TanStack Start's `server.ts`. This is the surface AI-builder\n// apps actually deploy to, and where require-based instrumentation can't reach.\n//\n// The engine already consumes a runtime-neutral request shape; this adapter builds that\n// shape from a `Request`, so no engine changes are needed beyond keeping the hot path\n// free of Node-only APIs.\nimport { RuleEngine } from './engine.js';\nimport { notify } from '../notify.js';\n\n// Cap how much request body we buffer for inspection. A larger body is left UNSCANNED\n// (fail-open) rather than buffered into memory — matches the node adapter's maxBodyBytes.\nconst MAX_BODY_BYTES = 1024 * 1024;\n\n// Build the engine's request shape from a WHATWG Request. The body is read from a\n// CLONE so the downstream handler still receives an intact request.\nexport async function fromFetchRequest(request, options = {}) {\n const maxBodyBytes = options.maxBodyBytes ?? MAX_BODY_BYTES;\n const url = new URL(request.url);\n const method = (request.method || 'GET').toUpperCase();\n\n const headers = {};\n request.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n\n const query = {};\n for (const [key, value] of url.searchParams) {\n if (key in query) {\n query[key] = Array.isArray(query[key]) ? [...query[key], value] : [query[key], value];\n } else {\n query[key] = value;\n }\n }\n\n let rawBody = '';\n if (method !== 'GET' && method !== 'HEAD') {\n rawBody = await readCappedText(request, maxBodyBytes);\n }\n\n const contentType = headers['content-type'] || '';\n let body = {};\n let files;\n if (rawBody) {\n const parsed = parseBody(rawBody, contentType);\n body = parsed.body;\n files = parsed.files;\n }\n\n const uri = url.pathname + url.search;\n const forwarded =\n headers['cf-connecting-ip'] || headers['x-forwarded-for'] || headers['x-real-ip'] || '';\n\n return {\n method,\n url: uri,\n originalUrl: uri,\n query,\n body,\n files,\n headers,\n ip: forwarded.split(',')[0].trim(),\n cookies: parseCookies(headers.cookie),\n // Verbatim body text: preserves literal keys (e.g. `__proto__`) that JSON.stringify\n // drops, so prototype-pollution rules on `raw` are robust.\n _rawBody: rawBody\n };\n}\n\n// Read a request body as text for inspection. Memory is bounded by a hard ceiling (4× the scan\n// cap): a body whose declared Content-Length exceeds it is left UNSCANNED (fail-open). Within the\n// ceiling, an oversize body is TRUNCATED to `max` and its prefix is still scanned — so a\n// front-loaded payload is caught — rather than being discarded outright. Reads a clone so the\n// downstream handler keeps an intact body. (`max` is compared in bytes against Content-Length; the\n// prefix slice is by character, which can only over-scan a multibyte body — the safe direction.)\nasync function readCappedText(request, max) {\n // Do NOT skip scanning based on a declared Content-Length: an attacker can declare a huge length\n // (or none) to dodge inspection while sending a small exploit body. Always stream-scan the prefix\n // up to `max` (buffering is bounded to `max`; the rest is drained but not retained). Anything past\n // the cap is unscanned — the documented prefix-scan tradeoff — but the body is never skipped whole.\n let clone;\n try {\n clone = request.clone();\n } catch {\n return '';\n }\n\n // Stream the read so a body WITHOUT a Content-Length can't buffer unbounded: retain only up to the\n // scan cap (`max`) for inspection, but keep draining to completion so the original request stays\n // intact. A front-loaded payload is still caught; anything past the cap is not scanned (same\n // partial-scan tradeoff as before, now with a hard memory bound regardless of Content-Length).\n const body = clone.body;\n if (body && typeof body.getReader === 'function') {\n const reader = body.getReader();\n const chunks = [];\n let buffered = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value || buffered >= max) continue; // keep draining, stop buffering past the cap\n const take = Math.min(value.byteLength, max - buffered);\n chunks.push(take === value.byteLength ? value : value.subarray(0, take));\n buffered += take;\n }\n } catch {\n return '';\n }\n try {\n return new TextDecoder().decode(concatChunks(chunks, buffered));\n } catch {\n return '';\n }\n }\n\n // No readable stream — fall back to text() with the post-read guard.\n try {\n const text = await clone.text();\n return text.length > max ? text.slice(0, max) : text;\n } catch {\n return '';\n }\n}\n\nfunction concatChunks(chunks, total) {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const c of chunks) {\n out.set(c, offset);\n offset += c.byteLength;\n }\n return out;\n}\n\n// Minimal multipart/form-data parser: enough to expose field names + values (so `post.<field>`\n// and `raw` rules match uploads, e.g. a `__proto__` field name) and file metadata (filename via\n// `files.<field>`). We only need the textual structure, not the binary file contents.\n// Parse a request body into { body, files } for parameter-scoped rules. Content-type detection is\n// deliberately permissive: many AI-built apps `JSON.parse(await req.text())` regardless of the\n// declared type, so a JSON body arriving as `application/vnd.api+json`, `application/ld+json`,\n// `text/plain`, `application/csp-report`, or with NO content-type must still populate post.<field>\n// (otherwise a field-scoped rule silently resolves to nothing). Unrecognized/binary bodies stay `{}`\n// and are still matchable via `raw`.\nexport function parseBody(rawBody, contentType) {\n const ct = String(contentType || '').toLowerCase();\n const isJson = ct.includes('application/json') || /\\+json\\b/.test(ct);\n const isForm = ct.includes('application/x-www-form-urlencoded');\n const isMultipart = ct.includes('multipart/form-data');\n // \"ambiguous\" = a type an app commonly parses as JSON/form even though it isn't declared as such.\n const isAmbiguous = ct === '' || ct.startsWith('text/plain') || ct.includes('csp-report') || ct.includes('/json');\n\n if (isMultipart) {\n const boundary = /boundary=(\"?)([^\";]+)\\1/i.exec(contentType)?.[2];\n if (boundary) return parseMultipart(rawBody, boundary);\n return { body: {}, files: undefined };\n }\n if (isForm) {\n const body = {};\n for (const [k, v] of new URLSearchParams(rawBody)) body[k] = k in body ? [].concat(body[k], v) : v;\n return { body, files: undefined };\n }\n if (isJson || isAmbiguous) {\n try {\n const parsed = JSON.parse(rawBody);\n if (parsed && typeof parsed === 'object') return { body: parsed, files: undefined };\n } catch {\n /* not JSON — leave body empty; `raw`/`all` still see the verbatim text */\n }\n }\n return { body: {}, files: undefined };\n}\n\nexport function parseMultipart(rawBody, boundary) {\n const body = {};\n const files = {};\n for (const part of rawBody.split('--' + boundary)) {\n // Tolerate both CRLF and LF-only line endings (some clients/proxies send bare \\n).\n const sep = /\\r?\\n\\r?\\n/.exec(part);\n if (!sep) continue;\n const rawHeaders = part.slice(0, sep.index);\n const disposition = /content-disposition:[^\\r\\n]*/i.exec(rawHeaders)?.[0];\n if (!disposition) continue;\n const name = /name=\"([^\"]*)\"/i.exec(disposition)?.[1];\n if (name == null) continue;\n const content = part.slice(sep.index + sep[0].length).replace(/\\r?\\n$/, '');\n const filename = /filename=\"([^\"]*)\"/i.exec(disposition)?.[1];\n if (filename !== undefined) {\n // Capture the part's declared content-type and CONTENT (not just the filename), so rules can\n // inspect an upload's bytes (files.<name>.content) and detect a declared-vs-actual type\n // mismatch (files.<name>.mismatch). The content rides inside the already-capped rawBody.\n const partType = /content-type:\\s*([^\\r\\n;]+)/i.exec(rawHeaders)?.[1]?.trim() || '';\n const file = { filename, type: partType, content };\n files[name] = name in files ? [].concat(files[name], file) : file;\n } else {\n body[name] = name in body ? [].concat(body[name], content) : content;\n }\n }\n return { body, files };\n}\n\nfunction parseCookies(header) {\n const cookies = {};\n if (!header) {\n return cookies;\n }\n for (const pair of header.split(';')) {\n const idx = pair.indexOf('=');\n if (idx === -1) {\n continue;\n }\n cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n return cookies;\n}\n\nfunction defaultBlockResponse(result) {\n return new Response(\n JSON.stringify({\n error: 'Blocked by Patchstack WAF',\n message: result.message,\n timestamp: new Date().toISOString()\n }),\n { status: 403, headers: { 'content-type': 'application/json' } }\n );\n}\n\n/**\n * Returns a guard `(request) => Promise<Response|null>`. A `Response` means blocked;\n * `null` means allowed — the caller should proceed to its handler. Accepts either a\n * rules bundle (`{ firewall, whitelists, whitelist_keys }`) or a `RuleEngine` instance.\n * Fails open: a rule that throws never blocks the request.\n */\nexport function createFetchMiddleware(rulesData, options = {}) {\n const engine =\n rulesData && typeof rulesData.evaluate === 'function'\n ? rulesData\n : new RuleEngine(rulesData);\n\n const guard = async (request) => {\n let req;\n let result;\n try {\n req = await fromFetchRequest(request); // shaping inside the try — a bad/relative request.url must fail open\n result = engine.evaluate(req);\n } catch (err) {\n notify(options.onError, err, 'onError');\n return null; // fail open\n }\n\n if (result.blocked) {\n // Contained: this runs after the block decision and before the block response is built, so an\n // escaping throw would replace the 403 with the callback's exception — reporting code deciding\n // the enforcement outcome.\n notify(options.onBlock, {\n rule: result.rule,\n message: result.message,\n request: { method: req.method, url: req.url, ip: req.ip }\n }, 'onBlock');\n return (options.response || defaultBlockResponse)(result);\n }\n\n return null;\n };\n\n guard.engine = engine;\n return guard;\n}\n\n/**\n * Wrap a fetch handler so every request is screened first. One-line hook:\n * export default { fetch: wrapFetchHandler(app.fetch, rulesData) }\n */\nexport function wrapFetchHandler(handler, rulesData, options = {}) {\n const guard = createFetchMiddleware(rulesData, options);\n const wrapped = async (request, ...rest) => {\n const blocked = await guard(request);\n return blocked ?? handler(request, ...rest);\n };\n wrapped.engine = guard.engine;\n return wrapped;\n}\n","// Raw Node.js / Connect adapter — for traditional Node servers where the request is a\n// `http.IncomingMessage` and the body is NOT already parsed (plain `http`, Connect,\n// Express without a body-parser, Fastify via its Node req). It buffers the body once,\n// builds the engine's request shape, and blocks or calls `next()`.\n//\n// This complements the Express `createMiddleware` (which assumes `req.body`/`req.query`\n// are already populated) and the Web-Fetch adapter (Workers/edge). Mount it FIRST, before\n// any body-parser — it consumes the stream and exposes the parsed body as `req.body`.\nimport { RuleEngine } from './engine.js';\nimport { parseBody } from './fetch.js';\nimport { notify } from '../notify.js';\n\n// Build the engine's request shape from a Node IncomingMessage + its raw body text.\nexport function fromNodeRequest(req, rawBody = '') {\n const method = (req.method || 'GET').toUpperCase();\n\n const headers = {};\n for (const [key, value] of Object.entries(req.headers || {})) {\n headers[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value;\n }\n\n // An unusual Host header or req.url can make `new URL` throw; shaping must never crash the\n // request (fail-open), so fall back to a safe base.\n const host = headers.host || 'localhost';\n let url;\n try {\n url = new URL(req.url || '/', `http://${host}`);\n } catch {\n try {\n url = new URL(req.url || '/', 'http://localhost');\n } catch {\n url = new URL('http://localhost/');\n }\n }\n\n const query = {};\n for (const [key, value] of url.searchParams) {\n if (key in query) {\n query[key] = Array.isArray(query[key]) ? [...query[key], value] : [query[key], value];\n } else {\n query[key] = value;\n }\n }\n\n const contentType = headers['content-type'] || '';\n let body = {};\n let files;\n if (rawBody) {\n // Same permissive content-type handling as the fetch adapter (+json / text/plain / no-CT bodies\n // still populate post.<field>; multipart exposes field + file metadata) on a raw-Node server too.\n const parsed = parseBody(rawBody, contentType);\n body = parsed.body;\n files = parsed.files;\n }\n\n const uri = url.pathname + url.search;\n const forwarded =\n headers['cf-connecting-ip'] || headers['x-forwarded-for'] || headers['x-real-ip'] || '';\n\n return {\n method,\n url: uri,\n originalUrl: uri,\n query,\n body,\n files,\n headers,\n ip: forwarded.split(',')[0].trim() || (req.socket && req.socket.remoteAddress) || '',\n cookies: parseCookies(headers.cookie),\n // Verbatim body text: preserves literal keys (e.g. `__proto__`) that JSON.stringify drops.\n _rawBody: rawBody\n };\n}\n\nfunction parseCookies(header) {\n const cookies = {};\n if (!header) {\n return cookies;\n }\n for (const pair of header.split(';')) {\n const idx = pair.indexOf('=');\n if (idx === -1) {\n continue;\n }\n cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n return cookies;\n}\n\nfunction defaultBlock(res, result) {\n res.statusCode = 403;\n res.setHeader('content-type', 'application/json');\n res.end(\n JSON.stringify({\n error: 'Blocked by Patchstack WAF',\n message: result.message,\n timestamp: new Date().toISOString()\n })\n );\n}\n\n/**\n * Connect/Express-style middleware `(req, res, next)` that buffers the body itself.\n * Accepts a `RuleEngine` instance or a `{ firewall, whitelists, whitelist_keys }` bundle.\n * Fails open: an engine error (or oversized body) never blocks the request.\n * Options: `{ maxBodyBytes = 1MiB, onBlock, onError, response }`.\n */\nexport function createNodeMiddleware(rulesData, options = {}) {\n const engine =\n rulesData && typeof rulesData.evaluate === 'function' ? rulesData : new RuleEngine(rulesData);\n const maxBytes = options.maxBodyBytes ?? 1024 * 1024;\n\n return function guard(req, res, next) {\n const chunks = [];\n let size = 0;\n let overflow = false;\n\n req.on('data', (chunk) => {\n size += chunk.length;\n if (size > maxBytes) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n\n req.on('error', (err) => next(err));\n\n req.on('end', () => {\n let result;\n let shaped;\n try {\n const rawBody = overflow ? '' : Buffer.concat(chunks).toString('utf8');\n shaped = fromNodeRequest(req, rawBody); // shaping is inside the try too — never crash\n result = engine.evaluate(shaped);\n } catch (err) {\n notify(options.onError, err, 'onError');\n return next(); // fail open\n }\n\n if (result.blocked) {\n // Contained, as on the fetch path: a throw here would replace the block response with the\n // callback's exception.\n notify(options.onBlock, {\n rule: result.rule,\n message: result.message,\n request: { method: shaped.method, url: shaped.url, ip: shaped.ip }\n }, 'onBlock');\n return (options.response || defaultBlock)(res, result);\n }\n\n // Expose the parsed body downstream so a body-parser isn't also required.\n req.body = shaped.body;\n next();\n });\n };\n}\n","// Egress guard MECHANISM only. Wraps the app's outbound calls so they can be screened; the\n// block DECISION is delegated to a caller-supplied `shouldBlock` predicate (which the runtime\n// builds from egress-phase rules — see defaults.js). No policy is hardcoded here.\n// WinterCG-first: always wraps the global `fetch` (Node 18+, Workers, Bun, Deno). On Node it\n// ALSO patches `node:http`/`node:https` so outbound calls made via those modules (axios, got,\n// the raw http client, …) — which never touch `fetch` — are screened too.\n\n/**\n * @param {{ shouldBlock: (url:string, host:string|null, method:string)=>boolean,\n * onBlock?: (info:{url:string,host:string|null,method:string})=>void,\n * dnsScreen?: boolean,\n * lookup?: Function }} opts\n * @returns {Promise<() => void>} uninstall (restores every patched surface)\n */\nimport { notify } from './notify.js';\n\nexport async function installEgressGuard({ shouldBlock, onBlock, onSkip, dnsScreen = true, lookup, allowHosts } = {}) {\n const restores = [];\n if (typeof shouldBlock !== 'function') return () => {};\n const exempt = new Set((allowHosts ?? []).map((h) => String(h).toLowerCase()));\n\n const block = (url, host, method) => {\n if (!shouldBlock(url, host, method)) return false;\n // Reported AFTER the decision and contained, because this call sits between deciding to block and\n // saying so. An escaping throw would replace a controlled block with the callback's exception, which\n // hands the enforcement outcome to reporting code — the inverse of what a block is for.\n notify(onBlock, { url, host, method }, 'onEgressBlock');\n return true;\n };\n\n // DNS resolution, shared by the fetch wrapper and the node http path. Instead of trusting the\n // hostname, resolve it and check the resolved address(es). On the node path we also PIN the socket\n // to the vetted IP (no time-of-check/use gap); on fetch we can't pin without a custom undici\n // dispatcher, so we screen the resolution but a re-resolve at connect is a residual window.\n // Needs node:dns + node:net; absent on edge runtimes, where the hostname rules still apply.\n let screen = null;\n if (dnsScreen) {\n try {\n const resolveLookup = lookup ?? (await import('node:dns')).lookup;\n const { isIP } = await import('node:net');\n if (typeof resolveLookup === 'function' && typeof isIP === 'function') {\n screen = { lookup: resolveLookup, isIP, isExempt: (h) => exempt.has(String(h).toLowerCase()) };\n }\n } catch {\n screen = null; // no node:dns/net here — skip, hostname rules still apply\n }\n }\n\n // A resolver failure means the destination was NOT screened by IP — the hostname check alone let it\n // through. That's a real (if rare) coverage hole, so report it via onSkip instead of failing open\n // silently. Still fail-open: a broken resolver must not take the app's outbound traffic down.\n const skip = (reason, detail) => notify(onSkip, { phase: 'egress', reason, detail }, 'onSkip');\n\n // True when a hostname resolves to a disallowed address. Fail-open: any resolver error → false.\n const resolvesToDisallowed = (url, host, method) =>\n new Promise((resolve) => {\n if (!screen) {\n // No node:dns/net here (edge runtime) or screening disabled — hostname rules only.\n if (host && dnsScreen) skip('resolver-unavailable', { host });\n return resolve(false);\n }\n if (!host || screen.isIP(host) !== 0 || screen.isExempt(host)) return resolve(false);\n try {\n screen.lookup(host, { all: true }, (err, addresses) => {\n if (err || !Array.isArray(addresses)) { skip('resolver-failed', { host }); return resolve(false); }\n for (const a of addresses) {\n const ip = a && typeof a === 'object' ? a.address : a;\n if (ip && block(url, ip, method)) return resolve(true);\n }\n resolve(false);\n });\n } catch {\n skip('resolver-failed', { host });\n resolve(false);\n }\n });\n\n // 1. global fetch — synchronous install, so it's active the instant this returns (no startup race).\n const originalFetch = globalThis.fetch;\n if (typeof originalFetch === 'function' && !originalFetch.__patchstackGuarded) {\n const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\n const MAX_REDIRECTS = 20;\n\n // Screen one outbound URL: hostname/allowlist/literal-IP check, then a DNS-resolution check for\n // real hostnames. Throws if the destination is disallowed.\n const screenUrl = async (u, method) => {\n let host = null;\n try {\n host = new URL(u).hostname;\n } catch {\n host = null;\n }\n if (block(u, host, method) || (await resolvesToDisallowed(u, host, method))) {\n throw new Error(`Patchstack blocked an outbound request to a disallowed address: ${host ?? u}`);\n }\n };\n\n const guarded = async (input, init) => {\n let cur;\n try {\n cur = new Request(input, { ...(init || {}), redirect: 'manual' });\n } catch {\n return originalFetch(input, init); // odd input we can't normalize — fail open, don't break the caller\n }\n const callerRedirect = (init && init.redirect) || (input && input.redirect) || 'follow';\n\n let url = cur.url;\n let method = cur.method;\n await screenUrl(url, method);\n\n // Caller manages redirects itself (manual/error) → screen once, hand back the raw response.\n if (callerRedirect !== 'follow') return originalFetch(input, init);\n\n // Otherwise follow redirects ourselves so EVERY hop is screened. Native `follow` re-resolves\n // internally and would let a 3xx to an internal address slip past the initial check — SSRF via\n // an open redirect. Buffer the body once (a stream can't be re-read) so 307/308 can replay it.\n const headers = new Headers(cur.headers);\n const signal = cur.signal;\n let body = method === 'GET' || method === 'HEAD' ? undefined : await cur.clone().arrayBuffer();\n\n for (let hop = 0; ; hop++) {\n const resp = await originalFetch(\n hop === 0 ? cur : new Request(url, { method, headers, body, redirect: 'manual', signal }),\n );\n const location = REDIRECT_STATUSES.has(resp.status) ? resp.headers.get('location') : null;\n if (!location) return resp;\n if (hop >= MAX_REDIRECTS) throw new Error('Patchstack blocked an outbound request: too many redirects');\n\n const next = new URL(location, url).href;\n // Fetch redirect semantics: 303, and 301/302 on a POST, become a bodyless GET.\n if (resp.status === 303 || ((resp.status === 301 || resp.status === 302) && method === 'POST')) {\n method = 'GET';\n body = undefined;\n headers.delete('content-type');\n headers.delete('content-length');\n }\n // Drop credentials on a cross-origin hop, mirroring the browser.\n if (new URL(next).origin !== new URL(url).origin) {\n headers.delete('authorization');\n headers.delete('cookie');\n }\n await screenUrl(next, method);\n url = next;\n }\n };\n guarded.__patchstackGuarded = true;\n globalThis.fetch = guarded;\n restores.push(() => {\n if (globalThis.fetch === guarded) globalThis.fetch = originalFetch;\n });\n }\n\n // node:http / node:https — best-effort; absent on Workers/Deno-without-node (import throws).\n for (const moduleName of ['node:http', 'node:https']) {\n try {\n const mod = await import(moduleName);\n const restore = patchHttpModule(mod.default ?? mod, block, screen);\n if (restore) restores.push(restore);\n } catch {\n /* module not available on this runtime — skip */\n }\n }\n\n // WebSocket egress is intentionally NOT screened. The WebSocket constructor is synchronous, so\n // the only check possible inline is a textual hostname match — which can't offer the\n // DNS-resolution guarantee the fetch and node:http/https paths give (a name that resolves to an\n // internal address would pass). A connection-pinning dispatcher could close that, but the\n // server-side, attacker-controlled-WebSocket sink is rare, and a partial hostname-only check\n // over-promises the control. Outbound SSRF screening covers fetch + node:http/https.\n\n return () => {\n for (const restore of restores) {\n try {\n restore();\n } catch {\n /* ignore */\n }\n }\n };\n}\n\n// Wrap http(s).request/get so a blocked destination throws before the socket opens.\nfunction patchHttpModule(http, block, screen) {\n if (!http || typeof http.request !== 'function' || http.__patchstackGuarded) return null;\n const originalRequest = http.request;\n const originalGet = http.get;\n\n const wrap = (original) =>\n function (...args) {\n const target = extractHttpTarget(args);\n if (target && block(target.url, target.host, target.method)) {\n throw new Error(`Patchstack blocked an outbound request to a disallowed address: ${target.host ?? target.url}`);\n }\n // DNS screen: only for real hostnames (a literal IP was already covered by the check above),\n // and skip an explicitly allowlisted host (the operator trusts it — don't second-guess its DNS).\n if (target && screen && target.host && screen.isIP(target.host) === 0 && !screen.isExempt(target.host)) {\n try {\n args = withScreeningLookup(args, target, block, screen.lookup);\n } catch {\n /* injection failed — proceed unscreened (fail-open) */\n }\n }\n return original.apply(this, args);\n };\n\n const guardedRequest = wrap(originalRequest);\n http.request = guardedRequest;\n let guardedGet;\n if (typeof originalGet === 'function') {\n guardedGet = wrap(originalGet);\n http.get = guardedGet;\n }\n http.__patchstackGuarded = true;\n\n return () => {\n // Only restore if our wrapper is still installed — don't clobber a wrapper another library\n // (an APM agent, etc.) layered on top of us after install.\n if (http.request === guardedRequest) http.request = originalRequest;\n if (guardedGet && http.get === guardedGet) http.get = originalGet;\n delete http.__patchstackGuarded;\n };\n}\n\n// http.request accepts (url), (url, options), or (options) — with an optional trailing callback.\nfunction extractHttpTarget(args) {\n const first = args[0];\n try {\n if (typeof first === 'string' || first instanceof URL) {\n const url = new URL(String(first));\n const opts = args.find((a) => a && typeof a === 'object' && !(a instanceof URL));\n return { url: url.href, host: url.hostname, method: (opts && opts.method) || 'GET' };\n }\n if (first && typeof first === 'object') {\n // Node defaults an absent host to localhost — reflect that so the block/screen see a real target.\n const host = normalizeHost(first.hostname || first.host) || 'localhost';\n const protocol = first.protocol || 'http:';\n const port = first.port ? `:${first.port}` : '';\n const path = first.path || '/';\n return { url: `${protocol}//${host}${port}${path}`, host, method: first.method || 'GET' };\n }\n } catch {\n /* fall through */\n }\n return null;\n}\n\n// Extract the bare host from a node http(s) options `host`/`hostname`, WITHOUT mangling IPv6.\n// `[::1]:8080` → `::1`, bare `::1`/`fe80::1` → unchanged, `example.com:443` → `example.com`.\n// (isInternalHost strips brackets and matches ::1 / fe80: / fc / fd, so this must not corrupt them.)\nfunction normalizeHost(raw) {\n const host = String(raw || '').trim();\n const bracketed = /^\\[([^\\]]+)\\]/.exec(host);\n if (bracketed) return bracketed[1];\n if ((host.match(/:/g) || []).length > 1) return host; // bare IPv6 — no host:port to split\n return host.split(':')[0];\n}\n\n// Given the addresses a hostname resolved to, return the first one the policy blocks (else null).\n// Reuses the same `block` predicate as the hostname check, so egress rules + allowlist apply to\n// the resolved IP too. Exported for tests.\nexport function screenResolved(addresses, target, block) {\n for (const a of addresses || []) {\n const ip = a && typeof a === 'object' ? a.address : a;\n if (ip && block(target.url, ip, target.method)) return ip;\n }\n return null;\n}\n\n// Build a DNS `lookup` that screens every resolved address before the socket connects, then hands\n// back the vetted addresses (pinning the connection to what we checked). A blocked address errors\n// the connection; a resolver error or our own failure falls through to normal resolution (fail-open).\nfunction withScreeningLookup(args, target, block, lookup) {\n const screeningLookup = (hostname, options, callback) => {\n let opts = options;\n let cb = callback;\n if (typeof opts === 'function') {\n cb = opts;\n opts = {};\n }\n if (!opts || typeof opts !== 'object') opts = {};\n try {\n lookup(hostname, { ...opts, all: true }, (err, addresses) => {\n if (err) return cb(err);\n const list = Array.isArray(addresses) ? addresses : [];\n const blocked = screenResolved(list, target, block);\n if (blocked) {\n return cb(new Error(`Patchstack blocked an outbound request to a disallowed address: ${target.host} resolved to ${blocked}`));\n }\n if (opts.all) return cb(null, list);\n const first = list[0];\n if (!first) return cb(new Error(`Patchstack: could not resolve ${hostname}`));\n return cb(null, first.address, first.family);\n });\n } catch {\n // Our screening threw — fall back to a plain resolution so we never break a request ourselves.\n try {\n lookup(hostname, opts, cb);\n } catch {\n cb(new Error(`Patchstack: lookup failed for ${hostname}`));\n }\n }\n };\n return injectLookupOption(args, screeningLookup);\n}\n\n// Return a new args array for http(s).request with our `lookup` set on the options object (cloned,\n// never mutating the caller's object), inserting an options object when the call didn't pass one.\nfunction injectLookupOption(args, lookup) {\n const first = args[0];\n if (first && typeof first === 'object' && !(first instanceof URL)) {\n return [{ ...first, lookup }, ...args.slice(1)];\n }\n const rest = args.slice(1);\n const optIdx = rest.findIndex((a) => a && typeof a === 'object' && !(a instanceof URL));\n if (optIdx !== -1) {\n const next = [...rest];\n next[optIdx] = { ...rest[optIdx], lookup };\n return [first, ...next];\n }\n const cbIdx = rest.findIndex((a) => typeof a === 'function');\n if (cbIdx === -1) return [first, { lookup }, ...rest];\n return [first, ...rest.slice(0, cbIdx), { lookup }, ...rest.slice(cbIdx)];\n}\n","// Default response- and egress-phase rule bundles, as JS constants (not JSON files) so\n// tsup bundles them into dist/protect.js without a copy step. These are just rules —\n// override per app via createProtection({ responseRules, egressRules }) or extend by\n// adding phase-tagged rules to the delivered bundle. Patterns are high-precision\n// (low false positive): structural markers a real secret has and normal content doesn't.\n//\n// Each rule carries a `prefilter`: cheap literal anchor(s) that MUST appear in the body for the\n// regex to have any chance of matching. The response screener runs the (expensive) regex only when\n// an anchor is present (case-insensitive), so a body with no candidate — the common case — skips\n// the scan entirely. This cuts CPU/latency and shrinks the regex/ReDoS surface. (Honored by the\n// response phase once the prefilter mechanism lands; a no-op before that.)\n\n// Response phase — secret / info exposure. Default action `redact` masks only the\n// offending span and still serves the page (a legit response that leaks one key gets\n// that key masked, not withheld). Use `action: \"block\"` to withhold the whole response.\nexport const DEFAULT_RESPONSE_RULES = [\n {\n id: 'resp-private-key',\n title: 'Private key in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['PRIVATE KEY'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/' } }]\n },\n {\n id: 'resp-aws-access-key',\n title: 'AWS access key id in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['AKIA', 'ASIA'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\\\b/' } }]\n },\n {\n id: 'resp-gcp-api-key',\n title: 'Google API key in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['AIza'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\bAIza[0-9A-Za-z_-]{35}\\\\b/' } }]\n },\n {\n id: 'resp-vendor-api-key',\n title: 'Vendor API key / token in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n // High-signal, prefix-anchored provider tokens that never legitimately appear in a response\n // body: Stripe (sk_live_/rk_live_), GitHub (gh[opsu]_ / github_pat_), GitLab (glpat-),\n // Slack (xox[baprs]-), Anthropic (sk-ant-), Google OAuth (ya29.), npm (npm_). Trailing\n // (?![0-9A-Za-z]) instead of \\\\b since some tokens end in - / _ .\n prefilter: ['sk_live_', 'rk_live_', 'ghp_', 'gho_', 'ghs_', 'ghu_', 'github_pat_', 'glpat-', 'xox', 'sk-ant-', 'ya29.', 'npm_'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:sk_live_[0-9A-Za-z]{16,}|rk_live_[0-9A-Za-z]{16,}|gh[opsu]_[0-9A-Za-z]{36}|github_pat_[0-9A-Za-z_]{60,}|glpat-[0-9A-Za-z_-]{20,}|xox[baprs]-[0-9A-Za-z-]{10,}|sk-ant-[0-9A-Za-z_-]{20,}|ya29\\\\.[0-9A-Za-z_-]{20,}|npm_[0-9A-Za-z]{36})(?![0-9A-Za-z])/' } }]\n },\n {\n id: 'resp-jwt',\n title: 'JWT in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['eyJ'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\beyJ[A-Za-z0-9_-]{8,}\\\\.eyJ[A-Za-z0-9_-]{8,}\\\\.[A-Za-z0-9_-]{8,}\\\\b/' } }]\n },\n {\n id: 'resp-db-connection-string',\n title: 'Database connection string with credentials in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['mongodb', 'postgres', 'mysql', 'redis', 'amqp'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:mongodb(?:\\\\+srv)?|postgres(?:ql)?|mysql|redis|amqps?):\\\\/\\\\/[^\\\\s:@\\\\/]+:[^\\\\s:@\\\\/]+@/i' } }]\n },\n {\n id: 'resp-stack-trace',\n title: 'Node stack trace leaking in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n // No prefilter: a Node stack frame has no single distinctive literal (` at ` is too common to\n // gate on). The pattern is linearly bounded per line, so it runs on every screened body.\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\n\\\\s+at\\\\s+.+\\\\(.+:\\\\d+:\\\\d+\\\\)/' } }]\n },\n {\n id: 'resp-sql-error',\n title: 'SQL / ORM error disclosure in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n prefilter: ['SQLSTATE', 'Sequelize', 'ER_', 'ORA-', 'PG::', 'SQLITE_ERROR', 'SQL syntax'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/(SQLSTATE\\\\[[0-9A-Z]+\\\\]|SequelizeDatabaseError|ER_[A-Z_]+|ORA-\\\\d{5}|PG::[A-Za-z]+Error|SQLITE_ERROR|You have an error in your SQL syntax)/i' } }]\n },\n {\n id: 'resp-exception-trace',\n title: 'Backend exception / stack trace disclosure in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n // Multi-language exception/traceback signatures a normal API response never carries:\n // Python traceback, Java \"Exception in thread\", .NET System.*Exception, JVM stack frames,\n // Go goroutine dumps. (Node `at fn (file:line:col)` frames are handled by resp-stack-trace.)\n prefilter: ['Traceback', 'Exception in thread', 'System.', 'goroutine ', '.java:', '.kt:', '.scala:', '.rb:', '.py:', '.cs:'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/(Traceback \\\\(most recent call last\\\\)|Exception in thread \"|System\\\\.[A-Za-z.]+Exception|\\\\bat [\\\\w.$]+\\\\([\\\\w]+\\\\.(?:java|kt|scala|rb|py|cs):\\\\d+\\\\)|goroutine \\\\d+ \\\\[)/' } }]\n }\n];\n\n// Egress phase — SSRF: block the app's outbound calls to internal / metadata addresses.\nexport const DEFAULT_EGRESS_RULES = [\n {\n id: 'egress-internal-address',\n title: 'Outbound request to an internal / metadata address (SSRF)',\n phase: 'egress',\n category: 'ssrf',\n rule_v2: [{ parameter: 'egress.host', match: { type: 'internal_host' } }]\n }\n];\n","// Full-page \"Access Denied\" block view, ported from the Patchstack WordPress plugin's\n// sample.php. Served (instead of JSON) when a BLOCKED request is a top-level document navigation\n// rather than an XHR/fetch — so a user who browses into a blocked URL sees a branded page, while\n// programmatic clients still get JSON. Self-contained (inline CSS + base64 SVGs); exposes no WAF\n// narrative and no rule details.\n\nfunction escapeHtml(s) {\n return String(s).replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\n}\n\n/** @param {{ url?: string, code?: string }} opts @returns {string} a complete HTML document */\nexport function renderBlockPage({ url = '', code = 'patchstack' } = {}) {\n const safeUrl = escapeHtml(url);\n const safeCode = escapeHtml(code || 'patchstack');\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style type=\"text/css\">\n\t\t.access-denied,.site-url-text{letter-spacing:.01em;color:#fff}body{background:#111216}.main-container{display:flex;justify-content:center;flex-direction:column;align-items:center}.container{width:688px;height:456px;background:#1c1e22;border-radius:16px;margin-top:120px}.content{height:330px;margin:64px}.access-denied{font-family:Verdana;font-style:normal;font-weight:600;font-size:24px;line-height:32px;display:flex;align-items:center}.descp-text,.site-url{display:flex;align-items:center}.site-url{margin-top:32px;width:560px;height:64px;background:#26282c;border-radius:4px}.reason-code-text,.site-url-text{font-family:Verdana;font-style:normal;border:none;background:#26282c}.site-url-text{text-overflow: ellipsis;font-weight:400;font-size:16px;line-height:24px;margin-left:16px;width:85%}.copy-icon{width:19px;height:22px;margin-left:20px}.copy-icon:hover{cursor:pointer}.reason-code-text{font-weight:600;font-size:24px;line-height:32px;letter-spacing:.01em;color:#AFE614;margin-left:16px;width:85%}.descp-text,.return-text{font-size:16px;line-height:24px;letter-spacing:.01em;font-weight:400}.return-text:hover{color: #AFE614}.descp-text,.logoText,.return-text,.secured-text{font-family:Verdana;font-style:normal;color:#fff}.descp{margin-top:32px;width:560px}.bottom-second-containers,.bottom-text-containers{align-items:center;display:flex}.bottom-container{margin-top:40px;display:flex;width:688px}.bottom-second-containers{margin-left:auto}.arrow-vector{margin-left:20px}.return-text{text-align:center;margin-left:12.59px;text-decoration:none}.secured-text{font-weight:400;font-size:11.2px;line-height:16px;display:flex;align-items:center;letter-spacing:.02em;margin-right:8px}.logoText{font-weight:600;font-size:16px;line-height:24px;letter-spacing:.01em;flex:none;order:1;flex-grow:0}.patch-logo{width:120px}.mobile-bottom-container{margin-top:40px;width:372px;display:none}.mobile-bottom-second-containers{margin-top:64px;display:flex;margin-left:16.5px}@media only screen and (max-width:900px){.container{background:#111216;width:370px;height:auto;margin-top:0}.content{margin-left:16.5px}.descp,.site-url{width:340px}.site-url-text{font-weight:400;font-size:14.4px;line-height:20px;letter-spacing:.02em;width:80%}.copy-icon{margin-left:20px}.reason-code-text{font-size:16px;line-height:32px;letter-spacing:.02em;margin-left:16px;width:80%}.descp-text,.return-text{font-size:14.4px;line-height:20px}.descp-text{display:flex;align-items:center;letter-spacing:.02em}.bottom-container{display:none}.mobile-bottom-container{display:block}.return-text{text-align:center;letter-spacing:.01em;color:#fff;margin-left:9.73px}}\n </style>\n <title>Access Denied</title>\n</head>\n<body>\n <div class=\"main-container\">\n <div class=\"container\">\n <div class=\"content\">\n <span class=\"access-denied\">Access Denied</span>\n <div class=\"site-url\">\n <input id=\"url\" class=\"site-url-text\" type=\"text\"\n value=\"${safeUrl}\" readonly>\n <img class=\"copy-icon\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=\" alt=\"Copy Text\" onmouseover=\"hover(this);\" onmouseout=\"unhover(this);\" onclick=\"copyText(id='url')\">\n </div>\n <div class=\"site-url\">\n <input id=\"response\" class=\"reason-code-text\" type=\"text\" value=\"${safeCode}\" readonly>\n <img class=\"copy-icon\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=\" alt=\"Copy Text\" onmouseover=\"hover(this);\" onmouseout=\"unhover(this);\" onclick=\"copyText(id='response')\">\n </div>\n <div class=\"descp\">\n <span class=\"descp-text\">This request has been blocked by Patchstack. If this message persists and you are a legitimate user, contact the site administrator with the reference above.</span>\n </div>\n </div>\n </div>\n <div class=\"bottom-container\">\n <div class=\"bottom-text-containers\">\n <img class=\"arrow-vector\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSIxMyIgdmlld0JveD0iMCAwIDggMTMiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03LjQxIDEuNDMzNDRMNiAwLjAyMzQzNzVMMCA2LjAyMzQ0TDYgMTIuMDIzNEw3LjQxIDEwLjYxMzRMMi44MyA2LjAyMzQ0TDcuNDEgMS40MzM0NFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==\" alt=\"Arrow Left\">\n <a href=\"/\" class=\"return-text\">Return to homepage</a>\n </div>\n <div class=\"bottom-second-containers\">\n <span class=\"secured-text\">This website is secured by</span>\n <a href=\"https://patchstack.com\" class=\"logoText\">\n <img class=\"patch-logo\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg0IiBoZWlnaHQ9IjIyIiB2aWV3Qm94PSIwIDAgMTg0IDIyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUwLjQwMDEgMTEuNTAwOEM1MC40MDAxIDkuMTAwNzggNDkuMzAwMSA4LjEwMDc4IDQ2LjYwMDEgOC4xMDA3OEM0NC4wMDAxIDguMTAwNzggNDIuNzAwMSA5LjIwMDc4IDQyLjcwMDEgMTEuNTAwOEM0Mi43MDAxIDEzLjgwMDggNDQuMDAwMSAxNC45MDA4IDQ2LjYwMDEgMTQuOTAwOEM0OS4zMDAxIDE0LjkwMDggNTAuNDAwMSAxMy44MDA4IDUwLjQwMDEgMTEuNTAwOFpNNTMuNDAwMSAxMS41MDA4QzUzLjQwMDEgMTUuMzAwOCA1MS4zMDAxIDE3LjYwMDggNDcuNDAwMSAxNy42MDA4QzQ1LjQwMDEgMTcuNjAwOCA0My42MDAxIDE2LjUwMDggNDIuNzAwMSAxNC41MDA4VjIxLjUwMDhIMzkuNjAwMVY1LjYwMDc4SDQyLjcwMDFWOC41MDA3OEM0My42MDAxIDYuNTAwNzggNDUuNDAwMSA1LjMwMDc4IDQ3LjUwMDEgNS4zMDA3OEM1MS40MDAxIDUuMzAwNzggNTMuNDAwMSA3LjcwMDc4IDUzLjQwMDEgMTEuNTAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjYuNDAwMSAxMS40OTg0QzY2LjQwMDEgOS4xOTg0NCA2NS4xMDAxIDguMDk4NDQgNjIuNTAwMSA4LjA5ODQ0QzU5LjgwMDEgOC4wOTg0NCA1OC43MDAxIDkuMDk4NDQgNTguNzAwMSAxMS40OTg0QzU4LjcwMDEgMTMuNzk4NCA1OS44MDAxIDE0Ljg5ODQgNjIuNTAwMSAxNC44OTg0QzY1LjEwMDEgMTQuODk4NCA2Ni40MDAxIDEzLjc5ODQgNjYuNDAwMSAxMS40OTg0Wk02Ni4zMDAxIDUuNTk4NDRINjkuNDAwMVYxNy4zOTg0SDY2LjMwMDFWMTQuNDk4NEM2NS40MDAxIDE2LjQ5ODQgNjMuNzAwMSAxNy42OTg0IDYxLjYwMDEgMTcuNjk4NEM1Ny43MDAxIDE3LjY5ODQgNTUuNjAwMSAxNS4yOTg0IDU1LjYwMDEgMTEuNTk4NEM1NS42MDAxIDcuNzk4NDQgNTcuNjAwMSA1LjM5ODQ0IDYxLjQwMDEgNS4zOTg0NEM2My41MDAxIDUuMzk4NDQgNjUuNDAwMSA2LjU5ODQ0IDY2LjIwMDEgOC41OTg0NFY1LjU5ODQ0SDY2LjMwMDFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTgxLjM5OTkgOC4zSDc3LjM5OTlWMTQuN0g4MS4wOTk5VjE3LjRINzcuOTk5OUM3NS43OTk5IDE3LjQgNzQuMjk5OSAxNS45IDc0LjI5OTkgMTMuN1Y4LjNINzEuMzk5OVY1LjZINzQuMjk5OVYySDc3LjM5OTlWNS41SDgxLjM5OTlWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik05My42OTk5IDEyLjIwMDhMOTYuNjk5OSAxMi45MDA4Qzk1Ljk5OTkgMTYuMDAwOCA5My42OTk5IDE3LjYwMDggODkuOTk5OSAxNy42MDA4Qzg3Ljc5OTkgMTcuNjAwOCA4Ni4wOTk5IDE3LjAwMDggODQuNzk5OSAxNS45MDA4QzgzLjQ5OTkgMTQuODAwOCA4Mi44OTk5IDEzLjMwMDggODIuODk5OSAxMS41MDA4QzgyLjg5OTkgOS42MDA3OCA4My40OTk5IDguMTAwNzggODQuNzk5OSA3LjAwMDc4Qzg2LjA5OTkgNS45MDA3OCA4Ny43OTk5IDUuMzAwNzggODkuOTk5OSA1LjMwMDc4QzkzLjY5OTkgNS4zMDA3OCA5NS45OTk5IDYuOTAwNzggOTYuNjk5OSAxMC4wMDA4TDkzLjY5OTkgMTAuNzAwOEM5My4yOTk5IDguODAwNzggOTIuMjk5OSA4LjEwMDc4IDg5Ljg5OTkgOC4xMDA3OEM4Ni45OTk5IDguMTAwNzggODUuODk5OSA5LjIwMDc4IDg1Ljg5OTkgMTEuNTAwOEM4NS44OTk5IDEzLjgwMDggODYuOTk5OSAxNC45MDA4IDg5Ljg5OTkgMTQuOTAwOEM5Mi4yOTk5IDE0LjkwMDggOTMuMjk5OSAxNC4yMDA4IDkzLjY5OTkgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTExLjggMTAuNlYxNy40SDEwOC43VjExLjJDMTA4LjcgOSAxMDcuOCA4LjEgMTA1LjkgOC4xQzEwMy42IDguMSAxMDIuNCA5LjMgMTAyLjQgMTEuN1YxNy4zSDk5LjI5OThWMC41SDEwMi40VjguM0MxMDMuMSA2LjMgMTA0LjYgNS4zIDEwNyA1LjNDMTEwLjIgNS4zIDExMS44IDcuMSAxMTEuOCAxMC42WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMTQuMSAxNC4wOTg0TDExNy4xIDEzLjI5ODRDMTE3LjQgMTQuNzk4NCAxMTguMyAxNS4yOTg0IDEyMC4yIDE1LjI5ODRDMTIxLjggMTUuMjk4NCAxMjIuNyAxNC44OTg0IDEyMi43IDE0LjA5ODRDMTIyLjcgMTMuMjk4NCAxMjIuMiAxMi45OTg0IDEyMC42IDEyLjc5ODRMMTE4LjIgMTIuNDk4NEMxMTUuOSAxMi4xOTg0IDExNC41IDEwLjg5ODQgMTE0LjUgOC45OTg0NEMxMTQuNSA2Ljc5ODQ0IDExNi41IDUuMzk4NDQgMTE5LjggNS4zOTg0NEMxMjMuMSA1LjM5ODQ0IDEyNSA2LjY5ODQ0IDEyNS41IDguODk4NDRMMTIyLjUgOS41OTg0NEMxMjIuMyA4LjA5ODQ0IDEyMS41IDcuNjk4NDQgMTE5LjkgNy42OTg0NEMxMTguNCA3LjY5ODQ0IDExNy42IDguMDk4NDQgMTE3LjYgOC44OTg0NEMxMTcuNiA5LjU5ODQ0IDExOC4xIDkuOTk4NDQgMTE5LjMgMTAuMDk4NEwxMjIuMiAxMC40OTg0QzEyNC40IDEwLjc5ODQgMTI1LjggMTEuOTk4NCAxMjUuOCAxMy44OTg0QzEyNS44IDE2LjI5ODQgMTIzLjcgMTcuNzk4NCAxMjAuMSAxNy43OTg0QzExNi43IDE3LjU5ODQgMTE0LjUgMTYuMTk4NCAxMTQuMSAxNC4wOTg0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMzYuNyA4LjNIMTMyLjdWMTQuN0gxMzYuNFYxNy40SDEzMy4zQzEzMS4xIDE3LjQgMTI5LjYgMTUuOSAxMjkuNiAxMy43VjguM0gxMjYuN1Y1LjZIMTI5LjZWMkgxMzIuN1Y1LjVIMTM2LjdWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNDkuMiAxMS40OTg0QzE0OS4yIDkuMTk4NDQgMTQ3LjkgOC4wOTg0NCAxNDUuMyA4LjA5ODQ0QzE0Mi42IDguMDk4NDQgMTQxLjUgOS4wOTg0NCAxNDEuNSAxMS40OTg0QzE0MS41IDEzLjc5ODQgMTQyLjYgMTQuODk4NCAxNDUuMyAxNC44OTg0QzE0Ny45IDE0Ljg5ODQgMTQ5LjIgMTMuNzk4NCAxNDkuMiAxMS40OTg0Wk0xNDkuMSA1LjU5ODQ0SDE1Mi4yVjE3LjM5ODRIMTQ5LjFWMTQuNDk4NEMxNDguMiAxNi40OTg0IDE0Ni41IDE3LjY5ODQgMTQ0LjQgMTcuNjk4NEMxNDAuNSAxNy42OTg0IDEzOC40IDE1LjI5ODQgMTM4LjQgMTEuNTk4NEMxMzguNCA3Ljc5ODQ0IDE0MC40IDUuMzk4NDQgMTQ0LjIgNS4zOTg0NEMxNDYuMyA1LjM5ODQ0IDE0OC4yIDYuNTk4NDQgMTQ5IDguNTk4NDRWNS41OTg0NEgxNDkuMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTY1LjYgMTIuMjAwOEwxNjguNiAxMi45MDA4QzE2Ny45IDE2LjAwMDggMTY1LjYgMTcuNjAwOCAxNjEuOSAxNy42MDA4QzE1OS43IDE3LjYwMDggMTU4IDE3LjAwMDggMTU2LjcgMTUuOTAwOEMxNTUuNCAxNC44MDA4IDE1NC44IDEzLjMwMDggMTU0LjggMTEuNTAwOEMxNTQuOCA5LjYwMDc4IDE1NS40IDguMTAwNzggMTU2LjcgNy4wMDA3OEMxNTggNS45MDA3OCAxNTkuNyA1LjMwMDc4IDE2MS45IDUuMzAwNzhDMTY1LjYgNS4zMDA3OCAxNjcuOSA2LjkwMDc4IDE2OC42IDEwLjAwMDhMMTY1LjYgMTAuNzAwOEMxNjUuMiA4LjgwMDc4IDE2NC4yIDguMTAwNzggMTYxLjggOC4xMDA3OEMxNTguOSA4LjEwMDc4IDE1Ny44IDkuMjAwNzggMTU3LjggMTEuNTAwOEMxNTcuOCAxMy44MDA4IDE1OC45IDE0LjkwMDggMTYxLjggMTQuOTAwOEMxNjQuMSAxNC45MDA4IDE2NS4xIDE0LjIwMDggMTY1LjYgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTgwLjI1NyAxNy40TDE3Ni42NTcgMTIuM0wxNzQuMTU3IDE0LjZWMTcuNEgxNzEuMTU3VjAuNUgxNzQuMTU3VjExLjFMMTc5Ljg1NyA1LjZIMTgzLjc1N0wxNzguNTU3IDEwLjVMMTgzLjk1NyAxNy40SDE4MC4yNTdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgMjEuNTAwOEg3LjNWMTYuMzAwOEgwVjIxLjUwMDhaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNy4yOTk4IDIxLjUwMDhIMTQuNTk5OFYxNi4zMDA4SDcuMjk5OFYyMS41MDA4WiIgZmlsbD0iIzZFQkUwMCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE0LjYwMDEgMTYuM0gyOS4yMDAxVjAuNUgxNC42MDAxVjE2LjNaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMCAxMUgxNC42VjAuNUgwVjExWiIgZmlsbD0iIzZFQkUwMCIvPgo8L3N2Zz4K\" alt=\"Patchstack Logo\">\n\t\t\t\t</a>\n </div>\n </div>\n \n <div class=\"mobile-bottom-container\">\n <div class=\"mobile-bottom-text-containers\">\n <img class=\"arrow-vector\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSIxMyIgdmlld0JveD0iMCAwIDggMTMiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03LjQxIDEuNDMzNDRMNiAwLjAyMzQzNzVMMCA2LjAyMzQ0TDYgMTIuMDIzNEw3LjQxIDEwLjYxMzRMMi44MyA2LjAyMzQ0TDcuNDEgMS40MzM0NFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==\" alt=\"Arrow Left\">\n <a href=\"/\" class=\"return-text\">Return to homepage</a>\n </div>\n <div class=\"mobile-bottom-second-containers\">\n <span class=\"secured-text\">This website is secured by</span>\n <a href=\"https://patchstack.com\" class=\"logoText\">\n <img class=\"patch-logo\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg0IiBoZWlnaHQ9IjIyIiB2aWV3Qm94PSIwIDAgMTg0IDIyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUwLjQwMDEgMTEuNTAwOEM1MC40MDAxIDkuMTAwNzggNDkuMzAwMSA4LjEwMDc4IDQ2LjYwMDEgOC4xMDA3OEM0NC4wMDAxIDguMTAwNzggNDIuNzAwMSA5LjIwMDc4IDQyLjcwMDEgMTEuNTAwOEM0Mi43MDAxIDEzLjgwMDggNDQuMDAwMSAxNC45MDA4IDQ2LjYwMDEgMTQuOTAwOEM0OS4zMDAxIDE0LjkwMDggNTAuNDAwMSAxMy44MDA4IDUwLjQwMDEgMTEuNTAwOFpNNTMuNDAwMSAxMS41MDA4QzUzLjQwMDEgMTUuMzAwOCA1MS4zMDAxIDE3LjYwMDggNDcuNDAwMSAxNy42MDA4QzQ1LjQwMDEgMTcuNjAwOCA0My42MDAxIDE2LjUwMDggNDIuNzAwMSAxNC41MDA4VjIxLjUwMDhIMzkuNjAwMVY1LjYwMDc4SDQyLjcwMDFWOC41MDA3OEM0My42MDAxIDYuNTAwNzggNDUuNDAwMSA1LjMwMDc4IDQ3LjUwMDEgNS4zMDA3OEM1MS40MDAxIDUuMzAwNzggNTMuNDAwMSA3LjcwMDc4IDUzLjQwMDEgMTEuNTAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjYuNDAwMSAxMS40OTg0QzY2LjQwMDEgOS4xOTg0NCA2NS4xMDAxIDguMDk4NDQgNjIuNTAwMSA4LjA5ODQ0QzU5LjgwMDEgOC4wOTg0NCA1OC43MDAxIDkuMDk4NDQgNTguNzAwMSAxMS40OTg0QzU4LjcwMDEgMTMuNzk4NCA1OS44MDAxIDE0Ljg5ODQgNjIuNTAwMSAxNC44OTg0QzY1LjEwMDEgMTQuODk4NCA2Ni40MDAxIDEzLjc5ODQgNjYuNDAwMSAxMS40OTg0Wk02Ni4zMDAxIDUuNTk4NDRINjkuNDAwMVYxNy4zOTg0SDY2LjMwMDFWMTQuNDk4NEM2NS40MDAxIDE2LjQ5ODQgNjMuNzAwMSAxNy42OTg0IDYxLjYwMDEgMTcuNjk4NEM1Ny43MDAxIDE3LjY5ODQgNTUuNjAwMSAxNS4yOTg0IDU1LjYwMDEgMTEuNTk4NEM1NS42MDAxIDcuNzk4NDQgNTcuNjAwMSA1LjM5ODQ0IDYxLjQwMDEgNS4zOTg0NEM2My41MDAxIDUuMzk4NDQgNjUuNDAwMSA2LjU5ODQ0IDY2LjIwMDEgOC41OTg0NFY1LjU5ODQ0SDY2LjMwMDFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTgxLjM5OTkgOC4zSDc3LjM5OTlWMTQuN0g4MS4wOTk5VjE3LjRINzcuOTk5OUM3NS43OTk5IDE3LjQgNzQuMjk5OSAxNS45IDc0LjI5OTkgMTMuN1Y4LjNINzEuMzk5OVY1LjZINzQuMjk5OVYySDc3LjM5OTlWNS41SDgxLjM5OTlWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik05My42OTk5IDEyLjIwMDhMOTYuNjk5OSAxMi45MDA4Qzk1Ljk5OTkgMTYuMDAwOCA5My42OTk5IDE3LjYwMDggODkuOTk5OSAxNy42MDA4Qzg3Ljc5OTkgMTcuNjAwOCA4Ni4wOTk5IDE3LjAwMDggODQuNzk5OSAxNS45MDA4QzgzLjQ5OTkgMTQuODAwOCA4Mi44OTk5IDEzLjMwMDggODIuODk5OSAxMS41MDA4QzgyLjg5OTkgOS42MDA3OCA4My40OTk5IDguMTAwNzggODQuNzk5OSA3LjAwMDc4Qzg2LjA5OTkgNS45MDA3OCA4Ny43OTk5IDUuMzAwNzggODkuOTk5OSA1LjMwMDc4QzkzLjY5OTkgNS4zMDA3OCA5NS45OTk5IDYuOTAwNzggOTYuNjk5OSAxMC4wMDA4TDkzLjY5OTkgMTAuNzAwOEM5My4yOTk5IDguODAwNzggOTIuMjk5OSA4LjEwMDc4IDg5Ljg5OTkgOC4xMDA3OEM4Ni45OTk5IDguMTAwNzggODUuODk5OSA5LjIwMDc4IDg1Ljg5OTkgMTEuNTAwOEM4NS44OTk5IDEzLjgwMDggODYuOTk5OSAxNC45MDA4IDg5Ljg5OTkgMTQuOTAwOEM5Mi4yOTk5IDE0LjkwMDggOTMuMjk5OSAxNC4yMDA4IDkzLjY5OTkgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTExLjggMTAuNlYxNy40SDEwOC43VjExLjJDMTA4LjcgOSAxMDcuOCA4LjEgMTA1LjkgOC4xQzEwMy42IDguMSAxMDIuNCA5LjMgMTAyLjQgMTEuN1YxNy4zSDk5LjI5OThWMC41SDEwMi40VjguM0MxMDMuMSA2LjMgMTA0LjYgNS4zIDEwNyA1LjNDMTEwLjIgNS4zIDExMS44IDcuMSAxMTEuOCAxMC42WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMTQuMSAxNC4wOTg0TDExNy4xIDEzLjI5ODRDMTE3LjQgMTQuNzk4NCAxMTguMyAxNS4yOTg0IDEyMC4yIDE1LjI5ODRDMTIxLjggMTUuMjk4NCAxMjIuNyAxNC44OTg0IDEyMi43IDE0LjA5ODRDMTIyLjcgMTMuMjk4NCAxMjIuMiAxMi45OTg0IDEyMC42IDEyLjc5ODRMMTE4LjIgMTIuNDk4NEMxMTUuOSAxMi4xOTg0IDExNC41IDEwLjg5ODQgMTE0LjUgOC45OTg0NEMxMTQuNSA2Ljc5ODQ0IDExNi41IDUuMzk4NDQgMTE5LjggNS4zOTg0NEMxMjMuMSA1LjM5ODQ0IDEyNSA2LjY5ODQ0IDEyNS41IDguODk4NDRMMTIyLjUgOS41OTg0NEMxMjIuMyA4LjA5ODQ0IDEyMS41IDcuNjk4NDQgMTE5LjkgNy42OTg0NEMxMTguNCA3LjY5ODQ0IDExNy42IDguMDk4NDQgMTE3LjYgOC44OTg0NEMxMTcuNiA5LjU5ODQ0IDExOC4xIDkuOTk4NDQgMTE5LjMgMTAuMDk4NEwxMjIuMiAxMC40OTg0QzEyNC40IDEwLjc5ODQgMTI1LjggMTEuOTk4NCAxMjUuOCAxMy44OTg0QzEyNS44IDE2LjI5ODQgMTIzLjcgMTcuNzk4NCAxMjAuMSAxNy43OTg0QzExNi43IDE3LjU5ODQgMTE0LjUgMTYuMTk4NCAxMTQuMSAxNC4wOTg0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMzYuNyA4LjNIMTMyLjdWMTQuN0gxMzYuNFYxNy40SDEzMy4zQzEzMS4xIDE3LjQgMTI5LjYgMTUuOSAxMjkuNiAxMy43VjguM0gxMjYuN1Y1LjZIMTI5LjZWMkgxMzIuN1Y1LjVIMTM2LjdWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNDkuMiAxMS40OTg0QzE0OS4yIDkuMTk4NDQgMTQ3LjkgOC4wOTg0NCAxNDUuMyA4LjA5ODQ0QzE0Mi42IDguMDk4NDQgMTQxLjUgOS4wOTg0NCAxNDEuNSAxMS40OTg0QzE0MS41IDEzLjc5ODQgMTQyLjYgMTQuODk4NCAxNDUuMyAxNC44OTg0QzE0Ny45IDE0Ljg5ODQgMTQ5LjIgMTMuNzk4NCAxNDkuMiAxMS40OTg0Wk0xNDkuMSA1LjU5ODQ0SDE1Mi4yVjE3LjM5ODRIMTQ5LjFWMTQuNDk4NEMxNDguMiAxNi40OTg0IDE0Ni41IDE3LjY5ODQgMTQ0LjQgMTcuNjk4NEMxNDAuNSAxNy42OTg0IDEzOC40IDE1LjI5ODQgMTM4LjQgMTEuNTk4NEMxMzguNCA3Ljc5ODQ0IDE0MC40IDUuMzk4NDQgMTQ0LjIgNS4zOTg0NEMxNDYuMyA1LjM5ODQ0IDE0OC4yIDYuNTk4NDQgMTQ5IDguNTk4NDRWNS41OTg0NEgxNDkuMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTY1LjYgMTIuMjAwOEwxNjguNiAxMi45MDA4QzE2Ny45IDE2LjAwMDggMTY1LjYgMTcuNjAwOCAxNjEuOSAxNy42MDA4QzE1OS43IDE3LjYwMDggMTU4IDE3LjAwMDggMTU2LjcgMTUuOTAwOEMxNTUuNCAxNC44MDA4IDE1NC44IDEzLjMwMDggMTU0LjggMTEuNTAwOEMxNTQuOCA5LjYwMDc4IDE1NS40IDguMTAwNzggMTU2LjcgNy4wMDA3OEMxNTggNS45MDA3OCAxNTkuNyA1LjMwMDc4IDE2MS45IDUuMzAwNzhDMTY1LjYgNS4zMDA3OCAxNjcuOSA2LjkwMDc4IDE2OC42IDEwLjAwMDhMMTY1LjYgMTAuNzAwOEMxNjUuMiA4LjgwMDc4IDE2NC4yIDguMTAwNzggMTYxLjggOC4xMDA3OEMxNTguOSA4LjEwMDc4IDE1Ny44IDkuMjAwNzggMTU3LjggMTEuNTAwOEMxNTcuOCAxMy44MDA4IDE1OC45IDE0LjkwMDggMTYxLjggMTQuOTAwOEMxNjQuMSAxNC45MDA4IDE2NS4xIDE0LjIwMDggMTY1LjYgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTgwLjI1NyAxNy40TDE3Ni42NTcgMTIuM0wxNzQuMTU3IDE0LjZWMTcuNEgxNzEuMTU3VjAuNUgxNzQuMTU3VjExLjFMMTc5Ljg1NyA1LjZIMTgzLjc1N0wxNzguNTU3IDEwLjVMMTgzLjk1NyAxNy40SDE4MC4yNTdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgMjEuNTAwOEg3LjNWMTYuMzAwOEgwVjIxLjUwMDhaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNy4yOTk4IDIxLjUwMDhIMTQuNTk5OFYxNi4zMDA4SDcuMjk5OFYyMS41MDA4WiIgZmlsbD0iIzZFQkUwMCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE0LjYwMDEgMTYuM0gyOS4yMDAxVjAuNUgxNC42MDAxVjE2LjNaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMCAxMUgxNC42VjAuNUgwVjExWiIgZmlsbD0iIzZFQkUwMCIvPgo8L3N2Zz4K\" alt=\"Patchstack Logo\">\n\t\t\t\t</a>\n </div>\n </div>\n </div>\n</body>\n<script>\n function copyText(id) {\n var copyText = document.getElementById(id)\n copyText.select();\n copyText.setSelectionRange(0, 99999);\n navigator.clipboard.writeText(copyText.value);\n }\n\n function hover(element) {\n element.setAttribute('src', 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==');\n }\n\n function unhover(element) {\n element.setAttribute('src', 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=');\n }\n</script>\n</html>`;\n}\n","// Rule cache as a TIERED store of a `{ bundle, etag }` envelope:\n// 1. memory — always present; last-known-good within the process. Survives refreshes and is\n// the fallback when the disk isn't writable (read-only FS, sandbox).\n// 2. durable — filesystem (default, via `cacheDir`) OR a pluggable adapter (`ruleCache`, e.g.\n// a KV store for filesystem-less runtimes). Survives process restarts.\n// read: memory → durable → null. write: memory + best-effort durable. Everything is fail-open —\n// a read/write error yields \"no cache\" rather than throwing.\n//\n// Node's fs/path are loaded LAZILY (dynamic import), never as a static top-level import: this module\n// is part of the WinterCG/edge-safe graph (Next edge middleware, Workers, Deno, Supabase Functions),\n// where a static `node:fs` import fails to resolve at build/load time and would take the whole guard\n// down. On those runtimes the disk tier simply reports \"no cache\" and the memory tier (or a pluggable\n// `ruleCache` adapter) carries last-known-good.\n\nlet fsMod; // memoized { readFileSync, writeFileSync, mkdirSync, join } | null (unavailable)\nasync function loadFs() {\n if (fsMod !== undefined) return fsMod;\n try {\n const [fs, path] = await Promise.all([import('node:fs'), import('node:path')]);\n fsMod = {\n readFileSync: fs.readFileSync,\n writeFileSync: fs.writeFileSync,\n mkdirSync: fs.mkdirSync,\n join: path.join,\n };\n } catch {\n fsMod = null; // no filesystem here (edge runtime) — memory/adapter tiers still work\n }\n return fsMod;\n}\n\nexport function makeStore(options = {}) {\n let mem = null;\n const durable = durableTier(options);\n return {\n async read() {\n if (mem) return mem;\n const env = await durable.read();\n if (env) mem = env;\n return env;\n },\n async write(env) {\n if (env) mem = env;\n await durable.write(env);\n },\n };\n}\n\n// The durable tier: a caller-supplied adapter wins; otherwise a disk cache under `cacheDir`; if\n// neither is configured, a no-op (memory-only — fine for a long-lived process).\nfunction durableTier(options) {\n const adapter = options.ruleCache;\n if (adapter && typeof adapter.read === 'function' && typeof adapter.write === 'function') {\n return {\n read: async () => {\n try {\n return toEnvelope(await adapter.read());\n } catch {\n return null;\n }\n },\n write: async (env) => {\n try {\n await adapter.write(env);\n } catch {\n /* best-effort */\n }\n },\n };\n }\n const dir = options.cacheDir;\n return {\n read: async () => cacheRead(dir),\n write: async (env) => cacheWrite(dir, env),\n };\n}\n\nasync function cacheWrite(dir, env) {\n if (!dir) return;\n const fs = await loadFs();\n if (!fs) return; // filesystem-less runtime — memory tier only\n try {\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(fs.join(dir, 'patchstack-rules.json'), JSON.stringify(env));\n } catch {\n /* cache is best-effort — the memory tier still holds last-known-good */\n }\n}\n\nasync function cacheRead(dir) {\n if (!dir) return null;\n const fs = await loadFs();\n if (!fs) return null;\n try {\n return toEnvelope(JSON.parse(fs.readFileSync(fs.join(dir, 'patchstack-rules.json'), 'utf8')));\n } catch {\n return null;\n }\n}\n\n// Accept the current { bundle, etag } envelope and a legacy bare bundle (pre-envelope cache files).\nexport function toEnvelope(value) {\n if (!value || typeof value !== 'object') return null;\n if (value.bundle && typeof value.bundle === 'object') {\n return { bundle: value.bundle, etag: value.etag ?? null };\n }\n if (Array.isArray(value.firewall) || Array.isArray(value.whitelists)) {\n return { bundle: value, etag: null }; // legacy bare-bundle cache file\n }\n return null;\n}\n","// Rule-bundle validation. Delivered rules are POLICY fetched over the network, and the engine executes\n// them on every request — so an upstream compromise, a schema drift, or a mistake in the corpus could\n// otherwise hand the app unbounded work (a 50k-rule bundle, a 500-deep condition tree, a pathological\n// regex) or silently unenforceable junk.\n//\n// Two principles:\n// 1. REJECT, don't silently skip. A rule that fails validation is dropped WITH a reported reason, so\n// an unenforceable rule is visible instead of quietly protecting nothing.\n// 2. Bound everything the engine will walk: rule count, conditions per rule, nesting depth, and\n// pattern length. Caps are deliberately far above any real corpus rule.\n//\n// Fail-open in spirit: validation never throws, and a bundle whose rules are all rejected simply means\n// \"no rules\" (the app keeps serving) — never a crash.\n\nexport const LIMITS = {\n maxRules: 5000,\n maxWhitelists: 2000,\n maxConditionsPerRule: 250,\n maxNestingDepth: 12,\n maxRegexLength: 1000,\n maxValueLength: 8192,\n};\n\nconst PHASES = new Set(['request', 'response', 'egress']);\nconst ACTIONS = new Set(['block', 'redact', 'encode', 'set-header', 'remove-header', 'harden-cookie']);\n\n/**\n * Validate a delivered bundle. Returns `{ bundle, rejected }` where `bundle` contains only rules that\n * passed and `rejected` is `[{ id, reason }]` for everything dropped.\n * @param {object} bundle\n * @returns {{ bundle: object, rejected: Array<{id: string, reason: string}> }}\n */\nexport function validateBundle(bundle, opts = {}) {\n const rejected = [];\n const inFirewall = Array.isArray(bundle?.firewall) ? bundle.firewall : [];\n const inWhitelists = Array.isArray(bundle?.whitelists) ? bundle.whitelists : [];\n\n const firewall = [];\n for (const rule of inFirewall) {\n if (firewall.length >= LIMITS.maxRules) {\n rejected.push({ id: idOf(rule), reason: `bundle exceeds maxRules (${LIMITS.maxRules})` });\n continue;\n }\n const reason = ruleProblem(rule);\n if (reason) rejected.push({ id: idOf(rule), reason });\n else firewall.push(rule);\n }\n\n const whitelists = [];\n for (const wl of inWhitelists) {\n if (whitelists.length >= LIMITS.maxWhitelists) {\n rejected.push({ id: idOf(wl), reason: `bundle exceeds maxWhitelists (${LIMITS.maxWhitelists})` });\n continue;\n }\n // A whitelist SUPPRESSES rules, so a malformed one is a protection risk, not a detection risk.\n // One with no `rule_id` applies to EVERY rule — a single tripped condition disables the whole\n // firewall for that request — so it must be opted into explicitly.\n if (!opts.allowGlobalWhitelists && wl && Array.isArray(wl.rule_v2) && !wl.rule_id) {\n rejected.push({ id: idOf(wl), reason: 'whitelist has no rule_id (would suppress every rule); set allowGlobalWhitelists to permit' });\n continue;\n }\n const reason = conditionsProblem(wl?.rule_v2);\n if (reason) rejected.push({ id: idOf(wl), reason: `whitelist: ${reason}` });\n else whitelists.push(wl);\n }\n\n return {\n bundle: { ...bundle, firewall, whitelists },\n rejected,\n };\n}\n\nfunction idOf(rule) {\n const id = rule?.id ?? rule?.rule_id;\n return id === undefined || id === null ? '(unidentified)' : String(id);\n}\n\n/** @returns {string|null} a reason the rule must be dropped, or null when it's acceptable. */\nfunction ruleProblem(rule) {\n if (!rule || typeof rule !== 'object') return 'not an object';\n if (rule.phase !== undefined && !PHASES.has(rule.phase)) return `unknown phase \"${rule.phase}\"`;\n if (rule.action !== undefined && !ACTIONS.has(rule.action)) return `unknown action \"${rule.action}\"`;\n const capOverride = rule.max_bytes;\n if (capOverride !== undefined && !(Number(capOverride) > 0)) return 'max_bytes must be a positive number';\n return conditionsProblem(rule.rule_v2);\n}\n\nfunction conditionsProblem(conditions, depth = 0) {\n if (!Array.isArray(conditions)) return 'rule_v2 must be an array of conditions';\n if (conditions.length === 0) return 'rule_v2 is empty (would never match)';\n if (depth > LIMITS.maxNestingDepth) return `nesting deeper than ${LIMITS.maxNestingDepth}`;\n if (conditions.length > LIMITS.maxConditionsPerRule) {\n return `more than ${LIMITS.maxConditionsPerRule} conditions`;\n }\n for (const c of conditions) {\n if (!c || typeof c !== 'object') return 'condition is not an object';\n if (Array.isArray(c.rules)) {\n const nested = conditionsProblem(c.rules, depth + 1);\n if (nested) return nested;\n continue; // a group carries no match of its own\n }\n const m = c.match;\n if (!m || typeof m !== 'object') return 'condition has no match object';\n if (typeof m.type !== 'string' || m.type === '') return 'match.type must be a non-empty string';\n if (m.type === 'regex') {\n if (typeof m.value !== 'string') return 'regex match.value must be a string';\n if (m.value.length > LIMITS.maxRegexLength) return `regex longer than ${LIMITS.maxRegexLength} chars`;\n } else if (typeof m.value === 'string' && m.value.length > LIMITS.maxValueLength) {\n return `match.value longer than ${LIMITS.maxValueLength} chars`;\n }\n if (m.match) {\n // array_key_value nests a sub-match; count it toward depth so a chain can't be unbounded.\n const nested = conditionsProblem([{ match: m.match }], depth + 1);\n if (nested) return nested;\n }\n }\n return null;\n}\n","// Rule source: pick where the ruleset comes from — an explicit bundle, or the live API by site\n// UUID (Pulse) or token — fetch it (conditional/If-None-Match via the persisted etag), and fall\n// back through last-known-good → bundled → empty. Fail-open: a fetch/parse error never throws.\n// The `store` (see ./store.js) is passed in so a refresh reuses the same tiered cache.\nimport { PatchstackRuleClient } from '../engine/index.js';\nimport { PulseRuleClient } from '../engine/pulse-client.js';\nimport { validateBundle } from './validate.js';\nimport { notify } from '../notify.js';\n\n// A LIVE update is accepted ATOMICALLY. Dropping individual invalid rules is fine for a bundle we\n// already trust (a cache entry, a bundled fallback), but for a fresh remote response it would let a\n// broken update REPLACE known-good policy with partial or empty policy — turning \"we validated it\" into\n// a loss of protection, and caching that loss. So: if any rule/whitelist fails validation, reject the\n// whole update, keep last-known-good, report it, and do NOT write the cache. Opt in to the old\n// behaviour with `acceptPartialBundle: true` (metrics still report every drop).\nfunction liveUpdateRejections(res, options) {\n if (options.acceptPartialBundle) return [];\n const { rejected } = validateBundle(\n { firewall: Array.isArray(res.firewall) ? res.firewall : [], whitelists: Array.isArray(res.whitelists) ? res.whitelists : [] },\n { allowGlobalWhitelists: options.allowGlobalWhitelists },\n );\n return rejected;\n}\n\nfunction reportRejections(rejected, options, label) {\n const report = options.onRuleRejected;\n for (const r of rejected) {\n if (typeof report === 'function') {\n notify(report, { ...r, accepted: false }, 'onRuleRejected');\n }\n }\n const sample = rejected.slice(0, 3).map((r) => `${r.id} (${r.reason})`).join('; ');\n notify(options.onError, new Error(\n `${label}: rejected the entire update because ${rejected.length} rule(s) failed validation — ` +\n `keeping the previous ruleset and NOT caching this response: ${sample}${rejected.length > 3 ? ', …' : ''}`,\n ), 'onError');\n}\n\nexport async function resolveRules(options, store, ctx = {}) {\n // The INITIAL load is on the app's startup path, so the runtime gives it a short budget (see\n // bootTimeoutMs) and falls back to cache/bundled rather than delaying boot; refreshes get the full\n // budget. A timeout here is not a protection gap by itself — last-known-good still applies.\n const timeoutMs = ctx.timeoutMs;\n if (options.siteUuid) {\n const prior = await store.read(); // { bundle, etag } | null\n const client = new PulseRuleClient({ siteUuid: options.siteUuid, baseUrl: options.pulseRulesUrl, etag: prior?.etag, timeoutMs, pulseAuth: ctx.pulseAuth, reportsDetections: options.reportDetections === true });\n const res = await client.getRules();\n if (res.success && res.notModified && prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (res.success && !res.notModified) {\n const rejected = liveUpdateRejections(res, options);\n if (rejected.length > 0) {\n reportRejections(rejected, options, 'rule update rejected');\n if (prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (options.rules) return normalizeBundle(options.rules, options);\n return emptyBundle();\n }\n const bundle = normalizeBundle(res, options);\n await store.write({ bundle, etag: res.etag ?? null });\n return bundle;\n }\n if (prior?.bundle) {\n notify(options.onError, new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); using cached bundle`), 'onError');\n return normalizeBundle(prior.bundle, options);\n }\n if (options.rules) {\n notify(options.onError, new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); using bundled fallback`), 'onError');\n return normalizeBundle(options.rules, options);\n }\n notify(options.onError, new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); no cache — running with no rules`), 'onError');\n return emptyBundle();\n }\n\n if (options.token) {\n const prior = await store.read();\n const client = new PatchstackRuleClient({ token: options.token, baseUrl: options.baseUrl, etag: prior?.etag, timeoutMs });\n const res = await client.getRules();\n if (res.success && res.notModified && prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (res.success && !res.notModified) {\n const rejected = liveUpdateRejections(res, options);\n if (rejected.length > 0) {\n reportRejections(rejected, options, 'rule update rejected');\n if (prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (options.rules) return normalizeBundle(options.rules, options);\n return emptyBundle();\n }\n const bundle = normalizeBundle(res, options);\n await store.write({ bundle, etag: res.etag ?? null });\n return bundle;\n }\n if (prior?.bundle) {\n notify(options.onError, new Error(`rule fetch failed (${res.error ?? 'no usable response'}); using cached bundle`), 'onError');\n return normalizeBundle(prior.bundle, options);\n }\n notify(options.onError, new Error(`rule fetch failed (${res.error ?? 'no usable response'}); no cache — running with no rules`), 'onError');\n return emptyBundle();\n }\n\n if (options.rules) {\n return normalizeBundle(options.rules, options);\n }\n\n return emptyBundle();\n}\n\n// Every rule path (live fetch, cache, bundled fallback) funnels through here, so this is where the\n// delivered policy is VALIDATED before the engine ever executes it: bounded rule count / conditions /\n// nesting / pattern length, known phases + actions. A rule that fails is dropped with a reported reason\n// (`onRuleRejected`) rather than silently kept — an unenforceable rule must never look enforced.\nexport function normalizeBundle(b, options = {}) {\n const enforcement = b?.enforcement ?? b?.mode;\n const { bundle: checked, rejected } = validateBundle(\n {\n firewall: Array.isArray(b.firewall) ? b.firewall : [],\n whitelists: Array.isArray(b.whitelists) ? b.whitelists : [],\n },\n { allowGlobalWhitelists: options.allowGlobalWhitelists },\n );\n if (rejected.length > 0) {\n const report = options.onRuleRejected;\n if (typeof report === 'function') {\n for (const r of rejected) {\n notify(report, r, 'onRuleRejected');\n }\n } else {\n const sample = rejected.slice(0, 3).map((r) => `${r.id} (${r.reason})`).join('; ');\n // eslint-disable-next-line no-console\n console.warn(\n `[patchstack] ${rejected.length} delivered rule(s) rejected as invalid/oversized and are NOT enforced: ${sample}` +\n (rejected.length > 3 ? ', …' : ''),\n );\n }\n }\n return {\n firewall: checked.firewall,\n whitelists: checked.whitelists,\n whitelist_keys: b.whitelist_keys ?? {},\n ...(enforcement === 'block' || enforcement === 'dry-run' ? { enforcement } : {}),\n };\n}\n\nexport function emptyBundle() {\n return { firewall: [], whitelists: [], whitelist_keys: {} };\n}\n","// Rule refresh scheduling — how a long-lived guard picks up rules that become relevant after boot\n// (a dependency added mid-session, a zero-day published). Two triggers, both driving the same\n// caller-supplied `tick` (which re-fetches + hot-swaps the engines):\n// - startRefresh: a self-scheduling poll LOOP (reschedules after each tick settles, so runs never\n// overlap), with ±jitter (avoid a thundering herd) and exponential backoff on consecutive\n// failures. `unref`'d — it never keeps the process alive.\n// - makeRefreshHandler: a PUSH endpoint — an authenticated fetch handler the platform/SaaS hits\n// for an immediate refresh (zero-day fast lane) instead of waiting for the next poll.\n\nimport { notify } from '../notify.js';\n\nconst JITTER_FRACTION = 0.1;\nconst MAX_BACKOFF_MULTIPLIER = 8; // cap consecutive-failure backoff at 8× the base interval\n\nexport function startRefresh(tick, { refreshMs, onError } = {}) {\n let stopped = false;\n let failures = 0;\n let timer = null;\n\n const schedule = () => {\n if (stopped) return;\n const backoff = Math.min(2 ** failures, MAX_BACKOFF_MULTIPLIER);\n const jitter = 1 - Math.random() * JITTER_FRACTION; // shorten by up to 10% so clients don't align\n const delay = refreshMs * backoff * jitter;\n timer = setTimeout(run, delay);\n timer?.unref?.();\n };\n\n const run = async () => {\n if (stopped) return;\n try {\n await tick();\n failures = 0;\n } catch (err) {\n failures++;\n notify(onError, err, 'onError');\n }\n schedule();\n };\n\n schedule();\n return {\n stop() {\n stopped = true;\n if (timer) clearTimeout(timer);\n },\n };\n}\n\nexport function makeRefreshHandler(tick, secret) {\n return async (request) => {\n // No secret configured → the endpoint doesn't exist (never an open refresh-DoS surface).\n if (!secret) return new Response('not found', { status: 404 });\n let provided = null;\n try {\n provided = request?.headers?.get?.('x-patchstack-refresh') ?? new URL(request.url).searchParams.get('token');\n } catch {\n provided = null;\n }\n if (provided !== secret) return new Response('forbidden', { status: 403 });\n let refreshed = true;\n try {\n await tick();\n } catch {\n refreshed = false; // fail-open: report the outcome, never throw\n }\n return new Response(JSON.stringify({ refreshed }), { status: 200, headers: { 'content-type': 'application/json' } });\n };\n}\n","import { pulseAuthHeader } from '../pulse-token.js';\nimport { isSafeOrigin } from './safe-origin.js';\n\n/**\n * Detection reporter: every rule that fired, whether or not it blocked.\n *\n * Separate from `firewall-log.js`, which posts ENFORCED blocks in the WordPress-compatible shape\n * (`fid`, `request_uri`, `ip`, `user_agent`). That path answers \"what did we stop\". This one answers a\n * question nothing else could: **what would this rule have stopped**, for a rule that carries\n * `enforcement: dry-run` and therefore blocks nothing.\n *\n * Without it, a rule that is quietly wrong and a rule that is protecting look identical from the\n * outside, because neither produces a block to report.\n *\n * ## The payload is deliberately small\n *\n * `rule_id`, route PATH, the parameters the rule reads, a timestamp, whether it was enforced, the phase,\n * and the bundle identity. That is enough to count hits per rule, compare them against traffic, and\n * decide whether a rule is wrong.\n *\n * What it never carries: **the matched value, the request body, headers, or query-string values**. A\n * channel that counts detections is a different thing from a copy of an application's traffic, and once\n * values are collected every question about retention, access and jurisdiction arrives with them.\n * Anything value-level belongs behind its own explicit opt-in with its own controls, not as a side\n * effect of counting.\n *\n * The route is the request PATH with any query string dropped, because `?token=…` is a value.\n */\n\nconst DEFAULT_BASE_URL = 'https://api.patchstack.com/monitor/pulse';\nconst DEFAULT_FLUSH_MS = 5000;\nconst MAX_BATCH = 50;\n/** Bounded so a detection storm costs memory it cannot grow out of. Oldest go first. */\nconst MAX_QUEUE = 500;\n\n/**\n * The parameters a rule reads, from its own definition.\n *\n * NOT \"the condition that matched\": the engine reports a rule, not which of its conditions fired, and\n * threading that out would mean changing evaluation for the sake of a reporting field. A narrowly scoped\n * rule reads exactly one parameter, so the two answers coincide there; for a broad rule this is the set\n * it reads, which is what the field name says.\n *\n * @param {any} rule\n * @returns {string[]}\n */\nexport function ruleParameters(rule) {\n const out = new Set();\n const walk = (conditions) => {\n if (!Array.isArray(conditions)) return;\n for (const condition of conditions) {\n if (!condition || typeof condition !== 'object') continue;\n if (typeof condition.parameter === 'string' && condition.parameter !== 'rules') {\n out.add(condition.parameter);\n }\n if (Array.isArray(condition.rules)) walk(condition.rules);\n }\n };\n walk(rule?.rule_v2);\n\n return [...out];\n}\n\n/**\n * The request path with the query string removed.\n *\n * A path is a route; a query string is data. `/api/preview?url=http://169.254.169.254/` names both the\n * endpoint and the attack payload, and only the first belongs in a counting channel.\n *\n * @param {unknown} path\n * @returns {string | null}\n */\nexport function routeOf(path) {\n if (typeof path !== 'string' || path === '') return null;\n const cut = path.search(/[?#]/);\n\n return cut === -1 ? path : path.slice(0, cut);\n}\n\n/**\n * @param {{\n * siteUuid?: string,\n * baseUrl?: string,\n * pulseAuth?: unknown,\n * rulesEtag?: string | null,\n * fetchImpl?: typeof fetch,\n * flushMs?: number,\n * maxQueue?: number,\n * }} opts\n */\nexport function createDetectionReporter(opts) {\n const siteUuid = opts.siteUuid ?? process.env?.PATCHSTACK_SITE_UUID;\n if (!siteUuid) {\n // Nothing to report against. A no-op rather than a throw: reporting is never worth failing a boot.\n return { record() {}, flush() {}, stop() {}, dropped: () => 0 };\n }\n\n const configured = opts.baseUrl ?? process.env?.PATCHSTACK_PULSE_RULES_URL;\n const baseUrl = typeof configured === 'string' && isSafeOrigin(configured)\n ? configured.replace(/\\/$/, '')\n : DEFAULT_BASE_URL;\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n const flushMs = Number.isFinite(opts.flushMs) && opts.flushMs > 0 ? opts.flushMs : DEFAULT_FLUSH_MS;\n const maxQueue = Number.isFinite(opts.maxQueue) && opts.maxQueue > 0 ? opts.maxQueue : MAX_QUEUE;\n\n /** @type {Array<Record<string, unknown>>} */\n let queue = [];\n /** @type {ReturnType<typeof setTimeout> | null} */\n let timer = null;\n let stopped = false;\n let dropped = 0;\n\n const flush = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (queue.length === 0 || typeof fetchImpl !== 'function') return;\n\n const batch = queue.splice(0, MAX_BATCH);\n // The count of what never made it, sent WITH the batch rather than inferred from a gap: a consumer\n // computing a false-positive rate needs to know its denominator is short, and silence about that\n // would make a truncated sample look like a complete one.\n const droppedWith = dropped;\n dropped = 0;\n\n void (async () => {\n try {\n const res = await fetchImpl(`${baseUrl}/detections/${encodeURIComponent(siteUuid)}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n // Same credential path as the rules fetch, and unauthenticated when none resolves: the\n // server accepts the UUID, and reporting must never hinge on getting a token.\n ...(await pulseAuthHeader({ pulseAuth: opts.pulseAuth, endpoint: baseUrl }, fetchImpl)),\n },\n body: JSON.stringify({ detections: batch, dropped: droppedWith }),\n });\n // Fail-open and silent: a rejected or unreachable endpoint must not disturb the app, and must\n // not retry into a loop either. The next flush carries whatever arrives next.\n if (res && typeof res.then === 'function') res.catch(() => {});\n } catch {\n /* ignore */\n }\n })();\n };\n\n return {\n /**\n * @param {{\n * rule?: { id?: string, rule_v2?: unknown },\n * phase?: string,\n * mode?: string,\n * path?: string,\n * }} detection\n */\n record(detection) {\n if (stopped) return;\n const ruleId = detection?.rule?.id;\n if (ruleId === undefined || ruleId === null || ruleId === '') return;\n\n if (queue.length >= maxQueue) {\n queue.shift();\n dropped++;\n }\n\n queue.push({\n rule_id: ruleId,\n route: routeOf(detection.path),\n parameters: ruleParameters(detection.rule),\n phase: detection.phase ?? null,\n // The state this detection was handled under, which is the whole point: `false` is a rule that\n // saw traffic it would have stopped.\n enforced: detection.mode === 'block',\n rules_etag: opts.rulesEtag ?? null,\n detected_at: new Date().toISOString(),\n });\n\n if (queue.length >= MAX_BATCH) {\n flush();\n\n return;\n }\n if (!timer) timer = setTimeout(flush, flushMs);\n },\n flush,\n stop() {\n stopped = true;\n flush();\n },\n dropped: () => dropped,\n };\n}\n","import { isSafeOrigin } from './safe-origin.js';\n// Fire-and-forget reporter: Connect runtime → existing connector POST /api/logs/log\n// (same path WordPress uses). Auth: WP-style api_key (`{secret}-{oauth.id}`) →\n// POST /oauth/token (client_credentials) → Bearer JWT on /api/logs/log.\n// Opt out: PATCHSTACK_TELEMETRY=off. Never put api_key in the public widget.\n\nconst DEFAULT_API_BASE = 'https://api.patchstack.com';\nconst DEFAULT_FLUSH_MS = 1000;\nconst MAX_BATCH = 50;\nconst TOKEN_SKEW_MS = 60_000;\n\n/**\n * Parse WP plugin api_key (`{secret}-{oauth.id}`) into client credentials.\n * @param {string} apiKey\n * @returns {{ clientId: string, clientSecret: string } | null}\n */\nexport function parseApiKey(apiKey) {\n if (typeof apiKey !== 'string' || apiKey.length === 0) return null;\n const idx = apiKey.lastIndexOf('-');\n if (idx <= 0 || idx === apiKey.length - 1) return null;\n const clientSecret = apiKey.slice(0, idx);\n const clientId = apiKey.slice(idx + 1);\n if (!/^\\d+$/.test(clientId) || clientSecret.length === 0) return null;\n return { clientId, clientSecret };\n}\n\n/**\n * Derive api.patchstack.com origin from a Pulse manifest/rules URL override.\n * @param {string | undefined} pulseOrManifestUrl\n */\nexport function resolveApiBase(pulseOrManifestUrl) {\n const fromEnv = typeof process !== 'undefined' ? process.env?.PATCHSTACK_API_BASE : undefined;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) {\n // The site api_key is exchanged for a token against this origin, so a hostile/injected env value\n // would be a credential-exfiltration path. Require HTTPS (localhost excepted for local testing);\n // anything else falls back to the default origin rather than shipping the key off-platform.\n const candidate = fromEnv.replace(/\\/$/, '');\n if (isSafeOrigin(candidate)) return candidate;\n // eslint-disable-next-line no-console\n console.warn(\n '[patchstack] ignoring PATCHSTACK_API_BASE: block-log reporting requires an https origin ' +\n '(or localhost). Falling back to the default API origin.',\n );\n }\n if (typeof pulseOrManifestUrl === 'string' && pulseOrManifestUrl.length > 0) {\n try {\n return new URL(pulseOrManifestUrl).origin;\n } catch {\n /* fall through */\n }\n }\n const endpoint = typeof process !== 'undefined' ? process.env?.PATCHSTACK_ENDPOINT : undefined;\n if (typeof endpoint === 'string' && endpoint.length > 0) {\n try {\n return new URL(endpoint).origin;\n } catch {\n /* fall through */\n }\n }\n return DEFAULT_API_BASE;\n}\n\n/**\n * @param {{\n * apiKey: string,\n * apiBase?: string,\n * sourceHost?: string,\n * fetchImpl?: typeof fetch,\n * flushMs?: number,\n * }} opts\n */\nexport function createFirewallLogReporter(opts) {\n const creds = parseApiKey(opts.apiKey);\n if (!creds) {\n return { record() {}, flush() {}, stop() {} };\n }\n\n const apiBase = (opts.apiBase ?? DEFAULT_API_BASE).replace(/\\/$/, '');\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n const flushMs = Number.isFinite(opts.flushMs) ? opts.flushMs : DEFAULT_FLUSH_MS;\n const sourceHost = typeof opts.sourceHost === 'string' ? opts.sourceHost : '';\n\n /** @type {Array<Record<string, unknown>>} */\n let queue = [];\n /** @type {ReturnType<typeof setTimeout> | null} */\n let timer = null;\n let stopped = false;\n\n /** @type {{ token: string, expiresAt: number } | null} */\n let cachedToken = null;\n /** @type {Promise<string | null> | null} */\n let tokenInflight = null;\n\n const fetchAccessToken = async () => {\n if (cachedToken && Date.now() < cachedToken.expiresAt - TOKEN_SKEW_MS) {\n return cachedToken.token;\n }\n if (tokenInflight) return tokenInflight;\n\n tokenInflight = (async () => {\n try {\n const res = await fetchImpl(`${apiBase}/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n body: JSON.stringify({\n grant_type: 'client_credentials',\n client_id: creds.clientId,\n client_secret: creds.clientSecret,\n }),\n });\n if (!res || !res.ok) return null;\n const body = await res.json();\n const token = body?.access_token;\n if (typeof token !== 'string' || token.length === 0) return null;\n const expiresIn = Number(body?.expires_in);\n const ttlMs = Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn * 1000 : 3600_000;\n cachedToken = { token, expiresAt: Date.now() + ttlMs };\n return token;\n } catch {\n return null;\n } finally {\n tokenInflight = null;\n }\n })();\n\n return tokenInflight;\n };\n\n const flush = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (queue.length === 0 || typeof fetchImpl !== 'function') return;\n\n const batch = queue.splice(0, MAX_BATCH);\n void (async () => {\n const token = await fetchAccessToken();\n if (!token) return;\n\n const body = new URLSearchParams();\n body.set('type', 'firewall');\n body.set('logs', JSON.stringify(batch));\n\n try {\n const p = fetchImpl(`${apiBase}/api/logs/log`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'User-Agent': '@patchstack/connect',\n ...(sourceHost ? { 'Source-Host': sourceHost } : {}),\n },\n body,\n });\n if (p && typeof p.then === 'function') p.catch(() => {});\n } catch {\n /* ignore */\n }\n })();\n };\n\n return {\n /**\n * @param {{\n * rule?: { id?: string | number },\n * method?: string | null,\n * path?: string | null,\n * ip?: string | null,\n * userAgent?: string | null,\n * }} event\n */\n record(event) {\n if (stopped) return;\n const fid = event?.rule?.id;\n if (fid === undefined || fid === null || fid === '') return;\n\n queue.push({\n fid,\n method: event.method ?? null,\n request_uri: event.path ?? null,\n ip: event.ip ?? null,\n user_agent: event.userAgent ?? null,\n log_date: new Date().toISOString(),\n });\n\n if (queue.length >= MAX_BATCH) {\n flush();\n return;\n }\n if (!timer) timer = setTimeout(flush, flushMs);\n },\n flush,\n stop() {\n stopped = true;\n flush();\n },\n };\n}\n\n/** @returns {boolean} */\nexport function telemetryEnabled() {\n const v = typeof process !== 'undefined' ? process.env?.PATCHSTACK_TELEMETRY : undefined;\n if (v === undefined || v === '') return true;\n return !/^(0|false|off|no)$/i.test(String(v));\n}\n","// Supabase-tunnel guard for AI-builder apps (Lovable / TanStack Start + Supabase).\n//\n// The vibe-coded app's browser talks straight to Supabase, bypassing the app's own server —\n// so a normal in-app WAF never sees the data traffic. The installer patches the generated\n// Supabase client to tunnel every call through this guard (running in the app's own server /\n// Worker). The guard runs the Patchstack protection policy on the tunneled request, then\n// forwards it to Supabase — pinned to the app's own project so it can't be turned into an\n// open proxy (SSRF).\n//\n// The heavy lifting (rule evaluation, dry-run/block, fail-open) is `protection.fetchGuard()`\n// from `createProtection` — this module is just the Supabase-specific tunnel around it.\n\nexport const GUARD_PATH = '/_patchstack/guard';\n\n// Headers we must not copy verbatim when re-emitting the upstream response.\nconst HOP_BY_HOP = new Set(['content-encoding', 'content-length', 'transfer-encoding', 'connection']);\n\n/**\n * @param {object} opts\n * @param {{ fetchGuard: () => (req: Request) => Promise<Response|null> }} opts.protection a createProtection() result\n * @param {string|undefined} opts.supabaseUrl the app's Supabase project URL (server-side env) — the only allowed forward target\n * @param {typeof fetch} [opts.fetchImpl] injectable fetch (tests)\n * @returns {(request: Request) => Promise<Response>}\n */\nexport function createSupabaseGuard({ protection, supabaseUrl, fetchImpl = fetch }) {\n const guard = protection.fetchGuard();\n const allowedOrigin = supabaseUrl ? new URL(supabaseUrl).origin : null;\n\n return async function handleGuardRequest(request) {\n const target = request.headers.get('x-ps-target');\n if (!target) return new Response('patchstack: missing x-ps-target', { status: 400 });\n\n let targetUrl;\n try {\n targetUrl = new URL(target);\n } catch {\n return new Response('patchstack: invalid target', { status: 400 });\n }\n\n // SSRF pin: only ever forward to the app's own Supabase project (origin from server-side\n // env, never the client-supplied header). Anything else — internal hosts, cloud metadata,\n // a different scheme — is rejected before any outbound request.\n if (!allowedOrigin || targetUrl.protocol !== 'https:' || targetUrl.origin !== allowedOrigin) {\n return new Response('patchstack: target not allowed', { status: 403 });\n }\n\n const hasBody = request.method !== 'GET' && request.method !== 'HEAD';\n const bodyText = hasBody ? await request.text() : '';\n\n // Evaluate the tunneled call against the policy. fetchGuard returns a 403 Response when it\n // blocks (block mode + match), or null to allow (allow, or dry-run — it records via onDetect).\n const evalReq = new Request(targetUrl.toString(), {\n method: request.method,\n headers: request.headers,\n body: hasBody ? bodyText : undefined,\n });\n const blocked = await guard(evalReq);\n if (blocked) return blocked;\n\n // Allowed → forward to Supabase, server-side.\n const forwardHeaders = new Headers(request.headers);\n forwardHeaders.delete('x-ps-target');\n forwardHeaders.delete('host');\n const upstream = await fetchImpl(targetUrl.toString(), {\n method: request.method,\n headers: forwardHeaders,\n body: hasBody ? bodyText : undefined,\n redirect: 'manual',\n });\n\n const outHeaders = new Headers();\n upstream.headers.forEach((value, key) => {\n if (!HOP_BY_HOP.has(key.toLowerCase())) outHeaders.set(key, value);\n });\n const buf = await upstream.arrayBuffer();\n const forwarded = new Response(buf, { status: upstream.status, statusText: upstream.statusText, headers: outHeaders });\n\n // Response phase: screen what Supabase returned (query results can leak secrets/PII) —\n // redact the offending spans / withhold, per the response rules. Fail-open if unavailable.\n return protection.screenResponse ? protection.screenResponse(forwarded) : forwarded;\n };\n}\n","// @patchstack/protect — \"Protect = respond\".\n//\n// One entry point that composes the node-waf engine + adapters with:\n// - a rule source: an explicit bundle, or fetched from the Patchstack API (token),\n// with a disk cache so the engine keeps working on last-known-good if the API is down\n// - execution modes: 'dry-run' (detect + log, never block — the safe onramp) and 'block' (enforce).\n// This API's default is 'dry-run'. NOTE the scaffolded guard (`patchstack-connect protect`)\n// deliberately passes mode: 'block' and only drops to dry-run when PATCHSTACK_MODE=dry-run — so an\n// installed guard ENFORCES by default even though this constructor's default doesn't. Precedence:\n// PATCHSTACK_MODE env > API `enforcement` > options.mode > dry-run.\n// - fail-open everywhere: a rule/engine error never blocks (or crashes) a request. Where the guard\n// fails open *without* inspecting (body caps, live streams, binary bodies, resolver failures) it\n// is counted and reported — see `protection.coverage()` / the `onSkip` option.\n//\n// Runtime guards: .express(), .node(), .fetch(handler) / .fetchGuard() — same policy,\n// every runtime an AI builder deploys to.\n// Vendored node-waf engine (this package is self-contained — no @patchstack/node-waf dep).\nimport { RuleEngine } from './engine/index.js';\nimport { matchValue, walkLeaves, safeRegExp } from './engine/engine.js';\nimport { PulseRuleClient } from './engine/pulse-client.js';\nimport { fromFetchRequest } from './engine/fetch.js';\nimport { fromNodeRequest } from './engine/node.js';\nimport { installEgressGuard } from './egress.js';\nimport { DEFAULT_RESPONSE_RULES, DEFAULT_EGRESS_RULES } from './defaults.js';\nimport { renderBlockPage } from './block-page.js';\n// Rule lifecycle (source / tiered store / refresh) lives in ./rules/ — this file stays focused on\n// composing the engine + guards and running the three screening phases.\nimport { makeStore } from './rules/store.js';\nimport { resolveRules } from './rules/source.js';\nimport { startRefresh, makeRefreshHandler } from './rules/refresh.js';\nimport { createDetectionReporter } from './detections.js';\nimport { notify } from './notify.js';\nimport { createFirewallLogReporter, resolveApiBase, telemetryEnabled } from './firewall-log.js';\n\n// Supabase-tunnel guard for AI-builder apps (Lovable / TanStack Start + Supabase).\nexport { createSupabaseGuard, GUARD_PATH } from './supabase-guard.js';\n\n// Per-site live rule client (Pulse). Re-exported for callers/tests that want to use it directly.\nexport { PulseRuleClient };\n\n// Server-function guard. Modern Lovable apps mutate data through TanStack server functions\n// (browser → server fn → server-side Supabase client), which bypass the browser-side tunnel the\n// Supabase guard relies on. This inspects the decoded server-fn call args against the SAME policy\n// by feeding them through fetchGuard (the args become the request body, so the engine resolves\n// `post.<field>` exactly as it does for a tunneled Supabase insert). Returns a block receipt\n// { rule?, message } to throw on, or null to allow (also null in dry-run — the detection is still\n// recorded by fetchGuard). Fail-open on any error.\nexport function createServerFnGuard({ protection }) {\n const guard = protection.fetchGuard();\n return async (data) => {\n let res;\n try {\n const req = new Request('https://patchstack.local/_serverfn', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(data ?? {}),\n });\n res = await guard(req);\n } catch {\n return null; // fail open\n }\n if (!res) return null; // allowed (or dry-run)\n let body = {};\n try {\n body = await res.clone().json();\n } catch {\n /* non-JSON block response */\n }\n return { rule: body.rule, message: body.message || 'Blocked by Patchstack' };\n };\n}\n\nexport async function createProtection(options = {}) {\n const onError = options.onError;\n const userOnDetect = options.onDetect ?? defaultOnDetect;\n\n // Report enforced blocks via existing connector POST /api/logs/log (WP path).\n // Needs api_key from provision / PATCHSTACK_API_KEY / .patchstackrc.json.\n // Opt out: PATCHSTACK_TELEMETRY=off. Never embed api_key in the public widget.\n const apiKey = await resolveApiKey(options);\n const firewallLog =\n apiKey && telemetryEnabled() && options.reportFirewallLog !== false\n ? createFirewallLogReporter({\n apiKey,\n apiBase: resolveApiBase(options.pulseRulesUrl ?? options.baseUrl),\n sourceHost: options.sourceHost,\n fetchImpl: options.fetchImpl,\n })\n : null;\n\n // Every detection, enforced or not, to the Pulse detections endpoint. Distinct from the block log\n // above: that records what was STOPPED, in the WordPress-compatible shape; this records what a rule\n // WOULD have stopped, which is otherwise unobservable for a rule carrying `enforcement: dry-run`.\n // Minimal payload by design; see `detections.js`.\n let detections = null;\n\n const onDetect = (detection) => {\n notify(userOnDetect, detection, 'onDetect');\n if (detections) detections.record(detection);\n if (firewallLog && detection?.mode === 'block') {\n firewallLog.record({\n rule: detection.rule,\n method: detection.method,\n path: detection.path,\n ip: detection.ip,\n userAgent: detection.userAgent,\n });\n }\n };\n\n // One tiered store (memory → filesystem/pluggable) shared by the initial load and every refresh.\n const store = makeStore(options);\n // Startup must not hang on the network: hosted platforms (Replit et al.) fail a deploy whose health\n // check is slow, and the guard can always boot from last-known-good / the bundled fallback. Refreshes\n // keep the full budget. Override with { bootTimeoutMs }.\n const bootTimeoutMs = Number(options.bootTimeoutMs) > 0 ? Number(options.bootTimeoutMs) : 5_000;\n // Resolved once and threaded through ctx: reading it is a filesystem hit on\n // runtimes that have one, and refreshes should not repeat it.\n const pulseAuth = await resolvePulseAuth(options);\n // A site UUID with no credential behind it, said out loud ONCE at boot.\n //\n // Resolution reads `.patchstackrc.json`, so it needs a filesystem and a working directory. The\n // runtimes this guard is built for do not all have one: on a Worker or an edge function the file is\n // absent and only `PATCHSTACK_PULSE_AUTH` / `PATCHSTACK_API_KEY` can carry the credential.\n //\n // Unauthenticated rule fetches are accepted today, so the failure is currently invisible — and it\n // stays invisible once they are not, because a rejected fetch fails open onto the cached or bundled\n // bundle. The guard then screens every request, reports healthy, and never receives another rule.\n // That silence is the whole problem: an app protected by rules frozen at install time looks exactly\n // like an app protected by current ones.\n //\n // A warning, not a throw. Booting is protection; refusing to boot over a missing credential would\n // trade a stale rule set for no rule set at all.\n if (options.siteUuid && !pulseAuth) {\n const message =\n 'Patchstack: no API credential resolved for site ' +\n options.siteUuid +\n '. Rule updates may be rejected and this guard would keep running on its cached rules. ' +\n 'Set PATCHSTACK_API_KEY (or pass { pulseAuth }) — required on runtimes without a filesystem.';\n notify(onError, new Error(message), 'onError');\n console.warn(message);\n }\n const bundle = await resolveRules(options, store, { timeoutMs: bootTimeoutMs, pulseAuth });\n // OPT-IN, deliberately. Two reasons, and the first is not about privacy: switching it on adds an\n // outbound POST to every guard that has a site UUID, which is a change in what an installed app does\n // on the network — the kind of thing that must be disclosed in the shipped docs before it is a default,\n // not after. The second is that the default belongs to whoever owns that disclosure, so the capability\n // lands here and the flip is a separate, deliberate change.\n if (options.reportDetections === true && options.siteUuid && telemetryEnabled()) {\n detections = createDetectionReporter({\n siteUuid: options.siteUuid,\n baseUrl: options.pulseRulesUrl,\n pulseAuth,\n // The bundle the guard is actually running, so a hit can be attributed to the rules that produced\n // it rather than to whatever is current when the report is read.\n rulesEtag: (await store.read())?.etag ?? null,\n fetchImpl: options.fetchImpl,\n flushMs: options.detectionFlushMs,\n });\n }\n // Mode is mutable so a Pulse refresh can flip dry-run ↔ block when SaaS enables production.\n // Precedence: PATCHSTACK_MODE env (local override) > API enforcement > options.mode > dry-run.\n let mode = resolveMode(options, bundle);\n // Rule-derived runtime state. Held in `let` bindings the guard methods below close over, so a\n // refresh (see the loop near the end) can hot-swap the engines by reassigning them — the\n // egress interception and the protection object itself stay in place, no re-install.\n let requestRules;\n let responseRules;\n let egressRules;\n let screenCap; // max body we'll buffer/screen (rules can raise it)\n let engine;\n let responseRuleSet;\n let egressEngine;\n\n // Split the delivered ruleset by phase (default \"request\"), merging phase defaults +\n // per-call overrides. Detection is fully rule-driven — nothing hardcoded.\n const applyBundle = (delivered) => {\n const incoming = delivered.firewall ?? [];\n requestRules = byPhase(incoming, 'request');\n responseRules = [...(options.responseRules ?? DEFAULT_RESPONSE_RULES), ...byPhase(incoming, 'response')];\n screenCap = responseScreenCap(responseRules);\n egressRules = [...(options.egressRules ?? DEFAULT_EGRESS_RULES), ...byPhase(incoming, 'egress')];\n engine = new RuleEngine({\n firewall: requestRules,\n whitelists: delivered.whitelists,\n whitelist_keys: delivered.whitelist_keys,\n onError,\n });\n // One engine per response rule so we can find ALL matches (to redact each). `action:\n // \"redact\"` masks the offending span(s); anything else withholds the whole response.\n responseRuleSet = responseRules.map((rule) => ({\n rule,\n engine: new RuleEngine({ firewall: [rule], onError }),\n redactors: rule.action === 'redact' || rule.action === 'encode' ? extractRedactors(rule) : null,\n // A redact/encode condition that carries body-transforming mutations (base64_decode, urldecode,\n // json_decode, …) detects on the DECODED body but the span redactors run on the RAW body — so\n // they mask nothing and the secret is served while the log says \"redacted\". Flag it so screenText\n // fails such a rule CLOSED (block) instead of serving a no-op redaction.\n mutatedSpan: (rule.action === 'redact' || rule.action === 'encode') && hasSpanMutations(rule),\n // Optional cheap pre-filter: literal anchor(s) that MUST appear for the (expensive) regex to\n // have any chance of matching. Lets screenText skip the full scan on bodies with no candidate —\n // the common case — cutting CPU/latency and shrinking the regex/ReDoS surface. Case-insensitive.\n prefilter: Array.isArray(rule.prefilter) && rule.prefilter.length\n ? rule.prefilter.map((s) => String(s).toLowerCase())\n : null,\n }));\n egressEngine = new RuleEngine({ firewall: egressRules, onError });\n };\n\n applyBundle(bundle);\n\n // Fail-open COVERAGE. The guard deliberately passes traffic through rather than risk breaking the\n // app: an oversized request body, a response past the screening cap, a live stream, a binary body, a\n // parse failure, a DNS resolver failure. Each of those is a real hole in enforcement, and until now\n // it was SILENT — \"always-on\" read as \"always inspected\". Every such bypass is now counted and\n // reported to `onSkip`, so a host can alert on it and `protection.coverage()` can be surfaced.\n // `onSkip` is a TRUSTED SERVER callback: `detail` carries operational context (sizes, statuses,\n // outbound hostnames) for logging/alerting. Do not forward it to a client response.\n const skipCounts = Object.create(null);\n const onSkip = typeof options.onSkip === 'function' ? options.onSkip : null;\n const recordSkip = (phase, reason, detail) => {\n const key = `${phase}:${reason}`;\n skipCounts[key] = (skipCounts[key] ?? 0) + 1;\n // A reporting callback must never affect request handling — including an async one, whose rejection\n // lands after a try/catch here would have returned.\n notify(onSkip, { phase, reason, detail, count: skipCounts[key] }, 'onSkip');\n };\n\n const maskFn =\n typeof options.maskWith === 'function'\n ? options.maskWith\n : () => (typeof options.maskWith === 'string' ? options.maskWith : '[REDACTED]');\n\n // Given a request/egress result, enforce (block mode) or just record (dry-run).\n //\n // A rule may carry its own `enforcement: 'dry-run'`, which wins over block mode for that rule alone.\n // Auto-generated rules arrive that way: their coordinate comes from best-effort static analysis, so they\n // are served to detect until a probe or a human has justified them, WITHOUT holding back the\n // hand-authored rules on the same site. A rule with no `enforcement` follows the bundle exactly as\n // before, so an older server that never sends the field behaves identically.\n const ruleMode = (rule) => (rule?.enforcement === 'dry-run' ? 'dry-run' : mode);\n\n const decide = (phase, result, block, allow, ctx = {}) => {\n if (!result || !result.blocked) return allow();\n const effectiveMode = ruleMode(result.rule);\n onDetect({\n phase,\n // The mode this detection was actually handled under, not the site's: a consumer counting blocks\n // would otherwise over-report, and the whole point of a dry-run rule is that it did not block.\n mode: effectiveMode,\n category: result.rule?.category,\n rule: result.rule,\n message: result.message,\n method: ctx.method,\n path: ctx.path,\n ip: ctx.ip,\n userAgent: ctx.userAgent,\n });\n return effectiveMode === 'block' ? block() : allow();\n };\n\n // Response phase core: screen a text body → { verdict: 'pass'|'block'|'redact', body? }.\n // redact masks matched spans; block withholds; block wins over redact. Enforcement only in\n // block mode (dry-run records via onDetect but returns 'pass').\n const screenText = (text, meta, reqCtx) => {\n let blockRule = null;\n const redactions = [];\n const headerMutations = [];\n let lowerText = null; // lazily lowercased body, only if a rule uses a prefilter\n for (const { rule, engine: re, redactors, prefilter, mutatedSpan } of responseRuleSet) {\n // Cheap pre-filter: if none of the rule's literal anchors is in the body, its regex can't\n // match — skip the full scan (the common no-secret case) before touching the engine.\n if (prefilter) {\n if (lowerText === null) lowerText = text.toLowerCase();\n if (!prefilter.some((p) => lowerText.includes(p))) continue;\n }\n let result;\n try {\n // Spread the originating request (method / originalUrl / headers) alongside the response,\n // so a response rule's `when` route/method scope resolves against the REAL request and so\n // request Host/Origin are visible to response rules — rather than the phantom empty request\n // the response phase used to build (which made `when` on a response rule inert).\n result = re.evaluate({ ...(reqCtx || {}), _response: { ...meta, body: text } });\n } catch (err) {\n notify(onError, err, 'onError');\n continue;\n }\n if (!result.blocked) continue;\n // Per-rule enforcement applies to every phase, not just the request. A generated response rule in\n // dry-run must not redact or withhold a body either: \"detect until justified\" is meaningless if the\n // rule still rewrites what the user sees.\n const responseMode = ruleMode(rule);\n onDetect({ phase: 'response', mode: responseMode, category: rule.category, rule, message: result.message });\n if (responseMode !== 'block') continue; // dry-run: observe only\n if (redactors && redactors.length) {\n // Span redactors on a mutation-decoded rule can't map back to the raw body → fail closed.\n const spanRedactors = redactors.filter((r) => !r.jsonPath);\n if (mutatedSpan && spanRedactors.length) {\n if (!blockRule) blockRule = rule;\n } else {\n redactions.push({ rule, redactors });\n }\n } else if (isHeaderMutation(rule.action)) headerMutations.push(rule);\n else if (!blockRule) blockRule = rule;\n }\n if (mode !== 'block' || (!blockRule && !redactions.length && !headerMutations.length)) return { verdict: 'pass' };\n if (blockRule) return { verdict: 'block' };\n let body = text;\n // Redact the offending spans in the body AND in every (string) header value — so a secret\n // that leaks in a header (Set-Cookie, an echoed X-Api-Key, …) is masked too, and a rule that\n // targets `response.header.*` actually strips the header rather than just detecting it.\n const headers = { ...(meta.headers || {}) };\n for (const { rule, redactors } of redactions) {\n const mask = maskFn(rule.category);\n // action `encode` HTML-escapes the matched value in place (neutralize stored XSS at output);\n // `redact` masks it. jsonPath redactors act on a structural JSON location, span redactors on\n // text spans in the body AND header values. Apply structural first (on clean JSON), then spans.\n const transform = rule.action === 'encode' ? htmlEscape : null;\n const pathRedactors = redactors.filter((r) => r.jsonPath);\n const spanRedactors = redactors.filter((r) => !r.jsonPath);\n if (pathRedactors.length) body = applyPathRedactors(body, pathRedactors, mask, screenCap, transform);\n if (!spanRedactors.length) continue;\n body = applyRedactors(body, spanRedactors, mask, transform);\n if (transform) continue; // encoding is a body/output concern — headers aren't HTML\n for (const name of Object.keys(headers)) {\n const value = headers[name];\n if (typeof value === 'string') {\n headers[name] = applyRedactors(value, spanRedactors, mask);\n } else if (Array.isArray(value)) {\n // Multi-valued headers (Set-Cookie) — redact each entry.\n headers[name] = value.map((item) => (typeof item === 'string' ? applyRedactors(item, spanRedactors, mask) : item));\n }\n }\n }\n for (const rule of headerMutations) applyHeaderMutation(headers, rule);\n return { verdict: 'redact', body, headers };\n };\n\n // Minimal request context for the response phase: what a response rule's `when` scope and any\n // request-header reference (Host/Origin) need — method, path, and request headers. No body.\n const reqContextFromFetch = (request) => {\n try {\n const u = new URL(request.url);\n const headers = headerObject(request.headers);\n // A fetch Request doesn't expose the Host header (it's set at send time), so derive it from the\n // URL — response rules that compare origins (open-redirect / CORS) need the request Host.\n if (!headers.host) headers.host = u.host;\n return { method: request.method, originalUrl: u.pathname + u.search, headers };\n } catch {\n return undefined;\n }\n };\n const reqContextFromNode = (req) => (req ? { method: req.method, originalUrl: req.url, headers: req.headers || {} } : undefined);\n\n // Screen a fetch Response (used by .fetch() and — via protection.screenResponse — the Supabase guard).\n const screenResp = async (response, reqCtx) => {\n const read = await readTextResponse(response, screenCap);\n if (read.skip) {\n // Nothing was screened — a leak/PII rule cannot have applied. Record it (a live stream and a\n // binary body are by design; a body-cap or read failure is a coverage hole worth alerting on).\n if (read.skip !== 'not-a-response') recordSkip('response', read.skip, { status: response?.status });\n return response;\n }\n const text = read.text;\n const r = screenText(text, { status: response.status, headers: headerObject(response.headers) }, reqCtx);\n if (r.verdict === 'block') return leakResponse();\n if (r.verdict === 'redact') return rebuildResponse(response, r.body, r.headers);\n return response;\n };\n\n // Wrap a Node ServerResponse so its (buffered, text) body is screened before it's sent.\n // Opt-in (buffering can delay a streamed response); over 512 KiB it stops buffering and\n // passes through unscanned.\n const wrapNodeResponse = (res, reqCtx) => {\n const origWrite = res.write.bind(res);\n const origEnd = res.end.bind(res);\n const chunks = [];\n let size = 0;\n let overflow = false;\n const MAX = screenCap;\n const collect = (chunk, enc) => {\n if (chunk == null) return;\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, typeof enc === 'string' ? enc : 'utf8');\n size += buf.length;\n if (size > MAX) {\n // Too big to screen — abandon buffering, but FLUSH what we already captured (the head) plus\n // this chunk before switching to pass-through, so the client gets a complete body (not a\n // truncated one missing everything before the cap was hit).\n for (const c of chunks) origWrite(c);\n chunks.length = 0;\n origWrite(buf);\n overflow = true;\n recordSkip('response', 'body-cap', { bytes: size });\n return;\n }\n chunks.push(buf);\n };\n res.write = function (chunk, enc, cb) {\n if (overflow) return origWrite(chunk, enc, cb);\n collect(chunk, enc);\n if (typeof enc === 'function') enc();\n else if (typeof cb === 'function') cb();\n return true;\n };\n res.end = function (chunk, enc, cb) {\n if (typeof chunk === 'function') { cb = chunk; chunk = undefined; enc = undefined; }\n else if (typeof enc === 'function') { cb = enc; enc = undefined; }\n if (overflow) { if (chunk != null) origWrite(chunk, enc); return origEnd(cb); }\n collect(chunk, enc);\n if (overflow) return origEnd(cb); // collect just flushed head + final chunk on overflow\n const buffer = Buffer.concat(chunks);\n let ct = res.getHeader ? res.getHeader('content-type') : undefined;\n if (Array.isArray(ct)) ct = ct[0];\n const kind = screenableContentType(ct);\n // Skip live streams / binary bodies (incl. an octet-stream that sniffs as binary) — untouched.\n if (kind === 'skip' || (kind === 'sniff' && looksBinary(buffer))) {\n recordSkip('response', kind === 'skip' ? (baseContentType(ct) === 'text/event-stream' ? 'live-stream' : 'non-text-content-type') : 'binary-body');\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n }\n const text = buffer.toString('utf8');\n let r;\n try {\n r = screenText(text, { status: res.statusCode, headers: res.getHeaders ? res.getHeaders() : {} }, reqCtx);\n } catch (err) {\n notify(onError, err, 'onError');\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n }\n if (r.verdict === 'block') {\n res.statusCode = 500;\n try { res.setHeader('content-type', 'application/json'); } catch { /* headers sent */ }\n return origEnd(JSON.stringify({ error: 'Response withheld by Patchstack (sensitive data detected)' }), cb);\n }\n if (r.verdict === 'redact') {\n try { res.removeHeader && res.removeHeader('content-length'); } catch { /* ignore */ }\n if (r.headers && res.setHeader) {\n const current = res.getHeaders ? res.getHeaders() : {};\n for (const [name, value] of Object.entries(r.headers)) {\n // content-length was just removed (the redacted body has a new length); never re-set a\n // stale one here or the response truncates/hangs.\n if (name.toLowerCase() === 'content-length') continue;\n if (value === null || value === undefined) {\n try { res.removeHeader && res.removeHeader(name); } catch { /* ignore */ } // header-mutation removal\n } else if (Array.isArray(value)) {\n try { res.setHeader(name, value); } catch { /* ignore invalid header */ } // Set-Cookie array\n } else if (typeof value === 'string' && current[name] !== value) {\n try { res.setHeader(name, value); } catch { /* ignore invalid header */ }\n }\n }\n }\n return origEnd(r.body, cb);\n }\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n };\n };\n\n // Egress phase: is this outbound call blocked? (records detection either way)\n const allow = new Set((options.allowHosts ?? []).map((h) => String(h).toLowerCase()));\n const egressShouldBlock = (url, host, method) => {\n if (host && allow.has(host.toLowerCase())) return false;\n let result;\n try {\n result = egressEngine.evaluate({ _egress: { url, host, method } });\n } catch (err) {\n notify(onError, err, 'onError');\n return false;\n }\n if (!result.blocked) return false;\n // Same for egress: a dry-run rule records the outbound attempt without preventing it. Blocking a\n // request the app makes is at least as disruptive as blocking one it receives.\n const egressMode = ruleMode(result.rule);\n onDetect({ phase: 'egress', mode: egressMode, category: result.rule?.category, rule: result.rule, message: result.message });\n return egressMode === 'block';\n };\n\n const protection = {\n get mode() {\n return mode;\n },\n get rules() {\n return { request: requestRules, response: responseRules, egress: egressRules };\n },\n\n /**\n * Enforcement coverage: how often the guard FAILED OPEN rather than inspecting, keyed\n * `<phase>:<reason>` (e.g. `response:body-cap`, `request:body-cap`, `response:live-stream`,\n * `egress:resolver-failed`). \"Always-on\" is not \"always inspected\" — surface this (or pass\n * `onSkip`) so an unscreened path is visible and alertable rather than silent.\n */\n coverage() {\n return { skipped: { ...skipCounts } };\n },\n\n // Screen a fetch Response through the response-phase rules (redact/block). Used by\n // .fetch(), and by the Supabase guard on its forwarded upstream response.\n screenResponse: (response, request) => screenResp(response, request ? reqContextFromFetch(request) : undefined),\n\n // (request) => Response | null (null = allow, caller proceeds). Request phase only.\n fetchGuard() {\n return async (request) => {\n let result;\n try {\n result = engine.evaluate(await fromFetchRequest(request));\n } catch (err) {\n notify(onError, err, 'onError');\n return null; // fail open\n }\n return decide('request', result, () => blockResponse(result, request), () => null, fetchRequestMeta(request));\n };\n },\n\n // Wrap a fetch handler: screens the request, then the response (redact/block).\n fetch(handler) {\n const guard = protection.fetchGuard();\n return async (request, ...rest) => {\n const blocked = await guard(request);\n if (blocked) return blocked;\n const response = await handler(request, ...rest);\n return screenResp(response, reqContextFromFetch(request));\n };\n },\n\n // Express middleware (request phase; expects express-parsed req.query/req.body).\n // Pass { screenResponses: true } to also screen the outgoing response (buffers it).\n express(exprOptions = {}) {\n return (req, res, next) => {\n let result;\n try {\n result = engine.evaluate(req);\n } catch (err) {\n notify(onError, err, 'onError');\n if (exprOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n return next();\n }\n decide(\n 'request',\n result,\n () => {\n if (isDocumentNavigation((n) => req.headers?.[n])) {\n res.status(403).type('html').send(renderBlockPage({ url: req.originalUrl || req.url || '/', code: result?.rule?.id }));\n } else {\n res.status(403).json(blockBody(result));\n }\n },\n () => {\n if (exprOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n next();\n },\n nodeRequestMeta(req),\n );\n };\n },\n\n // Node / Connect middleware — buffers the body itself (request phase).\n // Pass { screenResponses: true } to also screen the outgoing response (buffers it).\n node(nodeOptions = {}) {\n const maxBytes = nodeOptions.maxBodyBytes ?? 1024 * 1024;\n return (req, res, next) => {\n const chunks = [];\n let size = 0;\n let overflow = false;\n req.on('data', (chunk) => {\n size += chunk.length;\n if (size > maxBytes) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n req.on('error', (err) => {\n notify(onError, err, 'onError');\n next();\n });\n req.on('end', () => {\n if (overflow) recordSkip('request', 'body-cap', { bytes: size, limit: maxBytes });\n const rawBody = overflow ? '' : Buffer.concat(chunks).toString('utf8');\n let shaped;\n let result;\n try {\n shaped = fromNodeRequest(req, rawBody);\n result = engine.evaluate(shaped);\n } catch (err) {\n notify(onError, err, 'onError');\n return next();\n }\n decide(\n 'request',\n result,\n () => {\n res.statusCode = 403;\n if (isDocumentNavigation((n) => req.headers?.[n])) {\n res.setHeader('content-type', 'text/html; charset=utf-8');\n res.end(renderBlockPage({ url: req.url || '/', code: result?.rule?.id }));\n } else {\n res.setHeader('content-type', 'application/json');\n res.end(JSON.stringify(blockBody(result)));\n }\n },\n () => {\n // This guard consumed the request stream to screen it; re-expose the parsed\n // body so a downstream handler (without its own body-parser) can read it.\n if (req.body === undefined) req.body = shaped.body;\n if (nodeOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n next();\n },\n nodeRequestMeta(req),\n );\n });\n };\n },\n };\n\n // Egress interception is opt-in (it wraps the global fetch, and node:http/https on Node).\n if (options.egress) {\n protection.uninstallEgress = await installEgressGuard({\n shouldBlock: egressShouldBlock,\n onBlock: options.onEgressBlock,\n // Route egress coverage gaps (a DNS resolver failure / no resolver on this runtime) into the\n // same skip accounting as the request/response phases.\n onSkip: ({ reason, detail }) => recordSkip('egress', reason, detail),\n dnsScreen: options.screenDns !== false,\n allowHosts: options.allowHosts,\n });\n }\n\n // Live rule refresh. The guard otherwise reads its rules once, at process start — so a rule that\n // only becomes relevant after boot (a dependency added mid-session and flagged by Pulse, a\n // zero-day published) never applies until the process restarts. A refresh re-fetches and\n // hot-swaps the engines in place; the same tick can be driven by a poll loop (`refreshMs`), a\n // manual `protection.refresh()`, or an authenticated push (`protection.refreshHandler()`).\n const cwd = options.cwd ?? (typeof process !== 'undefined' ? process.cwd() : undefined);\n const live = Boolean(options.siteUuid || options.token);\n const refreshSecret = options.refreshSecret ?? (typeof process !== 'undefined' ? process.env.PATCHSTACK_REFRESH_SECRET : undefined);\n const refreshable = live && (options.refreshMs > 0 || Boolean(refreshSecret));\n\n // On the Pulse (siteUuid) path, re-post the dependency manifest before re-fetching. A targeted\n // `npm install <pkg>` fires no npm lifecycle hook, so nothing else re-scans; reporting here lets\n // the server flag a newly-added vulnerable dependency and the SAME tick's rule fetch pick up its\n // rule. Loaded once, up front, only when a refresh path is enabled — a refresh-off production\n // guard never pulls in the scan pipeline; a load/report failure never blocks the rule refresh.\n let reporter = null;\n if (refreshable && options.siteUuid && options.reportManifest !== false && cwd) {\n try {\n ({ reportManifest: reporter } = await import('./refresh-manifest.js'));\n } catch (err) {\n notify(onError, err, 'onError'); // scan pipeline unavailable (e.g. an edge runtime) — rules still refresh\n }\n }\n\n const runRefreshTick = async () => {\n if (reporter) {\n try {\n await reporter(cwd);\n } catch (err) {\n notify(onError, err, 'onError'); // a failed report must not stop the rule refresh\n }\n }\n const next = await resolveRules(options, store, { timeoutMs: options.refreshTimeoutMs, pulseAuth });\n mode = resolveMode(options, next);\n applyBundle(next);\n };\n\n if (live) {\n // Manual one-shot refresh (also the primitive the loop + push endpoint run).\n protection.refresh = () => runRefreshTick();\n // Authenticated push endpoint — the platform/SaaS hits it for an immediate refresh. No secret\n // configured → the handler 404s (never an open refresh trigger).\n protection.refreshHandler = () => makeRefreshHandler(runRefreshTick, refreshSecret);\n }\n\n if (options.refreshMs > 0 && live) {\n const loop = startRefresh(runRefreshTick, { refreshMs: options.refreshMs, onError });\n protection.stopRefresh = () => {\n loop.stop();\n firewallLog?.stop();\n detections?.stop();\n };\n } else if (firewallLog) {\n protection.stopRefresh = () => {\n firewallLog.stop();\n detections?.stop();\n };\n }\n\n return protection;\n}\n\n/**\n * Resolve runtime enforcement mode.\n * Precedence: PATCHSTACK_MODE env > Pulse `enforcement` on the rules bundle > options.mode > dry-run.\n */\nfunction resolveMode(options, bundle) {\n const env = typeof process !== 'undefined' ? process.env?.PATCHSTACK_MODE : undefined;\n if (env === 'block' || env === 'dry-run') return env;\n if (bundle?.enforcement === 'block' || bundle?.enforcement === 'dry-run') return bundle.enforcement;\n if (options?.mode === 'block') return 'block';\n if (options?.mode === 'dry-run') return 'dry-run';\n return 'dry-run';\n}\n\n/**\n * WP-format api_key for connector /api/logs/log. Never use the public site UUID.\n * The `.patchstackrc.json` fallback reads the filesystem, so fs/path are imported LAZILY — this\n * module must stay loadable on edge runtimes (Next edge middleware, Workers, Deno, Supabase\n * Functions), where a static `node:fs` import fails to resolve and would take the guard down.\n */\nasync function resolveApiKey(options) {\n if (typeof options?.apiKey === 'string' && options.apiKey.length > 0) return options.apiKey;\n if (typeof process !== 'undefined') {\n const fromEnv = process.env?.PATCHSTACK_API_KEY;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) return fromEnv;\n }\n try {\n if (typeof process === 'undefined' || typeof process.cwd !== 'function') return undefined;\n const [{ readFileSync }, { join }] = await Promise.all([import('node:fs'), import('node:path')]);\n const cwd = options?.cwd ?? process.cwd();\n const raw = readFileSync(join(cwd, '.patchstackrc.json'), 'utf8');\n const key = JSON.parse(raw)?.apiKey;\n if (typeof key === 'string' && key.length > 0) return key;\n } catch {\n /* missing, or no filesystem on this runtime — reporting stays off */\n }\n return undefined;\n}\n\n/**\n * Credential for the authenticated rules lookup (ADR-0018). Same resolution\n * order and the same edge-runtime caution as resolveApiKey, and falls back to\n * it so guards installed before pulseAuth existed keep authenticating.\n *\n * Returning undefined is fine: the rules fetch then goes out unauthenticated,\n * which the server still accepts.\n */\nasync function resolvePulseAuth(options) {\n if (typeof options?.pulseAuth === 'string' && options.pulseAuth.length > 0) return options.pulseAuth;\n if (typeof process !== 'undefined') {\n const fromEnv = process.env?.PATCHSTACK_PULSE_AUTH;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) return fromEnv;\n }\n try {\n if (typeof process === 'undefined' || typeof process.cwd !== 'function') return resolveApiKey(options);\n const [{ readFileSync }, { join }] = await Promise.all([import('node:fs'), import('node:path')]);\n const cwd = options?.cwd ?? process.cwd();\n const raw = readFileSync(join(cwd, '.patchstackrc.json'), 'utf8');\n const key = JSON.parse(raw)?.pulseAuth;\n if (typeof key === 'string' && key.length > 0) return key;\n } catch {\n /* missing, or no filesystem here — fall through to the apiKey path */\n }\n\n return resolveApiKey(options);\n}\n\n// --- phase / response helpers -------------------------------------------\n\nfunction byPhase(rules, phase) {\n return (rules ?? []).filter((r) => (r.phase ?? 'request') === phase);\n}\n\n// Classify a content-type for response screening: 'text' = screen; 'sniff' = screen only if the\n// bytes aren't binary (octet-stream is often a misdeclared JSON export/config); 'skip' = pass\n// through unscreened (live streams, known binary families). SSE is matched on the EXACT base type,\n// not a loose substring — `application/json; profile=\"event-stream\"` is not a stream.\nfunction baseContentType(ct) {\n return String(ct || '').toLowerCase().split(';')[0].trim();\n}\nfunction screenableContentType(ct) {\n const base = baseContentType(ct);\n if (base === 'text/event-stream') return 'skip'; // live token/SSE stream — never buffer\n if (base === '') return 'text';\n if (/(json|text|xml|html|javascript|csv|yaml|x-www-form-urlencoded)/.test(base)) return 'text';\n if (base === 'application/octet-stream') return 'sniff'; // maybe a text/JSON export mislabeled\n return 'skip'; // image/video/audio/font/pdf/zip/wasm/… — don't buffer binary\n}\n// Cheap binary sniff over a byte prefix: a NUL byte, or many control chars, means \"don't treat as text\".\nfunction looksBinary(bytes) {\n const n = Math.min(bytes.length, 512);\n let ctrl = 0;\n for (let i = 0; i < n; i++) {\n const b = bytes[i];\n if (b === 0) return true;\n if (b < 9 || (b > 13 && b < 32)) ctrl++;\n }\n return n > 0 && ctrl / n > 0.1;\n}\n\n// Returns { text } when the body was fully buffered for screening, or { skip: <reason> } when it was\n// NOT screened — the reason is surfaced to `onSkip`/coverage so a fail-open bypass is observable\n// instead of silent (an unscreened response is a real hole in enforcement).\nasync function readTextResponse(response, cap = DEFAULT_SCREEN_CAP) {\n if (!response || typeof response.clone !== 'function') return { skip: 'not-a-response' };\n const ct = response.headers?.get?.('content-type') || '';\n const kind = screenableContentType(ct);\n if (kind === 'skip') return { skip: baseContentType(ct) === 'text/event-stream' ? 'live-stream' : 'non-text-content-type' };\n const sniff = kind === 'sniff';\n const len = Number(response.headers?.get?.('content-length') || 0);\n if (len && len > cap) return { skip: 'body-cap' };\n let clone;\n try {\n clone = response.clone();\n } catch {\n return { skip: 'clone-failed' };\n }\n\n // Stream the read so a body WITHOUT a Content-Length can't buffer past the cap. Over the cap the\n // response is left UNSCREENED — but we keep draining the clone so the original stays intact.\n const body = clone.body;\n if (body && typeof body.getReader === 'function') {\n const reader = body.getReader();\n const chunks = [];\n let size = 0;\n let over = false;\n let sniffed = !sniff;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n if (!sniffed) {\n sniffed = true;\n if (looksBinary(value)) return { skip: 'binary-body' };\n }\n size += value.byteLength;\n if (over) continue; // keep draining, stop buffering\n if (size > cap) { over = true; continue; }\n chunks.push(value);\n }\n } catch {\n return { skip: 'read-failed' };\n }\n if (over) return { skip: 'body-cap' };\n try {\n return { text: new TextDecoder().decode(concatBytes(chunks, size)) };\n } catch {\n return { skip: 'decode-failed' };\n }\n }\n\n try {\n const text = await clone.text();\n if (text.length > cap) return { skip: 'body-cap' };\n if (sniff && looksBinary(new TextEncoder().encode(text.slice(0, 512)))) return { skip: 'binary-body' };\n return { text };\n } catch {\n return { skip: 'read-failed' };\n }\n}\n\nfunction concatBytes(chunks, total) {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const c of chunks) {\n out.set(c, offset);\n offset += c.byteLength;\n }\n return out;\n}\n\nfunction headerObject(headers) {\n const out = {};\n headers?.forEach?.((v, k) => { out[k.toLowerCase()] = v; });\n // Set-Cookie is multi-valued; forEach collapses it. Recover the individual cookies so each can\n // be screened (and re-emitted) separately.\n const setCookies = headers?.getSetCookie?.();\n if (setCookies && setCookies.length) out['set-cookie'] = setCookies;\n return out;\n}\n\n// True if any of the rule's conditions carries a body-transforming mutation on a SPAN match\n// (regex/contains/stripos) — those decode the body before matching, so a span redactor derived from\n// the literal/regex can't be located in the raw body. (array_key_value structural redaction decodes\n// the JSON itself, so json_decode there is fine and doesn't count.)\nfunction hasSpanMutations(rule) {\n let found = false;\n const walk = (conds) => {\n for (const c of conds ?? []) {\n if (found) return;\n if (Array.isArray(c.rules)) walk(c.rules);\n const isSpan = c.match && (c.match.type === 'regex' || c.match.type === 'contains' || c.match.type === 'stripos');\n if (isSpan && Array.isArray(c.mutations) && c.mutations.length) found = true;\n }\n };\n walk(rule.rule_v2);\n return found;\n}\n\n// Derive redaction targets from a rule's own conditions: regex → mask every match;\n// contains/stripos → mask the literal. (Other match types can't identify a span → the\n// rule falls back to block.)\nfunction extractRedactors(rule) {\n const out = [];\n const walk = (conds) => {\n for (const c of conds ?? []) {\n if (Array.isArray(c.rules)) walk(c.rules);\n const m = c.match;\n if (!m) continue;\n if (m.type === 'regex' && typeof m.value === 'string') {\n // Route through the SAME ReDoS guard detection uses — a catastrophic redactor pattern must\n // not hang the response path (safeRegExp returns null for dangerous/invalid patterns → skip).\n const safe = safeRegExp(m.value);\n if (safe) {\n const flags = safe.flags.includes('g') ? safe.flags : safe.flags + 'g';\n try {\n out.push({ re: new RegExp(safe.source, flags) });\n } catch {\n /* skip invalid */\n }\n }\n } else if ((m.type === 'contains' || m.type === 'stripos') && m.value != null) {\n out.push({ literal: String(m.value) });\n } else if (m.type === 'array_key_value' && m.match && isBodyParam(c.parameter)) {\n // Structural redaction: mask the value at a JSON path (fanning out over arrays) rather than\n // a text span — e.g. key \"orders.customers.email\" masks that field in every array element.\n const keys = Array.isArray(m.key) ? m.key : [m.key];\n for (const key of keys) {\n out.push({ jsonPath: String(key).split('.'), condition: m.match });\n }\n }\n }\n };\n walk(rule.rule_v2);\n return out;\n}\n\n// HTML-entity escape, for the `encode` action (neutralize markup rather than mask it). NOTE: this is\n// sound only for HTML text / attribute-VALUE contexts. It does NOT neutralize a `javascript:` / `data:`\n// URI or an event-handler name (those carry no HTML metacharacters) — use `block` for a rule that\n// targets a URL/scheme context. See the rule-authoring guidance in the triage-vpatch-npm skill.\nfunction htmlEscape(str) {\n return String(str).replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c]);\n}\n\n// `transform` (optional): map a matched span to its replacement (the `encode` action passes\n// htmlEscape). Without it, matches are replaced by the `mask` string (the `redact` action).\nfunction applyRedactors(body, redactors, mask, transform) {\n let out = body;\n for (const r of redactors) {\n if (r.re) out = out.replace(r.re, transform ? (m) => transform(m) : mask);\n else if (r.literal) {\n // Detection (matchValue for contains/stripos) is case-insensitive, so mask case-insensitively\n // too — otherwise a `contains: \"SECRET\"` redactor detects `secret` but masks nothing, serving\n // the leak while reporting a redaction. Escape the literal so it matches literally, not as regex.\n const re = new RegExp(r.literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'gi');\n out = out.replace(re, (m) => (transform ? transform(m) : mask));\n }\n }\n return out;\n}\n\n// A response-body redaction target (array_key_value masks the JSON body). A bare condition with no\n// parameter also defaults to the body.\nfunction isBodyParam(parameter) {\n return parameter == null || parameter === 'response.body' || parameter === 'raw' || parameter === 'response.raw';\n}\n\n// Build a predicate from the array_key_value nested match, so a path can be masked conditionally\n// (only leaves that match) or — with `isset` — unconditionally. Fail-closed (don't mask) on error.\nfunction conditionPredicate(condition) {\n if (!condition || !condition.type) return () => true;\n return (value) => {\n try {\n return matchValue(condition.type, value, condition.value, condition);\n } catch {\n return false;\n }\n };\n}\n\nconst DEFAULT_SCREEN_CAP = 512 * 1024;\n\n// Effective response-screening size cap for a rule set. Bodies larger than this are passed through\n// UNSCREENED (so a redact rule can't mask them — the leak/PII would slip out). A rule can raise the\n// ceiling for the whole response phase: `bypass_limit: true` removes the cap entirely (accepts the\n// memory cost on a hostile large body), or `max_bytes: <n>` raises it to n. The cap is shared (the\n// body is buffered once), so the effective cap is the MAX across all active response rules.\nfunction responseScreenCap(rules) {\n let cap = DEFAULT_SCREEN_CAP;\n for (const r of rules ?? []) {\n if (r && r.bypass_limit === true) return Infinity;\n const n = Number(r && r.max_bytes);\n if (Number.isFinite(n) && n > cap) cap = n;\n }\n return cap;\n}\n\n// Apply jsonPath redactors structurally: parse the JSON body, mask each targeted leaf (fanning out\n// over arrays at every path segment), re-serialize. Fail-open — a non-JSON / oversized / unparseable\n// body is returned unchanged, and any per-leaf error is swallowed.\nfunction applyPathRedactors(text, pathRedactors, mask, cap, transform) {\n if (!pathRedactors.length || typeof text !== 'string' || text.length > cap) return text;\n const head = text.trimStart()[0];\n if (head !== '{' && head !== '[') return text; // not a JSON object/array\n // Preserve out-of-safe-range integers across the parse→stringify round-trip: JSON.parse would\n // round e.g. a 20-digit id. We quote such number tokens to a sentinel string before parsing and\n // unquote them after stringifying, so untouched big ints survive losslessly.\n const preserved = preserveBigInts(text);\n let obj;\n try {\n obj = JSON.parse(preserved);\n } catch {\n return text;\n }\n let changed = false;\n for (const r of pathRedactors) {\n const pred = conditionPredicate(r.condition);\n walkLeaves(obj, r.jsonPath, (loc) => {\n try {\n if (pred(loc.value)) {\n // `encode`: escape the leaf's own value in place; `redact`: replace it with the mask.\n loc.parent[loc.key] = transform ? transform(String(loc.value)) : mask;\n changed = true;\n }\n } catch {\n /* skip this leaf */\n }\n });\n }\n return changed ? restoreBigInts(JSON.stringify(obj)) : text;\n}\n\nconst BIGINT_OPEN = '__PSBIGINT_9c2f__';\nconst BIGINT_CLOSE = '__DNEGIB__';\n\n// Quote every out-of-safe-range integer *value* (a bare number token outside a string) into a\n// sentinel string, so JSON.parse keeps it verbatim. String-aware scan (respects \\ escapes) so a\n// number inside a string value is never touched. Plain-ASCII sentinel → survives JSON.stringify.\nfunction preserveBigInts(text) {\n let out = '';\n let inStr = false;\n for (let i = 0; i < text.length; ) {\n const ch = text[i];\n if (inStr) {\n out += ch;\n if (ch === '\\\\') { out += text[i + 1] ?? ''; i += 2; continue; }\n if (ch === '\"') inStr = false;\n i++;\n continue;\n }\n if (ch === '\"') { inStr = true; out += ch; i++; continue; }\n if (ch === '-' || (ch >= '0' && ch <= '9')) {\n let j = ch === '-' ? i + 1 : i;\n let digits = 0;\n while (j < text.length && text[j] >= '0' && text[j] <= '9') { digits++; j++; }\n const next = text[j];\n const isIntToken = digits > 0 && next !== '.' && next !== 'e' && next !== 'E';\n if (isIntToken && digits >= 16) {\n out += `\"${BIGINT_OPEN}${text.slice(i, j)}${BIGINT_CLOSE}\"`;\n } else {\n out += text.slice(i, j || i + 1);\n }\n i = j > i ? j : i + 1;\n continue;\n }\n out += ch;\n i++;\n }\n return out;\n}\n\nfunction restoreBigInts(text) {\n if (!text.includes(BIGINT_OPEN)) return text;\n return text.replace(new RegExp(`\"${BIGINT_OPEN}(-?\\\\d+)${BIGINT_CLOSE}\"`, 'g'), '$1');\n}\n\n// Response-hardening actions. Mutate the (lowercase-keyed) headers object in place; a `null` value\n// signals removal to rebuildResponse / the node path. `set-header` sets/overwrites (or `ensure`s only\n// when absent); `remove-header` strips; `harden-cookie` adds missing HttpOnly/Secure/SameSite flags.\nfunction isHeaderMutation(action) {\n return action === 'set-header' || action === 'remove-header' || action === 'harden-cookie';\n}\n\nfunction applyHeaderMutation(headers, rule) {\n if (rule.action === 'remove-header') {\n for (const name of rule.remove_headers ?? []) headers[String(name).toLowerCase()] = null;\n return;\n }\n if (rule.action === 'set-header') {\n const ensure = rule.ensure === true; // set only when the header is absent (don't clobber)\n for (const [name, value] of Object.entries(rule.set_headers ?? {})) {\n const key = String(name).toLowerCase();\n const present = headers[key] != null && headers[key] !== '';\n if (ensure && present) continue;\n headers[key] = String(value);\n }\n return;\n }\n if (rule.action === 'harden-cookie') {\n const cookie = headers['set-cookie'];\n const flags = rule.cookie_flags ?? {};\n if (Array.isArray(cookie)) {\n headers['set-cookie'] = cookie.map((c) => (typeof c === 'string' ? hardenCookie(c, flags) : c));\n } else if (typeof cookie === 'string') {\n headers['set-cookie'] = hardenCookie(cookie, flags);\n }\n }\n}\n\nfunction hardenCookie(cookie, { httpOnly = true, secure = true, sameSite = 'Lax' } = {}) {\n let out = String(cookie);\n if (httpOnly && !/;\\s*httponly/i.test(out)) out += '; HttpOnly';\n if (secure && !/;\\s*secure/i.test(out)) out += '; Secure';\n if (sameSite && !/;\\s*samesite\\s*=/i.test(out)) out += `; SameSite=${sameSite}`;\n return out;\n}\n\nfunction rebuildResponse(response, body, redactedHeaders) {\n const headers = new Headers(response.headers);\n headers.delete('content-length'); // body length changed after redaction\n if (redactedHeaders) {\n for (const [name, value] of Object.entries(redactedHeaders)) {\n if (value === null || value === undefined) {\n try { headers.delete(name); } catch { /* skip */ } // header-mutation removal\n } else if (typeof value === 'string') {\n if (headers.get(name) !== value) {\n try { headers.set(name, value); } catch { /* invalid header name — skip */ }\n }\n } else if (Array.isArray(value)) {\n // Re-emit each (possibly redacted) Set-Cookie separately (Headers collapses them otherwise).\n try {\n headers.delete(name);\n for (const item of value) headers.append(name, String(item));\n } catch { /* skip */ }\n }\n }\n }\n // Null-body statuses (204/205/304/101) must not carry a body, or the Response constructor throws.\n const nullBody = response.status === 101 || response.status === 204 || response.status === 205 || response.status === 304;\n return new Response(nullBody ? null : body, { status: response.status, statusText: response.statusText, headers });\n}\n\nfunction leakResponse() {\n return new Response(JSON.stringify({ error: 'Response withheld by Patchstack (sensitive data detected)' }), {\n status: 500,\n headers: { 'content-type': 'application/json' }\n });\n}\n\n// (rule source / tiered store moved to ./rules/source.js + ./rules/store.js)\n\n// --- responses ----------------------------------------------------------\n\n// Client-facing block text — deliberately generic: no WAF narrative, no rule title. The reason\n// detail stays in the server-side log via onDetect.\nconst BLOCK_MESSAGE = 'This request has been blocked by Patchstack.';\n\nfunction blockBody(result) {\n // Human text is masked (no WAF narrative, no rule title). The opaque rule id stays for machine\n // consumers (server-fn receipts, support reference); full rule detail lives in the server log.\n return { error: BLOCK_MESSAGE, message: BLOCK_MESSAGE, rule: result?.rule?.id };\n}\n\n// A top-level document navigation (vs an XHR/fetch)? Browsers set Sec-Fetch-Dest on navigations;\n// fall back to the Accept header. Governs whether a block returns the HTML page or JSON.\nfunction isDocumentNavigation(getHeader) {\n const dest = getHeader('sec-fetch-dest');\n if (dest) return dest === 'document';\n return (getHeader('accept') || '').includes('text/html');\n}\n\n// Request-phase block. Serves the branded HTML \"Access Denied\" page to a browser navigation, and\n// masked JSON to XHR/fetch/programmatic clients.\nfunction blockResponse(result, request) {\n if (request && isDocumentNavigation((n) => request.headers.get(n))) {\n return new Response(renderBlockPage({ url: request.url, code: result?.rule?.id }), {\n status: 403,\n headers: { 'content-type': 'text/html; charset=utf-8' },\n });\n }\n return new Response(JSON.stringify(blockBody(result)), {\n status: 403,\n headers: { 'content-type': 'application/json' },\n });\n}\n\nfunction defaultOnDetect({ phase, mode, category, rule, message }) {\n const tag = mode === 'block' ? 'BLOCK' : 'DETECT (dry-run)';\n console.warn(`[patchstack] ${tag} phase=${phase ?? 'request'} category=${category ?? '?'} rule=${rule?.id ?? '?'} ${message ?? ''}`.trim());\n}\n\n/** @param {Request} request */\nfunction fetchRequestMeta(request) {\n if (!request) return {};\n let path = null;\n try {\n path = new URL(request.url).pathname;\n } catch {\n path = typeof request.url === 'string' ? request.url : null;\n }\n return {\n method: request.method ?? null,\n path,\n ip: request.headers?.get?.('x-forwarded-for') ?? null,\n userAgent: request.headers?.get?.('user-agent') ?? null,\n };\n}\n\n/** @param {import('http').IncomingMessage & { ip?: string, originalUrl?: string }} req */\nfunction nodeRequestMeta(req) {\n if (!req) return {};\n const headers = req.headers ?? {};\n const ua = headers['user-agent'] ?? headers['User-Agent'];\n const fwd = headers['x-forwarded-for'] ?? headers['X-Forwarded-For'];\n return {\n method: req.method ?? null,\n path: req.originalUrl || req.url || null,\n ip: req.ip ?? (typeof fwd === 'string' ? fwd : null),\n userAgent: typeof ua === 'string' ? ua : Array.isArray(ua) ? ua[0] : null,\n };\n}\n"],"mappings":"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","names":["warnOnce","warnOnce","DEFAULT_BASE_URL","DEFAULT_CACHE_TTL","JITTER_FRACTION","parseCookies","JITTER_FRACTION","DEFAULT_BASE_URL","DEFAULT_FLUSH_MS","MAX_BATCH","allow"]}
1
+ {"version":3,"sources":["../src/protect/safe-origin.js","../src/protect/engine/client.js","../src/protect/engine/request.js","../src/protect/notify.js","../src/protect/engine/normalizer.js","../src/protect/engine/engine.js","../src/protect/engine/pulse-client.js","../src/protect/engine/fetch.js","../src/protect/engine/node.js","../src/protect/egress.js","../src/protect/defaults.js","../src/protect/block-page.js","../src/protect/rules/store.js","../src/protect/rules/contract.js","../src/protect/rules/validate.js","../src/protect/rules/source.js","../src/protect/rules/refresh.js","../src/protect/detections.js","../src/protect/firewall-log.js","../src/protect/supabase-guard.js","../src/protect/runtime.js"],"sourcesContent":["// Which origins this guard is willing to talk to. Both of its remote conversations are security\n// sensitive: the RULE endpoint delivers policy the engine then executes on every request (an\n// attacker-controlled endpoint could remove protection wholesale or serve a CPU-expensive ruleset), and\n// the telemetry endpoint receives the site api_key. Env/CI injection is the realistic threat, so a\n// non-default override must be https — with localhost permitted so local development and tests work.\nexport function isSafeOrigin(value) {\n try {\n const u = new URL(value);\n if (u.protocol === 'https:') return true;\n return u.protocol === 'http:' && (u.hostname === 'localhost' || u.hostname === '127.0.0.1' || u.hostname === '[::1]' || u.hostname === '::1');\n } catch {\n return false;\n }\n}\n\n/**\n * Accept `candidate` only if it is a safe origin; otherwise warn once and fall back to `fallback`.\n * @param {string|undefined} candidate @param {string} fallback @param {string} label\n */\nexport function safeBaseUrl(candidate, fallback, label) {\n if (typeof candidate !== 'string' || candidate === '') return fallback;\n if (isSafeOrigin(candidate)) return candidate;\n warnOnce(label, `[patchstack] ignoring unsafe ${label} override (${candidate}): must be https (or localhost). Using the default.`);\n return fallback;\n}\n\nconst warned = new Set();\nfunction warnOnce(key, message) {\n if (warned.has(key)) return;\n warned.add(key);\n // eslint-disable-next-line no-console\n console.warn(message);\n}\n","import { safeBaseUrl } from '../safe-origin.js';\n\nconst DEFAULT_BASE_URL = 'https://api.patchstack.com';\nconst DEFAULT_CACHE_TTL = 300_000;\n// Randomly shorten the effective TTL by up to this fraction so many long-lived clients don't all\n// revalidate on the same tick (spreads load against the rules API).\nconst JITTER_FRACTION = 0.1;\n\n// Token-authenticated rules client. Conditional fetch (If-None-Match / 304) and the persisted\n// `etag` mirror PulseRuleClient; both are a no-op until the API emits an ETag and honors the\n// conditional request — with no ETag in the response the client behaves as before (full fetch).\nexport class PatchstackRuleClient {\n #token;\n #baseUrl;\n #cacheTtl;\n #cache = null;\n #cacheTime = null;\n #ttlEffective = 0;\n #etag;\n #timeoutMs;\n\n constructor({ token, baseUrl, cacheTtl, etag, timeoutMs } = {}) {\n // Bounded so app STARTUP can't hang on a slow API (see the boot budget in rules/source.js).\n this.#timeoutMs = Number(timeoutMs) > 0 ? Number(timeoutMs) : 30_000;\n this.#token = token ?? process.env.PATCHSTACK_WAF_TOKEN;\n this.#baseUrl = safeBaseUrl(baseUrl ?? process.env.PATCHSTACK_WAF_API_URL, DEFAULT_BASE_URL, 'rule endpoint');\n this.#cacheTtl = Number.isFinite(cacheTtl) && cacheTtl > 0 ? cacheTtl : DEFAULT_CACHE_TTL;\n this.#etag = etag ?? null;\n\n if (!this.#token) {\n throw new Error('Patchstack WAF token is required. Pass { token } or set PATCHSTACK_WAF_TOKEN env var.');\n }\n }\n\n async getRules() {\n const now = Date.now();\n\n if (this.#cache && this.#cacheTime !== null && (now - this.#cacheTime) < this.#ttlEffective) {\n return this.#cache;\n }\n\n const url = `${this.#baseUrl}/api/get-rules/3`;\n\n try {\n const headers = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.#token}`,\n 'LicenseID': '1' // Hard-coded, is never actually used but needed by the API\n };\n if (this.#etag) headers['If-None-Match'] = this.#etag;\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n signal: AbortSignal.timeout(this.#timeoutMs)\n });\n\n if (response.status === 304) {\n this.#touch(now); // revalidated — reset the clock (fresh jitter)\n return this.#cache ?? { success: true, notModified: true, etag: this.#etag, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n\n if (!response.ok) {\n return {\n success: false,\n error: `API returned ${response.status}: ${response.statusText}`,\n firewall: [],\n whitelists: [],\n whitelist_keys: {}\n };\n }\n\n const data = await response.json();\n\n // A 200 that isn't a genuine rule envelope must not be treated as \"no rules\". Report failure\n // so the caller falls back to the cache / bundled rules rather than caching an empty bundle.\n if (!data || !Array.isArray(data.firewall)) {\n return { success: false, error: 'unexpected response shape (no firewall array)', firewall: [], whitelists: [], whitelist_keys: {} };\n }\n\n const result = {\n success: true,\n notModified: false,\n etag: response.headers?.get?.('etag') ?? null,\n firewall: data.firewall,\n whitelists: Array.isArray(data.whitelists) ? data.whitelists : [],\n whitelist_keys: data.whitelist_keys ?? {}\n };\n\n this.#cache = result;\n this.#etag = result.etag;\n this.#touch(now);\n\n return result;\n } catch (err) {\n return {\n success: false,\n error: err.name === 'TimeoutError'\n ? 'Request timed out'\n : err.message,\n firewall: [],\n whitelists: [],\n whitelist_keys: {}\n };\n }\n }\n\n #touch(now) {\n this.#cacheTime = now;\n this.#ttlEffective = this.#cacheTtl - Math.floor(Math.random() * this.#cacheTtl * JITTER_FRACTION);\n }\n\n clearCache() {\n this.#cache = null;\n this.#cacheTime = null;\n this.#etag = null;\n }\n}\n","// Resolvable DATA attributes of an uploaded file part (files.<name>.<attr>). The engine only exposes\n// the raw data — WHAT counts as a malicious upload (signatures, type-vs-content mismatch) is expressed\n// in rules (see the triage-vpatch-npm skill), not hardcoded here.\nconst FILE_ATTRS = new Set(['content', 'filename', 'type']);\n\n// A captured file part is { filename, type, content }; tolerate the legacy bare-filename string.\nconst fileFilename = (f) => (f && typeof f === 'object' ? f.filename : f);\nconst fileAttribute = (f, attr) => (f && typeof f === 'object' ? f[attr] : attr === 'filename' ? f : undefined);\n\n// WinterCG-safe base64 decode: use Buffer on Node, fall back to atob/TextDecoder on\n// edge runtimes (Cloudflare Workers, Deno, Bun) where Buffer may be absent. Keeps the\n// engine hot path free of Node-only APIs (per the ADR engine-language decision).\nexport function base64DecodeUtf8(value) {\n const str = String(value);\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'base64').toString('utf-8');\n }\n const binary = atob(str);\n const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Decode HTML entities, so a payload written as `&lt;script&gt;` is screened as `<script>`.\n *\n * The mutation was documented, used by a shipped rule, and not implemented — so it was applied as a silent\n * no-op. Implemented here to make the name mean what it says, NOT to close a coverage gap: `normalizer.js`\n * already decodes entities across the whole request before matching, iteratively, which is why the\n * entity-encoded payloads that rule exists for were being caught anyway. Anyone reading this should not\n * conclude that entity coverage depended on this mutation; it did not, and the contract test is what turned\n * \"the name does nothing\" into something visible.\n *\n * Named entities are limited to the ones that matter for injection contexts plus the handful every encoder\n * emits. A full entity table would be a dependency, and the gap it leaves is a payload encoded with an\n * exotic named entity — which no encoder in this path produces. Numeric forms are handled generally, both\n * decimal and hex, because those are what an attacker writes by hand.\n */\nconst NAMED_ENTITIES = {\n lt: '<', gt: '>', amp: '&', quot: '\"', apos: \"'\", '#39': \"'\",\n nbsp: '\\u00a0', sol: '/', bsol: '\\\\', colon: ':', lpar: '(', rpar: ')', equals: '=', grave: '`',\n Tab: '\\t', NewLine: '\\n', semi: ';', excl: '!', num: '#', dollar: '$', percnt: '%', ast: '*',\n};\n\nfunction decodeHtmlEntities(input) {\n // One pass. Decoding repeatedly would turn `&amp;lt;` into `<`, which is not what a browser does — and a\n // guard that decodes further than the sink does is a guard that blocks strings the app never sees.\n return input.replace(/&(#[xX]?[0-9a-fA-F]+|[A-Za-z][A-Za-z0-9]*);?/g, (whole, body) => {\n if (body[0] === '#') {\n const hex = body[1] === 'x' || body[1] === 'X';\n const code = Number.parseInt(hex ? body.slice(2) : body.slice(1), hex ? 16 : 10);\n if (!Number.isFinite(code) || code < 0 || code > 0x10ffff) return whole;\n try {\n return String.fromCodePoint(code);\n } catch {\n return whole;\n }\n }\n\n return Object.prototype.hasOwnProperty.call(NAMED_ENTITIES, body) ? NAMED_ENTITIES[body] : whole;\n });\n}\n\nexport class RequestResolver {\n #req;\n #cookies;\n\n constructor(req) {\n this.#req = req;\n this.#cookies = null;\n }\n\n resolve(parameter) {\n if (!parameter || parameter === 'false') {\n return [null];\n }\n\n if (parameter === 'rules') {\n return [null];\n }\n\n if (parameter === 'raw') {\n return this.#resolveRaw();\n }\n\n if (parameter === 'all') {\n return this.#resolveAll();\n }\n\n const dotIndex = parameter.indexOf('.');\n if (dotIndex === -1) {\n return [];\n }\n\n const source = parameter.substring(0, dotIndex);\n const key = parameter.substring(dotIndex + 1);\n\n switch (source) {\n case 'get':\n return this.#resolveGet(key);\n case 'post':\n return this.#resolvePost(key);\n case 'request':\n return this.#resolveRequest(key);\n case 'cookie':\n return this.#resolveCookie(key);\n case 'server':\n return this.#resolveServer(key);\n case 'files':\n return this.#resolveFiles(key);\n case 'response':\n return this.#resolveResponse(key);\n case 'egress':\n return this.#resolveEgress(key);\n default:\n return [];\n }\n }\n\n // Response-phase sources (req._response = { status, headers, body }). Lets rules inspect\n // what the app is about to SEND — e.g. a leaked secret in the body — regardless of route.\n #resolveResponse(key) {\n const resp = this.#req._response;\n if (!resp) {\n return [];\n }\n if (key === 'status') {\n return resp.status !== undefined ? [String(resp.status)] : [];\n }\n if (key === 'body') {\n return resp.body != null && resp.body !== '' ? [resp.body] : [];\n }\n if (key === 'headers') {\n const headers = resp.headers ?? {};\n return [Object.entries(headers).map(([k, v]) => `${k}: ${v}`).join('\\n')];\n }\n if (key.startsWith('header.')) {\n const name = key.slice('header.'.length).toLowerCase();\n const value = (resp.headers ?? {})[name];\n return value !== undefined ? [value] : [];\n }\n return [];\n }\n\n // Egress-phase sources (req._egress = { url, host, method }). Lets rules inspect an\n // OUTBOUND request the app is about to make — SSRF at the egress boundary.\n #resolveEgress(key) {\n const eg = this.#req._egress;\n if (!eg) {\n return [];\n }\n if (key === 'url') return eg.url ? [eg.url] : [];\n if (key === 'host') return eg.host ? [eg.host] : [];\n if (key === 'method') return eg.method ? [eg.method] : [];\n return [];\n }\n\n applyMutations(mutations, value) {\n if (!mutations || !Array.isArray(mutations)) {\n return value;\n }\n\n let result = value;\n\n for (const mutation of mutations) {\n result = this.#applyMutation(mutation, result);\n }\n\n return result;\n }\n\n #applyMutation(mutation, value) {\n if (value === null || value === undefined) {\n return value;\n }\n\n switch (mutation) {\n case 'base64_decode':\n try {\n return base64DecodeUtf8(value);\n } catch {\n return value;\n }\n\n case 'json_decode':\n try {\n return JSON.parse(String(value));\n } catch {\n return value;\n }\n\n case 'json_encode':\n try {\n return JSON.stringify(value);\n } catch {\n return value;\n }\n\n case 'urldecode':\n try {\n return decodeURIComponent(String(value));\n } catch {\n return value;\n }\n\n case 'htmlentitydecode':\n return decodeHtmlEntities(String(value));\n\n case 'intval':\n return parseInt(String(value), 10) || 0;\n\n case 'getArrayValues':\n if (typeof value === 'object' && value !== null) {\n return Object.values(value);\n }\n return value;\n\n default:\n return value;\n }\n }\n\n #resolveGet(key) {\n const query = this.#req.query ?? {};\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(query, key);\n }\n\n const value = this.#getNestedValue(query, key);\n return value !== undefined ? [value] : [];\n }\n\n #resolvePost(key) {\n const body = this.#req.body ?? {};\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(body, key);\n }\n\n const value = this.#getNestedValue(body, key);\n return value !== undefined ? [value] : [];\n }\n\n #resolveRequest(key) {\n const query = this.#req.query ?? {};\n const body = this.#req.body ?? {};\n const cookies = this.#parseCookies();\n\n if (key.endsWith('*')) {\n return [\n ...this.#resolveWildcard(query, key),\n ...this.#resolveWildcard(body, key),\n ...this.#resolveWildcard(cookies, key)\n ];\n }\n\n const value = this.#getNestedValue(query, key)\n ?? this.#getNestedValue(body, key)\n ?? cookies[key];\n\n return value !== undefined ? [value] : [];\n }\n\n #resolveCookie(key) {\n const cookies = this.#parseCookies();\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(cookies, key);\n }\n\n const value = cookies[key];\n return value !== undefined ? [value] : [];\n }\n\n #resolveServer(key) {\n const req = this.#req;\n\n switch (key) {\n case 'REQUEST_URI':\n return [req.originalUrl ?? req.url ?? '/'];\n case 'REQUEST_METHOD':\n return [req.method ?? 'GET'];\n case 'HTTP_USER_AGENT':\n return req.headers?.['user-agent'] ? [req.headers['user-agent']] : [];\n case 'HTTP_REFERER':\n return req.headers?.referer ? [req.headers.referer] : [];\n case 'HTTP_HOST':\n return req.headers?.host ? [req.headers.host] : [];\n case 'REMOTE_ADDR':\n case 'ip':\n return [req.ip ?? req.socket?.remoteAddress ?? ''];\n case 'CONTENT_TYPE':\n return req.headers?.['content-type'] ? [req.headers['content-type']] : [];\n case 'CONTENT_LENGTH':\n return req.headers?.['content-length'] ? [req.headers['content-length']] : [];\n default: {\n if (key.startsWith('HTTP_')) {\n const headerName = key.substring(5).toLowerCase().replace(/_/g, '-');\n const headers = req.headers;\n // Presence, not truthiness. A header sent with an empty value IS present, and an `isset`\n // rule authored against it must see it — some bypasses are carried by the header existing\n // at all, so treating `Header:` as absent would make the rule quietly miss the shape it\n // was written for. (The named cases above keep value semantics: for host/origin/referer an\n // empty string and an absent header mean the same thing to the matchers that read them.)\n if (headers === null || typeof headers !== 'object') return [];\n return Object.prototype.hasOwnProperty.call(headers, headerName) ? [headers[headerName]] : [];\n }\n return [];\n }\n }\n }\n\n #resolveFiles(key) {\n const files = this.#req.files;\n if (!files || typeof files !== 'object') {\n return [];\n }\n\n // files.<name>.<attr> — content | filename | type. Fans out over multiple files uploaded under\n // the same field name.\n const dot = key.lastIndexOf('.');\n if (dot !== -1 && FILE_ATTRS.has(key.slice(dot + 1)) && Object.prototype.hasOwnProperty.call(files, key.slice(0, dot))) {\n const attr = key.slice(dot + 1);\n const entry = files[key.slice(0, dot)];\n const list = Array.isArray(entry) ? entry : [entry];\n const out = [];\n for (const f of list) {\n const v = fileAttribute(f, attr);\n if (v !== undefined && v !== '') out.push(v);\n }\n return out;\n }\n\n // Bare files.<name> (or wildcard) → the filename(s), preserving the legacy behavior that\n // filename-scoped rules rely on (the parser now stores a { filename, type, content } object).\n const filenamesOf = (entry) => (Array.isArray(entry) ? entry.map(fileFilename) : [fileFilename(entry)]);\n if (key.endsWith('*')) {\n const prefix = key.slice(0, -1);\n const out = [];\n for (const [k, entry] of Object.entries(files)) {\n if (k.startsWith(prefix)) out.push(...filenamesOf(entry));\n }\n return out.filter((v) => v !== undefined);\n }\n if (!Object.prototype.hasOwnProperty.call(files, key)) return [];\n return filenamesOf(files[key]).filter((v) => v !== undefined);\n }\n\n #resolveRaw() {\n // Use pre-captured raw body if available (set by normalizeRequest).\n // For string bodies, the original text is preserved verbatim.\n // For pre-parsed objects, serializeForRawDetection uses Object.getOwnPropertyNames()\n // to include __proto__ own-property keys that JSON.stringify() would silently drop.\n if (typeof this.#req._rawBody === 'string') {\n return this.#req._rawBody ? [this.#req._rawBody] : [];\n }\n\n const body = this.#req.body;\n\n if (body === undefined || body === null) {\n return [];\n }\n\n if (typeof body === 'string') {\n return [body];\n }\n\n try {\n return [JSON.stringify(body)];\n } catch {\n return [String(body)];\n }\n }\n\n #resolveAll() {\n const parts = [];\n\n const uri = this.#req.originalUrl ?? this.#req.url ?? '/';\n parts.push(uri);\n\n const queryString = uri.includes('?') ? uri.split('?')[1] : '';\n if (queryString) {\n parts.push(queryString);\n }\n\n const body = this.#req.body;\n if (body) {\n parts.push(typeof body === 'string' ? body : JSON.stringify(body));\n }\n // Also fold in the verbatim body: a body the adapter couldn't structurally parse (an unusual\n // content-type, a non-JSON payload) leaves `body` empty, but the raw text must still be matchable\n // by an `all` rule — otherwise it's only visible via `raw`.\n if (typeof this.#req._rawBody === 'string' && this.#req._rawBody) {\n parts.push(this.#req._rawBody);\n }\n\n const headers = this.#req.headers ?? {};\n const excludedHeaders = new Set([\n 'host', 'connection', 'cache-control', 'accept', 'accept-encoding',\n 'accept-language', 'priority', 'sec-ch-ua', 'sec-ch-ua-mobile',\n 'sec-ch-ua-platform', 'sec-fetch-dest', 'sec-fetch-mode',\n 'sec-fetch-site', 'sec-fetch-user', 'upgrade-insecure-requests'\n ]);\n\n for (const [name, value] of Object.entries(headers)) {\n if (!excludedHeaders.has(name)) {\n parts.push(`${name}: ${value}`);\n }\n }\n\n const cookies = this.#parseCookies();\n const cookieStr = Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ');\n if (cookieStr) {\n parts.push(cookieStr);\n }\n\n return [parts.join(' ')];\n }\n\n #resolveWildcard(obj, pattern) {\n if (typeof obj !== 'object' || obj === null) {\n return [];\n }\n\n const prefix = pattern.slice(0, -1);\n const values = [];\n\n for (const [key, value] of Object.entries(obj)) {\n if (key.startsWith(prefix)) {\n values.push(value);\n }\n }\n\n return values;\n }\n\n #getNestedValue(obj, key) {\n if (typeof obj !== 'object' || obj === null) {\n return undefined;\n }\n\n // Own-property only: `key in obj` would resolve `__proto__`/`constructor`/`toString` to the\n // prototype chain, so a rule like `post.__proto__` (detecting a literal `__proto__` field) would\n // read Object.prototype instead of request data and never match. Use hasOwnProperty.\n const own = (o, k) => o !== null && typeof o === 'object' && Object.prototype.hasOwnProperty.call(o, k);\n\n if (own(obj, key)) {\n return obj[key];\n }\n\n const parts = key.split('.');\n let current = obj;\n\n for (const part of parts) {\n if (!own(current, part)) {\n return undefined;\n }\n current = current[part];\n }\n\n return current;\n }\n\n #parseCookies() {\n if (this.#cookies !== null) {\n return this.#cookies;\n }\n\n if (this.#req.cookies) {\n this.#cookies = this.#req.cookies;\n return this.#cookies;\n }\n\n const header = this.#req.headers?.cookie;\n if (!header) {\n this.#cookies = {};\n return this.#cookies;\n }\n\n const cookies = {};\n\n for (const pair of header.split(';')) {\n const eqIndex = pair.indexOf('=');\n if (eqIndex === -1) {\n continue;\n }\n const name = pair.substring(0, eqIndex).trim();\n const value = pair.substring(eqIndex + 1).trim();\n cookies[name] = value;\n }\n\n this.#cookies = cookies;\n return this.#cookies;\n }\n}\n","/**\n * Deliver a value to a caller-supplied callback without letting it break anything.\n *\n * `onError`, `onDetect` and `onSkip` are hooks a host passes in, so their code is not ours and its\n * failure is not ours to inherit. This package's one promise is that it never takes down the app it\n * protects — an engine error fails open, a malformed rule is skipped, a slow API boots from cache. A\n * reporting hook that throws has to fail open for the same reason, and for a sharper one: it fires\n * exactly when something interesting happened, so an unguarded throw converts \"we noticed something\"\n * into \"the request died\", and does it only on the requests that mattered.\n *\n * `onSkip` was already wrapped this way, with the reason written next to it. This is that same rule,\n * applied to the hooks that were missed rather than restated for one of them.\n *\n * Not silent, though. A hook that throws is a bug in the host's code and swallowing it entirely would\n * hide it forever, so the first failure per hook is reported — once, because these run per request and a\n * persistently broken hook would otherwise print on every one. Same reasoning as the engine's\n * report-once for a persistently broken rule.\n *\n * @param {unknown} fn the callback, or anything that is not a function (then this is a no-op)\n * @param {unknown} arg the single argument to hand it\n * @param {string} label which hook, for the one-time warning\n * @returns {boolean} whether the callback ran to completion, so a caller can fall back to its own\n * reporting rather than losing the report entirely. For an ASYNC callback this can only mean it\n * started: a rejection arrives after we return, and is contained and warned about, but by then a\n * caller has already decided not to fall back. Synchronous handlers get the stronger answer.\n */\n\n/** Hooks already reported as broken. Module-scoped: one warning per hook per process, not per guard. */\nconst reported = new Set();\n\nexport function notify(fn, arg, label) {\n if (typeof fn !== 'function') return false;\n\n try {\n const result = fn(arg);\n\n // An ASYNC callback fails after this function has already returned. `async () => { throw ... }` does\n // not throw — it hands back a rejected promise, and an unhandled rejection terminates the process by\n // default on Node. So a try/catch alone would contain the synchronous hosts and leave the async ones\n // able to kill the app, which is a worse outcome than the throw we set out to contain.\n if (result !== null && typeof result === 'object' && typeof result.then === 'function') {\n try {\n result.then(undefined, (err) => warnOnce(label, err));\n } catch {\n // A `then` that throws on access. Nothing more to attach to; the value is not a usable promise.\n }\n }\n\n return true;\n } catch (err) {\n warnOnce(label, err);\n\n return false;\n }\n}\n\n/**\n * Report a broken callback once per process.\n *\n * Must not throw: it runs inside the containment, so its own failure would be the thing that breaks the\n * guarantee it exists to report on.\n */\nfunction warnOnce(label, err) {\n if (reported.has(label)) return;\n reported.add(label);\n try {\n // Named as the host's callback, not as a Patchstack failure: pointing at ourselves for someone\n // else's throw sends them reading the wrong code.\n console.warn(\n `Patchstack: the ${label} callback passed to createProtection failed and was ignored. ` +\n `Protection is unaffected; this is reported once per process. ` +\n `Cause: ${err && err.message ? err.message : String(err)}`,\n );\n } catch {\n /* no console on this runtime */\n }\n}\n\n/** Test seam: forget which hooks have been reported, so warn-once is assertable more than once. */\nexport function resetNotifyWarnings() {\n reported.clear();\n}\n","/**\n * Request Normalization Pipeline\n *\n * Normalizes request data to prevent encoding-based bypass attacks.\n * This module handles URL encoding, HTML entities, SQL comments, and other\n * obfuscation techniques that attackers use to evade pattern matching.\n */\n\nconst HTML_ENTITIES = {\n '&amp;': '&',\n '&lt;': '<',\n '&gt;': '>',\n '&quot;': '\"',\n '&#39;': \"'\",\n '&apos;': \"'\",\n '&nbsp;': ' ',\n '&#x27;': \"'\",\n '&#x22;': '\"',\n '&#x3C;': '<',\n '&#x3E;': '>',\n '&#x26;': '&',\n '&#34;': '\"',\n '&#60;': '<',\n '&#62;': '>',\n '&#38;': '&'\n};\n\nconst MAX_DECODE_ITERATIONS = 5;\n\nexport function normalize(value, options = {}) {\n if (typeof value !== 'string') {\n return value;\n }\n\n const opts = {\n urlDecode: true,\n htmlDecode: true,\n sqlComments: true,\n nullBytes: true,\n whitespace: true,\n ...options\n };\n\n let result = value;\n\n if (opts.nullBytes) {\n result = removeNullBytes(result);\n }\n\n if (opts.urlDecode) {\n result = urlDecode(result);\n }\n\n if (opts.htmlDecode) {\n result = htmlEntityDecode(result);\n }\n\n if (opts.sqlComments) {\n result = removeSqlComments(result);\n }\n\n if (opts.whitespace) {\n result = normalizeWhitespace(result);\n }\n\n return result;\n}\n\nexport function urlDecode(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value;\n let previous = '';\n let iterations = 0;\n\n while (result !== previous && iterations < MAX_DECODE_ITERATIONS) {\n previous = result;\n iterations++;\n\n try {\n result = decodeURIComponent(result);\n } catch {\n result = safeUrlDecode(result);\n break;\n }\n }\n\n return result;\n}\n\nfunction safeUrlDecode(value) {\n return value.replace(/%([0-9A-Fa-f]{2})/g, (match, hex) => {\n try {\n return String.fromCharCode(parseInt(hex, 16));\n } catch {\n return match;\n }\n });\n}\n\nexport function htmlEntityDecode(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value;\n\n for (const [entity, char] of Object.entries(HTML_ENTITIES)) {\n result = result.split(entity).join(char);\n }\n\n result = result.replace(/&#(\\d+);/g, (match, code) => {\n const num = parseInt(code, 10);\n return num > 0 && num < 65536 ? String.fromCharCode(num) : match;\n });\n\n result = result.replace(/&#x([0-9A-Fa-f]+);/g, (match, hex) => {\n const num = parseInt(hex, 16);\n return num > 0 && num < 65536 ? String.fromCharCode(num) : match;\n });\n\n return result;\n}\n\nexport function removeSqlComments(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n // Collapse inline block comments to a space (the anti-obfuscation goal). We must NOT strip the\n // line-comment forms (`--…`, `#…`) to end-of-line: on the WAF inspection path that DELETES\n // attacker-controlled spans from the value the engine sees while the app still processes the\n // original — e.g. `#<script>…` becomes empty and evades an XSS rule, though the browser still\n // runs it. Keeping the content only ever ADDS matches (more of the value is inspected), never\n // hides one. SQLi keyword detection is unaffected — the keywords remain visible.\n let result = value;\n\n result = result.replace(/\\/\\*[\\s\\S]*?\\*\\//g, ' ');\n result = result.replace(/\\/\\*![\\s\\S]*?\\*\\//g, ' ');\n\n return result;\n}\n\nexport function removeNullBytes(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value.replace(/\\x00/g, '');\n\n result = result.replace(/[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F]/g, '');\n\n return result;\n}\n\nexport function normalizeWhitespace(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value.replace(/[\\t\\r\\n\\f\\v]+/g, ' ');\n\n result = result.replace(/ {2,}/g, ' ');\n\n return result;\n}\n\nfunction serializeForRawDetection(body, visited = new Set(), isRoot = true) {\n if (body === null || body === undefined) {\n return isRoot ? '' : 'null';\n }\n\n if (typeof body === 'string') {\n return isRoot ? body : JSON.stringify(body);\n }\n\n if (typeof body !== 'object') {\n return String(body);\n }\n\n if (visited.has(body)) {\n return '[Circular]';\n }\n visited.add(body);\n\n if (Array.isArray(body)) {\n const items = body.map(item => serializeForRawDetection(item, visited, false));\n return '[' + items.join(',') + ']';\n }\n\n // Object.getOwnPropertyNames includes non-enumerable own properties, so a __proto__ key\n // set via Object.defineProperty (as modern JSON.parse may do) is included in the output.\n const keys = Object.getOwnPropertyNames(body);\n const parts = keys.map(key => {\n const val = serializeForRawDetection(body[key], visited, false);\n return JSON.stringify(key) + ':' + val;\n });\n\n return '{' + parts.join(',') + '}';\n}\n\nexport function normalizeRequest(req, options = {}) {\n // Prefer a caller-provided verbatim body string (set by the fetch/node adapters):\n // it preserves literal keys like `__proto__` that JSON.stringify drops, which is\n // what makes prototype-pollution rules on `raw` robust. Fall back to a\n // reconstruction from the parsed body (the Express path, which has no raw text).\n const rawBody = typeof req._rawBody === 'string'\n ? req._rawBody\n : serializeForRawDetection(req.body ?? null);\n\n return {\n query: normalizeObject(req.query || {}, options),\n body: normalizeObject(req.body || {}, options),\n headers: normalizeObject(req.headers || {}, options),\n url: normalize(req.url || '', options),\n originalUrl: normalize(req.originalUrl || req.url || '', options),\n _rawBody: rawBody\n };\n}\n\n// Depth bound for the recursive walk: a pathologically deep object would otherwise overflow the\n// stack, and the engine's per-rule catch would swallow that into a fail-open. Beyond the bound the\n// sub-value is left un-normalized (still matched, just in its raw form) rather than crashing.\nconst MAX_NORMALIZE_DEPTH = 200;\n\nexport function normalizeObject(value, options = {}, depth = 0) {\n if (typeof value === 'string') {\n return normalize(value, options);\n }\n\n if (depth >= MAX_NORMALIZE_DEPTH) {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map(item => normalizeObject(item, options, depth + 1));\n }\n\n if (typeof value === 'object' && value !== null) {\n const result = {};\n\n for (const [key, val] of Object.entries(value)) {\n result[key] = normalizeObject(val, options, depth + 1);\n }\n\n return result;\n }\n\n return value;\n}\n\nexport function createMatchVariants(value) {\n if (typeof value !== 'string') {\n return [value];\n }\n\n const variants = new Set();\n\n variants.add(value);\n\n const urlDecoded = urlDecode(value);\n variants.add(urlDecoded);\n\n const htmlDecoded = htmlEntityDecode(value);\n variants.add(htmlDecoded);\n\n const bothDecoded = htmlEntityDecode(urlDecoded);\n variants.add(bothDecoded);\n\n const normalized = normalize(value);\n variants.add(normalized);\n\n variants.add(value.toLowerCase());\n variants.add(normalized.toLowerCase());\n\n return Array.from(variants);\n}\n\nexport const _testExports = {\n safeUrlDecode,\n serializeForRawDetection,\n HTML_ENTITIES,\n MAX_DECODE_ITERATIONS\n};\n","import { RequestResolver } from './request.js';\nimport { notify } from '../notify.js';\nimport { normalizeRequest } from './normalizer.js';\nimport { base64DecodeUtf8 } from './request.js';\n\n// Catastrophic-backtracking shapes. Broad on purpose: a group whose inner content is quantified\n// (+, *, or {n,}) and is itself quantified — (a+)+, (\\w+)+, (.*)*, ([a-z]+)*, (ab+)+ — or an\n// alternation under an outer quantifier — (a|a)*, (x|y)+. A rule matching one of these is skipped\n// (safer than hanging the event loop). The `NEST` variants allow ONE level of nested parentheses in\n// the outer group so a quantified SUBGROUP is caught too (`((ab)+)+`, `((a|b)+)*`) — the earlier\n// `[^)]*` forms stopped at the first inner `)` and missed those. `.test()` scans every start offset,\n// so deeper nestings match at an inner window too.\nconst GRP = '(?:[^()]|\\\\([^()]*\\\\))*'; // group body allowing one level of nested parens\nconst REDOS_PATTERNS = [\n /\\([^)]*[+*}][^)]*\\)\\s*[+*]/,\n /\\([^)]*\\|[^)]*\\)\\s*[+*]/,\n new RegExp('\\\\(' + GRP + '[+*}]' + GRP + '\\\\)\\\\s*[+*]'), // nested quantified subgroup\n new RegExp('\\\\(' + GRP + '\\\\|' + GRP + '\\\\)\\\\s*[+*]') // nested alternation under a quantifier\n];\n\n// Report once when a rule's regex is rejected (ReDoS-shaped or unparseable). Unlike an unknown match\n// type, a rejected regex used to fail silently — so a delivered rule protected nothing and nobody knew.\nconst warnedRejectedPatterns = new Set();\nfunction warnRejectedPatternOnce(pattern) {\n const key = String(pattern);\n if (warnedRejectedPatterns.has(key)) return;\n warnedRejectedPatterns.add(key);\n console.warn(\n `[patchstack] rule_v2 regex pattern rejected (unsafe or invalid) — condition treated as no-match. ` +\n `The rule relying on it is NOT enforced: ${key}`\n );\n}\n\n// An absurdly long pattern is either a mistake or an attack on our own matcher; compiling and running\n// it on every request is unbounded work. The rule-bundle validator rejects these upstream — this is the\n// backstop for a caller-supplied bundle that never went through it.\nconst MAX_PATTERN_LENGTH = 1000;\n\nexport function safeRegExp(pattern) {\n if (!pattern) {\n return null;\n }\n if (typeof pattern !== 'string' || pattern.length > MAX_PATTERN_LENGTH) {\n return null;\n }\n\n for (const dangerous of REDOS_PATTERNS) {\n if (dangerous.test(pattern)) {\n return null;\n }\n }\n\n const match = pattern.match(/^\\/(.+?)\\/([gimsuy]*)$/s);\n if (!match) {\n return null;\n }\n\n try {\n return new RegExp(match[1], match[2]);\n } catch {\n return null;\n }\n}\n\n// ctype_*/is_numeric compare their character-class result to the rule's expected\n// `value`. Rules are almost always written `{ \"type\": \"ctype_digit\", \"value\": false }`\n// meaning \"flag when NOT of this class\" — so the result must be compared to matchVal,\n// not returned raw. Empty/absent values never match (mirrors engine-php's `$value != ''`\n// guard), otherwise every missing parameter false-positives a `value:false` rule.\n// When `value` is not a boolean (null/omitted) the legacy default of `true` is used, so\n// existing rules that relied on the raw class check are unaffected.\nfunction ctypeResult(strValue, isClass, matchVal) {\n if (strValue === '') {\n return false;\n }\n const expected = typeof matchVal === 'boolean' ? matchVal : true;\n return isClass === expected;\n}\n\n// Walk a dot-path into a decoded object and invoke `cb({ parent, key, value })` for every leaf it\n// reaches, fanning out over arrays at EVERY segment (not just the last) — so `orders.customers.email`\n// visits the email of each customer of each order, for arbitrary-length / arbitrarily-nested arrays.\n// `cb` gets the container + key so callers can either read (match) or set (redact) the leaf.\nexport function walkLeaves(node, segments, cb) {\n if (node === null || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n for (const el of node) walkLeaves(el, segments, cb);\n return;\n }\n const [head, ...rest] = segments;\n if (head === undefined || !Object.prototype.hasOwnProperty.call(node, head)) return;\n if (rest.length === 0) {\n const leaf = node[head];\n if (Array.isArray(leaf)) {\n for (let i = 0; i < leaf.length; i++) cb({ parent: leaf, key: i, value: leaf[i] });\n } else {\n cb({ parent: node, key: head, value: leaf });\n }\n } else {\n walkLeaves(node[head], rest, cb);\n }\n}\n\n// array_key_value: navigate `match.key` (a dot-path, or an array of paths) inside the decoded value\n// and run the nested `match.match` against every leaf it finds (fanning out over arrays at every\n// segment). Mirrors engine-php's recursive array_key_value handling.\nfunction arrayKeyValue(value, matchObj) {\n if (!matchObj || !matchObj.match || value === null || typeof value !== 'object') {\n return false;\n }\n const keys = Array.isArray(matchObj.key) ? matchObj.key : [matchObj.key];\n const sub = matchObj.match;\n\n for (const key of keys) {\n let matched = false;\n walkLeaves(value, String(key).split('.'), ({ value: leaf }) => {\n if (!matched && matchValue(sub.type, leaf, sub.value, sub)) matched = true;\n });\n if (matched) return true;\n }\n return false;\n}\n\n// Match types that operate on the whole container (not per-leaf): `isset` (presence) and\n// `array_in_array` / `array_key_value` (structural). Everything else is a scalar matcher that must\n// fan out over the leaves of an object/array value.\nconst WHOLE_VALUE_MATCH_TYPES = new Set(['isset', 'array_in_array', 'array_key_value']);\n\n// Iteratively collect every scalar (non-object) leaf of a structured value. Iterative + bounded\n// (depth and node caps) so a pathologically deep/large attacker payload STOPS at the bound rather\n// than throwing a RangeError that the per-rule catch would swallow into a fail-open bypass.\nfunction collectLeafValues(root, nodeCap = 20000, maxDepth = 1000) {\n const out = [];\n const stack = [[root, 0]];\n let visited = 0;\n while (stack.length) {\n const [node, depth] = stack.pop();\n if (node === null || node === undefined) continue;\n if (typeof node !== 'object') {\n out.push(node);\n continue;\n }\n if (depth >= maxDepth || visited >= nodeCap) continue;\n visited++;\n if (Array.isArray(node)) {\n for (let i = node.length - 1; i >= 0; i--) stack.push([node[i], depth + 1]);\n } else {\n for (const k of Object.keys(node)) stack.push([node[k], depth + 1]);\n }\n }\n return out;\n}\n\n// Emit a warning at most once per distinct key (keeps a persistent misconfiguration from spamming).\nconst warnedKeys = new Set();\nfunction warnOnce(key, message) {\n if (warnedKeys.has(key)) return;\n warnedKeys.add(key);\n console.warn(message);\n}\n\n// Report an unknown/removed match type once, so a rule referencing it is not silently\n// unenforced (ADR: \"unknown match type → skipped and logged, never silently passed\").\nconst warnedMatchTypes = new Set();\nfunction warnUnsupportedMatchType(type) {\n if (type == null || warnedMatchTypes.has(type)) {\n return;\n }\n warnedMatchTypes.add(type);\n console.warn(\n `[patchstack] Unsupported rule_v2 match type \"${type}\" — condition treated as no-match. ` +\n `Rules relying on it are not enforced by this engine.`\n );\n}\n\n// Internal / private / loopback / link-local / cloud-metadata host check behind the `internal_host`\n// match type. It CANONICALIZES the host before classifying —\n// a textual/prefix check is bypassable by alternate encodings (decimal/hex/octal IPv4, expanded or\n// IPv4-mapped IPv6), which is a classic SSRF evasion. Handles localhost / *.local / GCP metadata\n// names, every IPv4 spelling inet_aton accepts, and IPv6 loopback/link-local/unique-local/mapped.\n/**\n * The host to classify out of a rule parameter's value.\n *\n * `internal_host` was written for the egress phase, where the value IS the destination host. On the\n * request phase the same question arrives as an application parameter, and there the value is almost\n * always a full URL (`?url=http://169.254.169.254/latest/meta-data/`) or a `host:port` pair — neither of\n * which is a hostname, so classifying the raw string answered \"not internal\" for every one of them. A\n * request-phase SSRF rule was therefore expressible, servable and permanently inert: the exact failure\n * this engine has been repeatedly hardened against, in the one match type meant to prevent it.\n *\n * Only the host is extracted; the classification itself is unchanged, so every canonicalization defence\n * (decimal/hex IPv4, expanded and IPv4-mapped IPv6, trailing dots) still applies to what comes out. A\n * value that is already a bare host passes through untouched, which is what keeps the egress path and\n * the built-in default rule behaving exactly as before.\n */\nfunction hostFromValue(value) {\n const raw = String(value ?? '').trim();\n if (raw === '') return '';\n\n // A scheme (`http://`, and deliberately any other) or a protocol-relative URL. Parsing rather than\n // string-slicing is what makes the userinfo evasion (`http://trusted@169.254.169.254/`) resolve to the\n // host actually contacted, and keeps `http://evil.com#@127.0.0.1` resolving to evil.com.\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw) || raw.startsWith('//')) {\n try {\n return new URL(raw.startsWith('//') ? `http:${raw}` : raw).hostname;\n } catch {\n // Unparseable: hand the raw value on, where the host check rejects it rather than guessing.\n return raw;\n }\n }\n\n // `[::1]:8080` — bracketed IPv6 with or without a port.\n if (raw.startsWith('[')) {\n const end = raw.indexOf(']');\n if (end > 0) return raw.slice(1, end);\n }\n\n // `169.254.169.254:80`. Only a single colon followed by digits: a bare IPv6 address has several, and\n // must not have its last group mistaken for a port.\n const colon = raw.indexOf(':');\n if (colon > 0 && raw.indexOf(':', colon + 1) === -1 && /^\\d+$/.test(raw.slice(colon + 1))) {\n return raw.slice(0, colon);\n }\n\n return raw;\n}\n\nfunction isInternalHost(hostname) {\n if (!hostname) return false;\n let host = String(hostname).toLowerCase().replace(/^\\[|\\]$/g, '');\n host = host.replace(/%[^\\]]*$/, ''); // strip an IPv6 zone id (fe80::1%eth0)\n host = host.replace(/\\.$/, ''); // strip a single trailing dot (127.0.0.1.)\n\n if (host === 'localhost' || host.endsWith('.localhost') || host.endsWith('.local')) return true;\n if (host === 'metadata.google.internal') return true;\n\n // IPv6 (contains a colon): expand to 8 groups, then classify on the canonical form.\n if (host.includes(':')) {\n const g = expandIPv6(host);\n if (!g) return false;\n const allZeroHi = g[0] === 0 && g[1] === 0 && g[2] === 0 && g[3] === 0 && g[4] === 0;\n if (allZeroHi && g[5] === 0 && g[6] === 0 && (g[7] === 0 || g[7] === 1)) return true; // ::, ::1 loopback\n if ((g[0] & 0xffc0) === 0xfe80) return true; // link-local fe80::/10\n if ((g[0] & 0xfe00) === 0xfc00) return true; // unique-local fc00::/7\n if (allZeroHi && (g[5] === 0xffff || g[5] === 0)) {\n // IPv4-mapped (::ffff:a.b.c.d) / IPv4-compatible (::a.b.c.d) — classify the embedded v4.\n return isPrivateV4Int((((g[6] << 16) >>> 0) | g[7]) >>> 0);\n }\n return false;\n }\n\n // IPv4 in any inet_aton spelling (dotted quad, shorthand, decimal, hex, octal).\n const v4 = parseIPv4ToInt(host);\n if (v4 !== null) return isPrivateV4Int(v4);\n return false;\n}\n\n// Private / loopback / link-local / this-host / CGNAT test on a 32-bit IPv4 integer.\nfunction isPrivateV4Int(n) {\n const a = (n >>> 24) & 0xff;\n const b = (n >>> 16) & 0xff;\n if (a === 127 || a === 10 || a === 0) return true; // loopback / private / this-host\n if (a === 169 && b === 254) return true; // link-local incl. 169.254.169.254 metadata\n if (a === 172 && b >= 16 && b <= 31) return true; // 172.16.0.0/12\n if (a === 192 && b === 168) return true; // 192.168.0.0/16\n if (a === 100 && b >= 64 && b <= 127) return true; // 100.64.0.0/10 CGNAT (Alibaba metadata 100.100.100.200)\n return false;\n}\n\n// inet_aton-style parse: 1–4 dot-separated parts, each decimal / 0x-hex / 0-octal; the final part\n// fills the remaining low bytes. Returns a uint32, or null if the host isn't a numeric IPv4 form\n// (so ordinary hostnames — which contain letters outside [0-9a-fx] or other chars — return null).\nfunction parseIPv4ToInt(host) {\n if (!/^[0-9a-fx]+(\\.[0-9a-fx]+)*$/i.test(host)) return null;\n const parts = host.split('.');\n if (parts.length > 4) return null;\n const nums = [];\n for (const p of parts) {\n let n;\n if (/^0x[0-9a-f]+$/i.test(p)) n = parseInt(p, 16);\n else if (/^0[0-7]+$/.test(p)) n = parseInt(p, 8);\n else if (/^[0-9]+$/.test(p)) n = parseInt(p, 10);\n else return null; // e.g. a bare hex like \"7f\" (not inet_aton-valid without 0x)\n if (!Number.isInteger(n) || n < 0) return null;\n nums.push(n);\n }\n const last = nums.length - 1;\n let value = 0;\n for (let i = 0; i < last; i++) {\n if (nums[i] > 0xff) return null;\n value += nums[i] * 2 ** (8 * (3 - i));\n }\n const maxLast = 2 ** (8 * (5 - nums.length)) - 1; // the final part fills the remaining low bytes\n if (nums[last] > maxLast) return null;\n value += nums[last];\n if (value < 0 || value > 0xffffffff) return null;\n return value >>> 0;\n}\n\n// Expand an IPv6 string (any `::` compression, optional embedded IPv4 tail) to 8 numeric groups,\n// or null if it isn't valid IPv6. Lets the classifier compare the canonical form, not a string prefix.\nfunction expandIPv6(host) {\n if (!host.includes(':')) return null;\n let s = host;\n // Embedded IPv4 tail (::ffff:127.0.0.1) → convert the dotted part to two hex groups.\n const lastColon = s.lastIndexOf(':');\n const tail = s.slice(lastColon + 1);\n if (tail.includes('.')) {\n const v4 = parseIPv4ToInt(tail);\n if (v4 === null) return null;\n s = s.slice(0, lastColon + 1) + ((v4 >>> 16) & 0xffff).toString(16) + ':' + (v4 & 0xffff).toString(16);\n }\n const halves = s.split('::');\n if (halves.length > 2) return null;\n const head = halves[0] ? halves[0].split(':') : [];\n const back = halves.length === 2 ? (halves[1] ? halves[1].split(':') : []) : null;\n let groups;\n if (back === null) {\n groups = head;\n } else {\n const fill = 8 - head.length - back.length;\n if (fill < 0) return null;\n groups = [...head, ...Array(fill).fill('0'), ...back];\n }\n if (groups.length !== 8) return null;\n const out = [];\n for (const g of groups) {\n if (!/^[0-9a-f]{1,4}$/i.test(g)) return null;\n out.push(parseInt(g, 16));\n }\n return out;\n}\n\n// Bounded scan for three-part JWT candidates. `eyJ` anchors the header: a JWT header is base64url of a\n// JSON object, and every object starting `{\"` encodes to `eyJ`, so it is a cheap and precise filter.\n// The segment bounds keep a hostile body from turning this into an unbounded decode.\n// A candidate is an EXACT three-segment token: the boundaries reject a token that is really a segment\n// of something longer in either direction, since `jwt_claim_equals` promises positive identification and\n// \"the first three segments of this looked like a token\" is not that.\n//\n// They reject a CONTINUATION rather than any adjacent dot — a token followed by a sentence period, or\n// sitting in a URL path, is still a token — so only `.` followed by token characters ends the match.\n//\n// The segment bounds are also the size limit: a payload longer than 8192 characters never becomes a\n// candidate, so nothing oversized reaches the decoder. They fix the segment count too, so a candidate\n// always splits into exactly three parts.\nconst JWT_CANDIDATE =\n /(?<![A-Za-z0-9_-])(?<![A-Za-z0-9_-]\\.)eyJ[A-Za-z0-9_-]{2,2048}\\.[A-Za-z0-9_-]{2,8192}\\.[A-Za-z0-9_-]{0,2048}(?![A-Za-z0-9_-])(?!\\.[A-Za-z0-9_-])/g;\n\n// base64url → base64. `atob` rejects `-` and `_`, and Node's leniency differs, so the conversion is\n// explicit: the node and edge paths must agree about what decodes, or a rule masks on one runtime and\n// not the other.\nfunction base64UrlToBase64(segment) {\n const padded = segment.length % 4 === 0 ? segment : segment + '='.repeat(4 - (segment.length % 4));\n return padded.replace(/-/g, '+').replace(/_/g, '/');\n}\n\n/**\n * Every distinct JWT in `text` whose payload carries an OWN, TOP-LEVEL `claim` exactly equal to `value`.\n *\n * One implementation, used for both detection and redaction. If the matcher and the redactor decided\n * separately, a token could be reported and not masked — a detection that says \"redacted\" over a body\n * that still carries the secret — or masked without being reported.\n *\n * Everything that is not a positive identification is a no-match: not three segments, a payload that\n * does not decode or does not parse, a payload that is not a plain object, an oversized payload, a\n * missing claim, a claim that is not a string, or any other value. `hasOwnProperty` is what makes\n * \"own\" true — without it `constructor` or `toString` would read off the prototype and match nothing\n * the token actually said.\n *\n * No signature verification: the question is what the token CLAIMS to be, not whether it is authentic.\n * A forged claim gains an attacker nothing here — this decides whether OUR secret is leaving.\n */\nexport function jwtClaimSpans(text, claim, value) {\n const found = new Set();\n if (typeof text !== 'string' || typeof claim !== 'string' || claim === '') return [];\n const wanted = String(value);\n\n for (const [token] of text.matchAll(JWT_CANDIDATE)) {\n const parts = token.split('.');\n\n let payload;\n try {\n payload = JSON.parse(base64DecodeUtf8(base64UrlToBase64(parts[1])));\n } catch {\n continue; // undecodable or not JSON — not identified, so not matched\n }\n // `null` matters more than it looks: `hasOwnProperty.call(null, …)` THROWS, and an exception here\n // would leave the engine's per-rule catch to fail the rule open — a leak served because a\n // neighbouring token had the payload `null`. Arrays and primitives simply cannot carry a claim.\n if (payload === null || typeof payload !== 'object' || Array.isArray(payload)) continue;\n // \"Own\" is load-bearing only when something has polluted Object.prototype with a string-valued\n // property — then a payload carrying no `role` at all would otherwise read one off the prototype\n // and be judged on a value the token never contained.\n if (!Object.prototype.hasOwnProperty.call(payload, claim)) continue;\n\n const actual = payload[claim];\n if (typeof actual === 'string' && actual === wanted) found.add(token);\n }\n\n return [...found];\n}\n\n// Report a `when` block that names nothing this engine understands. Fail-open is correct for a scope that\n// cannot be EVALUATED, but a scope that cannot be UNDERSTOOD is an authoring mistake with the opposite\n// consequence: the rule silently applies to every request instead of one route, which for a blocking rule\n// is a false-positive surface across the whole app. Warned once so it is discoverable in a log.\nconst warnedScopes = new Set();\nfunction warnUnrecognisedScope(when) {\n const key = Object.keys(when).sort().join(',');\n if (warnedScopes.has(key)) return;\n warnedScopes.add(key);\n console.warn(\n `[patchstack] Rule scope \\`when: { ${key} }\\` names no supported key — the engine understands ` +\n `\\`method\\` and \\`path\\`. The scope is IGNORED and the rule applies to every request.`\n );\n}\n\n// Route/method scope for a rule's optional `when: { method, path }`. Fail-open: if the scope can't\n// be evaluated, the rule still applies (never silently suppress a rule).\nfunction ruleAppliesTo(when, resolver) {\n try {\n if (when.method === undefined && when.path === undefined && Object.keys(when).length > 0) {\n warnUnrecognisedScope(when);\n }\n if (when.method) {\n const methods = (Array.isArray(when.method) ? when.method : [when.method]).map((m) => String(m).toUpperCase());\n const actual = String(resolver.resolve('server.REQUEST_METHOD')[0] ?? 'GET').toUpperCase();\n if (!methods.includes(actual)) return false;\n }\n if (when.path) {\n const uri = String(resolver.resolve('server.REQUEST_URI')[0] ?? '/');\n if (!pathMatches(String(when.path), uri.split('?')[0])) return false;\n }\n return true;\n } catch {\n return true;\n }\n}\n\n// A `when.path` scope names an ENDPOINT, and no HTTP router decides which endpoint a request reached by\n// comparing the target byte for byte. Measured over raw sockets (so nothing upstream normalized the\n// target first) against Express 4.22, Express 5.2 and Fastify 5, each with one handler on `/api/fetch`:\n//\n// /api/fetch/ runs the handler on Express 404 on Fastify\n// /API/fetch runs the handler on Express 404 on Fastify\n// /api/%66etch 404 on Express runs the handler on Fastify\n//\n// All three run the handler, so all three are the endpoint the scope names and a scope has to cover\n// them; a byte comparison covers only the first. The percent-encoded one is already covered, because\n// `normalizeRequest` url-decodes the target before any rule is evaluated — decoding a second time here\n// would resolve a DOUBLE encoding as well and widen every scope onto requests no router resolves. What\n// is left to fold is case and one trailing slash.\n//\n// Deliberately NOT folded: duplicate slashes, dot segments and an encoded separator (`/api//fetch`,\n// `/api/./fetch`, `/api%2Ffetch`) reach no handler on either router, so folding them would widen every\n// scope in the field on a guess instead of on evidence. (A proxy that merges slashes does it before the\n// app is reached, so the engine already sees the merged path.)\n//\n// The direction of the remaining error is what makes the fold safe. If an app IS case- and slash-strict,\n// the extra requests a folded scope covers 404 anyway, so a rule firing there refuses something already\n// destined to fail. If the app is not strict, a rule that stays quiet lets the exploit through.\nfunction foldPathForScope(value) {\n const out = String(value).toLowerCase();\n // One trailing slash, not a run of them: one is what a router forgives, and `//` is in the not-folded\n // list above.\n return out.endsWith('/') ? out.slice(0, -1) : out;\n}\n\n// `when.path`: an exact path, a glob with `*`, or an explicit `/regex/`.\n//\n// The regex form is delimited by slashes — and EVERY path starts with one, so a path that also ends with\n// one used to be read as a regex. `path: '/admin/'`, the most ordinary way to write a directory, compiled\n// to the unanchored `/admin/` and scoped the rule to every path CONTAINING \"admin\": `/xadminy`,\n// `/admin/users`, all of it. A scope that reads narrow and behaves app-wide is the failure this whole\n// mechanism exists to avoid, so the regex form now has to actually use regex syntax. A delimiter pair\n// around a plain literal is a path, which is what the author typed, and the narrower of the two readings.\nconst REGEX_SYNTAX = /[\\\\^$*+?()[\\]{}|]/;\n\n// The same delimiter-and-flags shape `safeRegExp` compiles, so detecting the form and compiling it\n// cannot disagree about what a pattern is. Detection missed the flagged form entirely before: a scope\n// written `/^\\\\/admin$/i` does not end in a slash, so it was read as a literal path and matched nothing —\n// a rule scoped to a route that cannot exist protects nothing while reporting as scoped.\nconst REGEX_FORM = /^\\/(.+?)\\/([gimsuy]*)$/s;\n\nfunction isRegexForm(pattern) {\n const parts = REGEX_FORM.exec(pattern);\n // Flags alone do not settle it. `/admin/i` reads as a regex but asks for nothing a plain path does not,\n // now that an exact scope folds case — while `/blog/s` is a perfectly ordinary route. So the body has\n // to actually use regex syntax before the pattern is compiled as one, which is the narrower of the two\n // readings and the right way for an ambiguous scope to fail.\n return parts !== null && REGEX_SYNTAX.test(parts[1]);\n}\n\nfunction pathMatches(pattern, path) {\n // An author's own pattern runs against the target as it arrived, unfolded: they wrote the character\n // class and the flags, and silently lowercasing the subject would break a pattern that matches on case.\n if (isRegexForm(pattern)) {\n const re = safeRegExp(pattern);\n return re ? re.test(path) : false;\n }\n const target = foldPathForScope(path);\n const folded = foldPathForScope(pattern);\n if (folded.includes('*')) {\n // A trailing `/*` has to cover the bare directory as well. Measured: `/admin/` runs an Express\n // handler registered at `/admin/*` — and the fold above has already turned that request into\n // `/admin`, so without this the scope would miss the request it is named after. `/admin` itself is\n // then covered too, though it 404s on such a route: the same direction of error as the fold, and the\n // alternative is a scope that stands aside while the handler runs.\n const trailing = folded.endsWith('/*');\n const body = (trailing ? folded.slice(0, -2) : folded).replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '[^?]*');\n const re = safeRegExp('/^' + body + (trailing ? '(?:/[^?]*)?' : '') + '$/');\n return re ? re.test(target) : false;\n }\n return target === folded;\n}\n\n// Drop a default port so `app.com:443` and `app.com` compare equal (a real proxy shape), without\n// dropping a non-default port (so genuine cross-port stays distinguishable).\nfunction normalizeDefaultPort(host) {\n return String(host).toLowerCase().replace(/:(?:80|443)$/, '');\n}\n\n// CSRF primitive: does the request come from a different origin than its own Host? Lenient only when\n// the Origin AND Referer are TRULY ABSENT (a non-browser client). A present-but-opaque `Origin: null`\n// / empty / unparseable value is NOT same-origin — it's the sandboxed-iframe / opaque-origin signal a\n// CSRF attacker supplies, so it is treated as cross-origin.\nfunction isCrossOrigin(resolver) {\n try {\n const host = normalizeDefaultPort(String(resolver.resolve('server.HTTP_HOST')[0] ?? ''));\n if (!host) return false;\n const originRaw = resolver.resolve('server.HTTP_ORIGIN')[0];\n const hasOrigin = originRaw !== undefined && originRaw !== null;\n const src = hasOrigin ? originRaw : resolver.resolve('server.HTTP_REFERER')[0];\n if (src === undefined || src === null) return false; // both absent → lenient\n const s = String(src).trim();\n if (hasOrigin && (s === '' || s.toLowerCase() === 'null')) return true; // present but opaque → cross-origin\n const srcHost = hostFromUrl(s);\n if (srcHost === null) return hasOrigin; // present-but-unparseable Origin → treat as cross-origin\n return normalizeDefaultPort(srcHost) !== host;\n } catch {\n return false;\n }\n}\n\nfunction hostFromUrl(value) {\n try {\n return new URL(value).host.toLowerCase();\n } catch {\n return null;\n }\n}\n\n// Open-redirect primitive (response phase): a 3xx whose Location header points to a DIFFERENT origin\n// than the request's own Host. A relative Location (same-origin) never matches. Needs the request\n// Host, which the response phase threads in via reqCtx. Lenient: no Location, no request Host, or a\n// same-origin / relative target → not flagged (so it can't false-positive without the signal).\nfunction isOffOriginRedirect(resolver) {\n try {\n const status = Number(resolver.resolve('response.status')[0] ?? 0);\n if (status < 300 || status >= 400) return false;\n const location = resolver.resolve('response.header.location')[0];\n if (!location) return false;\n const host = String(resolver.resolve('server.HTTP_HOST')[0] ?? '').toLowerCase();\n if (!host) return false;\n // Resolve the Location the way a browser would before comparing hosts: strip TAB/CR/LF and\n // normalize backslashes to forward slashes (browsers do), then resolve against the request origin\n // as a base. A relative `/path` resolves to our own host (not flagged); a protocol-relative\n // `//evil.com` or backslash `/\\evil.com` resolves off-origin (flagged) — the canonical\n // open-redirect payloads that a base-less `new URL()` used to treat as \"relative & safe\".\n const loc = String(location).replace(/[\\t\\r\\n]/g, '').replace(/\\\\/g, '/');\n let target;\n try {\n target = new URL(loc, 'http://' + host).host.toLowerCase();\n } catch {\n return false; // unresolvable even with a base → not a redirect we can judge\n }\n return normalizeDefaultPort(target) !== normalizeDefaultPort(host);\n } catch {\n return false;\n }\n}\n\n// CORS-reflection primitive (response phase): the response allows credentials AND lets any origin\n// read it — either `Access-Control-Allow-Origin: *`, or it reflects the caller's own Origin (so\n// every origin is allowed) — rather than a fixed allowlisted origin. That combination lets any\n// malicious site read the authenticated response. Needs the request Origin (threaded via reqCtx).\n// Lenient: credentials not allowed, no ACAO, or a fixed (non-reflected, non-*) ACAO → not flagged.\nfunction isReflectedCorsWithCredentials(resolver) {\n try {\n const acac = String(resolver.resolve('response.header.access-control-allow-credentials')[0] ?? '').toLowerCase();\n if (acac !== 'true') return false; // only dangerous when credentials are allowed\n const acao = String(resolver.resolve('response.header.access-control-allow-origin')[0] ?? '');\n if (!acao) return false;\n if (acao === '*') return true; // wildcard + credentials\n if (acao.toLowerCase() === 'null') return true; // `null` + credentials is readable from a sandboxed iframe (Origin: null)\n const origin = String(resolver.resolve('server.HTTP_ORIGIN')[0] ?? '');\n if (!origin) return false;\n return acao === origin; // ACAO echoes the caller's Origin → any origin is allowed\n } catch {\n return false;\n }\n}\n\n// `matchObj` is the full match object; needed by types that read sibling fields\n// (array_key_value reads `key`/`match`). Optional so direct callers/tests can keep\n// using the (type, value, matchVal) signature.\nexport function matchValue(type, value, matchVal, matchObj) {\n if (value === null || value === undefined) {\n if (type === 'isset') {\n return false;\n }\n return false;\n }\n\n // Guard the coercion: String() on a pathologically deep array/object can throw RangeError\n // (stack overflow). Catching it here keeps a hostile nested value from failing the rule open.\n let strValue;\n if (typeof value === 'string') {\n strValue = value;\n } else {\n try {\n strValue = String(value);\n } catch {\n strValue = '';\n }\n }\n\n switch (type) {\n case 'equals':\n return strValue == matchVal;\n\n case 'equals_strict':\n // Type-strict by design (`'1' !== 1`). A rule that wants loose matching should use `equals`.\n return strValue === matchVal;\n\n case 'contains':\n // engine-php exposes `stripos` as an alias of `contains`.\n case 'stripos': {\n const lower = strValue.toLowerCase();\n const target = String(matchVal).toLowerCase();\n return lower.includes(target);\n }\n\n case 'not_contains': {\n const lower = strValue.toLowerCase();\n const target = String(matchVal).toLowerCase();\n return !lower.includes(target);\n }\n\n case 'regex': {\n const regex = safeRegExp(matchVal);\n if (!regex) {\n warnRejectedPatternOnce(matchVal);\n return false;\n }\n return regex.test(strValue);\n }\n\n case 'more_than':\n return Number(strValue) > Number(matchVal);\n\n case 'less_than':\n return Number(strValue) < Number(matchVal);\n\n case 'ctype_digit':\n return ctypeResult(strValue, /^\\d+$/.test(strValue), matchVal);\n\n case 'ctype_alnum':\n return ctypeResult(strValue, /^[\\w$\\u0080-\\uFFFF]+$/.test(strValue), matchVal);\n\n case 'is_numeric':\n return ctypeResult(strValue, !isNaN(strValue) && strValue.trim() !== '', matchVal);\n\n case 'isset':\n return true;\n\n case 'in_array': {\n const arr = (Array.isArray(matchVal) ? matchVal : [matchVal]).map((x) => String(x));\n return arr.includes(strValue);\n }\n\n case 'not_in_array': {\n const arr = (Array.isArray(matchVal) ? matchVal : [matchVal]).map((x) => String(x));\n return !arr.includes(strValue);\n }\n\n case 'array_in_array': {\n if (!Array.isArray(value) || !Array.isArray(matchVal)) {\n return false;\n }\n const target = matchVal.map((x) => String(x));\n return value.some((v) => target.includes(String(v)));\n }\n\n case 'hostname': {\n try {\n const url = new URL(strValue);\n return url.hostname === matchVal;\n } catch {\n return false;\n }\n }\n\n case 'internal_host':\n // SSRF: private / loopback / link-local / cloud-metadata destinations. The value may be a bare\n // host (egress) or a URL / host:port in an application parameter (request) — see `hostFromValue`.\n return isInternalHost(hostFromValue(strValue));\n\n case 'quotes':\n // engine-php exposes `inline_js_xss` as an alias of `quotes`.\n case 'inline_js_xss':\n return /['\"]/.test(strValue);\n\n case 'inline_xss':\n // Attribute-breakout heuristic: a quote AND a `>` or `=` (matches engine-php).\n return /['\"]/.test(strValue) && /[>=]/.test(strValue);\n\n case 'ctype_special': {\n // engine-php strips space, `_`, `-`, `,` then applies the alnum/unicode check,\n // and compares the result to `value` (usually false = \"flag when NOT clean\").\n const stripped = strValue.replace(/[ _\\-,]/g, '');\n return ctypeResult(strValue, /^[\\w$€-￿]*$/.test(stripped), matchVal);\n }\n\n case 'jwt_claim_equals':\n // Detection and redaction share `jwtClaimSpans`, so a token is reported exactly when it is\n // masked. `redact` derives its spans from the same call — see `extractRedactors`.\n return jwtClaimSpans(strValue, matchObj?.claim, matchVal).length > 0;\n\n case 'array_key_value':\n // Navigate `match.key` (dot-path, or array of paths) inside the decoded value and\n // run the nested `match.match` against it — e.g. a json_decode'd body where a\n // specific key must hold a specific value.\n return arrayKeyValue(value, matchObj);\n\n // file_contains (uploaded-file content scanning) is NOT WordPress-specific, but is\n // not yet implemented in the JS engine (needs multipart body access). Kept as an\n // explicit, documented no-match rather than silently hitting `default`.\n case 'file_contains':\n return false;\n\n // WordPress-only match types (current_user_cannot, general_xss, getShortcodeAtts,\n // getBlockAtts) have been removed: they depend on WP capabilities, wp_kses_post, and\n // the shortcode/block parsers, and have no meaning in a JS app. They now fall through\n // here and are reported once instead of pretending to be a no-match.\n default:\n warnUnsupportedMatchType(type);\n return false;\n }\n}\n\nexport class RuleEngine {\n #rules;\n #whitelists;\n #whitelistKeys;\n #onError;\n #reportedErrors = new Set();\n\n constructor({ firewall = [], whitelists = [], whitelist_keys = {}, onError } = {}) {\n this.#rules = firewall;\n this.#whitelists = whitelists;\n this.#whitelistKeys = whitelist_keys;\n this.#onError = onError;\n // A whitelist with no `rule_id` suppresses EVERY rule when its (attacker-reachable) condition\n // trips — almost never intended. And `whitelist_keys` is accepted but not implemented. Warn once\n // for each so a misconfiguration that silently weakens the firewall is visible to the operator.\n if (Array.isArray(whitelists) && whitelists.some((w) => w && Array.isArray(w.rule_v2) && !w.rule_id)) {\n warnOnce(\n 'whitelist-no-rule-id',\n '[patchstack] a whitelist has no `rule_id` — it suppresses ALL rules when it matches. ' +\n 'Scope each whitelist to a specific rule_id, and key it only on values an attacker cannot set.'\n );\n }\n if (whitelist_keys && typeof whitelist_keys === 'object' && Object.keys(whitelist_keys).length > 0) {\n warnOnce('whitelist-keys-unimplemented', '[patchstack] `whitelist_keys` is not implemented and has no effect.');\n }\n }\n\n // A mitigation engine must never take down the app it protects: any error while\n // evaluating a rule is reported and the request is allowed through (fail open). A\n // malformed rule is skipped without aborting the rest of the ruleset.\n #reportError(err) {\n // A host handler that runs takes over reporting. One that THROWS does not: fall through to the\n // built-in logging below, or a rule error would disappear into a broken reporter.\n if (notify(this.#onError, err, 'onError')) {\n return;\n }\n // Default: log once per distinct message so a persistently-broken rule doesn't\n // spam per request (never silently swallow — hosts can pass `onError` for structured logging).\n const key = err && err.message ? err.message : String(err);\n if (this.#reportedErrors.has(key)) {\n return;\n }\n this.#reportedErrors.add(key);\n console.error('[patchstack] rule evaluation error (rule skipped, request allowed):', err);\n }\n\n evaluate(req) {\n let normalizedReq;\n let resolver;\n try {\n normalizedReq = { ...req, ...normalizeRequest(req) };\n resolver = new RequestResolver(normalizedReq);\n } catch (err) {\n this.#reportError(err);\n return { blocked: false, rule: null, message: null }; // fail open\n }\n\n for (const rule of this.#rules) {\n try {\n // Route/method scope: a rule may declare `when: { method, path }` to apply only on matching\n // requests (so an auto-generated per-endpoint rule doesn't fire elsewhere).\n if (rule.when && !ruleAppliesTo(rule.when, resolver)) {\n continue;\n }\n\n const conditions = rule.rule_v2;\n\n if (!Array.isArray(conditions) || conditions.length === 0) {\n continue;\n }\n\n if (this.#evaluateRule(conditions, resolver)) {\n if (this.#isWhitelisted(normalizedReq, rule)) {\n continue;\n }\n\n return {\n blocked: true,\n rule,\n message: rule.message ?? `Blocked by Patchstack WAF rule: ${rule.title ?? rule.id}`\n };\n }\n } catch (err) {\n // Skip this rule and keep evaluating the rest — one bad rule never blocks a\n // request nor shadows the rules after it.\n this.#reportError(err);\n }\n }\n\n return { blocked: false, rule: null, message: null };\n }\n\n #evaluateRule(conditions, resolver) {\n const inclusiveConditions = conditions.filter(c => c.inclusive);\n const nonInclusiveConditions = conditions.filter(c => !c.inclusive);\n\n if (inclusiveConditions.length > 0) {\n let inclusiveHits = 0;\n\n for (const condition of inclusiveConditions) {\n if (this.#evaluateCondition(condition, resolver)) {\n inclusiveHits++;\n }\n }\n\n if (inclusiveHits === inclusiveConditions.length) {\n return true;\n }\n }\n\n for (const condition of nonInclusiveConditions) {\n if (this.#evaluateCondition(condition, resolver)) {\n return true;\n }\n }\n\n return false;\n }\n\n #evaluateCondition(condition, resolver) {\n const { parameter, match, mutations } = condition;\n\n if (parameter === 'rules' && Array.isArray(condition.rules)) {\n return this.#evaluateRule(condition.rules, resolver);\n }\n\n // `cross_origin` needs the request as a whole (Origin/Referer vs Host), not a single resolved\n // parameter — it's the CSRF primitive: true (→ block) when the request comes from another origin.\n if (match && match.type === 'cross_origin') {\n return isCrossOrigin(resolver);\n }\n\n // `off_origin` (response phase): true (→ block) when a 3xx redirects to a different origin than\n // the request Host — the open-redirect primitive. Like cross_origin, it needs the whole resolver.\n if (match && match.type === 'off_origin') {\n return isOffOriginRedirect(resolver);\n }\n\n // `cors_reflected` (response phase): true (→ block) when the response allows credentials and\n // reflects the caller's Origin (or uses `*`) — the CORS-misconfiguration primitive. Needs the\n // whole resolver (request Origin vs response ACAO/ACAC).\n if (match && match.type === 'cors_reflected') {\n return isReflectedCorsWithCredentials(resolver);\n }\n\n // `parameter` may be an array (e.g. [\"get.action\",\"post.action\"]) — the rule_v2 format\n // uses these pervasively to mean \"any of these sources\". Resolve each and OR the\n // candidate values together. (A bare string resolves as a single source.)\n const params = Array.isArray(parameter) ? parameter : [parameter];\n const values = params.flatMap((p) => resolver.resolve(p));\n\n if (values.length === 0) {\n return false;\n }\n\n for (let value of values) {\n if (mutations) {\n value = resolver.applyMutations(mutations, value);\n }\n\n // array_key_value inspects the object as a whole (it navigates a key path),\n // so it must not go through the per-value object iteration below.\n if (match.type === 'array_key_value') {\n if (matchValue(match.type, value, match.value, match)) {\n return true;\n }\n continue;\n }\n\n // A structured (object / array-of-object) value must be inspected at every leaf: a payload\n // nested deeper than a scalar rule expects would otherwise stringify to \"[object Object]\" and\n // evade the match, while the app still reads the live value. Whole-value match types\n // (isset / array_in_array) see the container; scalar matchers fan out over all leaves.\n if (typeof value === 'object' && value !== null) {\n if (WHOLE_VALUE_MATCH_TYPES.has(match.type)) {\n if (matchValue(match.type, value, match.value, match)) return true;\n } else {\n for (const leaf of collectLeafValues(value)) {\n if (matchValue(match.type, leaf, match.value, match)) return true;\n }\n }\n continue;\n }\n\n if (matchValue(match.type, value, match.value, match)) {\n return true;\n }\n }\n\n return false;\n }\n\n #isWhitelisted(req, rule) {\n if (!this.#whitelists || this.#whitelists.length === 0) {\n return false;\n }\n\n for (const whitelist of this.#whitelists) {\n if (!Array.isArray(whitelist.rule_v2)) {\n continue;\n }\n\n if (whitelist.rule_id && whitelist.rule_id !== rule.id) {\n continue;\n }\n\n const resolver = new RequestResolver(req);\n if (this.#evaluateRule(whitelist.rule_v2, resolver)) {\n return true;\n }\n }\n\n return false;\n }\n}\n\nexport const _testExports = { matchValue, safeRegExp, isInternalHost };\n","import { safeBaseUrl } from '../safe-origin.js';\nimport { pulseAuthHeader } from '../../pulse-token.js';\n\nconst DEFAULT_BASE_URL = 'https://api.patchstack.com/monitor/pulse';\nconst DEFAULT_CACHE_TTL = 300_000;\n// Randomly shorten the effective TTL by up to this fraction so many long-lived clients don't all\n// revalidate on the same tick (spreads load / avoids a thundering herd against the rules API).\nconst JITTER_FRACTION = 0.1;\n\n// Per-site rules client for Pulse (npm/JS) apps. The site UUID addresses the site in the path; it is\n// not a credential — the endpoint requires a verified token bound to that same site, so a fetch without\n// one is refused. Fail-open regardless: any error returns success:false + empty rules so\n// createProtection falls back to the disk cache or the bundled rules.\n//\n// Conditional fetch: pass a prior `etag` (persisted with the last cached bundle) and the client\n// sends `If-None-Match`; a 304 returns `{ notModified: true }` so the caller reuses its cache\n// without re-downloading, and a 200 returns the new bundle plus its `etag` to persist. This is a\n// no-op until the rules API emits an ETag and honors If-None-Match — with no ETag in the response\n// the client simply behaves as before (a full fetch every refresh).\nexport class PulseRuleClient {\n #siteUuid;\n #timeoutMs;\n #baseUrl;\n #cacheTtl;\n #cache = null;\n #cacheTime = null;\n #ttlEffective = 0;\n #etag;\n #pulseAuth;\n\n #reportsDetections;\n\n constructor({ siteUuid, baseUrl, cacheTtl, etag, timeoutMs, pulseAuth, reportsDetections } = {}) {\n // Bounded so app STARTUP can't hang on a slow API: hosted platforms fail a deploy whose health\n // check is slow, and we always have a cache/bundled fallback to boot from.\n this.#timeoutMs = Number(timeoutMs) > 0 ? Number(timeoutMs) : 30_000;\n this.#siteUuid = siteUuid ?? process.env.PATCHSTACK_SITE_UUID;\n // Rules are executed policy — refuse a plaintext remote override (see safe-origin.js).\n this.#baseUrl = safeBaseUrl(baseUrl ?? process.env.PATCHSTACK_PULSE_RULES_URL, DEFAULT_BASE_URL, 'rule endpoint');\n this.#cacheTtl = Number.isFinite(cacheTtl) && cacheTtl > 0 ? cacheTtl : DEFAULT_CACHE_TTL;\n this.#etag = etag ?? null;\n this.#pulseAuth = pulseAuth ?? null;\n // Whether this guard reports detections, declared on a request it already makes.\n //\n // Detections are only sent when a rule fires, so silence at the server means one of three things —\n // nothing matched, reporting is off, or reports are not arriving — and nothing distinguishes them.\n // Saying \"reporting is on\" on the rules fetch does, without a new outbound path or any request data:\n // the fetch is already periodic, already authenticated, and already carries this site's identity.\n //\n // A capability, not a timestamp: the server records when IT saw this, because a client clock is a\n // value from outside and \"alive as of\" is exactly the claim a stale or wrong clock would fake.\n this.#reportsDetections = reportsDetections === true;\n if (!this.#siteUuid) {\n throw new Error('Patchstack site UUID is required. Pass { siteUuid } or set PATCHSTACK_SITE_UUID.');\n }\n }\n\n async getRules() {\n const now = Date.now();\n if (this.#cache && this.#cacheTime !== null && now - this.#cacheTime < this.#ttlEffective) {\n return this.#cache;\n }\n const url = `${this.#baseUrl}/rules/${encodeURIComponent(this.#siteUuid)}`;\n try {\n // Sent without an `Authorization` header when no credential resolved or the exchange failed.\n // The server refuses that, and the refusal is handled the same way as any other failure: fall\n // back to cached or bundled rules. Attempting it anyway is deliberate — protection must never\n // hinge on the token path, and the runtime warns at boot when no credential resolves.\n const auth = await pulseAuthHeader(\n { pulseAuth: this.#pulseAuth, endpoint: this.#baseUrl, timeoutMs: this.#timeoutMs },\n fetch,\n );\n const headers = { Accept: 'application/json', ...auth };\n // Claimed only on an authenticated request. Fetching rules must never hinge on getting a token\n // (protection comes first), but CLAIMING a capability may: an unauthenticated request is one whose\n // statements about this site carry no weight, and this header asserts the reassuring thing — that\n // reporting is on.\n //\n // A courtesy, never the guarantee: a client-side gate only removes the accidental case. Anything\n // acting on this header has to require a verified token itself before believing it.\n if (this.#reportsDetections && typeof auth.Authorization === 'string') {\n headers['X-Patchstack-Detections'] = 'enabled';\n }\n if (this.#etag) headers['If-None-Match'] = this.#etag;\n const response = await fetch(url, { method: 'GET', headers, signal: AbortSignal.timeout(this.#timeoutMs) });\n\n if (response.status === 304) {\n this.#touch(now); // revalidated — reset the clock (fresh jitter)\n return this.#cache ?? { success: true, notModified: true, etag: this.#etag, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n if (!response.ok) {\n return { success: false, error: `API returned ${response.status}`, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n const data = await response.json();\n // A 200 that isn't a genuine rule envelope (schema drift, a proxy/interstitial page, an\n // {error} body) must not be treated as \"no rules\". Report failure so the caller falls back to\n // the cache / bundled rules rather than caching an empty bundle.\n if (!data || !Array.isArray(data.firewall)) {\n return { success: false, error: 'unexpected response shape (no firewall array)', firewall: [], whitelists: [], whitelist_keys: {} };\n }\n const result = {\n success: true,\n notModified: false,\n etag: response.headers?.get?.('etag') ?? null,\n firewall: data.firewall,\n whitelists: Array.isArray(data.whitelists) ? data.whitelists : [],\n whitelist_keys: data.whitelist_keys ?? {},\n ...enforcementField(data),\n };\n this.#cache = result;\n this.#etag = result.etag;\n this.#touch(now);\n return result;\n } catch (err) {\n return {\n success: false,\n error: err.name === 'TimeoutError' ? 'Request timed out' : err.message,\n firewall: [], whitelists: [], whitelist_keys: {},\n };\n }\n }\n\n #touch(now) {\n this.#cacheTime = now;\n this.#ttlEffective = this.#cacheTtl - Math.floor(Math.random() * this.#cacheTtl * JITTER_FRACTION);\n }\n\n clearCache() {\n this.#cache = null;\n this.#cacheTime = null;\n this.#etag = null;\n }\n}\n\n/** @param {unknown} data @returns {{ enforcement?: 'block'|'dry-run' }} */\nfunction enforcementField(data) {\n if (!data || typeof data !== 'object') return {};\n const v = data.enforcement ?? data.mode;\n return v === 'block' || v === 'dry-run' ? { enforcement: v } : {};\n}\n","// Web Fetch adapter — runs the node-waf engine on any runtime where a request is a\n// WHATWG `Request` and a handler returns a `Response`: Cloudflare Workers, Bun, Deno,\n// Hono, Next.js edge, and TanStack Start's `server.ts`. This is the surface AI-builder\n// apps actually deploy to, and where require-based instrumentation can't reach.\n//\n// The engine already consumes a runtime-neutral request shape; this adapter builds that\n// shape from a `Request`, so no engine changes are needed beyond keeping the hot path\n// free of Node-only APIs.\nimport { RuleEngine } from './engine.js';\nimport { notify } from '../notify.js';\n\n// Cap how much request body we buffer for inspection. A larger body is left UNSCANNED\n// (fail-open) rather than buffered into memory — matches the node adapter's maxBodyBytes.\nconst MAX_BODY_BYTES = 1024 * 1024;\n\n// Build the engine's request shape from a WHATWG Request. The body is read from a\n// CLONE so the downstream handler still receives an intact request.\nexport async function fromFetchRequest(request, options = {}) {\n const maxBodyBytes = options.maxBodyBytes ?? MAX_BODY_BYTES;\n const url = new URL(request.url);\n const method = (request.method || 'GET').toUpperCase();\n\n const headers = {};\n request.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n\n const query = {};\n for (const [key, value] of url.searchParams) {\n if (key in query) {\n query[key] = Array.isArray(query[key]) ? [...query[key], value] : [query[key], value];\n } else {\n query[key] = value;\n }\n }\n\n let rawBody = '';\n if (method !== 'GET' && method !== 'HEAD') {\n rawBody = await readCappedText(request, maxBodyBytes);\n }\n\n const contentType = headers['content-type'] || '';\n let body = {};\n let files;\n if (rawBody) {\n const parsed = parseBody(rawBody, contentType);\n body = parsed.body;\n files = parsed.files;\n }\n\n const uri = url.pathname + url.search;\n const forwarded =\n headers['cf-connecting-ip'] || headers['x-forwarded-for'] || headers['x-real-ip'] || '';\n\n return {\n method,\n url: uri,\n originalUrl: uri,\n query,\n body,\n files,\n headers,\n ip: forwarded.split(',')[0].trim(),\n cookies: parseCookies(headers.cookie),\n // Verbatim body text: preserves literal keys (e.g. `__proto__`) that JSON.stringify\n // drops, so prototype-pollution rules on `raw` are robust.\n _rawBody: rawBody\n };\n}\n\n// Read a request body as text for inspection. Memory is bounded by a hard ceiling (4× the scan\n// cap): a body whose declared Content-Length exceeds it is left UNSCANNED (fail-open). Within the\n// ceiling, an oversize body is TRUNCATED to `max` and its prefix is still scanned — so a\n// front-loaded payload is caught — rather than being discarded outright. Reads a clone so the\n// downstream handler keeps an intact body. (`max` is compared in bytes against Content-Length; the\n// prefix slice is by character, which can only over-scan a multibyte body — the safe direction.)\nasync function readCappedText(request, max) {\n // Do NOT skip scanning based on a declared Content-Length: an attacker can declare a huge length\n // (or none) to dodge inspection while sending a small exploit body. Always stream-scan the prefix\n // up to `max` (buffering is bounded to `max`; the rest is drained but not retained). Anything past\n // the cap is unscanned — the documented prefix-scan tradeoff — but the body is never skipped whole.\n let clone;\n try {\n clone = request.clone();\n } catch {\n return '';\n }\n\n // Stream the read so a body WITHOUT a Content-Length can't buffer unbounded: retain only up to the\n // scan cap (`max`) for inspection, but keep draining to completion so the original request stays\n // intact. A front-loaded payload is still caught; anything past the cap is not scanned (same\n // partial-scan tradeoff as before, now with a hard memory bound regardless of Content-Length).\n const body = clone.body;\n if (body && typeof body.getReader === 'function') {\n const reader = body.getReader();\n const chunks = [];\n let buffered = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value || buffered >= max) continue; // keep draining, stop buffering past the cap\n const take = Math.min(value.byteLength, max - buffered);\n chunks.push(take === value.byteLength ? value : value.subarray(0, take));\n buffered += take;\n }\n } catch {\n return '';\n }\n try {\n return new TextDecoder().decode(concatChunks(chunks, buffered));\n } catch {\n return '';\n }\n }\n\n // No readable stream — fall back to text() with the post-read guard.\n try {\n const text = await clone.text();\n return text.length > max ? text.slice(0, max) : text;\n } catch {\n return '';\n }\n}\n\nfunction concatChunks(chunks, total) {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const c of chunks) {\n out.set(c, offset);\n offset += c.byteLength;\n }\n return out;\n}\n\n// Minimal multipart/form-data parser: enough to expose field names + values (so `post.<field>`\n// and `raw` rules match uploads, e.g. a `__proto__` field name) and file metadata (filename via\n// `files.<field>`). We only need the textual structure, not the binary file contents.\n// Parse a request body into { body, files } for parameter-scoped rules. Content-type detection is\n// deliberately permissive: many AI-built apps `JSON.parse(await req.text())` regardless of the\n// declared type, so a JSON body arriving as `application/vnd.api+json`, `application/ld+json`,\n// `text/plain`, `application/csp-report`, or with NO content-type must still populate post.<field>\n// (otherwise a field-scoped rule silently resolves to nothing). Unrecognized/binary bodies stay `{}`\n// and are still matchable via `raw`.\nexport function parseBody(rawBody, contentType) {\n const ct = String(contentType || '').toLowerCase();\n const isJson = ct.includes('application/json') || /\\+json\\b/.test(ct);\n const isForm = ct.includes('application/x-www-form-urlencoded');\n const isMultipart = ct.includes('multipart/form-data');\n // \"ambiguous\" = a type an app commonly parses as JSON/form even though it isn't declared as such.\n const isAmbiguous = ct === '' || ct.startsWith('text/plain') || ct.includes('csp-report') || ct.includes('/json');\n\n if (isMultipart) {\n const boundary = /boundary=(\"?)([^\";]+)\\1/i.exec(contentType)?.[2];\n if (boundary) return parseMultipart(rawBody, boundary);\n return { body: {}, files: undefined };\n }\n if (isForm) {\n const body = {};\n for (const [k, v] of new URLSearchParams(rawBody)) body[k] = k in body ? [].concat(body[k], v) : v;\n return { body, files: undefined };\n }\n if (isJson || isAmbiguous) {\n try {\n const parsed = JSON.parse(rawBody);\n if (parsed && typeof parsed === 'object') return { body: parsed, files: undefined };\n } catch {\n /* not JSON — leave body empty; `raw`/`all` still see the verbatim text */\n }\n }\n return { body: {}, files: undefined };\n}\n\nexport function parseMultipart(rawBody, boundary) {\n const body = {};\n const files = {};\n for (const part of rawBody.split('--' + boundary)) {\n // Tolerate both CRLF and LF-only line endings (some clients/proxies send bare \\n).\n const sep = /\\r?\\n\\r?\\n/.exec(part);\n if (!sep) continue;\n const rawHeaders = part.slice(0, sep.index);\n const disposition = /content-disposition:[^\\r\\n]*/i.exec(rawHeaders)?.[0];\n if (!disposition) continue;\n const name = /name=\"([^\"]*)\"/i.exec(disposition)?.[1];\n if (name == null) continue;\n const content = part.slice(sep.index + sep[0].length).replace(/\\r?\\n$/, '');\n const filename = /filename=\"([^\"]*)\"/i.exec(disposition)?.[1];\n if (filename !== undefined) {\n // Capture the part's declared content-type and CONTENT (not just the filename), so rules can\n // inspect an upload's bytes (files.<name>.content) and detect a declared-vs-actual type\n // mismatch (files.<name>.mismatch). The content rides inside the already-capped rawBody.\n const partType = /content-type:\\s*([^\\r\\n;]+)/i.exec(rawHeaders)?.[1]?.trim() || '';\n const file = { filename, type: partType, content };\n files[name] = name in files ? [].concat(files[name], file) : file;\n } else {\n body[name] = name in body ? [].concat(body[name], content) : content;\n }\n }\n return { body, files };\n}\n\nfunction parseCookies(header) {\n const cookies = {};\n if (!header) {\n return cookies;\n }\n for (const pair of header.split(';')) {\n const idx = pair.indexOf('=');\n if (idx === -1) {\n continue;\n }\n cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n return cookies;\n}\n\nfunction defaultBlockResponse(result) {\n return new Response(\n JSON.stringify({\n error: 'Blocked by Patchstack WAF',\n message: result.message,\n timestamp: new Date().toISOString()\n }),\n { status: 403, headers: { 'content-type': 'application/json' } }\n );\n}\n\n/**\n * Returns a guard `(request) => Promise<Response|null>`. A `Response` means blocked;\n * `null` means allowed — the caller should proceed to its handler. Accepts either a\n * rules bundle (`{ firewall, whitelists, whitelist_keys }`) or a `RuleEngine` instance.\n * Fails open: a rule that throws never blocks the request.\n */\nexport function createFetchMiddleware(rulesData, options = {}) {\n const engine =\n rulesData && typeof rulesData.evaluate === 'function'\n ? rulesData\n : new RuleEngine(rulesData);\n\n const guard = async (request) => {\n let req;\n let result;\n try {\n req = await fromFetchRequest(request); // shaping inside the try — a bad/relative request.url must fail open\n result = engine.evaluate(req);\n } catch (err) {\n notify(options.onError, err, 'onError');\n return null; // fail open\n }\n\n if (result.blocked) {\n // Contained: this runs after the block decision and before the block response is built, so an\n // escaping throw would replace the 403 with the callback's exception — reporting code deciding\n // the enforcement outcome.\n notify(options.onBlock, {\n rule: result.rule,\n message: result.message,\n request: { method: req.method, url: req.url, ip: req.ip }\n }, 'onBlock');\n return (options.response || defaultBlockResponse)(result);\n }\n\n return null;\n };\n\n guard.engine = engine;\n return guard;\n}\n\n/**\n * Wrap a fetch handler so every request is screened first. One-line hook:\n * export default { fetch: wrapFetchHandler(app.fetch, rulesData) }\n */\nexport function wrapFetchHandler(handler, rulesData, options = {}) {\n const guard = createFetchMiddleware(rulesData, options);\n const wrapped = async (request, ...rest) => {\n const blocked = await guard(request);\n return blocked ?? handler(request, ...rest);\n };\n wrapped.engine = guard.engine;\n return wrapped;\n}\n","// Raw Node.js / Connect adapter — for traditional Node servers where the request is a\n// `http.IncomingMessage` and the body is NOT already parsed (plain `http`, Connect,\n// Express without a body-parser, Fastify via its Node req). It buffers the body once,\n// builds the engine's request shape, and blocks or calls `next()`.\n//\n// This complements the Express `createMiddleware` (which assumes `req.body`/`req.query`\n// are already populated) and the Web-Fetch adapter (Workers/edge). Mount it FIRST, before\n// any body-parser — it consumes the stream and exposes the parsed body as `req.body`.\nimport { RuleEngine } from './engine.js';\nimport { parseBody } from './fetch.js';\nimport { notify } from '../notify.js';\n\n// Build the engine's request shape from a Node IncomingMessage + its raw body text.\nexport function fromNodeRequest(req, rawBody = '') {\n const method = (req.method || 'GET').toUpperCase();\n\n const headers = {};\n for (const [key, value] of Object.entries(req.headers || {})) {\n headers[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value;\n }\n\n // An unusual Host header or req.url can make `new URL` throw; shaping must never crash the\n // request (fail-open), so fall back to a safe base.\n const host = headers.host || 'localhost';\n let url;\n try {\n url = new URL(req.url || '/', `http://${host}`);\n } catch {\n try {\n url = new URL(req.url || '/', 'http://localhost');\n } catch {\n url = new URL('http://localhost/');\n }\n }\n\n const query = {};\n for (const [key, value] of url.searchParams) {\n if (key in query) {\n query[key] = Array.isArray(query[key]) ? [...query[key], value] : [query[key], value];\n } else {\n query[key] = value;\n }\n }\n\n const contentType = headers['content-type'] || '';\n let body = {};\n let files;\n if (rawBody) {\n // Same permissive content-type handling as the fetch adapter (+json / text/plain / no-CT bodies\n // still populate post.<field>; multipart exposes field + file metadata) on a raw-Node server too.\n const parsed = parseBody(rawBody, contentType);\n body = parsed.body;\n files = parsed.files;\n }\n\n const uri = url.pathname + url.search;\n const forwarded =\n headers['cf-connecting-ip'] || headers['x-forwarded-for'] || headers['x-real-ip'] || '';\n\n return {\n method,\n url: uri,\n originalUrl: uri,\n query,\n body,\n files,\n headers,\n ip: forwarded.split(',')[0].trim() || (req.socket && req.socket.remoteAddress) || '',\n cookies: parseCookies(headers.cookie),\n // Verbatim body text: preserves literal keys (e.g. `__proto__`) that JSON.stringify drops.\n _rawBody: rawBody\n };\n}\n\nfunction parseCookies(header) {\n const cookies = {};\n if (!header) {\n return cookies;\n }\n for (const pair of header.split(';')) {\n const idx = pair.indexOf('=');\n if (idx === -1) {\n continue;\n }\n cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n return cookies;\n}\n\nfunction defaultBlock(res, result) {\n res.statusCode = 403;\n res.setHeader('content-type', 'application/json');\n res.end(\n JSON.stringify({\n error: 'Blocked by Patchstack WAF',\n message: result.message,\n timestamp: new Date().toISOString()\n })\n );\n}\n\n/**\n * Connect/Express-style middleware `(req, res, next)` that buffers the body itself.\n * Accepts a `RuleEngine` instance or a `{ firewall, whitelists, whitelist_keys }` bundle.\n * Fails open: an engine error (or oversized body) never blocks the request.\n * Options: `{ maxBodyBytes = 1MiB, onBlock, onError, response }`.\n */\nexport function createNodeMiddleware(rulesData, options = {}) {\n const engine =\n rulesData && typeof rulesData.evaluate === 'function' ? rulesData : new RuleEngine(rulesData);\n const maxBytes = options.maxBodyBytes ?? 1024 * 1024;\n\n return function guard(req, res, next) {\n const chunks = [];\n let size = 0;\n let overflow = false;\n\n req.on('data', (chunk) => {\n size += chunk.length;\n if (size > maxBytes) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n\n req.on('error', (err) => next(err));\n\n req.on('end', () => {\n let result;\n let shaped;\n try {\n const rawBody = overflow ? '' : Buffer.concat(chunks).toString('utf8');\n shaped = fromNodeRequest(req, rawBody); // shaping is inside the try too — never crash\n result = engine.evaluate(shaped);\n } catch (err) {\n notify(options.onError, err, 'onError');\n return next(); // fail open\n }\n\n if (result.blocked) {\n // Contained, as on the fetch path: a throw here would replace the block response with the\n // callback's exception.\n notify(options.onBlock, {\n rule: result.rule,\n message: result.message,\n request: { method: shaped.method, url: shaped.url, ip: shaped.ip }\n }, 'onBlock');\n return (options.response || defaultBlock)(res, result);\n }\n\n // Expose the parsed body downstream so a body-parser isn't also required.\n req.body = shaped.body;\n next();\n });\n };\n}\n","// Egress guard MECHANISM only. Wraps the app's outbound calls so they can be screened; the\n// block DECISION is delegated to a caller-supplied `shouldBlock` predicate (which the runtime\n// builds from egress-phase rules — see defaults.js). No policy is hardcoded here.\n// WinterCG-first: always wraps the global `fetch` (Node 18+, Workers, Bun, Deno). On Node it\n// ALSO patches `node:http`/`node:https` so outbound calls made via those modules (axios, got,\n// the raw http client, …) — which never touch `fetch` — are screened too.\n\n/**\n * @param {{ shouldBlock: (url:string, host:string|null, method:string)=>boolean,\n * onBlock?: (info:{url:string,host:string|null,method:string})=>void,\n * dnsScreen?: boolean,\n * lookup?: Function }} opts\n * @returns {Promise<() => void>} uninstall (restores every patched surface)\n */\nimport { notify } from './notify.js';\n\nexport async function installEgressGuard({ shouldBlock, onBlock, onSkip, dnsScreen = true, lookup, allowHosts } = {}) {\n const restores = [];\n if (typeof shouldBlock !== 'function') return () => {};\n const exempt = new Set((allowHosts ?? []).map((h) => String(h).toLowerCase()));\n\n const block = (url, host, method) => {\n if (!shouldBlock(url, host, method)) return false;\n // Reported AFTER the decision and contained, because this call sits between deciding to block and\n // saying so. An escaping throw would replace a controlled block with the callback's exception, which\n // hands the enforcement outcome to reporting code — the inverse of what a block is for.\n notify(onBlock, { url, host, method }, 'onEgressBlock');\n return true;\n };\n\n // DNS resolution, shared by the fetch wrapper and the node http path. Instead of trusting the\n // hostname, resolve it and check the resolved address(es). On the node path we also PIN the socket\n // to the vetted IP (no time-of-check/use gap); on fetch we can't pin without a custom undici\n // dispatcher, so we screen the resolution but a re-resolve at connect is a residual window.\n // Needs node:dns + node:net; absent on edge runtimes, where the hostname rules still apply.\n let screen = null;\n if (dnsScreen) {\n try {\n const resolveLookup = lookup ?? (await import('node:dns')).lookup;\n const { isIP } = await import('node:net');\n if (typeof resolveLookup === 'function' && typeof isIP === 'function') {\n screen = { lookup: resolveLookup, isIP, isExempt: (h) => exempt.has(String(h).toLowerCase()) };\n }\n } catch {\n screen = null; // no node:dns/net here — skip, hostname rules still apply\n }\n }\n\n // A resolver failure means the destination was NOT screened by IP — the hostname check alone let it\n // through. That's a real (if rare) coverage hole, so report it via onSkip instead of failing open\n // silently. Still fail-open: a broken resolver must not take the app's outbound traffic down.\n const skip = (reason, detail) => notify(onSkip, { phase: 'egress', reason, detail }, 'onSkip');\n\n // True when a hostname resolves to a disallowed address. Fail-open: any resolver error → false.\n const resolvesToDisallowed = (url, host, method) =>\n new Promise((resolve) => {\n if (!screen) {\n // No node:dns/net here (edge runtime) or screening disabled — hostname rules only.\n if (host && dnsScreen) skip('resolver-unavailable', { host });\n return resolve(false);\n }\n if (!host || screen.isIP(host) !== 0 || screen.isExempt(host)) return resolve(false);\n try {\n screen.lookup(host, { all: true }, (err, addresses) => {\n if (err || !Array.isArray(addresses)) { skip('resolver-failed', { host }); return resolve(false); }\n for (const a of addresses) {\n const ip = a && typeof a === 'object' ? a.address : a;\n if (ip && block(url, ip, method)) return resolve(true);\n }\n resolve(false);\n });\n } catch {\n skip('resolver-failed', { host });\n resolve(false);\n }\n });\n\n // 1. global fetch — synchronous install, so it's active the instant this returns (no startup race).\n const originalFetch = globalThis.fetch;\n if (typeof originalFetch === 'function' && !originalFetch.__patchstackGuarded) {\n const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\n const MAX_REDIRECTS = 20;\n\n // Screen one outbound URL: hostname/allowlist/literal-IP check, then a DNS-resolution check for\n // real hostnames. Throws if the destination is disallowed.\n const screenUrl = async (u, method) => {\n let host = null;\n try {\n host = new URL(u).hostname;\n } catch {\n host = null;\n }\n if (block(u, host, method) || (await resolvesToDisallowed(u, host, method))) {\n throw new Error(`Patchstack blocked an outbound request to a disallowed address: ${host ?? u}`);\n }\n };\n\n const guarded = async (input, init) => {\n let cur;\n try {\n cur = new Request(input, { ...(init || {}), redirect: 'manual' });\n } catch {\n return originalFetch(input, init); // odd input we can't normalize — fail open, don't break the caller\n }\n const callerRedirect = (init && init.redirect) || (input && input.redirect) || 'follow';\n\n let url = cur.url;\n let method = cur.method;\n await screenUrl(url, method);\n\n // Caller manages redirects itself (manual/error) → screen once, hand back the raw response.\n if (callerRedirect !== 'follow') return originalFetch(input, init);\n\n // Otherwise follow redirects ourselves so EVERY hop is screened. Native `follow` re-resolves\n // internally and would let a 3xx to an internal address slip past the initial check — SSRF via\n // an open redirect. Buffer the body once (a stream can't be re-read) so 307/308 can replay it.\n const headers = new Headers(cur.headers);\n const signal = cur.signal;\n let body = method === 'GET' || method === 'HEAD' ? undefined : await cur.clone().arrayBuffer();\n\n for (let hop = 0; ; hop++) {\n const resp = await originalFetch(\n hop === 0 ? cur : new Request(url, { method, headers, body, redirect: 'manual', signal }),\n );\n const location = REDIRECT_STATUSES.has(resp.status) ? resp.headers.get('location') : null;\n if (!location) return resp;\n if (hop >= MAX_REDIRECTS) throw new Error('Patchstack blocked an outbound request: too many redirects');\n\n const next = new URL(location, url).href;\n // Fetch redirect semantics: 303, and 301/302 on a POST, become a bodyless GET.\n if (resp.status === 303 || ((resp.status === 301 || resp.status === 302) && method === 'POST')) {\n method = 'GET';\n body = undefined;\n headers.delete('content-type');\n headers.delete('content-length');\n }\n // Drop credentials on a cross-origin hop, mirroring the browser.\n if (new URL(next).origin !== new URL(url).origin) {\n headers.delete('authorization');\n headers.delete('cookie');\n }\n await screenUrl(next, method);\n url = next;\n }\n };\n guarded.__patchstackGuarded = true;\n globalThis.fetch = guarded;\n restores.push(() => {\n if (globalThis.fetch === guarded) globalThis.fetch = originalFetch;\n });\n }\n\n // node:http / node:https — best-effort; absent on Workers/Deno-without-node (import throws).\n for (const moduleName of ['node:http', 'node:https']) {\n try {\n const mod = await import(moduleName);\n const restore = patchHttpModule(mod.default ?? mod, block, screen);\n if (restore) restores.push(restore);\n } catch {\n /* module not available on this runtime — skip */\n }\n }\n\n // WebSocket egress is intentionally NOT screened. The WebSocket constructor is synchronous, so\n // the only check possible inline is a textual hostname match — which can't offer the\n // DNS-resolution guarantee the fetch and node:http/https paths give (a name that resolves to an\n // internal address would pass). A connection-pinning dispatcher could close that, but the\n // server-side, attacker-controlled-WebSocket sink is rare, and a partial hostname-only check\n // over-promises the control. Outbound SSRF screening covers fetch + node:http/https.\n\n return () => {\n for (const restore of restores) {\n try {\n restore();\n } catch {\n /* ignore */\n }\n }\n };\n}\n\n// Wrap http(s).request/get so a blocked destination throws before the socket opens.\nfunction patchHttpModule(http, block, screen) {\n if (!http || typeof http.request !== 'function' || http.__patchstackGuarded) return null;\n const originalRequest = http.request;\n const originalGet = http.get;\n\n const wrap = (original) =>\n function (...args) {\n const target = extractHttpTarget(args);\n if (target && block(target.url, target.host, target.method)) {\n throw new Error(`Patchstack blocked an outbound request to a disallowed address: ${target.host ?? target.url}`);\n }\n // DNS screen: only for real hostnames (a literal IP was already covered by the check above),\n // and skip an explicitly allowlisted host (the operator trusts it — don't second-guess its DNS).\n if (target && screen && target.host && screen.isIP(target.host) === 0 && !screen.isExempt(target.host)) {\n try {\n args = withScreeningLookup(args, target, block, screen.lookup);\n } catch {\n /* injection failed — proceed unscreened (fail-open) */\n }\n }\n return original.apply(this, args);\n };\n\n const guardedRequest = wrap(originalRequest);\n http.request = guardedRequest;\n let guardedGet;\n if (typeof originalGet === 'function') {\n guardedGet = wrap(originalGet);\n http.get = guardedGet;\n }\n http.__patchstackGuarded = true;\n\n return () => {\n // Only restore if our wrapper is still installed — don't clobber a wrapper another library\n // (an APM agent, etc.) layered on top of us after install.\n if (http.request === guardedRequest) http.request = originalRequest;\n if (guardedGet && http.get === guardedGet) http.get = originalGet;\n delete http.__patchstackGuarded;\n };\n}\n\n// http.request accepts (url), (url, options), or (options) — with an optional trailing callback.\nfunction extractHttpTarget(args) {\n const first = args[0];\n try {\n if (typeof first === 'string' || first instanceof URL) {\n const url = new URL(String(first));\n const opts = args.find((a) => a && typeof a === 'object' && !(a instanceof URL));\n return { url: url.href, host: url.hostname, method: (opts && opts.method) || 'GET' };\n }\n if (first && typeof first === 'object') {\n // Node defaults an absent host to localhost — reflect that so the block/screen see a real target.\n const host = normalizeHost(first.hostname || first.host) || 'localhost';\n const protocol = first.protocol || 'http:';\n const port = first.port ? `:${first.port}` : '';\n const path = first.path || '/';\n return { url: `${protocol}//${host}${port}${path}`, host, method: first.method || 'GET' };\n }\n } catch {\n /* fall through */\n }\n return null;\n}\n\n// Extract the bare host from a node http(s) options `host`/`hostname`, WITHOUT mangling IPv6.\n// `[::1]:8080` → `::1`, bare `::1`/`fe80::1` → unchanged, `example.com:443` → `example.com`.\n// (isInternalHost strips brackets and matches ::1 / fe80: / fc / fd, so this must not corrupt them.)\nfunction normalizeHost(raw) {\n const host = String(raw || '').trim();\n const bracketed = /^\\[([^\\]]+)\\]/.exec(host);\n if (bracketed) return bracketed[1];\n if ((host.match(/:/g) || []).length > 1) return host; // bare IPv6 — no host:port to split\n return host.split(':')[0];\n}\n\n// Given the addresses a hostname resolved to, return the first one the policy blocks (else null).\n// Reuses the same `block` predicate as the hostname check, so egress rules + allowlist apply to\n// the resolved IP too. Exported for tests.\nexport function screenResolved(addresses, target, block) {\n for (const a of addresses || []) {\n const ip = a && typeof a === 'object' ? a.address : a;\n if (ip && block(target.url, ip, target.method)) return ip;\n }\n return null;\n}\n\n// Build a DNS `lookup` that screens every resolved address before the socket connects, then hands\n// back the vetted addresses (pinning the connection to what we checked). A blocked address errors\n// the connection; a resolver error or our own failure falls through to normal resolution (fail-open).\nfunction withScreeningLookup(args, target, block, lookup) {\n const screeningLookup = (hostname, options, callback) => {\n let opts = options;\n let cb = callback;\n if (typeof opts === 'function') {\n cb = opts;\n opts = {};\n }\n if (!opts || typeof opts !== 'object') opts = {};\n try {\n lookup(hostname, { ...opts, all: true }, (err, addresses) => {\n if (err) return cb(err);\n const list = Array.isArray(addresses) ? addresses : [];\n const blocked = screenResolved(list, target, block);\n if (blocked) {\n return cb(new Error(`Patchstack blocked an outbound request to a disallowed address: ${target.host} resolved to ${blocked}`));\n }\n if (opts.all) return cb(null, list);\n const first = list[0];\n if (!first) return cb(new Error(`Patchstack: could not resolve ${hostname}`));\n return cb(null, first.address, first.family);\n });\n } catch {\n // Our screening threw — fall back to a plain resolution so we never break a request ourselves.\n try {\n lookup(hostname, opts, cb);\n } catch {\n cb(new Error(`Patchstack: lookup failed for ${hostname}`));\n }\n }\n };\n return injectLookupOption(args, screeningLookup);\n}\n\n// Return a new args array for http(s).request with our `lookup` set on the options object (cloned,\n// never mutating the caller's object), inserting an options object when the call didn't pass one.\nfunction injectLookupOption(args, lookup) {\n const first = args[0];\n if (first && typeof first === 'object' && !(first instanceof URL)) {\n return [{ ...first, lookup }, ...args.slice(1)];\n }\n const rest = args.slice(1);\n const optIdx = rest.findIndex((a) => a && typeof a === 'object' && !(a instanceof URL));\n if (optIdx !== -1) {\n const next = [...rest];\n next[optIdx] = { ...rest[optIdx], lookup };\n return [first, ...next];\n }\n const cbIdx = rest.findIndex((a) => typeof a === 'function');\n if (cbIdx === -1) return [first, { lookup }, ...rest];\n return [first, ...rest.slice(0, cbIdx), { lookup }, ...rest.slice(cbIdx)];\n}\n","// Default response- and egress-phase rule bundles, as JS constants (not JSON files) so\n// tsup bundles them into dist/protect.js without a copy step. These are just rules —\n// override per app via createProtection({ responseRules, egressRules }) or extend by\n// adding phase-tagged rules to the delivered bundle. Patterns are high-precision\n// (low false positive): structural markers a real secret has and normal content doesn't.\n//\n// Each rule carries a `prefilter`: cheap literal anchor(s) that MUST appear in the body for the\n// regex to have any chance of matching. The response screener runs the (expensive) regex only when\n// an anchor is present (case-insensitive), so a body with no candidate — the common case — skips\n// the scan entirely. This cuts CPU/latency and shrinks the regex/ReDoS surface. (Honored by the\n// response phase once the prefilter mechanism lands; a no-op before that.)\n\n// Response phase — secret / info exposure. Default action `redact` masks only the\n// offending span and still serves the page (a legit response that leaks one key gets\n// that key masked, not withheld). Use `action: \"block\"` to withhold the whole response.\nexport const DEFAULT_RESPONSE_RULES = [\n {\n id: 'resp-private-key',\n title: 'Private key in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['PRIVATE KEY'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/' } }]\n },\n {\n id: 'resp-aws-access-key',\n title: 'AWS access key id in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['AKIA', 'ASIA'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\\\b/' } }]\n },\n {\n id: 'resp-gcp-api-key',\n title: 'Google API key in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['AIza'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\bAIza[0-9A-Za-z_-]{35}\\\\b/' } }]\n },\n {\n id: 'resp-vendor-api-key',\n title: 'Vendor API key / token in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n // High-signal, prefix-anchored provider tokens that never legitimately appear in a response\n // body: Stripe (sk_live_/rk_live_), GitHub (gh[opsu]_ / github_pat_), GitLab (glpat-),\n // Slack (xox[baprs]-), Anthropic (sk-ant-), Google OAuth (ya29.), npm (npm_). Trailing\n // (?![0-9A-Za-z]) instead of \\\\b since some tokens end in - / _ .\n prefilter: ['sk_live_', 'rk_live_', 'ghp_', 'gho_', 'ghs_', 'ghu_', 'github_pat_', 'glpat-', 'xox', 'sk-ant-', 'ya29.', 'npm_'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:sk_live_[0-9A-Za-z]{16,}|rk_live_[0-9A-Za-z]{16,}|gh[opsu]_[0-9A-Za-z]{36}|github_pat_[0-9A-Za-z_]{60,}|glpat-[0-9A-Za-z_-]{20,}|xox[baprs]-[0-9A-Za-z-]{10,}|sk-ant-[0-9A-Za-z_-]{20,}|ya29\\\\.[0-9A-Za-z_-]{20,}|npm_[0-9A-Za-z]{36})(?![0-9A-Za-z])/' } }]\n },\n {\n id: 'resp-supabase-secret-key',\n title: 'Supabase secret key in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n // Supabase's opaque key format, which is not a JWT and so is not covered by the JWT rule below.\n // `sb_secret_` is the elevated one — documented as full access, bypassing Row Level Security,\n // backend-only — while `sb_publishable_` is meant for public clients and must pass untouched.\n // Both key systems are live at once: new keys sit alongside the legacy `anon` / `service_role`\n // JWTs rather than replacing them, so this rule and the JWT rule cover different halves.\n //\n // The documented grammar is the prefix, 22 base64url characters, `_`, then an 8-character\n // base64url checksum. Matching it exactly is what keeps the mask on the key and off the response\n // around it: a variable-length suffix would either run past the key into adjacent syntax or stop\n // short and leave the tail of a real key visible. The trailing guard rejects a longer run of key\n // characters, which is not this format.\n prefilter: ['sb_secret_'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\bsb_secret_[A-Za-z0-9_-]{22}_[A-Za-z0-9_-]{8}(?![A-Za-z0-9_-])/' } }]\n },\n {\n id: 'resp-supabase-service-role-key',\n title: 'Supabase service_role key in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n // Scoped to the one role that must never leave: `service_role` bypasses Row Level Security\n // entirely. A JWT in a response body is not inherently a leak — the Supabase `anon` key is public\n // by design and a login response carries the user's own access token — so this decides on the\n // decoded payload rather than on the shape of the token.\n //\n // `jwt_claim_equals` also yields the matching token spans, which is what lets `redact` mask those\n // tokens and serve the rest of the response.\n //\n // A broad any-JWT rule is a reasonable thing to want; it belongs in the managed hardening bundle as\n // an opt-in, observe-only rule rather than an on-by-default mask.\n prefilter: ['eyJ'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'jwt_claim_equals', claim: 'role', value: 'service_role' } }]\n },\n {\n id: 'resp-db-connection-string',\n title: 'Database connection string with credentials in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['mongodb', 'postgres', 'mysql', 'redis', 'amqp'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:mongodb(?:\\\\+srv)?|postgres(?:ql)?|mysql|redis|amqps?):\\\\/\\\\/[^\\\\s:@\\\\/]+:[^\\\\s:@\\\\/]+@/i' } }]\n },\n {\n id: 'resp-stack-trace',\n title: 'Node stack trace leaking in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n // No prefilter: a Node stack frame has no single distinctive literal (` at ` is too common to\n // gate on). The pattern is linearly bounded per line, so it runs on every screened body.\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\n\\\\s+at\\\\s+.+\\\\(.+:\\\\d+:\\\\d+\\\\)/' } }]\n },\n {\n id: 'resp-sql-error',\n title: 'SQL / ORM error disclosure in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n prefilter: ['SQLSTATE', 'Sequelize', 'ER_', 'ORA-', 'PG::', 'SQLITE_ERROR', 'SQL syntax'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/(SQLSTATE\\\\[[0-9A-Z]+\\\\]|SequelizeDatabaseError|ER_[A-Z_]+|ORA-\\\\d{5}|PG::[A-Za-z]+Error|SQLITE_ERROR|You have an error in your SQL syntax)/i' } }]\n },\n {\n id: 'resp-exception-trace',\n title: 'Backend exception / stack trace disclosure in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n // Multi-language exception/traceback signatures a normal API response never carries:\n // Python traceback, Java \"Exception in thread\", .NET System.*Exception, JVM stack frames,\n // Go goroutine dumps. (Node `at fn (file:line:col)` frames are handled by resp-stack-trace.)\n prefilter: ['Traceback', 'Exception in thread', 'System.', 'goroutine ', '.java:', '.kt:', '.scala:', '.rb:', '.py:', '.cs:'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/(Traceback \\\\(most recent call last\\\\)|Exception in thread \"|System\\\\.[A-Za-z.]+Exception|\\\\bat [\\\\w.$]+\\\\([\\\\w]+\\\\.(?:java|kt|scala|rb|py|cs):\\\\d+\\\\)|goroutine \\\\d+ \\\\[)/' } }]\n }\n];\n\n// Egress phase — SSRF: block the app's outbound calls to internal / metadata addresses.\nexport const DEFAULT_EGRESS_RULES = [\n {\n id: 'egress-internal-address',\n title: 'Outbound request to an internal / metadata address (SSRF)',\n phase: 'egress',\n category: 'ssrf',\n rule_v2: [{ parameter: 'egress.host', match: { type: 'internal_host' } }]\n }\n];\n","// Full-page \"Access Denied\" block view, ported from the Patchstack WordPress plugin's\n// sample.php. Served (instead of JSON) when a BLOCKED request is a top-level document navigation\n// rather than an XHR/fetch — so a user who browses into a blocked URL sees a branded page, while\n// programmatic clients still get JSON. Self-contained (inline CSS + base64 SVGs); exposes no WAF\n// narrative and no rule details.\n\nfunction escapeHtml(s) {\n return String(s).replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\n}\n\n/** @param {{ url?: string, code?: string }} opts @returns {string} a complete HTML document */\nexport function renderBlockPage({ url = '', code = 'patchstack' } = {}) {\n const safeUrl = escapeHtml(url);\n const safeCode = escapeHtml(code || 'patchstack');\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style type=\"text/css\">\n\t\t.access-denied,.site-url-text{letter-spacing:.01em;color:#fff}body{background:#111216}.main-container{display:flex;justify-content:center;flex-direction:column;align-items:center}.container{width:688px;height:456px;background:#1c1e22;border-radius:16px;margin-top:120px}.content{height:330px;margin:64px}.access-denied{font-family:Verdana;font-style:normal;font-weight:600;font-size:24px;line-height:32px;display:flex;align-items:center}.descp-text,.site-url{display:flex;align-items:center}.site-url{margin-top:32px;width:560px;height:64px;background:#26282c;border-radius:4px}.reason-code-text,.site-url-text{font-family:Verdana;font-style:normal;border:none;background:#26282c}.site-url-text{text-overflow: ellipsis;font-weight:400;font-size:16px;line-height:24px;margin-left:16px;width:85%}.copy-icon{width:19px;height:22px;margin-left:20px}.copy-icon:hover{cursor:pointer}.reason-code-text{font-weight:600;font-size:24px;line-height:32px;letter-spacing:.01em;color:#AFE614;margin-left:16px;width:85%}.descp-text,.return-text{font-size:16px;line-height:24px;letter-spacing:.01em;font-weight:400}.return-text:hover{color: #AFE614}.descp-text,.logoText,.return-text,.secured-text{font-family:Verdana;font-style:normal;color:#fff}.descp{margin-top:32px;width:560px}.bottom-second-containers,.bottom-text-containers{align-items:center;display:flex}.bottom-container{margin-top:40px;display:flex;width:688px}.bottom-second-containers{margin-left:auto}.arrow-vector{margin-left:20px}.return-text{text-align:center;margin-left:12.59px;text-decoration:none}.secured-text{font-weight:400;font-size:11.2px;line-height:16px;display:flex;align-items:center;letter-spacing:.02em;margin-right:8px}.logoText{font-weight:600;font-size:16px;line-height:24px;letter-spacing:.01em;flex:none;order:1;flex-grow:0}.patch-logo{width:120px}.mobile-bottom-container{margin-top:40px;width:372px;display:none}.mobile-bottom-second-containers{margin-top:64px;display:flex;margin-left:16.5px}@media only screen and (max-width:900px){.container{background:#111216;width:370px;height:auto;margin-top:0}.content{margin-left:16.5px}.descp,.site-url{width:340px}.site-url-text{font-weight:400;font-size:14.4px;line-height:20px;letter-spacing:.02em;width:80%}.copy-icon{margin-left:20px}.reason-code-text{font-size:16px;line-height:32px;letter-spacing:.02em;margin-left:16px;width:80%}.descp-text,.return-text{font-size:14.4px;line-height:20px}.descp-text{display:flex;align-items:center;letter-spacing:.02em}.bottom-container{display:none}.mobile-bottom-container{display:block}.return-text{text-align:center;letter-spacing:.01em;color:#fff;margin-left:9.73px}}\n </style>\n <title>Access Denied</title>\n</head>\n<body>\n <div class=\"main-container\">\n <div class=\"container\">\n <div class=\"content\">\n <span class=\"access-denied\">Access Denied</span>\n <div class=\"site-url\">\n <input id=\"url\" class=\"site-url-text\" type=\"text\"\n value=\"${safeUrl}\" readonly>\n <img class=\"copy-icon\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=\" alt=\"Copy Text\" onmouseover=\"hover(this);\" onmouseout=\"unhover(this);\" onclick=\"copyText(id='url')\">\n </div>\n <div class=\"site-url\">\n <input id=\"response\" class=\"reason-code-text\" type=\"text\" value=\"${safeCode}\" readonly>\n <img class=\"copy-icon\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=\" alt=\"Copy Text\" onmouseover=\"hover(this);\" onmouseout=\"unhover(this);\" onclick=\"copyText(id='response')\">\n </div>\n <div class=\"descp\">\n <span class=\"descp-text\">This request has been blocked by Patchstack. If this message persists and you are a legitimate user, contact the site administrator with the reference above.</span>\n </div>\n </div>\n </div>\n <div class=\"bottom-container\">\n <div class=\"bottom-text-containers\">\n <img class=\"arrow-vector\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSIxMyIgdmlld0JveD0iMCAwIDggMTMiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03LjQxIDEuNDMzNDRMNiAwLjAyMzQzNzVMMCA2LjAyMzQ0TDYgMTIuMDIzNEw3LjQxIDEwLjYxMzRMMi44MyA2LjAyMzQ0TDcuNDEgMS40MzM0NFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==\" alt=\"Arrow Left\">\n <a href=\"/\" class=\"return-text\">Return to homepage</a>\n </div>\n <div class=\"bottom-second-containers\">\n <span class=\"secured-text\">This website is secured by</span>\n <a href=\"https://patchstack.com\" class=\"logoText\">\n <img class=\"patch-logo\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg0IiBoZWlnaHQ9IjIyIiB2aWV3Qm94PSIwIDAgMTg0IDIyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUwLjQwMDEgMTEuNTAwOEM1MC40MDAxIDkuMTAwNzggNDkuMzAwMSA4LjEwMDc4IDQ2LjYwMDEgOC4xMDA3OEM0NC4wMDAxIDguMTAwNzggNDIuNzAwMSA5LjIwMDc4IDQyLjcwMDEgMTEuNTAwOEM0Mi43MDAxIDEzLjgwMDggNDQuMDAwMSAxNC45MDA4IDQ2LjYwMDEgMTQuOTAwOEM0OS4zMDAxIDE0LjkwMDggNTAuNDAwMSAxMy44MDA4IDUwLjQwMDEgMTEuNTAwOFpNNTMuNDAwMSAxMS41MDA4QzUzLjQwMDEgMTUuMzAwOCA1MS4zMDAxIDE3LjYwMDggNDcuNDAwMSAxNy42MDA4QzQ1LjQwMDEgMTcuNjAwOCA0My42MDAxIDE2LjUwMDggNDIuNzAwMSAxNC41MDA4VjIxLjUwMDhIMzkuNjAwMVY1LjYwMDc4SDQyLjcwMDFWOC41MDA3OEM0My42MDAxIDYuNTAwNzggNDUuNDAwMSA1LjMwMDc4IDQ3LjUwMDEgNS4zMDA3OEM1MS40MDAxIDUuMzAwNzggNTMuNDAwMSA3LjcwMDc4IDUzLjQwMDEgMTEuNTAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjYuNDAwMSAxMS40OTg0QzY2LjQwMDEgOS4xOTg0NCA2NS4xMDAxIDguMDk4NDQgNjIuNTAwMSA4LjA5ODQ0QzU5LjgwMDEgOC4wOTg0NCA1OC43MDAxIDkuMDk4NDQgNTguNzAwMSAxMS40OTg0QzU4LjcwMDEgMTMuNzk4NCA1OS44MDAxIDE0Ljg5ODQgNjIuNTAwMSAxNC44OTg0QzY1LjEwMDEgMTQuODk4NCA2Ni40MDAxIDEzLjc5ODQgNjYuNDAwMSAxMS40OTg0Wk02Ni4zMDAxIDUuNTk4NDRINjkuNDAwMVYxNy4zOTg0SDY2LjMwMDFWMTQuNDk4NEM2NS40MDAxIDE2LjQ5ODQgNjMuNzAwMSAxNy42OTg0IDYxLjYwMDEgMTcuNjk4NEM1Ny43MDAxIDE3LjY5ODQgNTUuNjAwMSAxNS4yOTg0IDU1LjYwMDEgMTEuNTk4NEM1NS42MDAxIDcuNzk4NDQgNTcuNjAwMSA1LjM5ODQ0IDYxLjQwMDEgNS4zOTg0NEM2My41MDAxIDUuMzk4NDQgNjUuNDAwMSA2LjU5ODQ0IDY2LjIwMDEgOC41OTg0NFY1LjU5ODQ0SDY2LjMwMDFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTgxLjM5OTkgOC4zSDc3LjM5OTlWMTQuN0g4MS4wOTk5VjE3LjRINzcuOTk5OUM3NS43OTk5IDE3LjQgNzQuMjk5OSAxNS45IDc0LjI5OTkgMTMuN1Y4LjNINzEuMzk5OVY1LjZINzQuMjk5OVYySDc3LjM5OTlWNS41SDgxLjM5OTlWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik05My42OTk5IDEyLjIwMDhMOTYuNjk5OSAxMi45MDA4Qzk1Ljk5OTkgMTYuMDAwOCA5My42OTk5IDE3LjYwMDggODkuOTk5OSAxNy42MDA4Qzg3Ljc5OTkgMTcuNjAwOCA4Ni4wOTk5IDE3LjAwMDggODQuNzk5OSAxNS45MDA4QzgzLjQ5OTkgMTQuODAwOCA4Mi44OTk5IDEzLjMwMDggODIuODk5OSAxMS41MDA4QzgyLjg5OTkgOS42MDA3OCA4My40OTk5IDguMTAwNzggODQuNzk5OSA3LjAwMDc4Qzg2LjA5OTkgNS45MDA3OCA4Ny43OTk5IDUuMzAwNzggODkuOTk5OSA1LjMwMDc4QzkzLjY5OTkgNS4zMDA3OCA5NS45OTk5IDYuOTAwNzggOTYuNjk5OSAxMC4wMDA4TDkzLjY5OTkgMTAuNzAwOEM5My4yOTk5IDguODAwNzggOTIuMjk5OSA4LjEwMDc4IDg5Ljg5OTkgOC4xMDA3OEM4Ni45OTk5IDguMTAwNzggODUuODk5OSA5LjIwMDc4IDg1Ljg5OTkgMTEuNTAwOEM4NS44OTk5IDEzLjgwMDggODYuOTk5OSAxNC45MDA4IDg5Ljg5OTkgMTQuOTAwOEM5Mi4yOTk5IDE0LjkwMDggOTMuMjk5OSAxNC4yMDA4IDkzLjY5OTkgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTExLjggMTAuNlYxNy40SDEwOC43VjExLjJDMTA4LjcgOSAxMDcuOCA4LjEgMTA1LjkgOC4xQzEwMy42IDguMSAxMDIuNCA5LjMgMTAyLjQgMTEuN1YxNy4zSDk5LjI5OThWMC41SDEwMi40VjguM0MxMDMuMSA2LjMgMTA0LjYgNS4zIDEwNyA1LjNDMTEwLjIgNS4zIDExMS44IDcuMSAxMTEuOCAxMC42WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMTQuMSAxNC4wOTg0TDExNy4xIDEzLjI5ODRDMTE3LjQgMTQuNzk4NCAxMTguMyAxNS4yOTg0IDEyMC4yIDE1LjI5ODRDMTIxLjggMTUuMjk4NCAxMjIuNyAxNC44OTg0IDEyMi43IDE0LjA5ODRDMTIyLjcgMTMuMjk4NCAxMjIuMiAxMi45OTg0IDEyMC42IDEyLjc5ODRMMTE4LjIgMTIuNDk4NEMxMTUuOSAxMi4xOTg0IDExNC41IDEwLjg5ODQgMTE0LjUgOC45OTg0NEMxMTQuNSA2Ljc5ODQ0IDExNi41IDUuMzk4NDQgMTE5LjggNS4zOTg0NEMxMjMuMSA1LjM5ODQ0IDEyNSA2LjY5ODQ0IDEyNS41IDguODk4NDRMMTIyLjUgOS41OTg0NEMxMjIuMyA4LjA5ODQ0IDEyMS41IDcuNjk4NDQgMTE5LjkgNy42OTg0NEMxMTguNCA3LjY5ODQ0IDExNy42IDguMDk4NDQgMTE3LjYgOC44OTg0NEMxMTcuNiA5LjU5ODQ0IDExOC4xIDkuOTk4NDQgMTE5LjMgMTAuMDk4NEwxMjIuMiAxMC40OTg0QzEyNC40IDEwLjc5ODQgMTI1LjggMTEuOTk4NCAxMjUuOCAxMy44OTg0QzEyNS44IDE2LjI5ODQgMTIzLjcgMTcuNzk4NCAxMjAuMSAxNy43OTg0QzExNi43IDE3LjU5ODQgMTE0LjUgMTYuMTk4NCAxMTQuMSAxNC4wOTg0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMzYuNyA4LjNIMTMyLjdWMTQuN0gxMzYuNFYxNy40SDEzMy4zQzEzMS4xIDE3LjQgMTI5LjYgMTUuOSAxMjkuNiAxMy43VjguM0gxMjYuN1Y1LjZIMTI5LjZWMkgxMzIuN1Y1LjVIMTM2LjdWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNDkuMiAxMS40OTg0QzE0OS4yIDkuMTk4NDQgMTQ3LjkgOC4wOTg0NCAxNDUuMyA4LjA5ODQ0QzE0Mi42IDguMDk4NDQgMTQxLjUgOS4wOTg0NCAxNDEuNSAxMS40OTg0QzE0MS41IDEzLjc5ODQgMTQyLjYgMTQuODk4NCAxNDUuMyAxNC44OTg0QzE0Ny45IDE0Ljg5ODQgMTQ5LjIgMTMuNzk4NCAxNDkuMiAxMS40OTg0Wk0xNDkuMSA1LjU5ODQ0SDE1Mi4yVjE3LjM5ODRIMTQ5LjFWMTQuNDk4NEMxNDguMiAxNi40OTg0IDE0Ni41IDE3LjY5ODQgMTQ0LjQgMTcuNjk4NEMxNDAuNSAxNy42OTg0IDEzOC40IDE1LjI5ODQgMTM4LjQgMTEuNTk4NEMxMzguNCA3Ljc5ODQ0IDE0MC40IDUuMzk4NDQgMTQ0LjIgNS4zOTg0NEMxNDYuMyA1LjM5ODQ0IDE0OC4yIDYuNTk4NDQgMTQ5IDguNTk4NDRWNS41OTg0NEgxNDkuMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTY1LjYgMTIuMjAwOEwxNjguNiAxMi45MDA4QzE2Ny45IDE2LjAwMDggMTY1LjYgMTcuNjAwOCAxNjEuOSAxNy42MDA4QzE1OS43IDE3LjYwMDggMTU4IDE3LjAwMDggMTU2LjcgMTUuOTAwOEMxNTUuNCAxNC44MDA4IDE1NC44IDEzLjMwMDggMTU0LjggMTEuNTAwOEMxNTQuOCA5LjYwMDc4IDE1NS40IDguMTAwNzggMTU2LjcgNy4wMDA3OEMxNTggNS45MDA3OCAxNTkuNyA1LjMwMDc4IDE2MS45IDUuMzAwNzhDMTY1LjYgNS4zMDA3OCAxNjcuOSA2LjkwMDc4IDE2OC42IDEwLjAwMDhMMTY1LjYgMTAuNzAwOEMxNjUuMiA4LjgwMDc4IDE2NC4yIDguMTAwNzggMTYxLjggOC4xMDA3OEMxNTguOSA4LjEwMDc4IDE1Ny44IDkuMjAwNzggMTU3LjggMTEuNTAwOEMxNTcuOCAxMy44MDA4IDE1OC45IDE0LjkwMDggMTYxLjggMTQuOTAwOEMxNjQuMSAxNC45MDA4IDE2NS4xIDE0LjIwMDggMTY1LjYgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTgwLjI1NyAxNy40TDE3Ni42NTcgMTIuM0wxNzQuMTU3IDE0LjZWMTcuNEgxNzEuMTU3VjAuNUgxNzQuMTU3VjExLjFMMTc5Ljg1NyA1LjZIMTgzLjc1N0wxNzguNTU3IDEwLjVMMTgzLjk1NyAxNy40SDE4MC4yNTdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgMjEuNTAwOEg3LjNWMTYuMzAwOEgwVjIxLjUwMDhaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNy4yOTk4IDIxLjUwMDhIMTQuNTk5OFYxNi4zMDA4SDcuMjk5OFYyMS41MDA4WiIgZmlsbD0iIzZFQkUwMCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE0LjYwMDEgMTYuM0gyOS4yMDAxVjAuNUgxNC42MDAxVjE2LjNaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMCAxMUgxNC42VjAuNUgwVjExWiIgZmlsbD0iIzZFQkUwMCIvPgo8L3N2Zz4K\" alt=\"Patchstack Logo\">\n\t\t\t\t</a>\n </div>\n </div>\n \n <div class=\"mobile-bottom-container\">\n <div class=\"mobile-bottom-text-containers\">\n <img class=\"arrow-vector\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSIxMyIgdmlld0JveD0iMCAwIDggMTMiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03LjQxIDEuNDMzNDRMNiAwLjAyMzQzNzVMMCA2LjAyMzQ0TDYgMTIuMDIzNEw3LjQxIDEwLjYxMzRMMi44MyA2LjAyMzQ0TDcuNDEgMS40MzM0NFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==\" alt=\"Arrow Left\">\n <a href=\"/\" class=\"return-text\">Return to homepage</a>\n </div>\n <div class=\"mobile-bottom-second-containers\">\n <span class=\"secured-text\">This website is secured by</span>\n <a href=\"https://patchstack.com\" class=\"logoText\">\n <img class=\"patch-logo\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg0IiBoZWlnaHQ9IjIyIiB2aWV3Qm94PSIwIDAgMTg0IDIyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUwLjQwMDEgMTEuNTAwOEM1MC40MDAxIDkuMTAwNzggNDkuMzAwMSA4LjEwMDc4IDQ2LjYwMDEgOC4xMDA3OEM0NC4wMDAxIDguMTAwNzggNDIuNzAwMSA5LjIwMDc4IDQyLjcwMDEgMTEuNTAwOEM0Mi43MDAxIDEzLjgwMDggNDQuMDAwMSAxNC45MDA4IDQ2LjYwMDEgMTQuOTAwOEM0OS4zMDAxIDE0LjkwMDggNTAuNDAwMSAxMy44MDA4IDUwLjQwMDEgMTEuNTAwOFpNNTMuNDAwMSAxMS41MDA4QzUzLjQwMDEgMTUuMzAwOCA1MS4zMDAxIDE3LjYwMDggNDcuNDAwMSAxNy42MDA4QzQ1LjQwMDEgMTcuNjAwOCA0My42MDAxIDE2LjUwMDggNDIuNzAwMSAxNC41MDA4VjIxLjUwMDhIMzkuNjAwMVY1LjYwMDc4SDQyLjcwMDFWOC41MDA3OEM0My42MDAxIDYuNTAwNzggNDUuNDAwMSA1LjMwMDc4IDQ3LjUwMDEgNS4zMDA3OEM1MS40MDAxIDUuMzAwNzggNTMuNDAwMSA3LjcwMDc4IDUzLjQwMDEgMTEuNTAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjYuNDAwMSAxMS40OTg0QzY2LjQwMDEgOS4xOTg0NCA2NS4xMDAxIDguMDk4NDQgNjIuNTAwMSA4LjA5ODQ0QzU5LjgwMDEgOC4wOTg0NCA1OC43MDAxIDkuMDk4NDQgNTguNzAwMSAxMS40OTg0QzU4LjcwMDEgMTMuNzk4NCA1OS44MDAxIDE0Ljg5ODQgNjIuNTAwMSAxNC44OTg0QzY1LjEwMDEgMTQuODk4NCA2Ni40MDAxIDEzLjc5ODQgNjYuNDAwMSAxMS40OTg0Wk02Ni4zMDAxIDUuNTk4NDRINjkuNDAwMVYxNy4zOTg0SDY2LjMwMDFWMTQuNDk4NEM2NS40MDAxIDE2LjQ5ODQgNjMuNzAwMSAxNy42OTg0IDYxLjYwMDEgMTcuNjk4NEM1Ny43MDAxIDE3LjY5ODQgNTUuNjAwMSAxNS4yOTg0IDU1LjYwMDEgMTEuNTk4NEM1NS42MDAxIDcuNzk4NDQgNTcuNjAwMSA1LjM5ODQ0IDYxLjQwMDEgNS4zOTg0NEM2My41MDAxIDUuMzk4NDQgNjUuNDAwMSA2LjU5ODQ0IDY2LjIwMDEgOC41OTg0NFY1LjU5ODQ0SDY2LjMwMDFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTgxLjM5OTkgOC4zSDc3LjM5OTlWMTQuN0g4MS4wOTk5VjE3LjRINzcuOTk5OUM3NS43OTk5IDE3LjQgNzQuMjk5OSAxNS45IDc0LjI5OTkgMTMuN1Y4LjNINzEuMzk5OVY1LjZINzQuMjk5OVYySDc3LjM5OTlWNS41SDgxLjM5OTlWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik05My42OTk5IDEyLjIwMDhMOTYuNjk5OSAxMi45MDA4Qzk1Ljk5OTkgMTYuMDAwOCA5My42OTk5IDE3LjYwMDggODkuOTk5OSAxNy42MDA4Qzg3Ljc5OTkgMTcuNjAwOCA4Ni4wOTk5IDE3LjAwMDggODQuNzk5OSAxNS45MDA4QzgzLjQ5OTkgMTQuODAwOCA4Mi44OTk5IDEzLjMwMDggODIuODk5OSAxMS41MDA4QzgyLjg5OTkgOS42MDA3OCA4My40OTk5IDguMTAwNzggODQuNzk5OSA3LjAwMDc4Qzg2LjA5OTkgNS45MDA3OCA4Ny43OTk5IDUuMzAwNzggODkuOTk5OSA1LjMwMDc4QzkzLjY5OTkgNS4zMDA3OCA5NS45OTk5IDYuOTAwNzggOTYuNjk5OSAxMC4wMDA4TDkzLjY5OTkgMTAuNzAwOEM5My4yOTk5IDguODAwNzggOTIuMjk5OSA4LjEwMDc4IDg5Ljg5OTkgOC4xMDA3OEM4Ni45OTk5IDguMTAwNzggODUuODk5OSA5LjIwMDc4IDg1Ljg5OTkgMTEuNTAwOEM4NS44OTk5IDEzLjgwMDggODYuOTk5OSAxNC45MDA4IDg5Ljg5OTkgMTQuOTAwOEM5Mi4yOTk5IDE0LjkwMDggOTMuMjk5OSAxNC4yMDA4IDkzLjY5OTkgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTExLjggMTAuNlYxNy40SDEwOC43VjExLjJDMTA4LjcgOSAxMDcuOCA4LjEgMTA1LjkgOC4xQzEwMy42IDguMSAxMDIuNCA5LjMgMTAyLjQgMTEuN1YxNy4zSDk5LjI5OThWMC41SDEwMi40VjguM0MxMDMuMSA2LjMgMTA0LjYgNS4zIDEwNyA1LjNDMTEwLjIgNS4zIDExMS44IDcuMSAxMTEuOCAxMC42WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMTQuMSAxNC4wOTg0TDExNy4xIDEzLjI5ODRDMTE3LjQgMTQuNzk4NCAxMTguMyAxNS4yOTg0IDEyMC4yIDE1LjI5ODRDMTIxLjggMTUuMjk4NCAxMjIuNyAxNC44OTg0IDEyMi43IDE0LjA5ODRDMTIyLjcgMTMuMjk4NCAxMjIuMiAxMi45OTg0IDEyMC42IDEyLjc5ODRMMTE4LjIgMTIuNDk4NEMxMTUuOSAxMi4xOTg0IDExNC41IDEwLjg5ODQgMTE0LjUgOC45OTg0NEMxMTQuNSA2Ljc5ODQ0IDExNi41IDUuMzk4NDQgMTE5LjggNS4zOTg0NEMxMjMuMSA1LjM5ODQ0IDEyNSA2LjY5ODQ0IDEyNS41IDguODk4NDRMMTIyLjUgOS41OTg0NEMxMjIuMyA4LjA5ODQ0IDEyMS41IDcuNjk4NDQgMTE5LjkgNy42OTg0NEMxMTguNCA3LjY5ODQ0IDExNy42IDguMDk4NDQgMTE3LjYgOC44OTg0NEMxMTcuNiA5LjU5ODQ0IDExOC4xIDkuOTk4NDQgMTE5LjMgMTAuMDk4NEwxMjIuMiAxMC40OTg0QzEyNC40IDEwLjc5ODQgMTI1LjggMTEuOTk4NCAxMjUuOCAxMy44OTg0QzEyNS44IDE2LjI5ODQgMTIzLjcgMTcuNzk4NCAxMjAuMSAxNy43OTg0QzExNi43IDE3LjU5ODQgMTE0LjUgMTYuMTk4NCAxMTQuMSAxNC4wOTg0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMzYuNyA4LjNIMTMyLjdWMTQuN0gxMzYuNFYxNy40SDEzMy4zQzEzMS4xIDE3LjQgMTI5LjYgMTUuOSAxMjkuNiAxMy43VjguM0gxMjYuN1Y1LjZIMTI5LjZWMkgxMzIuN1Y1LjVIMTM2LjdWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNDkuMiAxMS40OTg0QzE0OS4yIDkuMTk4NDQgMTQ3LjkgOC4wOTg0NCAxNDUuMyA4LjA5ODQ0QzE0Mi42IDguMDk4NDQgMTQxLjUgOS4wOTg0NCAxNDEuNSAxMS40OTg0QzE0MS41IDEzLjc5ODQgMTQyLjYgMTQuODk4NCAxNDUuMyAxNC44OTg0QzE0Ny45IDE0Ljg5ODQgMTQ5LjIgMTMuNzk4NCAxNDkuMiAxMS40OTg0Wk0xNDkuMSA1LjU5ODQ0SDE1Mi4yVjE3LjM5ODRIMTQ5LjFWMTQuNDk4NEMxNDguMiAxNi40OTg0IDE0Ni41IDE3LjY5ODQgMTQ0LjQgMTcuNjk4NEMxNDAuNSAxNy42OTg0IDEzOC40IDE1LjI5ODQgMTM4LjQgMTEuNTk4NEMxMzguNCA3Ljc5ODQ0IDE0MC40IDUuMzk4NDQgMTQ0LjIgNS4zOTg0NEMxNDYuMyA1LjM5ODQ0IDE0OC4yIDYuNTk4NDQgMTQ5IDguNTk4NDRWNS41OTg0NEgxNDkuMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTY1LjYgMTIuMjAwOEwxNjguNiAxMi45MDA4QzE2Ny45IDE2LjAwMDggMTY1LjYgMTcuNjAwOCAxNjEuOSAxNy42MDA4QzE1OS43IDE3LjYwMDggMTU4IDE3LjAwMDggMTU2LjcgMTUuOTAwOEMxNTUuNCAxNC44MDA4IDE1NC44IDEzLjMwMDggMTU0LjggMTEuNTAwOEMxNTQuOCA5LjYwMDc4IDE1NS40IDguMTAwNzggMTU2LjcgNy4wMDA3OEMxNTggNS45MDA3OCAxNTkuNyA1LjMwMDc4IDE2MS45IDUuMzAwNzhDMTY1LjYgNS4zMDA3OCAxNjcuOSA2LjkwMDc4IDE2OC42IDEwLjAwMDhMMTY1LjYgMTAuNzAwOEMxNjUuMiA4LjgwMDc4IDE2NC4yIDguMTAwNzggMTYxLjggOC4xMDA3OEMxNTguOSA4LjEwMDc4IDE1Ny44IDkuMjAwNzggMTU3LjggMTEuNTAwOEMxNTcuOCAxMy44MDA4IDE1OC45IDE0LjkwMDggMTYxLjggMTQuOTAwOEMxNjQuMSAxNC45MDA4IDE2NS4xIDE0LjIwMDggMTY1LjYgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTgwLjI1NyAxNy40TDE3Ni42NTcgMTIuM0wxNzQuMTU3IDE0LjZWMTcuNEgxNzEuMTU3VjAuNUgxNzQuMTU3VjExLjFMMTc5Ljg1NyA1LjZIMTgzLjc1N0wxNzguNTU3IDEwLjVMMTgzLjk1NyAxNy40SDE4MC4yNTdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgMjEuNTAwOEg3LjNWMTYuMzAwOEgwVjIxLjUwMDhaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNy4yOTk4IDIxLjUwMDhIMTQuNTk5OFYxNi4zMDA4SDcuMjk5OFYyMS41MDA4WiIgZmlsbD0iIzZFQkUwMCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE0LjYwMDEgMTYuM0gyOS4yMDAxVjAuNUgxNC42MDAxVjE2LjNaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMCAxMUgxNC42VjAuNUgwVjExWiIgZmlsbD0iIzZFQkUwMCIvPgo8L3N2Zz4K\" alt=\"Patchstack Logo\">\n\t\t\t\t</a>\n </div>\n </div>\n </div>\n</body>\n<script>\n function copyText(id) {\n var copyText = document.getElementById(id)\n copyText.select();\n copyText.setSelectionRange(0, 99999);\n navigator.clipboard.writeText(copyText.value);\n }\n\n function hover(element) {\n element.setAttribute('src', 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==');\n }\n\n function unhover(element) {\n element.setAttribute('src', 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=');\n }\n</script>\n</html>`;\n}\n","// Rule cache as a TIERED store of a `{ bundle, etag }` envelope:\n// 1. memory — always present; last-known-good within the process. Survives refreshes and is\n// the fallback when the disk isn't writable (read-only FS, sandbox).\n// 2. durable — filesystem (default, via `cacheDir`) OR a pluggable adapter (`ruleCache`, e.g.\n// a KV store for filesystem-less runtimes). Survives process restarts.\n// read: memory → durable → null. write: memory + best-effort durable. Everything is fail-open —\n// a read/write error yields \"no cache\" rather than throwing.\n//\n// Node's fs/path are loaded LAZILY (dynamic import), never as a static top-level import: this module\n// is part of the WinterCG/edge-safe graph (Next edge middleware, Workers, Deno, Supabase Functions),\n// where a static `node:fs` import fails to resolve at build/load time and would take the whole guard\n// down. On those runtimes the disk tier simply reports \"no cache\" and the memory tier (or a pluggable\n// `ruleCache` adapter) carries last-known-good.\n\nlet fsMod; // memoized { readFileSync, writeFileSync, mkdirSync, join } | null (unavailable)\nasync function loadFs() {\n if (fsMod !== undefined) return fsMod;\n try {\n const [fs, path] = await Promise.all([import('node:fs'), import('node:path')]);\n fsMod = {\n readFileSync: fs.readFileSync,\n writeFileSync: fs.writeFileSync,\n mkdirSync: fs.mkdirSync,\n join: path.join,\n };\n } catch {\n fsMod = null; // no filesystem here (edge runtime) — memory/adapter tiers still work\n }\n return fsMod;\n}\n\nexport function makeStore(options = {}) {\n let mem = null;\n const durable = durableTier(options);\n return {\n async read() {\n if (mem) return mem;\n const env = await durable.read();\n if (env) mem = env;\n return env;\n },\n async write(env) {\n if (env) mem = env;\n await durable.write(env);\n },\n };\n}\n\n// The durable tier: a caller-supplied adapter wins; otherwise a disk cache under `cacheDir`; if\n// neither is configured, a no-op (memory-only — fine for a long-lived process).\nfunction durableTier(options) {\n const adapter = options.ruleCache;\n if (adapter && typeof adapter.read === 'function' && typeof adapter.write === 'function') {\n return {\n read: async () => {\n try {\n return toEnvelope(await adapter.read());\n } catch {\n return null;\n }\n },\n write: async (env) => {\n try {\n await adapter.write(env);\n } catch {\n /* best-effort */\n }\n },\n };\n }\n const dir = options.cacheDir;\n return {\n read: async () => cacheRead(dir),\n write: async (env) => cacheWrite(dir, env),\n };\n}\n\nasync function cacheWrite(dir, env) {\n if (!dir) return;\n const fs = await loadFs();\n if (!fs) return; // filesystem-less runtime — memory tier only\n try {\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(fs.join(dir, 'patchstack-rules.json'), JSON.stringify(env));\n } catch {\n /* cache is best-effort — the memory tier still holds last-known-good */\n }\n}\n\nasync function cacheRead(dir) {\n if (!dir) return null;\n const fs = await loadFs();\n if (!fs) return null;\n try {\n return toEnvelope(JSON.parse(fs.readFileSync(fs.join(dir, 'patchstack-rules.json'), 'utf8')));\n } catch {\n return null;\n }\n}\n\n// Accept the current { bundle, etag } envelope and a legacy bare bundle (pre-envelope cache files).\nexport function toEnvelope(value) {\n if (!value || typeof value !== 'object') return null;\n if (value.bundle && typeof value.bundle === 'object') {\n return { bundle: value.bundle, etag: value.etag ?? null };\n }\n if (Array.isArray(value.firewall) || Array.isArray(value.whitelists)) {\n return { bundle: value, etag: null }; // legacy bare-bundle cache file\n }\n return null;\n}\n","// The engine's rule vocabulary and shape rules, as data.\n//\n// A rule document is produced in one place, forwarded through others, and executed here. Each of those\n// layers needs the same answer to \"is this rule one the engine can run\", and a layer that answers it more\n// loosely admits a rule that is delivered, counted as protection, and never matches.\n//\n// The data is richer than a set of name lists so that a consumer can DERIVE validation from it: which\n// sources take a key and what keys they take, what a condition group looks like, what operands each match\n// type needs, which keys a `when` scope understands, which properties belong to which action. Lists alone\n// leave every consumer to reimplement the rest in its own language, and those implementations diverge.\n//\n// `rule-contract.json` is the published form. `tests/protect/rule-contract.test.ts` reads the engine's own\n// source and asserts these descriptions match what it implements.\n\nexport const CONTRACT_VERSION = '2.7';\n\n/**\n * Every parameter source, and what it accepts after the dot.\n *\n * - `keyed: false` — used bare (`raw`), never with a key.\n * - `keys: 'any'` — an application field name, which is per-app and cannot be enumerated.\n * - `keys: [...]` — the only keys the resolver answers for.\n * - `key_prefixes: [...]` — a key may instead begin with one of these.\n * - `optional_key_suffixes: [...]` — a key may end with one of these to select part of the value.\n * - `wildcard: true` — a key may end in `*` to fan out over matching fields.\n */\nexport const SOURCES = Object.freeze({\n raw: Object.freeze({ keyed: false }),\n all: Object.freeze({ keyed: false }),\n false: Object.freeze({ keyed: false }),\n rules: Object.freeze({ keyed: false, group: true }),\n\n get: Object.freeze({ keyed: true, keys: 'any', wildcard: true }),\n post: Object.freeze({ keyed: true, keys: 'any', wildcard: true }),\n request: Object.freeze({ keyed: true, keys: 'any', wildcard: true }),\n cookie: Object.freeze({ keyed: true, keys: 'any', wildcard: true }),\n\n // A bare `files.<name>` resolves to the filename; an optional trailing attribute selects another part\n // of the upload. Optional, not required — filename-scoped rules use the bare form.\n files: Object.freeze({\n keyed: true,\n keys: 'any',\n wildcard: true,\n optional_key_suffixes: Object.freeze(['content', 'filename', 'type']),\n }),\n\n server: Object.freeze({\n keyed: true,\n keys: Object.freeze([\n 'REQUEST_URI', 'REQUEST_METHOD', 'HTTP_USER_AGENT', 'HTTP_REFERER', 'HTTP_HOST',\n 'REMOTE_ADDR', 'ip', 'CONTENT_TYPE', 'CONTENT_LENGTH',\n ]),\n key_prefixes: Object.freeze(['HTTP_']),\n }),\n\n response: Object.freeze({\n keyed: true,\n keys: Object.freeze(['status', 'body', 'headers']),\n key_prefixes: Object.freeze(['header.']),\n }),\n\n egress: Object.freeze({ keyed: true, keys: Object.freeze(['url', 'host', 'method']) }),\n});\n\n/**\n * Match types the engine evaluates.\n *\n * `file_contains` is absent: the engine returns false for it unconditionally, so a rule built on it can\n * never match. Publishing it would invite a consumer to author one.\n */\nexport const MATCH_TYPES = Object.freeze([\n 'array_in_array', 'array_key_value', 'contains', 'cors_reflected', 'cross_origin',\n 'ctype_alnum', 'ctype_digit', 'ctype_special', 'equals', 'equals_strict',\n 'hostname', 'in_array', 'inline_js_xss', 'inline_xss', 'internal_host', 'is_numeric',\n 'isset', 'jwt_claim_equals', 'less_than', 'more_than', 'not_contains', 'not_in_array', 'off_origin',\n 'quotes', 'regex', 'stripos',\n]);\n\n/** Match types evaluated against the whole request or response, which therefore carry no parameter. */\nexport const PARAMETERLESS_MATCH_TYPES = Object.freeze(['cross_origin', 'off_origin', 'cors_reflected']);\n\n/** Match types that read no operand: presence or a character-class test of the resolved value alone. */\nexport const OPERANDLESS_MATCH_TYPES = Object.freeze([\n 'isset', 'quotes', 'is_numeric', 'ctype_alnum', 'ctype_digit', 'ctype_special',\n 'inline_xss', 'inline_js_xss', 'internal_host',\n 'cross_origin', 'off_origin', 'cors_reflected',\n]);\n\n/**\n * Operand shapes that have to hold for a match to be able to do anything.\n *\n * Presence is not enough. An operand of the wrong shape leaves a condition that resolves a value and then\n * answers the same way for every request — inert in most cases, and for the substring matchers, true for\n * ALL of them: `contains` with an empty value matches every request, which is a rule that blocks all\n * traffic while reading as one targeted signature.\n *\n * `positive-number` a finite positive JSON number. Not a string that looks like one, and not\n * `Infinity`: the response cap is only raised when the value is FINITE, so a\n * non-finite one validates as \"no limit\" and delivers the default.\n * `boolean` exactly true or false. The runtime tests these by identity, so `\"true\"` is not\n * a truthy spelling of true — it is the same as false, silently.\n * `non-empty-string` a string with something in it\n * `regex-literal` a delimited `/pattern/flags` whose pattern is non-empty — what the engine's\n * pattern compiler accepts. An undelimited value, or `//`, compiles to nothing\n * and the condition can never match.\n * `string-or-string-list` one non-empty string, or a non-empty list of them\n * `scalar-or-non-empty-list` one scalar, or a non-empty list of them. The list membership matchers\n * stringify every entry, so a number or boolean entry is as usable as a\n * string, and a lone `''` is a real signature (\"this parameter is empty\").\n * Only the EMPTY list is degenerate.\n * `non-empty-list` a non-empty array of scalars. Distinct from the above because\n * `array_in_array` compares array against array and never matches a bare one.\n * `scalar` one string, number or boolean. `''` is allowed: \"this parameter is empty\" is\n * a real signature, so emptiness alone is not the defect here.\n * `string` a string specifically — for the type-strict comparison, whose subject is\n * always stringified first, so a number operand can never equal it.\n * `numeric` a scalar that coerces to a finite number. The comparators coerce with\n * `Number()`, so an operand that yields NaN makes every comparison false.\n * `object` a plain object\n * `non-empty-object` a plain object with at least one entry\n * `non-empty-string-list` a non-empty array of non-empty strings\n * `header-name-map` a non-empty object whose every key is a valid HTTP field name\n * `header-name-list` a non-empty array of valid HTTP field names\n * `cookie-flags` a `harden-cookie` configuration that still hardens something. Each flag may\n * be turned off individually, but turning off all three leaves an action that\n * matches, reports, and rewrites the cookie to itself. `sameSite` is\n * interpolated into the header verbatim, so it is held to the three values the\n * attribute actually takes — anything else is either ignored by the browser or\n * appends further cookie attributes.\n */\nexport const OPERAND_SHAPES = Object.freeze([\n 'positive-number', 'boolean', 'non-empty-string', 'regex-literal', 'string-or-string-list', 'scalar', 'string', 'numeric',\n 'scalar-or-non-empty-list', 'non-empty-list', 'object', 'non-empty-object', 'non-empty-string-list',\n 'header-name-map', 'header-name-list', 'cookie-flags',\n]);\n\n/** The flags `harden-cookie` understands, and the values `SameSite` takes. */\nexport const COOKIE_FLAGS = Object.freeze(['httpOnly', 'secure', 'sameSite']);\nexport const SAME_SITE_VALUES = Object.freeze(['Strict', 'Lax', 'None']);\n\n/**\n * An HTTP field name, as the platform's `Headers` accepts it (RFC 9110 token). A name outside this set\n * makes `Headers.get`/`set`/`delete` throw, and the runtime skips the mutation — so a reviewed\n * hardening rule is served and changes nothing.\n */\nexport const HEADER_NAME = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\n\n/** Mirrors the engine's pattern compiler: `/body/flags`, body non-empty, only its supported flags. */\nexport const REGEX_LITERAL = /^\\/(.+?)\\/([gimsuy]*)$/s;\n\nexport const MATCH_OPERANDS = Object.freeze({\n array_key_value: Object.freeze({\n required: Object.freeze(['key', 'match']),\n shapes: Object.freeze({ key: 'string-or-string-list', match: 'object' }),\n }),\n // Decides on a JWT's DECODED payload rather than its shape, and yields the matching token spans so\n // `redact` masks only those tokens. `claim` names an own, top-level claim; `value` is compared with\n // exact string equality. Anything not positively identified — undecodable, not JSON, not an object,\n // oversized, claim missing, claim not a string, or a different value — is a no-match.\n jwt_claim_equals: Object.freeze({\n required: Object.freeze(['claim', 'value']),\n shapes: Object.freeze({ claim: 'non-empty-string', value: 'non-empty-string' }),\n }),\n regex: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'regex-literal' }) }),\n contains: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'non-empty-string' }) }),\n stripos: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'non-empty-string' }) }),\n not_contains: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'non-empty-string' }) }),\n // A hostname is compared to `new URL(value).hostname`, so an empty one only ever equals the empty\n // hostname of an opaque-scheme URL (`file:`, `mailto:`, `data:`) — never a host, and a false positive\n // on those. The other three are the list-membership family: an empty list makes `in_array` and\n // `array_in_array` inert and `not_in_array` true for every value.\n hostname: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'non-empty-string' }) }),\n in_array: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'scalar-or-non-empty-list' }) }),\n not_in_array: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'scalar-or-non-empty-list' }) }),\n array_in_array: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'non-empty-list' }) }),\n // The comparison family. `equals` coerces, so any scalar can match. `equals_strict` does not, and its\n // subject is always a string by the time it is compared — so a number operand is unconditionally\n // inert, which is the trap the engine's own note about `'1' !== 1` describes.\n equals: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'scalar' }) }),\n equals_strict: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'string' }) }),\n less_than: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'numeric' }) }),\n more_than: Object.freeze({ required: Object.freeze(['value']), shapes: Object.freeze({ value: 'numeric' }) }),\n});\n\n/** @returns {string|null} why this operand cannot serve that shape, or null */\nexport function operandShapeProblem(shape, value) {\n const isFilledString = (v) => typeof v === 'string' && v.trim() !== '';\n const isScalar = (v) => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean';\n const isHeaderName = (v) => typeof v === 'string' && HEADER_NAME.test(v);\n\n switch (shape) {\n case 'positive-number':\n // A JSON number, not a string that parses as one. Both sides of that distinction are real: the cap\n // calculation coerces, so `\"1048576\"` does raise it, while the three layers that validate a document\n // disagreed about whether to accept it. One rule is worth more than the coercion.\n return typeof value === 'number' && Number.isFinite(value) && value > 0\n ? null\n : 'must be a finite positive number';\n case 'boolean':\n return typeof value === 'boolean' ? null : 'must be true or false';\n case 'non-empty-string':\n return isFilledString(value) ? null : 'must be a non-empty string';\n case 'regex-literal':\n // Length is not checked here: `maxRegexLength` is enforced once, at the gate, so the limit has a\n // single owner and one message.\n if (!isFilledString(value)) return 'must be a non-empty string';\n return REGEX_LITERAL.test(value) ? null : 'must be a delimited pattern, as in /pattern/i';\n case 'string-or-string-list':\n if (isFilledString(value)) return null;\n if (Array.isArray(value) && value.length > 0 && value.every(isFilledString)) return null;\n return 'must be a non-empty string, or a non-empty list of them';\n case 'scalar':\n return isScalar(value) ? null : 'must be a string, number or boolean';\n case 'string':\n return typeof value === 'string' ? null : 'must be a string (this comparison is type-strict)';\n case 'numeric':\n return isScalar(value) && Number.isFinite(Number(value)) ? null : 'must be a number';\n case 'scalar-or-non-empty-list':\n if (isScalar(value)) return null;\n if (Array.isArray(value) && value.length > 0 && value.every(isScalar)) return null;\n return 'must be a scalar, or a non-empty list of them';\n case 'non-empty-list':\n return Array.isArray(value) && value.length > 0 && value.every(isScalar)\n ? null\n : 'must be a non-empty list of scalars';\n case 'header-name-map': {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return 'must be an object';\n const names = Object.keys(value);\n if (names.length === 0) return 'must be an object with at least one entry';\n const bad = names.find((name) => !isHeaderName(name));\n return bad === undefined ? null : `names an invalid HTTP header, ${JSON.stringify(bad)}`;\n }\n case 'header-name-list': {\n if (!Array.isArray(value) || value.length === 0) return 'must be a non-empty list of HTTP header names';\n const bad = value.find((name) => !isHeaderName(name));\n return bad === undefined ? null : `names an invalid HTTP header, ${JSON.stringify(bad)}`;\n }\n case 'cookie-flags': {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return 'must be an object';\n\n // An unknown key is silently ignored by the runtime — including a near-miss like `httponly`,\n // which reads as disabling a flag and leaves it on. A rule has to mean what it says.\n const unknown = Object.keys(value).filter((key) => !COOKIE_FLAGS.includes(key));\n if (unknown.length > 0) return `has no flag named ${unknown.map((k) => JSON.stringify(k)).join(', ')}`;\n\n for (const flag of ['httpOnly', 'secure']) {\n if (value[flag] !== undefined && typeof value[flag] !== 'boolean') {\n // The runtime only tests truthiness, so the string 'false' would turn the flag ON.\n return `flag \"${flag}\" must be true or false`;\n }\n }\n\n const sameSite = value.sameSite;\n const sameSiteOff = sameSite === false;\n if (sameSite !== undefined && !sameSiteOff) {\n if (typeof sameSite !== 'string' || !SAME_SITE_VALUES.some((v) => v.toLowerCase() === sameSite.toLowerCase())) {\n return `flag \"sameSite\" must be false, or one of ${SAME_SITE_VALUES.join(', ')}`;\n }\n }\n\n // Each default may be turned off on its own; all of them together is the no-op.\n if (value.httpOnly === false && value.secure === false && sameSiteOff) {\n return 'turns off every flag, so it would harden nothing';\n }\n return null;\n }\n case 'object':\n return value !== null && typeof value === 'object' && !Array.isArray(value) ? null : 'must be an object';\n case 'non-empty-object':\n return value !== null && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length > 0\n ? null\n : 'must be an object with at least one entry';\n case 'non-empty-string-list':\n return Array.isArray(value) && value.length > 0 && value.every(isFilledString)\n ? null\n : 'must be a non-empty list of non-empty strings';\n default:\n return null;\n }\n}\n\n/** Mutations the resolver applies. Anything else is a silent no-op. */\nexport const MUTATIONS = Object.freeze([\n 'base64_decode', 'getArrayValues', 'htmlentitydecode', 'intval', 'json_decode', 'json_encode', 'urldecode',\n]);\n\nexport const PHASES = Object.freeze(['request', 'response', 'egress']);\n\nexport const ACTIONS = Object.freeze([\n 'block', 'redact', 'encode', 'set-header', 'remove-header', 'harden-cookie',\n]);\n\n/**\n * Keys a `when` scope understands.\n *\n * A scope naming anything else is ignored and the rule applies to every request, so an unrecognised key\n * silently widens a rule that was authored to be narrow.\n */\n/**\n * A property that is PRESENT and null is refused, wherever it appears on a rule.\n *\n * Absent and authored-null are different events, and only one of them is a statement of intent. Omitting a\n * property means \"whatever the engine defaults to\" — a real and common thing to mean. Writing `null` means\n * something upstream produced a value it did not have, and the two are indistinguishable to every layer\n * that defaults an absent field. `phase: null` becomes the request phase, so a rule authored for egress\n * evaluates on the wrong side of the request and never fires; `action: null` becomes a block on a rule that\n * meant to redact.\n *\n * This was the engine's behaviour for `phase`, `rule_v2` and `max_bytes` and nothing else, which made it\n * look like a property of those three fields rather than a rule about documents. Stated once, it applies to\n * all of them — and a consumer reads `rule_properties` and this flag rather than keeping its own list.\n */\nexport const NULL_VALUED_PROPERTIES = 'refused';\n\n/**\n * The shape each rule-level property has to have, beyond not being null.\n *\n * Every one of these is read by the runtime in a way the old validator did not check, and each failure is\n * silent in the same direction — the document says one thing and the guard does another:\n *\n * `bypass_limit` is tested `=== true`, so `\"true\"`, `1` and `\"yes\"` all validate as \"remove the response\n * screening cap\" and remove nothing. The leak is still served.\n *\n * `enforcement` is tested `=== 'dry-run'`, so `\"dryrun\"` validates and means \"follow the bundle\" — a rule\n * authored to observe only BLOCKS when the bundle is in block mode.\n *\n * `ensure` is tested `=== true`, so a `set-header` rule meant to preserve an existing header overwrites it.\n *\n * `max_bytes` raises the cap only when finite, so `Infinity` and `\"Infinity\"` validate as \"no limit\" and\n * deliver the default. That one is the sharpest: it is the only value whose intent is the largest possible\n * raise, and its effect is no raise at all. `bypass_limit: true` is how that intent is expressed.\n */\nexport const RULE_PROPERTY_SHAPES = Object.freeze({\n max_bytes: 'positive-number',\n bypass_limit: 'boolean',\n ensure: 'boolean',\n prefilter: 'non-empty-string-list',\n});\n\n/**\n * The only enforcement value that means anything: a rule can narrow to detect-only, never opt INTO blocking.\n *\n * Published for a reviewer's benefit, and deliberately NOT enforced — see `rulePropertyProblem`. Anything\n * else is read as \"follow the site\", which never weakens protection and keeps an older guard working when a\n * newer server sends a value it has not learned.\n */\nexport const ENFORCEMENT_VALUES = Object.freeze(['dry-run']);\n\nexport const WHEN_KEYS = Object.freeze(['path', 'method']);\n\n/** Properties the engine or runtime reads on a rule. */\nexport const RULE_PROPERTIES = Object.freeze([\n 'id', 'rule_id', 'title', 'category', 'phase', 'action', 'rule_v2', 'when',\n 'message', 'enforcement', 'source_revision', 'prefilter',\n 'max_bytes', 'bypass_limit',\n 'set_headers', 'remove_headers', 'cookie_flags', 'ensure',\n]);\n\n/**\n * What each action needs, what it defaults, and which phases can carry it out.\n *\n * `required` is a property without which the action does nothing. `defaulted` is one the runtime supplies\n * — `harden-cookie` applies HttpOnly, Secure and SameSite=Lax on its own, so requiring `cookie_flags`\n * would drop a rule that works.\n *\n * `phases` matters as much: the header mutations are carried out on the response, and a request-phase rule\n * carrying one is not a header mutation at all — it falls through to the blocking path and answers 403.\n */\nexport const ACTION_PROPERTIES = Object.freeze({\n block: Object.freeze({ required: Object.freeze([]), defaulted: Object.freeze([]), phases: Object.freeze(['request', 'response', 'egress']) }),\n redact: Object.freeze({ required: Object.freeze([]), defaulted: Object.freeze([]), phases: Object.freeze(['response']) }),\n encode: Object.freeze({ required: Object.freeze([]), defaulted: Object.freeze([]), phases: Object.freeze(['response']) }),\n 'set-header': Object.freeze({\n required: Object.freeze(['set_headers']),\n defaulted: Object.freeze(['ensure']),\n phases: Object.freeze(['response']),\n shapes: Object.freeze({ set_headers: 'header-name-map' }),\n }),\n 'remove-header': Object.freeze({\n required: Object.freeze(['remove_headers']),\n defaulted: Object.freeze([]),\n phases: Object.freeze(['response']),\n shapes: Object.freeze({ remove_headers: 'header-name-list' }),\n }),\n 'harden-cookie': Object.freeze({\n required: Object.freeze([]),\n defaulted: Object.freeze(['cookie_flags']),\n phases: Object.freeze(['response']),\n shapes: Object.freeze({ cookie_flags: 'cookie-flags' }),\n }),\n});\n\nexport const LIMITS = Object.freeze({\n maxRules: 5000,\n maxWhitelists: 2000,\n maxConditionsPerRule: 250,\n maxNestingDepth: 12,\n maxRegexLength: 1000,\n maxValueLength: 8192,\n});\n\n/** The shape of a condition group: this parameter, and a nested `rules` array. */\nexport const GROUP_PARAMETER = 'rules';\n\n/** Is this condition a group? Groups carry no match of their own. */\nexport function isGroup(condition) {\n return condition?.parameter === GROUP_PARAMETER && Array.isArray(condition?.rules);\n}\n\n/**\n * @returns {string|null} why this condition is neither a well-formed group nor a well-formed leaf\n *\n * The engine takes the group branch only for `parameter: 'rules'` WITH a `rules` array, and that branch\n * returns before it reads `match`. So `parameter: 'rules'` on its own resolves to a null value and tests\n * nothing, and a group carrying a `match` has that match ignored — both are conditions a reviewer approves\n * believing they do something.\n */\nexport function conditionShapeProblem(condition) {\n if (!condition || typeof condition !== 'object') return 'condition is not an object';\n\n const named = condition.parameter === GROUP_PARAMETER;\n const nested = Array.isArray(condition.rules);\n\n if (named && !nested) {\n return `parameter \"${GROUP_PARAMETER}\" is a group and needs a \"rules\" array`;\n }\n if (nested && !named) {\n return `condition carries nested rules but its parameter is ${JSON.stringify(condition.parameter)}; a group must be {\"parameter\":\"${GROUP_PARAMETER}\"}`;\n }\n if (named && nested) {\n if (condition.match !== undefined) return 'a group carries no match of its own; the engine ignores it';\n if (condition.mutations !== undefined) return 'a group carries no mutations of its own; the engine ignores them';\n if (condition.rules.length === 0) return 'group has no conditions (would never match)';\n }\n\n return null;\n}\n\n/**\n * Is this a parameter the resolver can resolve?\n *\n * @returns {string|null} why it cannot, or null when it can\n */\nexport function parameterProblem(parameter) {\n if (parameter === undefined || parameter === null) return null;\n\n if (Array.isArray(parameter)) {\n if (parameter.length === 0) return 'parameter list is empty';\n for (const member of parameter) {\n const problem = parameterProblem(member);\n if (problem !== null) return problem;\n }\n\n return null;\n }\n\n if (typeof parameter !== 'string' || parameter === '') return 'parameter must be a non-empty string';\n\n // Reached only for a LEAF: a whole group is recognised before this and never validated as a parameter.\n // So `rules` here — bare, or as a member of a list — is a leaf naming the group source, which the\n // resolver answers with a null value.\n if (parameter === GROUP_PARAMETER) {\n return `\"${GROUP_PARAMETER}\" is the group source and is only valid as a whole group, not as a parameter`;\n }\n\n const dot = parameter.indexOf('.');\n const name = dot === -1 ? parameter : parameter.slice(0, dot);\n const source = SOURCES[name];\n\n if (source === undefined) return `unknown parameter source \"${name}\"`;\n\n if (dot === -1) {\n return source.keyed === true\n ? `source \"${name}\" needs a key, e.g. \"${name}.fieldname\"`\n : null;\n }\n\n if (source.keyed !== true) return `source \"${name}\" takes no key`;\n\n const key = parameter.slice(dot + 1);\n if (key === '') return `source \"${name}\" needs a key after the dot`;\n\n return keyProblem(name, source, key);\n}\n\nfunction keyProblem(name, source, key) {\n if (key.endsWith('*') && source.wildcard !== true) {\n return `\"${name}\" does not fan out over a wildcard key`;\n }\n\n const bare = key.endsWith('*') ? key.slice(0, -1) : key;\n\n // An application field name cannot be enumerated, so any non-empty key is accepted.\n if (source.keys === 'any') return null;\n\n if (Array.isArray(source.keys) && source.keys.includes(bare)) return null;\n\n for (const prefix of source.key_prefixes ?? []) {\n if (bare.startsWith(prefix) && bare.length > prefix.length) return null;\n }\n\n const accepted = [\n ...(Array.isArray(source.keys) ? source.keys : []),\n ...(source.key_prefixes ?? []).map((p) => `${p}…`),\n ].join(', ');\n\n return `\"${name}\" has no key \"${key}\" (expected one of: ${accepted})`;\n}\n\n/** @returns {string|null} why this match is not one the engine evaluates, or null */\nexport function matchProblem(match, parameter) {\n if (!match || typeof match !== 'object') return 'condition has no match object';\n if (typeof match.type !== 'string' || match.type === '') return 'match.type must be a non-empty string';\n if (!MATCH_TYPES.includes(match.type)) return `unknown match type \"${match.type}\"`;\n\n const parameterless = PARAMETERLESS_MATCH_TYPES.includes(match.type);\n if (!parameterless && (parameter === undefined || parameter === null)) {\n return `match type \"${match.type}\" needs a parameter`;\n }\n if (parameterless && parameter !== undefined && parameter !== null) {\n return `match type \"${match.type}\" reads the whole request and takes no parameter`;\n }\n\n const operands = MATCH_OPERANDS[match.type];\n if (operands) {\n for (const required of operands.required) {\n if (match[required] === undefined || match[required] === null) {\n return `match type \"${match.type}\" needs \"${required}\"`;\n }\n }\n for (const [operand, shape] of Object.entries(operands.shapes ?? {})) {\n if (match[operand] === undefined) continue;\n const problem = operandShapeProblem(shape, match[operand]);\n if (problem) return `match type \"${match.type}\" operand \"${operand}\" ${problem}`;\n }\n } else if (!OPERANDLESS_MATCH_TYPES.includes(match.type) && match.value === undefined) {\n return `match type \"${match.type}\" needs a value`;\n }\n\n return null;\n}\n\n/** @returns {string|null} why one of these mutations is not implemented, or null */\nexport function mutationsProblem(mutations) {\n if (mutations === undefined || mutations === null) return null;\n if (!Array.isArray(mutations)) return 'mutations must be an array';\n\n for (const mutation of mutations) {\n if (typeof mutation !== 'string' || !MUTATIONS.includes(mutation)) {\n return `unknown mutation \"${String(mutation)}\" — applied as a no-op, so the condition would test the untransformed value`;\n }\n }\n\n return null;\n}\n\n/**\n * @returns {string|null} why this scope would not narrow the rule, or null\n */\nexport function whenProblem(when) {\n if (when === undefined || when === null) return null;\n if (typeof when !== 'object' || Array.isArray(when)) return 'when must be an object';\n\n const keys = Object.keys(when);\n if (keys.length === 0) return 'when is empty — remove it, or name a path or method';\n\n const unknown = keys.filter((key) => !WHEN_KEYS.includes(key));\n if (unknown.length > 0) {\n return `when names no supported key (${unknown.join(', ')}) — the engine understands ${WHEN_KEYS.join(' and ')}, `\n + 'and an unrecognised scope is ignored, so the rule would apply to every request';\n }\n\n // The values, not only the names. A scope the engine cannot use fails in both directions: an empty string\n // is falsy, so the check is skipped and the rule applies everywhere; an empty list or a non-string matches\n // nothing, so the rule applies nowhere. Neither is what the author wrote.\n if ('path' in when && !isNonEmptyString(when.path)) {\n return 'when.path must be a non-empty string';\n }\n\n if ('method' in when) {\n const methods = Array.isArray(when.method) ? when.method : [when.method];\n if (methods.length === 0) return 'when.method is an empty list, so the rule would match no method';\n if (!methods.every(isNonEmptyString)) return 'when.method must be a non-empty string, or a list of them';\n }\n\n return null;\n}\n\nfunction isNonEmptyString(value) {\n return typeof value === 'string' && value.trim() !== '';\n}\n\n/** @returns {string|null} why this action cannot be carried out as written, or null */\n/**\n * @returns {string|null} why a property authored as null is refused, or null when none is\n */\nexport function nullPropertyProblem(rule) {\n if (!rule || typeof rule !== 'object') return null;\n\n for (const property of RULE_PROPERTIES) {\n if (property in rule && rule[property] === null) {\n return `\"${property}\" is present but null; omit it to mean the default, since a null is a value that `\n + 'was meant to be something and is not';\n }\n }\n\n return null;\n}\n\n/**\n * @returns {string|null} why a rule-level property cannot do what it says, or null\n */\nexport function rulePropertyProblem(rule) {\n if (!rule || typeof rule !== 'object') return null;\n\n for (const [property, shape] of Object.entries(RULE_PROPERTY_SHAPES)) {\n if (rule[property] === undefined || rule[property] === null) continue;\n const problem = operandShapeProblem(shape, rule[property]);\n if (problem) return `\"${property}\" ${problem}`;\n }\n\n // `enforcement` is deliberately NOT checked here, and this is the one place in the contract where a\n // value the runtime does not recognise is left alone on purpose.\n //\n // The engine reads an unknown enforcement as \"follow the site\", which is the conservative reading for a\n // protection control — it never turns blocking off. That is also what makes it forward compatible: a\n // NEWER server may send a value this guard has not learned, and the rule must still protect. Refusing the\n // rule here would drop it instead, so an older guard would lose the protection entirely the moment the\n // vocabulary grew. Trading real enforcement for a stricter reading of a field that already fails safe is\n // the wrong way round.\n //\n // A typo'd enforcement is still worth catching — but as a question about what the author meant, which is\n // review's job, not as a claim that the guard cannot run the document.\n return null;\n}\n\nexport function actionProblem(action, rule) {\n if (action === undefined || action === null) return null;\n if (!ACTIONS.includes(action)) return `unknown action \"${action}\"`;\n\n const spec = ACTION_PROPERTIES[action];\n\n for (const required of spec?.required ?? []) {\n if (rule?.[required] === undefined || rule?.[required] === null) {\n return `action \"${action}\" needs \"${required}\"`;\n }\n }\n\n for (const [property, shape] of Object.entries(spec?.shapes ?? {})) {\n if (rule?.[property] === undefined || rule?.[property] === null) continue;\n const problem = operandShapeProblem(shape, rule[property]);\n if (problem) return `action \"${action}\" property \"${property}\" ${problem}`;\n }\n\n const phase = rule?.phase ?? 'request';\n if (spec !== undefined && !spec.phases.includes(phase)) {\n return `action \"${action}\" is carried out on the ${spec.phases.join('/')} phase; on \"${phase}\" the rule blocks instead`;\n }\n\n return null;\n}\n\n/** The whole contract, for publishing and for a consumer to load. */\nexport function ruleContract() {\n const sources = Object.fromEntries(\n Object.entries(SOURCES).map(([name, spec]) => [name, {\n keyed: spec.keyed === true,\n ...(spec.group === true ? { group: true } : {}),\n ...(spec.keys !== undefined ? { keys: spec.keys === 'any' ? 'any' : [...spec.keys] } : {}),\n ...(spec.key_prefixes ? { key_prefixes: [...spec.key_prefixes] } : {}),\n ...(spec.optional_key_suffixes ? { optional_key_suffixes: [...spec.optional_key_suffixes] } : {}),\n ...(spec.wildcard === true ? { wildcard: true } : {}),\n }]),\n );\n\n return {\n version: CONTRACT_VERSION,\n sources,\n group_parameter: GROUP_PARAMETER,\n match_types: [...MATCH_TYPES],\n parameterless_match_types: [...PARAMETERLESS_MATCH_TYPES],\n operandless_match_types: [...OPERANDLESS_MATCH_TYPES],\n operand_shapes: [...OPERAND_SHAPES],\n match_operands: Object.fromEntries(\n Object.entries(MATCH_OPERANDS).map(([k, v]) => [k, { required: [...v.required], shapes: { ...(v.shapes ?? {}) } }]),\n ),\n mutations: [...MUTATIONS],\n phases: [...PHASES],\n actions: [...ACTIONS],\n action_properties: Object.fromEntries(\n Object.entries(ACTION_PROPERTIES).map(([k, v]) => [k, {\n required: [...v.required],\n defaulted: [...v.defaulted],\n phases: [...v.phases],\n shapes: { ...(v.shapes ?? {}) },\n }]),\n ),\n when_keys: [...WHEN_KEYS],\n null_valued_properties: NULL_VALUED_PROPERTIES,\n rule_property_shapes: { ...RULE_PROPERTY_SHAPES },\n enforcement_values: [...ENFORCEMENT_VALUES],\n rule_properties: [...RULE_PROPERTIES],\n limits: { ...LIMITS },\n };\n}\n","// Rule-bundle validation. Delivered rules are POLICY fetched over the network, and the engine executes\n// them on every request — so an upstream compromise, a schema drift, or a mistake in the corpus could\n// otherwise hand the app unbounded work (a 50k-rule bundle, a 500-deep condition tree, a pathological\n// regex) or silently unenforceable junk.\n//\n// Two principles:\n// 1. REJECT, don't silently skip. A rule that fails validation is dropped WITH a reported reason, so\n// an unenforceable rule is visible instead of quietly protecting nothing.\n// 2. Bound everything the engine will walk: rule count, conditions per rule, nesting depth, and\n// pattern length. Caps are deliberately far above any real corpus rule.\n//\n// Fail-open in spirit: validation never throws, and a bundle whose rules are all rejected simply means\n// \"no rules\" (the app keeps serving) — never a crash.\n\n// Vocabulary, shape and bounds all come from the shared contract, so this file and the layers upstream of\n// it are checking one description of what the engine runs.\nimport {\n ACTIONS as CONTRACT_ACTIONS,\n LIMITS as CONTRACT_LIMITS,\n PHASES as CONTRACT_PHASES,\n actionProblem,\n conditionShapeProblem,\n isGroup,\n matchProblem,\n mutationsProblem,\n parameterProblem,\n whenProblem,\n nullPropertyProblem,\n rulePropertyProblem,\n} from './contract.js';\n\nexport const LIMITS = CONTRACT_LIMITS;\n\nconst PHASES = new Set(CONTRACT_PHASES);\nconst ACTIONS = new Set(CONTRACT_ACTIONS);\n\n/**\n * Validate a delivered bundle. Returns `{ bundle, rejected }` where `bundle` contains only rules that\n * passed and `rejected` is `[{ id, reason }]` for everything dropped.\n * @param {object} bundle\n * @returns {{ bundle: object, rejected: Array<{id: string, reason: string}> }}\n */\nexport function validateBundle(bundle, opts = {}) {\n const rejected = [];\n const inFirewall = Array.isArray(bundle?.firewall) ? bundle.firewall : [];\n const inWhitelists = Array.isArray(bundle?.whitelists) ? bundle.whitelists : [];\n\n const firewall = [];\n for (const rule of inFirewall) {\n if (firewall.length >= LIMITS.maxRules) {\n rejected.push({ id: idOf(rule), reason: `bundle exceeds maxRules (${LIMITS.maxRules})` });\n continue;\n }\n const reason = ruleProblem(rule);\n if (reason) rejected.push({ id: idOf(rule), reason });\n else firewall.push(rule);\n }\n\n const whitelists = [];\n for (const wl of inWhitelists) {\n if (whitelists.length >= LIMITS.maxWhitelists) {\n rejected.push({ id: idOf(wl), reason: `bundle exceeds maxWhitelists (${LIMITS.maxWhitelists})` });\n continue;\n }\n // A whitelist SUPPRESSES rules, so a malformed one is a protection risk, not a detection risk.\n // One with no `rule_id` applies to EVERY rule — a single tripped condition disables the whole\n // firewall for that request — so it must be opted into explicitly.\n if (!opts.allowGlobalWhitelists && wl && Array.isArray(wl.rule_v2) && !wl.rule_id) {\n rejected.push({ id: idOf(wl), reason: 'whitelist has no rule_id (would suppress every rule); set allowGlobalWhitelists to permit' });\n continue;\n }\n const reason = conditionsProblem(wl?.rule_v2);\n if (reason) rejected.push({ id: idOf(wl), reason: `whitelist: ${reason}` });\n else whitelists.push(wl);\n }\n\n return {\n bundle: { ...bundle, firewall, whitelists },\n rejected,\n };\n}\n\nfunction idOf(rule) {\n const id = rule?.id ?? rule?.rule_id;\n return id === undefined || id === null ? '(unidentified)' : String(id);\n}\n\n/** @returns {string|null} a reason the rule must be dropped, or null when it's acceptable. */\nfunction ruleProblem(rule) {\n if (!rule || typeof rule !== 'object') return 'not an object';\n\n // Before anything reads a property: a property that is PRESENT and null is not an omission. Every layer\n // that defaults an absent field would default this one too, so `phase: null` runs on the request phase and\n // a rule authored for egress never fires. Checked first because the checks below default as they read.\n const nullReason = nullPropertyProblem(rule);\n if (nullReason) return nullReason;\n\n if (rule.phase !== undefined && !PHASES.has(rule.phase)) return `unknown phase \"${rule.phase}\"`;\n\n // The action's own properties, not just its name: `set-header` without `set_headers` matches and then\n // does nothing, which reads as a rule that is enforced.\n const actionReason = actionProblem(rule.action, rule);\n if (actionReason) return actionReason;\n\n // An unrecognised scope key is IGNORED by the engine, so the rule applies to every request — a scope\n // authored to narrow a rule that instead widens it to everything.\n const scopeReason = whenProblem(rule.when);\n if (scopeReason) return scopeReason;\n\n // The rule-level properties the RUNTIME reads by identity or coercion. `Number(x) > 0` used to stand in\n // for the cap check, and it accepted `Infinity` — which the cap calculation then ignores for not being\n // finite, so the rule validated as \"no limit\" and delivered the default.\n const propertyReason = rulePropertyProblem(rule);\n if (propertyReason) return propertyReason;\n\n return conditionsProblem(rule.rule_v2);\n}\n\nfunction conditionsProblem(conditions, depth = 0) {\n if (!Array.isArray(conditions)) return 'rule_v2 must be an array of conditions';\n if (conditions.length === 0) return 'rule_v2 is empty (would never match)';\n if (depth > LIMITS.maxNestingDepth) return `nesting deeper than ${LIMITS.maxNestingDepth}`;\n if (conditions.length > LIMITS.maxConditionsPerRule) {\n return `more than ${LIMITS.maxConditionsPerRule} conditions`;\n }\n for (const c of conditions) {\n const shapeReason = conditionShapeProblem(c);\n if (shapeReason) return shapeReason;\n\n if (isGroup(c)) {\n const nested = conditionsProblem(c.rules, depth + 1);\n if (nested) return nested;\n continue; // a group carries no match of its own\n }\n // The vocabulary checks. Without them the only question asked was whether `match.type` was a\n // non-empty string, so an invented source, an invented match type and an invented mutation all passed\n // — and each of them produces a rule that is delivered, counted as protection, and never matches.\n const parameterReason = parameterProblem(c.parameter);\n if (parameterReason) return parameterReason;\n\n const mutationReason = mutationsProblem(c.mutations);\n if (mutationReason) return mutationReason;\n\n const matchReason = matchProblem(c.match, c.parameter);\n if (matchReason) return matchReason;\n\n const m = c.match;\n if (m.type === 'regex') {\n if (typeof m.value !== 'string') return 'regex match.value must be a string';\n if (m.value.length > LIMITS.maxRegexLength) return `regex longer than ${LIMITS.maxRegexLength} chars`;\n } else if (typeof m.value === 'string' && m.value.length > LIMITS.maxValueLength) {\n return `match.value longer than ${LIMITS.maxValueLength} chars`;\n }\n if (m.match) {\n // array_key_value nests a sub-match; count it toward depth so a chain can't be unbounded.\n //\n // The PARENT's parameter is carried down. The sub-match has none of its own — it applies to whatever\n // the key path navigated to — so validating it in isolation reported every one of them as a match\n // type missing a parameter, which would have rejected a shipped capability as malformed.\n const nested = conditionsProblem([{ parameter: c.parameter, match: m.match }], depth + 1);\n if (nested) return nested;\n }\n }\n return null;\n}\n","// Rule source: pick where the ruleset comes from — an explicit bundle, or the live API by site\n// UUID (Pulse) or token — fetch it (conditional/If-None-Match via the persisted etag), and fall\n// back through last-known-good → bundled → empty. Fail-open: a fetch/parse error never throws.\n// The `store` (see ./store.js) is passed in so a refresh reuses the same tiered cache.\n//\n// Every returned bundle carries `source: { ok, reason? }` — whether the RULES came from the source or\n// from a fallback. Absorbing a failure into usable rules is right for protection and insufficient for a\n// caller that has its own decision to make: a poller reading only thrown errors treats an outage as a\n// healthy poll. The rules answer \"what do I enforce\"; `source` answers \"are these current\".\nimport { PatchstackRuleClient } from '../engine/index.js';\nimport { PulseRuleClient } from '../engine/pulse-client.js';\nimport { validateBundle } from './validate.js';\nimport { notify } from '../notify.js';\n\n// A LIVE update is accepted ATOMICALLY. Dropping individual invalid rules is fine for a bundle we\n// already trust (a cache entry, a bundled fallback), but for a fresh remote response it would let a\n// broken update REPLACE known-good policy with partial or empty policy — turning \"we validated it\" into\n// a loss of protection, and caching that loss. So: if any rule/whitelist fails validation, reject the\n// whole update, keep last-known-good, report it, and do NOT write the cache. Opt in to the old\n// behaviour with `acceptPartialBundle: true` (metrics still report every drop).\nfunction liveUpdateRejections(res, options) {\n if (options.acceptPartialBundle) return [];\n const { rejected } = validateBundle(\n { firewall: Array.isArray(res.firewall) ? res.firewall : [], whitelists: Array.isArray(res.whitelists) ? res.whitelists : [] },\n { allowGlobalWhitelists: options.allowGlobalWhitelists },\n );\n return rejected;\n}\n\nfunction reportRejections(rejected, options, label) {\n const report = options.onRuleRejected;\n for (const r of rejected) {\n if (typeof report === 'function') {\n notify(report, { ...r, accepted: false }, 'onRuleRejected');\n }\n }\n const sample = rejected.slice(0, 3).map((r) => `${r.id} (${r.reason})`).join('; ');\n notify(options.onError, new Error(\n `${label}: rejected the entire update because ${rejected.length} rule(s) failed validation — ` +\n `keeping the previous ruleset and NOT caching this response: ${sample}${rejected.length > 3 ? ', …' : ''}`,\n ), 'onError');\n}\n\n/** A bundle plus the outcome of the attempt that produced it. `source` is never written to the store. */\nfunction fromSource(bundle, reason) {\n return reason === undefined ? { ...bundle, source: { ok: true } } : { ...bundle, source: { ok: false, reason } };\n}\n\nexport async function resolveRules(options, store, ctx = {}) {\n // The INITIAL load is on the app's startup path, so the runtime gives it a short budget (see\n // bootTimeoutMs) and falls back to cache/bundled rather than delaying boot; refreshes get the full\n // budget. A timeout here is not a protection gap by itself — last-known-good still applies.\n const timeoutMs = ctx.timeoutMs;\n if (options.siteUuid) {\n const prior = await store.read(); // { bundle, etag } | null\n const client = new PulseRuleClient({ siteUuid: options.siteUuid, baseUrl: options.pulseRulesUrl, etag: prior?.etag, timeoutMs, pulseAuth: ctx.pulseAuth, reportsDetections: options.reportDetections === true });\n const res = await client.getRules();\n if (res.success && res.notModified && prior?.bundle) return fromSource(normalizeBundle(prior.bundle, options));\n if (res.success && !res.notModified) {\n const rejected = liveUpdateRejections(res, options);\n if (rejected.length > 0) {\n reportRejections(rejected, options, 'rule update rejected');\n // Reached the source and refused what it sent. Not ok: the running rules are not the delivered\n // ones, and asking again at the normal interval re-downloads the same rejected bundle.\n if (prior?.bundle) return fromSource(normalizeBundle(prior.bundle, options), 'update rejected');\n if (options.rules) return fromSource(normalizeBundle(options.rules, options), 'update rejected');\n return fromSource(emptyBundle(), 'update rejected');\n }\n const bundle = normalizeBundle(res, options);\n await store.write({ bundle, etag: res.etag ?? null });\n return fromSource(bundle);\n }\n if (prior?.bundle) {\n notify(options.onError, new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); using cached bundle`), 'onError');\n return fromSource(normalizeBundle(prior.bundle, options), res.error ?? 'no usable response');\n }\n if (options.rules) {\n notify(options.onError, new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); using bundled fallback`), 'onError');\n return fromSource(normalizeBundle(options.rules, options), res.error ?? 'no usable response');\n }\n notify(options.onError, new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); no cache — running with no rules`), 'onError');\n return fromSource(emptyBundle(), res.error ?? 'no usable response');\n }\n\n if (options.token) {\n const prior = await store.read();\n const client = new PatchstackRuleClient({ token: options.token, baseUrl: options.baseUrl, etag: prior?.etag, timeoutMs });\n const res = await client.getRules();\n if (res.success && res.notModified && prior?.bundle) return fromSource(normalizeBundle(prior.bundle, options));\n if (res.success && !res.notModified) {\n const rejected = liveUpdateRejections(res, options);\n if (rejected.length > 0) {\n reportRejections(rejected, options, 'rule update rejected');\n // Reached the source and refused what it sent. Not ok: the running rules are not the delivered\n // ones, and asking again at the normal interval re-downloads the same rejected bundle.\n if (prior?.bundle) return fromSource(normalizeBundle(prior.bundle, options), 'update rejected');\n if (options.rules) return fromSource(normalizeBundle(options.rules, options), 'update rejected');\n return fromSource(emptyBundle(), 'update rejected');\n }\n const bundle = normalizeBundle(res, options);\n await store.write({ bundle, etag: res.etag ?? null });\n return fromSource(bundle);\n }\n if (prior?.bundle) {\n notify(options.onError, new Error(`rule fetch failed (${res.error ?? 'no usable response'}); using cached bundle`), 'onError');\n return fromSource(normalizeBundle(prior.bundle, options), res.error ?? 'no usable response');\n }\n notify(options.onError, new Error(`rule fetch failed (${res.error ?? 'no usable response'}); no cache — running with no rules`), 'onError');\n return fromSource(emptyBundle(), res.error ?? 'no usable response');\n }\n\n // No live source configured, so the bundle IS the source and cannot be behind one.\n if (options.rules) {\n return fromSource(normalizeBundle(options.rules, options));\n }\n\n return fromSource(emptyBundle());\n}\n\n// Every rule path (live fetch, cache, bundled fallback) funnels through here, so this is where the\n// delivered policy is VALIDATED before the engine ever executes it: bounded rule count / conditions /\n// nesting / pattern length, known phases + actions. A rule that fails is dropped with a reported reason\n// (`onRuleRejected`) rather than silently kept — an unenforceable rule must never look enforced.\nexport function normalizeBundle(b, options = {}) {\n const enforcement = b?.enforcement ?? b?.mode;\n const { bundle: checked, rejected } = validateBundle(\n {\n firewall: Array.isArray(b.firewall) ? b.firewall : [],\n whitelists: Array.isArray(b.whitelists) ? b.whitelists : [],\n },\n { allowGlobalWhitelists: options.allowGlobalWhitelists },\n );\n if (rejected.length > 0) {\n const report = options.onRuleRejected;\n if (typeof report === 'function') {\n for (const r of rejected) {\n notify(report, r, 'onRuleRejected');\n }\n } else {\n const sample = rejected.slice(0, 3).map((r) => `${r.id} (${r.reason})`).join('; ');\n // eslint-disable-next-line no-console\n console.warn(\n `[patchstack] ${rejected.length} delivered rule(s) rejected as invalid/oversized and are NOT enforced: ${sample}` +\n (rejected.length > 3 ? ', …' : ''),\n );\n }\n }\n return {\n firewall: checked.firewall,\n whitelists: checked.whitelists,\n whitelist_keys: b.whitelist_keys ?? {},\n ...(enforcement === 'block' || enforcement === 'dry-run' ? { enforcement } : {}),\n };\n}\n\nexport function emptyBundle() {\n return { firewall: [], whitelists: [], whitelist_keys: {} };\n}\n","// Rule refresh scheduling — how a long-lived guard picks up rules that become relevant after boot\n// (a dependency added mid-session, a zero-day published). Two triggers, both driving the same\n// caller-supplied `tick` (which re-fetches + hot-swaps the engines):\n// - startRefresh: a self-scheduling poll LOOP (reschedules after each tick settles, so runs never\n// overlap), with ±jitter (avoid a thundering herd) and exponential backoff on consecutive\n// failures. `unref`'d — it never keeps the process alive. A tick counts as failed when it throws\n// OR when it reports `{ ok: false }`: the rule resolver absorbs an API or network failure into\n// usable fallback rules, so a thrown error is not the only shape an outage takes, and a poller\n// that waits for one keeps the whole fleet knocking at the normal interval while it lasts.\n// - makeRefreshHandler: a PUSH endpoint — an authenticated fetch handler the platform/SaaS hits\n// for an immediate refresh (zero-day fast lane) instead of waiting for the next poll.\n\nimport { notify } from '../notify.js';\n\nconst JITTER_FRACTION = 0.1;\nconst MAX_BACKOFF_MULTIPLIER = 8; // cap consecutive-failure backoff at 8× the base interval\n\nexport function startRefresh(tick, { refreshMs, onError } = {}) {\n let stopped = false;\n let failures = 0;\n let timer = null;\n\n const schedule = () => {\n if (stopped) return;\n const backoff = Math.min(2 ** failures, MAX_BACKOFF_MULTIPLIER);\n const jitter = 1 - Math.random() * JITTER_FRACTION; // shorten by up to 10% so clients don't align\n const delay = refreshMs * backoff * jitter;\n timer = setTimeout(run, delay);\n timer?.unref?.();\n };\n\n const run = async () => {\n if (stopped) return;\n try {\n const status = await tick();\n if (status && status.ok === false) failures++;\n else failures = 0;\n } catch (err) {\n failures++;\n notify(onError, err, 'onError');\n }\n schedule();\n };\n\n schedule();\n return {\n stop() {\n stopped = true;\n if (timer) clearTimeout(timer);\n },\n };\n}\n\nexport function makeRefreshHandler(tick, secret) {\n return async (request) => {\n // No secret configured → the endpoint doesn't exist (never an open refresh-DoS surface).\n if (!secret) return new Response('not found', { status: 404 });\n let provided = null;\n try {\n provided = request?.headers?.get?.('x-patchstack-refresh') ?? new URL(request.url).searchParams.get('token');\n } catch {\n provided = null;\n }\n if (provided !== secret) return new Response('forbidden', { status: 403 });\n let refreshed = true;\n try {\n // `{ ok: false }` means the tick ran but the rules did not come from the source, which is not a\n // refresh — the caller pushed because it had something to deliver, and it did not arrive.\n const status = await tick();\n if (status && status.ok === false) refreshed = false;\n } catch {\n refreshed = false; // fail-open: report the outcome, never throw\n }\n return new Response(JSON.stringify({ refreshed }), { status: 200, headers: { 'content-type': 'application/json' } });\n };\n}\n","import { pulseAuthHeader } from '../pulse-token.js';\nimport { isSafeOrigin } from './safe-origin.js';\n\n/**\n * Detection reporter: every rule that fired, whether or not it blocked.\n *\n * Separate from `firewall-log.js`, which posts ENFORCED blocks in the WordPress-compatible shape\n * (`fid`, `request_uri`, `ip`, `user_agent`). That path answers \"what did we stop\". This one answers a\n * question nothing else could: **what would this rule have stopped**, for a rule that carries\n * `enforcement: dry-run` and therefore blocks nothing.\n *\n * Without it, a rule that is quietly wrong and a rule that is protecting look identical from the\n * outside, because neither produces a block to report.\n *\n * ## The payload is deliberately small\n *\n * `rule_id`, route PATH, the parameters the rule reads, a timestamp, whether it was enforced, the phase,\n * the bundle identity, and the rule's own revision where the bundle carried one. That is enough to count\n * hits per rule, compare them against traffic, and decide whether a rule is wrong.\n *\n * What it never carries: **the matched value, the request body, headers, or query-string values**. A\n * channel that counts detections is a different thing from a copy of an application's traffic, and once\n * values are collected every question about retention, access and jurisdiction arrives with them.\n * Anything value-level belongs behind its own explicit opt-in with its own controls, not as a side\n * effect of counting.\n *\n * The route is the request PATH with any query string dropped, because `?token=…` is a value.\n */\n\nconst DEFAULT_BASE_URL = 'https://api.patchstack.com/monitor/pulse';\nconst DEFAULT_FLUSH_MS = 5000;\nconst MAX_BATCH = 50;\n/** Bounded so a detection storm costs memory it cannot grow out of. Oldest go first. */\nconst MAX_QUEUE = 500;\n\n/**\n * The parameters a rule reads, from its own definition.\n *\n * NOT \"the condition that matched\": the engine reports a rule, not which of its conditions fired, and\n * threading that out would mean changing evaluation for the sake of a reporting field. A narrowly scoped\n * rule reads exactly one parameter, so the two answers coincide there; for a broad rule this is the set\n * it reads, which is what the field name says.\n *\n * @param {any} rule\n * @returns {string[]}\n */\nexport function ruleParameters(rule) {\n const out = new Set();\n const walk = (conditions) => {\n if (!Array.isArray(conditions)) return;\n for (const condition of conditions) {\n if (!condition || typeof condition !== 'object') continue;\n if (typeof condition.parameter === 'string' && condition.parameter !== 'rules') {\n out.add(condition.parameter);\n }\n if (Array.isArray(condition.rules)) walk(condition.rules);\n }\n };\n walk(rule?.rule_v2);\n\n return [...out];\n}\n\n/**\n * The rule's own revision, from the served rule.\n *\n * Read off the delivered rule rather than derived: whoever served it knows what document this is, and a\n * value computed here would be this client's opinion of it. Accepts a string or a number, because the two\n * kinds of rule that carry one number their revisions differently.\n *\n * @param {any} rule\n * @returns {string | null}\n */\nexport function revisionOf(rule) {\n const revision = rule?.source_revision;\n if (typeof revision === 'string' && revision !== '') return revision;\n if (typeof revision === 'number' && Number.isFinite(revision)) return String(revision);\n\n return null;\n}\n\n/**\n * The request path with the query string removed.\n *\n * A path is a route; a query string is data. `/api/preview?url=http://169.254.169.254/` names both the\n * endpoint and the attack payload, and only the first belongs in a counting channel.\n *\n * @param {unknown} path\n * @returns {string | null}\n */\nexport function routeOf(path) {\n if (typeof path !== 'string' || path === '') return null;\n const cut = path.search(/[?#]/);\n\n return cut === -1 ? path : path.slice(0, cut);\n}\n\n/**\n * @param {{\n * siteUuid?: string,\n * baseUrl?: string,\n * pulseAuth?: unknown,\n * rulesEtag?: string | null,\n * fetchImpl?: typeof fetch,\n * flushMs?: number,\n * maxQueue?: number,\n * }} opts\n */\nexport function createDetectionReporter(opts) {\n const siteUuid = opts.siteUuid ?? process.env?.PATCHSTACK_SITE_UUID;\n if (!siteUuid) {\n // Nothing to report against. A no-op rather than a throw: reporting is never worth failing a boot.\n // It answers the whole interface, so a caller never has to know which kind it holds.\n return {\n record() {}, flush() {}, stop() {}, setRulesEtag() {}, dropped: () => 0,\n health: () => ({ sent: 0, delivered: 0, failed: 0, dropped: 0, lastDeliveredAt: null }),\n };\n }\n\n const configured = opts.baseUrl ?? process.env?.PATCHSTACK_PULSE_RULES_URL;\n const baseUrl = typeof configured === 'string' && isSafeOrigin(configured)\n ? configured.replace(/\\/$/, '')\n : DEFAULT_BASE_URL;\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n const flushMs = Number.isFinite(opts.flushMs) && opts.flushMs > 0 ? opts.flushMs : DEFAULT_FLUSH_MS;\n const maxQueue = Number.isFinite(opts.maxQueue) && opts.maxQueue > 0 ? opts.maxQueue : MAX_QUEUE;\n\n // The bundle identity stamped onto each event. MUTABLE, because the guard's rules are: a refresh\n // hot-swaps the ruleset in place, and a reporter holding the boot-time value would attribute a hit\n // produced by the new bundle to the old one — sending a reviewer to a rule document that is not the\n // one that fired. Updated by the runtime only after an accepted swap (see `setRulesEtag`).\n let rulesEtag = opts.rulesEtag ?? null;\n\n // Delivery health. The capability declaration says a guard INTENDS to report; these say whether\n // anything arrived. Counts and one timestamp only — a clean app and a broken delivery path are\n // otherwise indistinguishable, and distinguishing them needs no request data at all.\n let sent = 0;\n let delivered = 0;\n let failed = 0;\n let droppedTotal = 0;\n /** @type {string | null} */\n let lastDeliveredAt = null;\n\n /** @type {Array<Record<string, unknown>>} */\n let queue = [];\n /** @type {ReturnType<typeof setTimeout> | null} */\n let timer = null;\n let stopped = false;\n let dropped = 0;\n\n const flush = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (queue.length === 0 || typeof fetchImpl !== 'function') return;\n\n const batch = queue.splice(0, MAX_BATCH);\n // The count of what never made it, sent WITH the batch rather than inferred from a gap: a consumer\n // computing a false-positive rate needs to know its denominator is short, and silence about that\n // would make a truncated sample look like a complete one.\n const droppedWith = dropped;\n dropped = 0;\n droppedTotal += droppedWith;\n sent += batch.length;\n\n void (async () => {\n try {\n const res = await fetchImpl(`${baseUrl}/detections/${encodeURIComponent(siteUuid)}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n // Same credential path as the rules fetch. The detections endpoint is site-addressed and\n // requires a verified, site-bound token, so a batch sent without one is refused — which is\n // why the runtime does not build a reporter when no credential resolves, rather than\n // posting into a 401.\n ...(await pulseAuthHeader({ pulseAuth: opts.pulseAuth, endpoint: baseUrl }, fetchImpl)),\n },\n body: JSON.stringify({ detections: batch, dropped: droppedWith }),\n });\n // Fail-open and no retry: a rejected or unreachable endpoint must not disturb the app, and a\n // retry loop over a refusing endpoint is worse than the lost batch. The outcome is counted, so\n // that a delivery path which refuses everything is distinguishable from an app where no rule\n // fired — both are silence at the server otherwise.\n if (res && res.ok) {\n delivered += batch.length;\n lastDeliveredAt = new Date().toISOString();\n } else {\n failed += batch.length;\n }\n } catch {\n failed += batch.length;\n }\n })();\n };\n\n return {\n /**\n * @param {{\n * rule?: { id?: string, rule_v2?: unknown },\n * phase?: string,\n * mode?: string,\n * path?: string,\n * }} detection\n */\n record(detection) {\n if (stopped) return;\n const ruleId = detection?.rule?.id;\n if (ruleId === undefined || ruleId === null || ruleId === '') return;\n\n if (queue.length >= maxQueue) {\n queue.shift();\n dropped++;\n }\n\n queue.push({\n rule_id: ruleId,\n route: routeOf(detection.path),\n parameters: ruleParameters(detection.rule),\n phase: detection.phase ?? null,\n // The state this detection was handled under, which is the whole point: `false` is a rule that\n // saw traffic it would have stopped.\n enforced: detection.mode === 'block',\n rules_etag: rulesEtag,\n // The revision of THIS rule, as the bundle delivered it. The bundle identity above answers \"which\n // bundle\", which changes whenever anything in it changes — so it cannot say whether the counts for\n // one rule describe the document that rule has now. Passed through untouched, and null when the\n // bundle carried none.\n rule_revision: revisionOf(detection.rule),\n detected_at: new Date().toISOString(),\n });\n\n if (queue.length >= MAX_BATCH) {\n flush();\n\n return;\n }\n if (!timer) timer = setTimeout(flush, flushMs);\n },\n flush,\n stop() {\n stopped = true;\n flush();\n },\n /**\n * Point later events at the bundle now running. Called after an ACCEPTED swap only — a rejected\n * or failed refresh keeps the previous rules, so it must keep the previous identity too.\n *\n * Already-queued events are not rewritten: the stamp is taken when an event is recorded, which is\n * the only moment the two are known to agree.\n *\n * @param {string | null | undefined} next\n */\n setRulesEtag(next) {\n rulesEtag = next ?? null;\n },\n dropped: () => dropped,\n /**\n * Delivery health, counted in events: attempted, acknowledged, refused or unreachable, and dropped\n * for queue pressure — plus when a batch was last acknowledged. No request data of any kind.\n */\n health: () => ({ sent, delivered, failed, dropped: droppedTotal + dropped, lastDeliveredAt }),\n };\n}\n","import { isSafeOrigin } from './safe-origin.js';\n// Fire-and-forget reporter: Connect runtime → existing connector POST /api/logs/log\n// (same path WordPress uses). Auth: WP-style api_key (`{secret}-{oauth.id}`) →\n// POST /oauth/token (client_credentials) → Bearer JWT on /api/logs/log.\n// Opt out: PATCHSTACK_TELEMETRY=off. Never put api_key in the public widget.\n\nconst DEFAULT_API_BASE = 'https://api.patchstack.com';\nconst DEFAULT_FLUSH_MS = 1000;\nconst MAX_BATCH = 50;\nconst TOKEN_SKEW_MS = 60_000;\n\n/**\n * Parse WP plugin api_key (`{secret}-{oauth.id}`) into client credentials.\n * @param {string} apiKey\n * @returns {{ clientId: string, clientSecret: string } | null}\n */\nexport function parseApiKey(apiKey) {\n if (typeof apiKey !== 'string' || apiKey.length === 0) return null;\n const idx = apiKey.lastIndexOf('-');\n if (idx <= 0 || idx === apiKey.length - 1) return null;\n const clientSecret = apiKey.slice(0, idx);\n const clientId = apiKey.slice(idx + 1);\n if (!/^\\d+$/.test(clientId) || clientSecret.length === 0) return null;\n return { clientId, clientSecret };\n}\n\n/**\n * Derive api.patchstack.com origin from a Pulse manifest/rules URL override.\n * @param {string | undefined} pulseOrManifestUrl\n */\nexport function resolveApiBase(pulseOrManifestUrl) {\n const fromEnv = typeof process !== 'undefined' ? process.env?.PATCHSTACK_API_BASE : undefined;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) {\n // The site api_key is exchanged for a token against this origin, so a hostile/injected env value\n // would be a credential-exfiltration path. Require HTTPS (localhost excepted for local testing);\n // anything else falls back to the default origin rather than shipping the key off-platform.\n const candidate = fromEnv.replace(/\\/$/, '');\n if (isSafeOrigin(candidate)) return candidate;\n // eslint-disable-next-line no-console\n console.warn(\n '[patchstack] ignoring PATCHSTACK_API_BASE: block-log reporting requires an https origin ' +\n '(or localhost). Falling back to the default API origin.',\n );\n }\n if (typeof pulseOrManifestUrl === 'string' && pulseOrManifestUrl.length > 0) {\n try {\n return new URL(pulseOrManifestUrl).origin;\n } catch {\n /* fall through */\n }\n }\n const endpoint = typeof process !== 'undefined' ? process.env?.PATCHSTACK_ENDPOINT : undefined;\n if (typeof endpoint === 'string' && endpoint.length > 0) {\n try {\n return new URL(endpoint).origin;\n } catch {\n /* fall through */\n }\n }\n return DEFAULT_API_BASE;\n}\n\n/**\n * @param {{\n * apiKey: string,\n * apiBase?: string,\n * sourceHost?: string,\n * fetchImpl?: typeof fetch,\n * flushMs?: number,\n * }} opts\n */\nexport function createFirewallLogReporter(opts) {\n const creds = parseApiKey(opts.apiKey);\n if (!creds) {\n return { record() {}, flush() {}, stop() {} };\n }\n\n const apiBase = (opts.apiBase ?? DEFAULT_API_BASE).replace(/\\/$/, '');\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n const flushMs = Number.isFinite(opts.flushMs) ? opts.flushMs : DEFAULT_FLUSH_MS;\n const sourceHost = typeof opts.sourceHost === 'string' ? opts.sourceHost : '';\n\n /** @type {Array<Record<string, unknown>>} */\n let queue = [];\n /** @type {ReturnType<typeof setTimeout> | null} */\n let timer = null;\n let stopped = false;\n\n /** @type {{ token: string, expiresAt: number } | null} */\n let cachedToken = null;\n /** @type {Promise<string | null> | null} */\n let tokenInflight = null;\n\n const fetchAccessToken = async () => {\n if (cachedToken && Date.now() < cachedToken.expiresAt - TOKEN_SKEW_MS) {\n return cachedToken.token;\n }\n if (tokenInflight) return tokenInflight;\n\n tokenInflight = (async () => {\n try {\n const res = await fetchImpl(`${apiBase}/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n body: JSON.stringify({\n grant_type: 'client_credentials',\n client_id: creds.clientId,\n client_secret: creds.clientSecret,\n }),\n });\n if (!res || !res.ok) return null;\n const body = await res.json();\n const token = body?.access_token;\n if (typeof token !== 'string' || token.length === 0) return null;\n const expiresIn = Number(body?.expires_in);\n const ttlMs = Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn * 1000 : 3600_000;\n cachedToken = { token, expiresAt: Date.now() + ttlMs };\n return token;\n } catch {\n return null;\n } finally {\n tokenInflight = null;\n }\n })();\n\n return tokenInflight;\n };\n\n const flush = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (queue.length === 0 || typeof fetchImpl !== 'function') return;\n\n const batch = queue.splice(0, MAX_BATCH);\n void (async () => {\n const token = await fetchAccessToken();\n if (!token) return;\n\n const body = new URLSearchParams();\n body.set('type', 'firewall');\n body.set('logs', JSON.stringify(batch));\n\n try {\n const p = fetchImpl(`${apiBase}/api/logs/log`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'User-Agent': '@patchstack/connect',\n ...(sourceHost ? { 'Source-Host': sourceHost } : {}),\n },\n body,\n });\n if (p && typeof p.then === 'function') p.catch(() => {});\n } catch {\n /* ignore */\n }\n })();\n };\n\n return {\n /**\n * @param {{\n * rule?: { id?: string | number },\n * method?: string | null,\n * path?: string | null,\n * ip?: string | null,\n * userAgent?: string | null,\n * }} event\n */\n record(event) {\n if (stopped) return;\n const fid = event?.rule?.id;\n if (fid === undefined || fid === null || fid === '') return;\n\n queue.push({\n fid,\n method: event.method ?? null,\n request_uri: event.path ?? null,\n ip: event.ip ?? null,\n user_agent: event.userAgent ?? null,\n log_date: new Date().toISOString(),\n });\n\n if (queue.length >= MAX_BATCH) {\n flush();\n return;\n }\n if (!timer) timer = setTimeout(flush, flushMs);\n },\n flush,\n stop() {\n stopped = true;\n flush();\n },\n };\n}\n\n/** @returns {boolean} */\nexport function telemetryEnabled() {\n const v = typeof process !== 'undefined' ? process.env?.PATCHSTACK_TELEMETRY : undefined;\n if (v === undefined || v === '') return true;\n return !/^(0|false|off|no)$/i.test(String(v));\n}\n","// Supabase-tunnel guard for AI-builder apps (Lovable / TanStack Start + Supabase).\n//\n// The vibe-coded app's browser talks straight to Supabase, bypassing the app's own server —\n// so a normal in-app WAF never sees the data traffic. The installer patches the generated\n// Supabase client to tunnel every call through this guard (running in the app's own server /\n// Worker). The guard runs the Patchstack protection policy on the tunneled request, then\n// forwards it to Supabase — pinned to the app's own project so it can't be turned into an\n// open proxy (SSRF).\n//\n// The heavy lifting (rule evaluation, dry-run/block, fail-open) is `protection.fetchGuard()`\n// from `createProtection` — this module is just the Supabase-specific tunnel around it.\n\nexport const GUARD_PATH = '/_patchstack/guard';\n\n// Headers we must not copy verbatim when re-emitting the upstream response.\nconst HOP_BY_HOP = new Set(['content-encoding', 'content-length', 'transfer-encoding', 'connection']);\n\n/**\n * @param {object} opts\n * @param {{ fetchGuard: () => (req: Request) => Promise<Response|null> }} opts.protection a createProtection() result\n * @param {string|undefined} opts.supabaseUrl the app's Supabase project URL (server-side env) — the only allowed forward target\n * @param {typeof fetch} [opts.fetchImpl] injectable fetch (tests)\n * @returns {(request: Request) => Promise<Response>}\n */\nexport function createSupabaseGuard({ protection, supabaseUrl, fetchImpl = fetch }) {\n const guard = protection.fetchGuard();\n const allowedOrigin = supabaseUrl ? new URL(supabaseUrl).origin : null;\n\n return async function handleGuardRequest(request) {\n const target = request.headers.get('x-ps-target');\n if (!target) return new Response('patchstack: missing x-ps-target', { status: 400 });\n\n let targetUrl;\n try {\n targetUrl = new URL(target);\n } catch {\n return new Response('patchstack: invalid target', { status: 400 });\n }\n\n // SSRF pin: only ever forward to the app's own Supabase project (origin from server-side\n // env, never the client-supplied header). Anything else — internal hosts, cloud metadata,\n // a different scheme — is rejected before any outbound request.\n if (!allowedOrigin || targetUrl.protocol !== 'https:' || targetUrl.origin !== allowedOrigin) {\n return new Response('patchstack: target not allowed', { status: 403 });\n }\n\n const hasBody = request.method !== 'GET' && request.method !== 'HEAD';\n const bodyText = hasBody ? await request.text() : '';\n\n // Evaluate the tunneled call against the policy. fetchGuard returns a 403 Response when it\n // blocks (block mode + match), or null to allow (allow, or dry-run — it records via onDetect).\n const evalReq = new Request(targetUrl.toString(), {\n method: request.method,\n headers: request.headers,\n body: hasBody ? bodyText : undefined,\n });\n const blocked = await guard(evalReq);\n if (blocked) return blocked;\n\n // Allowed → forward to Supabase, server-side.\n const forwardHeaders = new Headers(request.headers);\n forwardHeaders.delete('x-ps-target');\n forwardHeaders.delete('host');\n const upstream = await fetchImpl(targetUrl.toString(), {\n method: request.method,\n headers: forwardHeaders,\n body: hasBody ? bodyText : undefined,\n redirect: 'manual',\n });\n\n const outHeaders = new Headers();\n upstream.headers.forEach((value, key) => {\n if (!HOP_BY_HOP.has(key.toLowerCase())) outHeaders.set(key, value);\n });\n const buf = await upstream.arrayBuffer();\n const forwarded = new Response(buf, { status: upstream.status, statusText: upstream.statusText, headers: outHeaders });\n\n // Response phase: screen what Supabase returned (query results can leak secrets/PII) —\n // redact the offending spans / withhold, per the response rules. Fail-open if unavailable.\n return protection.screenResponse ? protection.screenResponse(forwarded) : forwarded;\n };\n}\n","// @patchstack/protect — \"Protect = respond\".\n//\n// One entry point that composes the node-waf engine + adapters with:\n// - a rule source: an explicit bundle, or fetched from the Patchstack API (token),\n// with a disk cache so the engine keeps working on last-known-good if the API is down\n// - execution modes: 'dry-run' (detect + log, never block — the safe onramp) and 'block' (enforce).\n// This API's default is 'dry-run'. NOTE the scaffolded guard (`patchstack-connect protect`)\n// deliberately passes mode: 'block' and only drops to dry-run when PATCHSTACK_MODE=dry-run — so an\n// installed guard ENFORCES by default even though this constructor's default doesn't. Precedence:\n// PATCHSTACK_MODE env > API `enforcement` > options.mode > dry-run.\n// - fail-open everywhere: a rule/engine error never blocks (or crashes) a request. Where the guard\n// fails open *without* inspecting (body caps, live streams, binary bodies, resolver failures) it\n// is counted and reported — see `protection.coverage()` / the `onSkip` option.\n//\n// Runtime guards: .express(), .node(), .fetch(handler) / .fetchGuard() — same policy,\n// every runtime an AI builder deploys to.\n// Vendored node-waf engine (this package is self-contained — no @patchstack/node-waf dep).\nimport { RuleEngine } from './engine/index.js';\nimport { matchValue, walkLeaves, safeRegExp, jwtClaimSpans } from './engine/engine.js';\nimport { PulseRuleClient } from './engine/pulse-client.js';\nimport { fromFetchRequest } from './engine/fetch.js';\nimport { fromNodeRequest } from './engine/node.js';\nimport { installEgressGuard } from './egress.js';\nimport { DEFAULT_RESPONSE_RULES, DEFAULT_EGRESS_RULES } from './defaults.js';\nimport { renderBlockPage } from './block-page.js';\n// Rule lifecycle (source / tiered store / refresh) lives in ./rules/ — this file stays focused on\n// composing the engine + guards and running the three screening phases.\nimport { makeStore } from './rules/store.js';\nimport { resolveRules } from './rules/source.js';\nimport { startRefresh, makeRefreshHandler } from './rules/refresh.js';\nimport { createDetectionReporter } from './detections.js';\nimport { notify } from './notify.js';\nimport { createFirewallLogReporter, resolveApiBase, telemetryEnabled } from './firewall-log.js';\n\n// Supabase-tunnel guard for AI-builder apps (Lovable / TanStack Start + Supabase).\nexport { createSupabaseGuard, GUARD_PATH } from './supabase-guard.js';\n\n// Per-site live rule client (Pulse). Re-exported for callers/tests that want to use it directly.\nexport { PulseRuleClient };\n\n// Server-function guard. Modern Lovable apps mutate data through TanStack server functions\n// (browser → server fn → server-side Supabase client), which bypass the browser-side tunnel the\n// Supabase guard relies on. This inspects the decoded server-fn call args against the SAME policy\n// by feeding them through fetchGuard (the args become the request body, so the engine resolves\n// `post.<field>` exactly as it does for a tunneled Supabase insert). Returns a block receipt\n// { rule?, message } to throw on, or null to allow (also null in dry-run — the detection is still\n// recorded by fetchGuard). Fail-open on any error.\nexport function createServerFnGuard({ protection }) {\n const guard = protection.fetchGuard();\n return async (data) => {\n let res;\n try {\n const req = new Request('https://patchstack.local/_serverfn', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(data ?? {}),\n });\n res = await guard(req);\n } catch {\n return null; // fail open\n }\n if (!res) return null; // allowed (or dry-run)\n let body = {};\n try {\n body = await res.clone().json();\n } catch {\n /* non-JSON block response */\n }\n return { rule: body.rule, message: body.message || 'Blocked by Patchstack' };\n };\n}\n\nexport async function createProtection(options = {}) {\n const onError = options.onError;\n const userOnDetect = options.onDetect ?? defaultOnDetect;\n\n // Report enforced blocks via existing connector POST /api/logs/log (WP path).\n // Needs api_key from provision / PATCHSTACK_API_KEY / the config files.\n // Opt out: PATCHSTACK_TELEMETRY=off. Never embed api_key in the public widget.\n const apiKey = await resolveApiKey(options);\n const firewallLog =\n apiKey && telemetryEnabled() && options.reportFirewallLog !== false\n ? createFirewallLogReporter({\n apiKey,\n apiBase: resolveApiBase(options.pulseRulesUrl ?? options.baseUrl),\n sourceHost: options.sourceHost,\n fetchImpl: options.fetchImpl,\n })\n : null;\n\n // Every detection, enforced or not, to the Pulse detections endpoint. Distinct from the block log\n // above: that records what was STOPPED, in the WordPress-compatible shape; this records what a rule\n // WOULD have stopped, which is otherwise unobservable for a rule carrying `enforcement: dry-run`.\n // Minimal payload by design; see `detections.js`.\n let detections = null;\n\n const onDetect = (detection) => {\n notify(userOnDetect, detection, 'onDetect');\n if (detections) detections.record(detection);\n if (firewallLog && detection?.mode === 'block') {\n firewallLog.record({\n rule: detection.rule,\n method: detection.method,\n path: detection.path,\n ip: detection.ip,\n userAgent: detection.userAgent,\n });\n }\n };\n\n // One tiered store (memory → filesystem/pluggable) shared by the initial load and every refresh.\n const store = makeStore(options);\n // Startup must not hang on the network: hosted platforms (Replit et al.) fail a deploy whose health\n // check is slow, and the guard can always boot from last-known-good / the bundled fallback. Refreshes\n // keep the full budget. Override with { bootTimeoutMs }.\n const bootTimeoutMs = Number(options.bootTimeoutMs) > 0 ? Number(options.bootTimeoutMs) : 5_000;\n // Resolved once and threaded through ctx: reading it is a filesystem hit on\n // runtimes that have one, and refreshes should not repeat it.\n const pulseAuth = await resolvePulseAuth(options);\n // A site UUID with no credential behind it, said out loud ONCE at boot.\n //\n // Resolution reads the config files, so it needs a filesystem and a working directory. The\n // runtimes this guard is built for do not all have one: on a Worker or an edge function the file is\n // absent and only `PATCHSTACK_PULSE_AUTH` / `PATCHSTACK_API_KEY` can carry the credential.\n //\n // Every site-addressed Pulse endpoint requires a verified, site-bound credential; only a first-time\n // provisioning call is anonymous. So a missing credential is not a future problem — the rules fetch is\n // refused now. And the refusal is invisible, because a failed fetch fails open onto the cached or\n // bundled bundle: the guard then screens every request, reports healthy, and never receives another\n // rule. That silence is the whole problem — an app protected by rules frozen at install time looks\n // exactly like an app protected by current ones.\n //\n // A warning, not a throw. Booting is protection; refusing to boot over a missing credential would\n // trade a stale rule set for no rule set at all.\n if (options.siteUuid && !pulseAuth) {\n const message =\n 'Patchstack: no API credential resolved for site ' +\n options.siteUuid +\n '. Rule updates will be rejected and this guard would keep running on its cached rules. ' +\n 'Set PATCHSTACK_API_KEY (or pass { pulseAuth }) — required on runtimes without a filesystem.';\n notify(onError, new Error(message), 'onError');\n console.warn(message);\n }\n const bundle = await resolveRules(options, store, { timeoutMs: bootTimeoutMs, pulseAuth });\n // OPT-IN, deliberately. Two reasons, and the first is not about privacy: switching it on adds an\n // outbound POST to every guard that has a site UUID, which is a change in what an installed app does\n // on the network — the kind of thing that must be disclosed in the shipped docs before it is a default,\n // not after. The second is that the default belongs to whoever owns that disclosure, so the capability\n // lands here and the flip is a separate, deliberate change.\n //\n // And it needs a credential. The detections endpoint is site-addressed and site-bound-token-only, so a\n // reporter built without one queues events, posts them, and is refused — spending an outbound request\n // per batch to accomplish nothing, while `reportDetections: true` in the config says reporting is on.\n // Refusing to build it is the honest outcome; `protection.detectionReporting` says which it is.\n let detectionReporting = 'off';\n if (options.reportDetections === true && options.siteUuid && telemetryEnabled()) {\n if (!pulseAuth) {\n detectionReporting = 'unavailable-no-credential';\n const message =\n 'Patchstack: detection reporting is enabled for site ' +\n options.siteUuid +\n ' but no API credential resolved, so no report could be delivered. Reporting is off.';\n notify(onError, new Error(message), 'onError');\n console.warn(message);\n } else {\n detectionReporting = 'on';\n detections = createDetectionReporter({\n siteUuid: options.siteUuid,\n baseUrl: options.pulseRulesUrl,\n pulseAuth,\n // The bundle the guard is actually running, so a hit can be attributed to the rules that produced\n // it rather than to whatever is current when the report is read. Kept current across refreshes —\n // see the refresh tick below.\n rulesEtag: (await store.read())?.etag ?? null,\n fetchImpl: options.fetchImpl,\n flushMs: options.detectionFlushMs,\n });\n }\n }\n // Mode is mutable so a Pulse refresh can flip dry-run ↔ block when SaaS enables production.\n // Precedence: PATCHSTACK_MODE env (local override) > API enforcement > options.mode > dry-run.\n let mode = resolveMode(options, bundle);\n // Rule-derived runtime state. Held in `let` bindings the guard methods below close over, so a\n // refresh (see the loop near the end) can hot-swap the engines by reassigning them — the\n // egress interception and the protection object itself stay in place, no re-install.\n let requestRules;\n let responseRules;\n let egressRules;\n let screenCap; // max body we'll buffer/screen (rules can raise it)\n let engine;\n let responseRuleSet;\n let egressEngine;\n\n // Split the delivered ruleset by phase (default \"request\"), merging phase defaults +\n // per-call overrides. Detection is fully rule-driven — nothing hardcoded.\n const applyBundle = (delivered) => {\n const incoming = delivered.firewall ?? [];\n requestRules = byPhase(incoming, 'request');\n responseRules = [...(options.responseRules ?? DEFAULT_RESPONSE_RULES), ...byPhase(incoming, 'response')];\n screenCap = responseScreenCap(responseRules);\n egressRules = [...(options.egressRules ?? DEFAULT_EGRESS_RULES), ...byPhase(incoming, 'egress')];\n engine = new RuleEngine({\n firewall: requestRules,\n whitelists: delivered.whitelists,\n whitelist_keys: delivered.whitelist_keys,\n onError,\n });\n // One engine per response rule so we can find ALL matches (to redact each). `action:\n // \"redact\"` masks the offending span(s); anything else withholds the whole response.\n responseRuleSet = responseRules.map((rule) => ({\n rule,\n engine: new RuleEngine({ firewall: [rule], onError }),\n redactors: rule.action === 'redact' || rule.action === 'encode' ? extractRedactors(rule) : null,\n // A redact/encode condition that carries body-transforming mutations (base64_decode, urldecode,\n // json_decode, …) detects on the DECODED body but the span redactors run on the RAW body — so\n // they mask nothing and the secret is served while the log says \"redacted\". Flag it so screenText\n // fails such a rule CLOSED (block) instead of serving a no-op redaction.\n mutatedSpan: (rule.action === 'redact' || rule.action === 'encode') && hasSpanMutations(rule),\n // Optional cheap pre-filter: literal anchor(s) that MUST appear for the (expensive) regex to\n // have any chance of matching. Lets screenText skip the full scan on bodies with no candidate —\n // the common case — cutting CPU/latency and shrinking the regex/ReDoS surface. Case-insensitive.\n prefilter: Array.isArray(rule.prefilter) && rule.prefilter.length\n ? rule.prefilter.map((s) => String(s).toLowerCase())\n : null,\n }));\n egressEngine = new RuleEngine({ firewall: egressRules, onError });\n };\n\n applyBundle(bundle);\n\n // Fail-open COVERAGE. The guard deliberately passes traffic through rather than risk breaking the\n // app: an oversized request body, a response past the screening cap, a live stream, a binary body, a\n // parse failure, a DNS resolver failure. Each of those is a real hole in enforcement, and until now\n // it was SILENT — \"always-on\" read as \"always inspected\". Every such bypass is now counted and\n // reported to `onSkip`, so a host can alert on it and `protection.coverage()` can be surfaced.\n // `onSkip` is a TRUSTED SERVER callback: `detail` carries operational context (sizes, statuses,\n // outbound hostnames) for logging/alerting. Do not forward it to a client response.\n const skipCounts = Object.create(null);\n const onSkip = typeof options.onSkip === 'function' ? options.onSkip : null;\n const recordSkip = (phase, reason, detail) => {\n const key = `${phase}:${reason}`;\n skipCounts[key] = (skipCounts[key] ?? 0) + 1;\n // A reporting callback must never affect request handling — including an async one, whose rejection\n // lands after a try/catch here would have returned.\n notify(onSkip, { phase, reason, detail, count: skipCounts[key] }, 'onSkip');\n };\n\n const maskFn =\n typeof options.maskWith === 'function'\n ? options.maskWith\n : () => (typeof options.maskWith === 'string' ? options.maskWith : '[REDACTED]');\n\n // Given a request/egress result, enforce (block mode) or just record (dry-run).\n //\n // A rule may carry its own `enforcement: 'dry-run'`, which wins over block mode for that rule alone.\n // Auto-generated rules arrive that way: their coordinate comes from best-effort static analysis, so they\n // are served to detect until a probe or a human has justified them, WITHOUT holding back the\n // hand-authored rules on the same site. A rule with no `enforcement` follows the bundle exactly as\n // before, so an older server that never sends the field behaves identically.\n const ruleMode = (rule) => (rule?.enforcement === 'dry-run' ? 'dry-run' : mode);\n\n const decide = (phase, result, block, allow, ctx = {}) => {\n if (!result || !result.blocked) return allow();\n const effectiveMode = ruleMode(result.rule);\n onDetect({\n phase,\n // The mode this detection was actually handled under, not the site's: a consumer counting blocks\n // would otherwise over-report, and the whole point of a dry-run rule is that it did not block.\n mode: effectiveMode,\n category: result.rule?.category,\n rule: result.rule,\n message: result.message,\n method: ctx.method,\n path: ctx.path,\n ip: ctx.ip,\n userAgent: ctx.userAgent,\n });\n return effectiveMode === 'block' ? block() : allow();\n };\n\n // Response phase core: screen a text body → { verdict: 'pass'|'block'|'redact', body? }.\n // redact masks matched spans; block withholds; block wins over redact. Enforcement only in\n // block mode (dry-run records via onDetect but returns 'pass').\n const screenText = (text, meta, reqCtx) => {\n let blockRule = null;\n const redactions = [];\n const headerMutations = [];\n let lowerText = null; // lazily lowercased body, only if a rule uses a prefilter\n for (const { rule, engine: re, redactors, prefilter, mutatedSpan } of responseRuleSet) {\n // Cheap pre-filter: if none of the rule's literal anchors is in the body, its regex can't\n // match — skip the full scan (the common no-secret case) before touching the engine.\n if (prefilter) {\n if (lowerText === null) lowerText = text.toLowerCase();\n if (!prefilter.some((p) => lowerText.includes(p))) continue;\n }\n let result;\n try {\n // Spread the originating request (method / originalUrl / headers) alongside the response,\n // so a response rule's `when` route/method scope resolves against the REAL request and so\n // request Host/Origin are visible to response rules — rather than the phantom empty request\n // the response phase used to build (which made `when` on a response rule inert).\n result = re.evaluate({ ...(reqCtx || {}), _response: { ...meta, body: text } });\n } catch (err) {\n notify(onError, err, 'onError');\n continue;\n }\n if (!result.blocked) continue;\n // Per-rule enforcement applies to every phase, not just the request. A generated response rule in\n // dry-run must not redact or withhold a body either: \"detect until justified\" is meaningless if the\n // rule still rewrites what the user sees.\n const responseMode = ruleMode(rule);\n onDetect({ phase: 'response', mode: responseMode, category: rule.category, rule, message: result.message });\n if (responseMode !== 'block') continue; // dry-run: observe only\n if (redactors && redactors.length) {\n // Span redactors on a mutation-decoded rule can't map back to the raw body → fail closed.\n const spanRedactors = redactors.filter((r) => !r.jsonPath);\n if (mutatedSpan && spanRedactors.length) {\n if (!blockRule) blockRule = rule;\n } else {\n redactions.push({ rule, redactors });\n }\n } else if (isHeaderMutation(rule.action)) headerMutations.push(rule);\n else if (!blockRule) blockRule = rule;\n }\n if (mode !== 'block' || (!blockRule && !redactions.length && !headerMutations.length)) return { verdict: 'pass' };\n if (blockRule) return { verdict: 'block' };\n let body = text;\n // Redact the offending spans in the body AND in every (string) header value — so a secret\n // that leaks in a header (Set-Cookie, an echoed X-Api-Key, …) is masked too, and a rule that\n // targets `response.header.*` actually strips the header rather than just detecting it.\n const headers = { ...(meta.headers || {}) };\n for (const { rule, redactors } of redactions) {\n const mask = maskFn(rule.category);\n // action `encode` HTML-escapes the matched value in place (neutralize stored XSS at output);\n // `redact` masks it. jsonPath redactors act on a structural JSON location, span redactors on\n // text spans in the body AND header values. Apply structural first (on clean JSON), then spans.\n const transform = rule.action === 'encode' ? htmlEscape : null;\n const pathRedactors = redactors.filter((r) => r.jsonPath);\n const spanRedactors = redactors.filter((r) => !r.jsonPath);\n if (pathRedactors.length) body = applyPathRedactors(body, pathRedactors, mask, screenCap, transform);\n if (!spanRedactors.length) continue;\n body = applyRedactors(body, spanRedactors, mask, transform);\n if (transform) continue; // encoding is a body/output concern — headers aren't HTML\n for (const name of Object.keys(headers)) {\n const value = headers[name];\n if (typeof value === 'string') {\n headers[name] = applyRedactors(value, spanRedactors, mask);\n } else if (Array.isArray(value)) {\n // Multi-valued headers (Set-Cookie) — redact each entry.\n headers[name] = value.map((item) => (typeof item === 'string' ? applyRedactors(item, spanRedactors, mask) : item));\n }\n }\n }\n for (const rule of headerMutations) applyHeaderMutation(headers, rule);\n return { verdict: 'redact', body, headers };\n };\n\n // Minimal request context for the response phase: what a response rule's `when` scope and any\n // request-header reference (Host/Origin) need — method, path, and request headers. No body.\n const reqContextFromFetch = (request) => {\n try {\n const u = new URL(request.url);\n const headers = headerObject(request.headers);\n // A fetch Request doesn't expose the Host header (it's set at send time), so derive it from the\n // URL — response rules that compare origins (open-redirect / CORS) need the request Host.\n if (!headers.host) headers.host = u.host;\n return { method: request.method, originalUrl: u.pathname + u.search, headers };\n } catch {\n return undefined;\n }\n };\n const reqContextFromNode = (req) => (req ? { method: req.method, originalUrl: req.url, headers: req.headers || {} } : undefined);\n\n // Screen a fetch Response (used by .fetch() and — via protection.screenResponse — the Supabase guard).\n const screenResp = async (response, reqCtx) => {\n const read = await readTextResponse(response, screenCap);\n if (read.skip) {\n // Nothing was screened — a leak/PII rule cannot have applied. Record it (a live stream and a\n // binary body are by design; a body-cap or read failure is a coverage hole worth alerting on).\n if (read.skip !== 'not-a-response') recordSkip('response', read.skip, { status: response?.status });\n return response;\n }\n const text = read.text;\n const r = screenText(text, { status: response.status, headers: headerObject(response.headers) }, reqCtx);\n if (r.verdict === 'block') return leakResponse();\n if (r.verdict === 'redact') return rebuildResponse(response, r.body, r.headers);\n return response;\n };\n\n // Wrap a Node ServerResponse so its (buffered, text) body is screened before it's sent.\n // Opt-in (buffering can delay a streamed response); over 512 KiB it stops buffering and\n // passes through unscanned.\n const wrapNodeResponse = (res, reqCtx) => {\n const origWrite = res.write.bind(res);\n const origEnd = res.end.bind(res);\n const chunks = [];\n let size = 0;\n let overflow = false;\n const MAX = screenCap;\n const collect = (chunk, enc) => {\n if (chunk == null) return;\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, typeof enc === 'string' ? enc : 'utf8');\n size += buf.length;\n if (size > MAX) {\n // Too big to screen — abandon buffering, but FLUSH what we already captured (the head) plus\n // this chunk before switching to pass-through, so the client gets a complete body (not a\n // truncated one missing everything before the cap was hit).\n for (const c of chunks) origWrite(c);\n chunks.length = 0;\n origWrite(buf);\n overflow = true;\n recordSkip('response', 'body-cap', { bytes: size });\n return;\n }\n chunks.push(buf);\n };\n res.write = function (chunk, enc, cb) {\n if (overflow) return origWrite(chunk, enc, cb);\n collect(chunk, enc);\n if (typeof enc === 'function') enc();\n else if (typeof cb === 'function') cb();\n return true;\n };\n res.end = function (chunk, enc, cb) {\n if (typeof chunk === 'function') { cb = chunk; chunk = undefined; enc = undefined; }\n else if (typeof enc === 'function') { cb = enc; enc = undefined; }\n if (overflow) { if (chunk != null) origWrite(chunk, enc); return origEnd(cb); }\n collect(chunk, enc);\n if (overflow) return origEnd(cb); // collect just flushed head + final chunk on overflow\n const buffer = Buffer.concat(chunks);\n let ct = res.getHeader ? res.getHeader('content-type') : undefined;\n if (Array.isArray(ct)) ct = ct[0];\n const kind = screenableContentType(ct);\n // Skip live streams / binary bodies (incl. an octet-stream that sniffs as binary) — untouched.\n if (kind === 'skip' || (kind === 'sniff' && looksBinary(buffer))) {\n recordSkip('response', kind === 'skip' ? (baseContentType(ct) === 'text/event-stream' ? 'live-stream' : 'non-text-content-type') : 'binary-body');\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n }\n const text = buffer.toString('utf8');\n let r;\n try {\n r = screenText(text, { status: res.statusCode, headers: res.getHeaders ? res.getHeaders() : {} }, reqCtx);\n } catch (err) {\n notify(onError, err, 'onError');\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n }\n if (r.verdict === 'block') {\n res.statusCode = 500;\n try { res.setHeader('content-type', 'application/json'); } catch { /* headers sent */ }\n return origEnd(JSON.stringify({ error: 'Response withheld by Patchstack (sensitive data detected)' }), cb);\n }\n if (r.verdict === 'redact') {\n try { res.removeHeader && res.removeHeader('content-length'); } catch { /* ignore */ }\n if (r.headers && res.setHeader) {\n const current = res.getHeaders ? res.getHeaders() : {};\n for (const [name, value] of Object.entries(r.headers)) {\n // content-length was just removed (the redacted body has a new length); never re-set a\n // stale one here or the response truncates/hangs.\n if (name.toLowerCase() === 'content-length') continue;\n if (value === null || value === undefined) {\n try { res.removeHeader && res.removeHeader(name); } catch { /* ignore */ } // header-mutation removal\n } else if (Array.isArray(value)) {\n try { res.setHeader(name, value); } catch { /* ignore invalid header */ } // Set-Cookie array\n } else if (typeof value === 'string' && current[name] !== value) {\n try { res.setHeader(name, value); } catch { /* ignore invalid header */ }\n }\n }\n }\n return origEnd(r.body, cb);\n }\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n };\n };\n\n // Egress phase: is this outbound call blocked? (records detection either way)\n const allow = new Set((options.allowHosts ?? []).map((h) => String(h).toLowerCase()));\n const egressShouldBlock = (url, host, method) => {\n if (host && allow.has(host.toLowerCase())) return false;\n let result;\n try {\n result = egressEngine.evaluate({ _egress: { url, host, method } });\n } catch (err) {\n notify(onError, err, 'onError');\n return false;\n }\n if (!result.blocked) return false;\n // Same for egress: a dry-run rule records the outbound attempt without preventing it. Blocking a\n // request the app makes is at least as disruptive as blocking one it receives.\n const egressMode = ruleMode(result.rule);\n onDetect({ phase: 'egress', mode: egressMode, category: result.rule?.category, rule: result.rule, message: result.message });\n return egressMode === 'block';\n };\n\n const protection = {\n get mode() {\n return mode;\n },\n get rules() {\n return { request: requestRules, response: responseRules, egress: egressRules };\n },\n\n /**\n * Enforcement coverage: how often the guard FAILED OPEN rather than inspecting, keyed\n * `<phase>:<reason>` (e.g. `response:body-cap`, `request:body-cap`, `response:live-stream`,\n * `egress:resolver-failed`). \"Always-on\" is not \"always inspected\" — surface this (or pass\n * `onSkip`) so an unscreened path is visible and alertable rather than silent.\n */\n coverage() {\n return { skipped: { ...skipCounts } };\n },\n\n // Screen a fetch Response through the response-phase rules (redact/block). Used by\n // .fetch(), and by the Supabase guard on its forwarded upstream response.\n screenResponse: (response, request) => screenResp(response, request ? reqContextFromFetch(request) : undefined),\n\n // (request) => Response | null (null = allow, caller proceeds). Request phase only.\n fetchGuard() {\n return async (request) => {\n let result;\n try {\n result = engine.evaluate(await fromFetchRequest(request));\n } catch (err) {\n notify(onError, err, 'onError');\n return null; // fail open\n }\n return decide('request', result, () => blockResponse(result, request), () => null, fetchRequestMeta(request));\n };\n },\n\n // Wrap a fetch handler: screens the request, then the response (redact/block).\n fetch(handler) {\n const guard = protection.fetchGuard();\n return async (request, ...rest) => {\n const blocked = await guard(request);\n if (blocked) return blocked;\n const response = await handler(request, ...rest);\n return screenResp(response, reqContextFromFetch(request));\n };\n },\n\n // Express middleware (request phase; expects express-parsed req.query/req.body).\n // Pass { screenResponses: true } to also screen the outgoing response (buffers it).\n express(exprOptions = {}) {\n return (req, res, next) => {\n let result;\n try {\n result = engine.evaluate(req);\n } catch (err) {\n notify(onError, err, 'onError');\n if (exprOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n return next();\n }\n decide(\n 'request',\n result,\n () => {\n if (isDocumentNavigation((n) => req.headers?.[n])) {\n res.status(403).type('html').send(renderBlockPage({ url: req.originalUrl || req.url || '/', code: result?.rule?.id }));\n } else {\n res.status(403).json(blockBody(result));\n }\n },\n () => {\n if (exprOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n next();\n },\n nodeRequestMeta(req),\n );\n };\n },\n\n // Node / Connect middleware — buffers the body itself (request phase). Register it BEFORE any body\n // parser: it reads the request stream, and exposes what it read as `req.body` so a parser is not\n // also needed. (`.express()` is the other way round — it reads a body somebody else parsed.)\n // Pass { screenResponses: true } to also screen the outgoing response (buffers it).\n node(nodeOptions = {}) {\n const maxBytes = nodeOptions.maxBodyBytes ?? 1024 * 1024;\n return (req, res, next) => {\n // Registered after a body parser, the stream is already at its end: 'data' and 'end' will not fire\n // again, and waiting for them would hold the request open for as long as the client allows. Screen\n // the body the parser left instead — a guard that stops serving the app is a worse outcome than the\n // registration order it was trying to insist on.\n if (req.readableEnded || req.body !== undefined) {\n screenNodeRequest(req, res, next, '', req.body);\n return;\n }\n\n const chunks = [];\n let size = 0;\n let overflow = false;\n req.on('data', (chunk) => {\n size += chunk.length;\n if (size > maxBytes) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n req.on('error', (err) => {\n notify(onError, err, 'onError');\n next();\n });\n req.on('end', () => {\n if (overflow) recordSkip('request', 'body-cap', { bytes: size, limit: maxBytes });\n screenNodeRequest(req, res, next, overflow ? '' : Buffer.concat(chunks).toString('utf8'));\n });\n };\n\n // `parsedBody`, when given, is a body somebody else already parsed: it replaces the shaped body\n // rather than being re-serialized, because re-encoding it would have to guess a format and a form\n // body handed back as JSON resolves no `post.<field>` at all.\n function screenNodeRequest(req, res, next, rawBody, parsedBody) {\n let shaped;\n let result;\n try {\n shaped = fromNodeRequest(req, rawBody);\n if (parsedBody !== undefined && parsedBody !== null) shaped.body = parsedBody;\n result = engine.evaluate(shaped);\n } catch (err) {\n notify(onError, err, 'onError');\n return next();\n }\n decide(\n 'request',\n result,\n () => {\n res.statusCode = 403;\n if (isDocumentNavigation((n) => req.headers?.[n])) {\n res.setHeader('content-type', 'text/html; charset=utf-8');\n res.end(renderBlockPage({ url: req.url || '/', code: result?.rule?.id }));\n } else {\n res.setHeader('content-type', 'application/json');\n res.end(JSON.stringify(blockBody(result)));\n }\n },\n () => {\n // This guard consumed the request stream to screen it; re-expose the parsed\n // body so a downstream handler (without its own body-parser) can read it.\n if (req.body === undefined) req.body = shaped.body;\n if (nodeOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n next();\n },\n nodeRequestMeta(req),\n );\n }\n },\n };\n\n // Egress interception is opt-in (it wraps the global fetch, and node:http/https on Node).\n if (options.egress) {\n protection.uninstallEgress = await installEgressGuard({\n shouldBlock: egressShouldBlock,\n onBlock: options.onEgressBlock,\n // Route egress coverage gaps (a DNS resolver failure / no resolver on this runtime) into the\n // same skip accounting as the request/response phases.\n onSkip: ({ reason, detail }) => recordSkip('egress', reason, detail),\n dnsScreen: options.screenDns !== false,\n allowHosts: options.allowHosts,\n });\n }\n\n // Live rule refresh. The guard otherwise reads its rules once, at process start — so a rule that\n // only becomes relevant after boot (a dependency added mid-session and flagged by Pulse, a\n // zero-day published) never applies until the process restarts. A refresh re-fetches and\n // hot-swaps the engines in place; the same tick can be driven by a poll loop (`refreshMs`), a\n // manual `protection.refresh()`, or an authenticated push (`protection.refreshHandler()`).\n const cwd = options.cwd ?? (typeof process !== 'undefined' ? process.cwd() : undefined);\n const live = Boolean(options.siteUuid || options.token);\n const refreshSecret = options.refreshSecret ?? (typeof process !== 'undefined' ? process.env.PATCHSTACK_REFRESH_SECRET : undefined);\n const refreshable = live && (options.refreshMs > 0 || Boolean(refreshSecret));\n\n // On the Pulse (siteUuid) path, re-post the dependency manifest before re-fetching. A targeted\n // `npm install <pkg>` fires no npm lifecycle hook, so nothing else re-scans; reporting here lets\n // the server flag a newly-added vulnerable dependency and the SAME tick's rule fetch pick up its\n // rule. Loaded once, up front, only when a refresh path is enabled — a refresh-off production\n // guard never pulls in the scan pipeline; a load/report failure never blocks the rule refresh.\n let reporter = null;\n if (refreshable && options.siteUuid && options.reportManifest !== false && cwd) {\n try {\n ({ reportManifest: reporter } = await import('./refresh-manifest.js'));\n } catch (err) {\n notify(onError, err, 'onError'); // scan pipeline unavailable (e.g. an edge runtime) — rules still refresh\n }\n }\n\n const runRefreshTick = async () => {\n if (reporter) {\n try {\n await reporter(cwd);\n } catch (err) {\n notify(onError, err, 'onError'); // a failed report must not stop the rule refresh\n }\n }\n const next = await resolveRules(options, store, { timeoutMs: options.refreshTimeoutMs, pulseAuth });\n mode = resolveMode(options, next);\n applyBundle(next);\n // After the swap, and only after it: later detections belong to the bundle now running. A refresh\n // that fell back to the cached or bundled ruleset kept the previous rules, and `store.read()` then\n // still holds the previous identity — which is exactly the answer that stays true.\n if (detections) detections.setRulesEtag((await store.read())?.etag ?? null);\n\n // The tick's own outcome, separate from the guard's. `resolveRules` deliberately absorbs an API or\n // network failure and returns usable rules, which is right for protection and wrong for a poller:\n // a scheduler that only counts THROWN errors reads a fleet-wide outage as a healthy poll and keeps\n // knocking at the normal interval. Reported, not thrown — a caller's manual `refresh()` must not\n // start failing because the platform is down and the cached rules held.\n return next.source ?? { ok: true };\n };\n\n if (live) {\n // Manual one-shot refresh (also the primitive the loop + push endpoint run).\n protection.refresh = () => runRefreshTick();\n // Authenticated push endpoint — the platform/SaaS hits it for an immediate refresh. No secret\n // configured → the handler 404s (never an open refresh trigger).\n protection.refreshHandler = () => makeRefreshHandler(runRefreshTick, refreshSecret);\n }\n\n const loop = options.refreshMs > 0 && live\n ? startRefresh(runRefreshTick, { refreshMs: options.refreshMs, onError })\n : null;\n\n // One method, always present, that reaches everything holding a timer or a buffer: the refresh loop,\n // the block log, the detection reporter. Always present because a lifecycle method that exists only\n // for some configurations is one a caller cannot rely on — and each of these components can be the\n // only one installed, so any of them can be the one left running.\n protection.stop = () => {\n loop?.stop();\n firewallLog?.stop();\n detections?.stop();\n };\n // The name callers already have, kept as an alias for it.\n protection.stopRefresh = protection.stop;\n // Which of the three states reporting is in: requested and running, requested but undeliverable, or\n // not requested. A boolean would collapse the middle one into \"off\", which is the reassuring reading.\n protection.detectionReporting = detectionReporting;\n // Delivery health, when there is a reporter: what was attempted, acknowledged, refused, and dropped.\n if (detections) protection.detectionHealth = () => detections.health();\n\n return protection;\n}\n\n/**\n * Resolve runtime enforcement mode.\n * Precedence: PATCHSTACK_MODE env > Pulse `enforcement` on the rules bundle > options.mode > dry-run.\n */\nfunction resolveMode(options, bundle) {\n const env = typeof process !== 'undefined' ? process.env?.PATCHSTACK_MODE : undefined;\n if (env === 'block' || env === 'dry-run') return env;\n if (bundle?.enforcement === 'block' || bundle?.enforcement === 'dry-run') return bundle.enforcement;\n if (options?.mode === 'block') return 'block';\n if (options?.mode === 'dry-run') return 'dry-run';\n return 'dry-run';\n}\n\n/**\n * The two files a credential can live in, in the order they are read.\n *\n * Setup writes the credential to the local file and the public identity to the other one, because the\n * public file is meant to be committed. The committed file is still read second: installs that predate\n * the split keep their credential there, and a guard that stopped authenticating on upgrade would lose\n * live rules and reporting on every one of them.\n */\nconst CREDENTIAL_FILES = ['.patchstackrc.local.json', '.patchstackrc.json'];\n\n/**\n * A credential field from the config files, or undefined.\n *\n * fs/path are imported LAZILY — this module must stay loadable on edge runtimes (Next edge middleware,\n * Workers, Deno, Supabase Functions), where a static `node:fs` import fails to resolve and would take the\n * guard down.\n */\nasync function readCredentialField(options, field) {\n if (typeof process === 'undefined' || typeof process.cwd !== 'function') return undefined;\n\n let readFileSync;\n let join;\n try {\n const [fs, pathMod] = await Promise.all([import('node:fs'), import('node:path')]);\n readFileSync = fs.readFileSync;\n join = pathMod.join;\n } catch {\n return undefined; // no filesystem on this runtime\n }\n\n const cwd = options?.cwd ?? process.cwd();\n for (const filename of CREDENTIAL_FILES) {\n try {\n const value = JSON.parse(readFileSync(join(cwd, filename), 'utf8'))?.[field];\n if (typeof value === 'string' && value.length > 0) return value;\n } catch {\n /* missing or unparseable — try the next one */\n }\n }\n\n return undefined;\n}\n\n/** WP-format api_key for connector /api/logs/log. Never use the public site UUID. */\nasync function resolveApiKey(options) {\n if (typeof options?.apiKey === 'string' && options.apiKey.length > 0) return options.apiKey;\n if (typeof process !== 'undefined') {\n const fromEnv = process.env?.PATCHSTACK_API_KEY;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) return fromEnv;\n }\n\n return readCredentialField(options, 'apiKey');\n}\n\n/**\n * Credential for the authenticated rules lookup (ADR-0018). Same resolution\n * order and the same edge-runtime caution as resolveApiKey, and falls back to\n * it so guards installed before pulseAuth existed keep authenticating.\n *\n * Returning undefined does not fail the boot — protection still runs on the cached or bundled rules —\n * but the fetch then goes out unauthenticated and the platform refuses it, so the guard stops receiving\n * rules. That is why the caller warns about it at boot rather than treating it as a normal state.\n */\nasync function resolvePulseAuth(options) {\n if (typeof options?.pulseAuth === 'string' && options.pulseAuth.length > 0) return options.pulseAuth;\n if (typeof process !== 'undefined') {\n const fromEnv = process.env?.PATCHSTACK_PULSE_AUTH;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) return fromEnv;\n }\n const fromFile = await readCredentialField(options, 'pulseAuth');\n if (fromFile !== undefined) return fromFile;\n\n return resolveApiKey(options);\n}\n\n// --- phase / response helpers -------------------------------------------\n\nfunction byPhase(rules, phase) {\n return (rules ?? []).filter((r) => (r.phase ?? 'request') === phase);\n}\n\n// Classify a content-type for response screening: 'text' = screen; 'sniff' = screen only if the\n// bytes aren't binary (octet-stream is often a misdeclared JSON export/config); 'skip' = pass\n// through unscreened (live streams, known binary families). SSE is matched on the EXACT base type,\n// not a loose substring — `application/json; profile=\"event-stream\"` is not a stream.\nfunction baseContentType(ct) {\n return String(ct || '').toLowerCase().split(';')[0].trim();\n}\nfunction screenableContentType(ct) {\n const base = baseContentType(ct);\n if (base === 'text/event-stream') return 'skip'; // live token/SSE stream — never buffer\n if (base === '') return 'text';\n if (/(json|text|xml|html|javascript|csv|yaml|x-www-form-urlencoded)/.test(base)) return 'text';\n if (base === 'application/octet-stream') return 'sniff'; // maybe a text/JSON export mislabeled\n return 'skip'; // image/video/audio/font/pdf/zip/wasm/… — don't buffer binary\n}\n// Cheap binary sniff over a byte prefix: a NUL byte, or many control chars, means \"don't treat as text\".\nfunction looksBinary(bytes) {\n const n = Math.min(bytes.length, 512);\n let ctrl = 0;\n for (let i = 0; i < n; i++) {\n const b = bytes[i];\n if (b === 0) return true;\n if (b < 9 || (b > 13 && b < 32)) ctrl++;\n }\n return n > 0 && ctrl / n > 0.1;\n}\n\n// Returns { text } when the body was fully buffered for screening, or { skip: <reason> } when it was\n// NOT screened — the reason is surfaced to `onSkip`/coverage so a fail-open bypass is observable\n// instead of silent (an unscreened response is a real hole in enforcement).\nasync function readTextResponse(response, cap = DEFAULT_SCREEN_CAP) {\n if (!response || typeof response.clone !== 'function') return { skip: 'not-a-response' };\n const ct = response.headers?.get?.('content-type') || '';\n const kind = screenableContentType(ct);\n if (kind === 'skip') return { skip: baseContentType(ct) === 'text/event-stream' ? 'live-stream' : 'non-text-content-type' };\n const sniff = kind === 'sniff';\n const len = Number(response.headers?.get?.('content-length') || 0);\n if (len && len > cap) return { skip: 'body-cap' };\n let clone;\n try {\n clone = response.clone();\n } catch {\n return { skip: 'clone-failed' };\n }\n\n // Stream the read so a body WITHOUT a Content-Length can't buffer past the cap. Over the cap the\n // response is left UNSCREENED — but we keep draining the clone so the original stays intact.\n const body = clone.body;\n if (body && typeof body.getReader === 'function') {\n const reader = body.getReader();\n const chunks = [];\n let size = 0;\n let over = false;\n let sniffed = !sniff;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n if (!sniffed) {\n sniffed = true;\n if (looksBinary(value)) return { skip: 'binary-body' };\n }\n size += value.byteLength;\n if (over) continue; // keep draining, stop buffering\n if (size > cap) { over = true; continue; }\n chunks.push(value);\n }\n } catch {\n return { skip: 'read-failed' };\n }\n if (over) return { skip: 'body-cap' };\n try {\n return { text: new TextDecoder().decode(concatBytes(chunks, size)) };\n } catch {\n return { skip: 'decode-failed' };\n }\n }\n\n try {\n const text = await clone.text();\n if (text.length > cap) return { skip: 'body-cap' };\n if (sniff && looksBinary(new TextEncoder().encode(text.slice(0, 512)))) return { skip: 'binary-body' };\n return { text };\n } catch {\n return { skip: 'read-failed' };\n }\n}\n\nfunction concatBytes(chunks, total) {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const c of chunks) {\n out.set(c, offset);\n offset += c.byteLength;\n }\n return out;\n}\n\nfunction headerObject(headers) {\n const out = {};\n headers?.forEach?.((v, k) => { out[k.toLowerCase()] = v; });\n // Set-Cookie is multi-valued; forEach collapses it. Recover the individual cookies so each can\n // be screened (and re-emitted) separately.\n const setCookies = headers?.getSetCookie?.();\n if (setCookies && setCookies.length) out['set-cookie'] = setCookies;\n return out;\n}\n\n// True if any of the rule's conditions carries a body-transforming mutation on a SPAN match\n// (regex/contains/stripos) — those decode the body before matching, so a span redactor derived from\n// the literal/regex can't be located in the raw body. (array_key_value structural redaction decodes\n// the JSON itself, so json_decode there is fine and doesn't count.)\nfunction hasSpanMutations(rule) {\n let found = false;\n const walk = (conds) => {\n for (const c of conds ?? []) {\n if (found) return;\n if (Array.isArray(c.rules)) walk(c.rules);\n const isSpan = c.match && (c.match.type === 'regex' || c.match.type === 'contains' || c.match.type === 'stripos');\n if (isSpan && Array.isArray(c.mutations) && c.mutations.length) found = true;\n }\n };\n walk(rule.rule_v2);\n return found;\n}\n\n// Derive redaction targets from a rule's own conditions: regex → mask every match;\n// contains/stripos → mask the literal. (Other match types can't identify a span → the\n// rule falls back to block.)\nfunction extractRedactors(rule) {\n const out = [];\n const walk = (conds) => {\n for (const c of conds ?? []) {\n if (Array.isArray(c.rules)) walk(c.rules);\n const m = c.match;\n if (!m) continue;\n if (m.type === 'regex' && typeof m.value === 'string') {\n // Route through the SAME ReDoS guard detection uses — a catastrophic redactor pattern must\n // not hang the response path (safeRegExp returns null for dangerous/invalid patterns → skip).\n const safe = safeRegExp(m.value);\n if (safe) {\n const flags = safe.flags.includes('g') ? safe.flags : safe.flags + 'g';\n try {\n out.push({ re: new RegExp(safe.source, flags) });\n } catch {\n /* skip invalid */\n }\n }\n } else if ((m.type === 'contains' || m.type === 'stripos') && m.value != null) {\n out.push({ literal: String(m.value) });\n } else if (m.type === 'jwt_claim_equals' && typeof m.claim === 'string') {\n // A span-producing target, not a predicate. A boolean-only matcher would leave `redact` with\n // no span to mask, so the rule would fall back to withholding the WHOLE response — turning a\n // one-token leak into an outage. The spans come from the same `jwtClaimSpans` the matcher\n // used, so what is reported and what is masked cannot diverge.\n out.push({ jwtClaim: { claim: m.claim, value: String(m.value ?? '') } });\n } else if (m.type === 'array_key_value' && m.match && isBodyParam(c.parameter)) {\n // Structural redaction: mask the value at a JSON path (fanning out over arrays) rather than\n // a text span — e.g. key \"orders.customers.email\" masks that field in every array element.\n const keys = Array.isArray(m.key) ? m.key : [m.key];\n for (const key of keys) {\n out.push({ jsonPath: String(key).split('.'), condition: m.match });\n }\n }\n }\n };\n walk(rule.rule_v2);\n return out;\n}\n\n// HTML-entity escape, for the `encode` action (neutralize markup rather than mask it). NOTE: this is\n// sound only for HTML text / attribute-VALUE contexts. It does NOT neutralize a `javascript:` / `data:`\n// URI or an event-handler name (those carry no HTML metacharacters) — use `block` for a rule that\n// targets a URL/scheme context. See the rule-authoring guidance in the triage-vpatch-npm skill.\nfunction htmlEscape(str) {\n return String(str).replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c]);\n}\n\n// `transform` (optional): map a matched span to its replacement (the `encode` action passes\n// htmlEscape). Without it, matches are replaced by the `mask` string (the `redact` action).\nfunction applyRedactors(body, redactors, mask, transform) {\n let out = body;\n for (const r of redactors) {\n if (r.re) out = out.replace(r.re, transform ? (m) => transform(m) : mask);\n else if (r.jwtClaim) {\n // Spans are computed against the body as it stands, then each distinct token is replaced\n // literally — so a response carrying two matching tokens loses both, and a token that appears\n // twice loses both copies.\n for (const token of jwtClaimSpans(out, r.jwtClaim.claim, r.jwtClaim.value)) {\n out = out.split(token).join(transform ? transform(token) : mask);\n }\n } else if (r.literal) {\n // Detection (matchValue for contains/stripos) is case-insensitive, so mask case-insensitively\n // too — otherwise a `contains: \"SECRET\"` redactor detects `secret` but masks nothing, serving\n // the leak while reporting a redaction. Escape the literal so it matches literally, not as regex.\n const re = new RegExp(r.literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'gi');\n out = out.replace(re, (m) => (transform ? transform(m) : mask));\n }\n }\n return out;\n}\n\n// A response-body redaction target (array_key_value masks the JSON body). A bare condition with no\n// parameter also defaults to the body.\nfunction isBodyParam(parameter) {\n return parameter == null || parameter === 'response.body' || parameter === 'raw' || parameter === 'response.raw';\n}\n\n// Build a predicate from the array_key_value nested match, so a path can be masked conditionally\n// (only leaves that match) or — with `isset` — unconditionally. Fail-closed (don't mask) on error.\nfunction conditionPredicate(condition) {\n if (!condition || !condition.type) return () => true;\n return (value) => {\n try {\n return matchValue(condition.type, value, condition.value, condition);\n } catch {\n return false;\n }\n };\n}\n\nconst DEFAULT_SCREEN_CAP = 512 * 1024;\n\n// Effective response-screening size cap for a rule set. Bodies larger than this are passed through\n// UNSCREENED (so a redact rule can't mask them — the leak/PII would slip out). A rule can raise the\n// ceiling for the whole response phase: `bypass_limit: true` removes the cap entirely (accepts the\n// memory cost on a hostile large body), or `max_bytes: <n>` raises it to n. The cap is shared (the\n// body is buffered once), so the effective cap is the MAX across all active response rules.\nfunction responseScreenCap(rules) {\n let cap = DEFAULT_SCREEN_CAP;\n for (const r of rules ?? []) {\n if (r && r.bypass_limit === true) return Infinity;\n const n = Number(r && r.max_bytes);\n if (Number.isFinite(n) && n > cap) cap = n;\n }\n return cap;\n}\n\n// Apply jsonPath redactors structurally: parse the JSON body, mask each targeted leaf (fanning out\n// over arrays at every path segment), re-serialize. Fail-open — a non-JSON / oversized / unparseable\n// body is returned unchanged, and any per-leaf error is swallowed.\nfunction applyPathRedactors(text, pathRedactors, mask, cap, transform) {\n if (!pathRedactors.length || typeof text !== 'string' || text.length > cap) return text;\n const head = text.trimStart()[0];\n if (head !== '{' && head !== '[') return text; // not a JSON object/array\n // Preserve out-of-safe-range integers across the parse→stringify round-trip: JSON.parse would\n // round e.g. a 20-digit id. We quote such number tokens to a sentinel string before parsing and\n // unquote them after stringifying, so untouched big ints survive losslessly.\n const preserved = preserveBigInts(text);\n let obj;\n try {\n obj = JSON.parse(preserved);\n } catch {\n return text;\n }\n let changed = false;\n for (const r of pathRedactors) {\n const pred = conditionPredicate(r.condition);\n walkLeaves(obj, r.jsonPath, (loc) => {\n try {\n if (pred(loc.value)) {\n // `encode`: escape the leaf's own value in place; `redact`: replace it with the mask.\n loc.parent[loc.key] = transform ? transform(String(loc.value)) : mask;\n changed = true;\n }\n } catch {\n /* skip this leaf */\n }\n });\n }\n return changed ? restoreBigInts(JSON.stringify(obj)) : text;\n}\n\nconst BIGINT_OPEN = '__PSBIGINT_9c2f__';\nconst BIGINT_CLOSE = '__DNEGIB__';\n\n// Quote every out-of-safe-range integer *value* (a bare number token outside a string) into a\n// sentinel string, so JSON.parse keeps it verbatim. String-aware scan (respects \\ escapes) so a\n// number inside a string value is never touched. Plain-ASCII sentinel → survives JSON.stringify.\nfunction preserveBigInts(text) {\n let out = '';\n let inStr = false;\n for (let i = 0; i < text.length; ) {\n const ch = text[i];\n if (inStr) {\n out += ch;\n if (ch === '\\\\') { out += text[i + 1] ?? ''; i += 2; continue; }\n if (ch === '\"') inStr = false;\n i++;\n continue;\n }\n if (ch === '\"') { inStr = true; out += ch; i++; continue; }\n if (ch === '-' || (ch >= '0' && ch <= '9')) {\n let j = ch === '-' ? i + 1 : i;\n let digits = 0;\n while (j < text.length && text[j] >= '0' && text[j] <= '9') { digits++; j++; }\n const next = text[j];\n const isIntToken = digits > 0 && next !== '.' && next !== 'e' && next !== 'E';\n if (isIntToken && digits >= 16) {\n out += `\"${BIGINT_OPEN}${text.slice(i, j)}${BIGINT_CLOSE}\"`;\n } else {\n out += text.slice(i, j || i + 1);\n }\n i = j > i ? j : i + 1;\n continue;\n }\n out += ch;\n i++;\n }\n return out;\n}\n\nfunction restoreBigInts(text) {\n if (!text.includes(BIGINT_OPEN)) return text;\n return text.replace(new RegExp(`\"${BIGINT_OPEN}(-?\\\\d+)${BIGINT_CLOSE}\"`, 'g'), '$1');\n}\n\n// Response-hardening actions. Mutate the (lowercase-keyed) headers object in place; a `null` value\n// signals removal to rebuildResponse / the node path. `set-header` sets/overwrites (or `ensure`s only\n// when absent); `remove-header` strips; `harden-cookie` adds missing HttpOnly/Secure/SameSite flags.\nfunction isHeaderMutation(action) {\n return action === 'set-header' || action === 'remove-header' || action === 'harden-cookie';\n}\n\nfunction applyHeaderMutation(headers, rule) {\n if (rule.action === 'remove-header') {\n for (const name of rule.remove_headers ?? []) headers[String(name).toLowerCase()] = null;\n return;\n }\n if (rule.action === 'set-header') {\n const ensure = rule.ensure === true; // set only when the header is absent (don't clobber)\n for (const [name, value] of Object.entries(rule.set_headers ?? {})) {\n const key = String(name).toLowerCase();\n const present = headers[key] != null && headers[key] !== '';\n if (ensure && present) continue;\n headers[key] = String(value);\n }\n return;\n }\n if (rule.action === 'harden-cookie') {\n const cookie = headers['set-cookie'];\n const flags = rule.cookie_flags ?? {};\n if (Array.isArray(cookie)) {\n headers['set-cookie'] = cookie.map((c) => (typeof c === 'string' ? hardenCookie(c, flags) : c));\n } else if (typeof cookie === 'string') {\n headers['set-cookie'] = hardenCookie(cookie, flags);\n }\n }\n}\n\n// `SameSite` takes exactly three values, and the flag is interpolated into the header — so anything\n// else is either ignored by the browser or appends further cookie attributes (`Lax; Domain=…`). The\n// contract refuses such a rule, and this drops it if one arrives anyway.\nconst SAME_SITE = ['Strict', 'Lax', 'None'];\n\nfunction hardenCookie(cookie, { httpOnly = true, secure = true, sameSite = 'Lax' } = {}) {\n let out = String(cookie);\n if (httpOnly && !/;\\s*httponly/i.test(out)) out += '; HttpOnly';\n if (secure && !/;\\s*secure/i.test(out)) out += '; Secure';\n const canonical = SAME_SITE.find((v) => typeof sameSite === 'string' && v.toLowerCase() === sameSite.toLowerCase());\n if (canonical && !/;\\s*samesite\\s*=/i.test(out)) out += `; SameSite=${canonical}`;\n return out;\n}\n\nfunction rebuildResponse(response, body, redactedHeaders) {\n const headers = new Headers(response.headers);\n headers.delete('content-length'); // body length changed after redaction\n if (redactedHeaders) {\n for (const [name, value] of Object.entries(redactedHeaders)) {\n if (value === null || value === undefined) {\n try { headers.delete(name); } catch { /* skip */ } // header-mutation removal\n } else if (typeof value === 'string') {\n // `get` has to be inside the guard too: it throws on an invalid field name just as `set` does,\n // and it runs first — so an unusable name from a rule crashed response screening instead of\n // being skipped, taking the fail-open guarantee with it.\n try {\n if (headers.get(name) !== value) headers.set(name, value);\n } catch { /* invalid header name — skip */ }\n } else if (Array.isArray(value)) {\n // Re-emit each (possibly redacted) Set-Cookie separately (Headers collapses them otherwise).\n try {\n headers.delete(name);\n for (const item of value) headers.append(name, String(item));\n } catch { /* skip */ }\n }\n }\n }\n // Null-body statuses (204/205/304/101) must not carry a body, or the Response constructor throws.\n const nullBody = response.status === 101 || response.status === 204 || response.status === 205 || response.status === 304;\n return new Response(nullBody ? null : body, { status: response.status, statusText: response.statusText, headers });\n}\n\nfunction leakResponse() {\n return new Response(JSON.stringify({ error: 'Response withheld by Patchstack (sensitive data detected)' }), {\n status: 500,\n headers: { 'content-type': 'application/json' }\n });\n}\n\n// (rule source / tiered store moved to ./rules/source.js + ./rules/store.js)\n\n// --- responses ----------------------------------------------------------\n\n// Client-facing block text — deliberately generic: no WAF narrative, no rule title. The reason\n// detail stays in the server-side log via onDetect.\nconst BLOCK_MESSAGE = 'This request has been blocked by Patchstack.';\n\nfunction blockBody(result) {\n // Human text is masked (no WAF narrative, no rule title). The opaque rule id stays for machine\n // consumers (server-fn receipts, support reference); full rule detail lives in the server log.\n return { error: BLOCK_MESSAGE, message: BLOCK_MESSAGE, rule: result?.rule?.id };\n}\n\n// A top-level document navigation (vs an XHR/fetch)? Browsers set Sec-Fetch-Dest on navigations;\n// fall back to the Accept header. Governs whether a block returns the HTML page or JSON.\nfunction isDocumentNavigation(getHeader) {\n const dest = getHeader('sec-fetch-dest');\n if (dest) return dest === 'document';\n return (getHeader('accept') || '').includes('text/html');\n}\n\n// Request-phase block. Serves the branded HTML \"Access Denied\" page to a browser navigation, and\n// masked JSON to XHR/fetch/programmatic clients.\nfunction blockResponse(result, request) {\n if (request && isDocumentNavigation((n) => request.headers.get(n))) {\n return new Response(renderBlockPage({ url: request.url, code: result?.rule?.id }), {\n status: 403,\n headers: { 'content-type': 'text/html; charset=utf-8' },\n });\n }\n return new Response(JSON.stringify(blockBody(result)), {\n status: 403,\n headers: { 'content-type': 'application/json' },\n });\n}\n\nfunction defaultOnDetect({ phase, mode, category, rule, message }) {\n const tag = mode === 'block' ? 'BLOCK' : 'DETECT (dry-run)';\n console.warn(`[patchstack] ${tag} phase=${phase ?? 'request'} category=${category ?? '?'} rule=${rule?.id ?? '?'} ${message ?? ''}`.trim());\n}\n\n/** @param {Request} request */\nfunction fetchRequestMeta(request) {\n if (!request) return {};\n let path = null;\n try {\n path = new URL(request.url).pathname;\n } catch {\n path = typeof request.url === 'string' ? request.url : null;\n }\n return {\n method: request.method ?? null,\n path,\n ip: request.headers?.get?.('x-forwarded-for') ?? null,\n userAgent: request.headers?.get?.('user-agent') ?? null,\n };\n}\n\n/** @param {import('http').IncomingMessage & { ip?: string, originalUrl?: string }} req */\nfunction nodeRequestMeta(req) {\n if (!req) return {};\n const headers = req.headers ?? {};\n const ua = headers['user-agent'] ?? headers['User-Agent'];\n const fwd = headers['x-forwarded-for'] ?? headers['X-Forwarded-For'];\n return {\n method: req.method ?? null,\n path: req.originalUrl || req.url || null,\n ip: req.ip ?? (typeof fwd === 'string' ? fwd : null),\n userAgent: typeof ua === 'string' ? ua : Array.isArray(ua) ? ua[0] : null,\n };\n}\n"],"mappings":";;;;;AAKO,SAAS,aAAa,OAAO;AAClC,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,KAAK;AACvB,QAAI,EAAE,aAAa,SAAU,QAAO;AACpC,WAAO,EAAE,aAAa,YAAY,EAAE,aAAa,eAAe,EAAE,aAAa,eAAe,EAAE,aAAa,WAAW,EAAE,aAAa;AAAA,EACzI,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,YAAY,WAAW,UAAU,OAAO;AACtD,MAAI,OAAO,cAAc,YAAY,cAAc,GAAI,QAAO;AAC9D,MAAI,aAAa,SAAS,EAAG,QAAO;AACpC,WAAS,OAAO,gCAAgC,KAAK,cAAc,SAAS,qDAAqD;AACjI,SAAO;AACT;AAEA,IAAM,SAAS,oBAAI,IAAI;AACvB,SAAS,SAAS,KAAK,SAAS;AAC9B,MAAI,OAAO,IAAI,GAAG,EAAG;AACrB,SAAO,IAAI,GAAG;AAEd,UAAQ,KAAK,OAAO;AACtB;;;AC9BA,IAAM,mBAAmB;AACzB,IAAM,oBAAoB;AAG1B,IAAM,kBAAkB;AAKjB,IAAM,uBAAN,MAA2B;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EAEA,YAAY,EAAE,OAAO,SAAS,UAAU,MAAM,UAAU,IAAI,CAAC,GAAG;AAE9D,SAAK,aAAa,OAAO,SAAS,IAAI,IAAI,OAAO,SAAS,IAAI;AAC9D,SAAK,SAAS,SAAS,QAAQ,IAAI;AACnC,SAAK,WAAW,YAAY,WAAW,QAAQ,IAAI,wBAAwB,kBAAkB,eAAe;AAC5G,SAAK,YAAY,OAAO,SAAS,QAAQ,KAAK,WAAW,IAAI,WAAW;AACxE,SAAK,QAAQ,QAAQ;AAErB,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,uFAAuF;AAAA,IACzG;AAAA,EACF;AAAA,EAEA,MAAM,WAAW;AACf,UAAM,MAAM,KAAK,IAAI;AAErB,QAAI,KAAK,UAAU,KAAK,eAAe,QAAS,MAAM,KAAK,aAAc,KAAK,eAAe;AAC3F,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,MAAM,GAAG,KAAK,QAAQ;AAE5B,QAAI;AACF,YAAM,UAAU;AAAA,QACd,gBAAgB;AAAA,QAChB,iBAAiB,UAAU,KAAK,MAAM;AAAA,QACtC,aAAa;AAAA;AAAA,MACf;AACA,UAAI,KAAK,MAAO,SAAQ,eAAe,IAAI,KAAK;AAEhD,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR;AAAA,QACA,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,QACvB,QAAQ,YAAY,QAAQ,KAAK,UAAU;AAAA,MAC7C,CAAC;AAED,UAAI,SAAS,WAAW,KAAK;AAC3B,aAAK,OAAO,GAAG;AACf,eAAO,KAAK,UAAU,EAAE,SAAS,MAAM,aAAa,MAAM,MAAM,KAAK,OAAO,UAAU,CAAC,GAAG,YAAY,CAAC,GAAG,gBAAgB,CAAC,EAAE;AAAA,MAC/H;AAEA,UAAI,CAAC,SAAS,IAAI;AAChB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,OAAO,gBAAgB,SAAS,MAAM,KAAK,SAAS,UAAU;AAAA,UAC9D,UAAU,CAAC;AAAA,UACX,YAAY,CAAC;AAAA,UACb,gBAAgB,CAAC;AAAA,QACnB;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,SAAS,KAAK;AAIjC,UAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAC1C,eAAO,EAAE,SAAS,OAAO,OAAO,iDAAiD,UAAU,CAAC,GAAG,YAAY,CAAC,GAAG,gBAAgB,CAAC,EAAE;AAAA,MACpI;AAEA,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT,aAAa;AAAA,QACb,MAAM,SAAS,SAAS,MAAM,MAAM,KAAK;AAAA,QACzC,UAAU,KAAK;AAAA,QACf,YAAY,MAAM,QAAQ,KAAK,UAAU,IAAI,KAAK,aAAa,CAAC;AAAA,QAChE,gBAAgB,KAAK,kBAAkB,CAAC;AAAA,MAC1C;AAEA,WAAK,SAAS;AACd,WAAK,QAAQ,OAAO;AACpB,WAAK,OAAO,GAAG;AAEf,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,IAAI,SAAS,iBAChB,sBACA,IAAI;AAAA,QACR,UAAU,CAAC;AAAA,QACX,YAAY,CAAC;AAAA,QACb,gBAAgB,CAAC;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAO,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,gBAAgB,KAAK,YAAY,KAAK,MAAM,KAAK,OAAO,IAAI,KAAK,YAAY,eAAe;AAAA,EACnG;AAAA,EAEA,aAAa;AACX,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,QAAQ;AAAA,EACf;AACF;;;ACnHA,IAAM,aAAa,oBAAI,IAAI,CAAC,WAAW,YAAY,MAAM,CAAC;AAG1D,IAAM,eAAe,CAAC,MAAO,KAAK,OAAO,MAAM,WAAW,EAAE,WAAW;AACvE,IAAM,gBAAgB,CAAC,GAAG,SAAU,KAAK,OAAO,MAAM,WAAW,EAAE,IAAI,IAAI,SAAS,aAAa,IAAI;AAK9F,SAAS,iBAAiB,OAAO;AACtC,QAAM,MAAM,OAAO,KAAK;AACxB,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,OAAO,KAAK,KAAK,QAAQ,EAAE,SAAS,OAAO;AAAA,EACpD;AACA,QAAM,SAAS,KAAK,GAAG;AACvB,QAAM,QAAQ,WAAW,KAAK,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAC5D,SAAO,IAAI,YAAY,EAAE,OAAO,KAAK;AACvC;AAiBA,IAAM,iBAAiB;AAAA,EACrB,IAAI;AAAA,EAAK,IAAI;AAAA,EAAK,KAAK;AAAA,EAAK,MAAM;AAAA,EAAK,MAAM;AAAA,EAAK,OAAO;AAAA,EACzD,MAAM;AAAA,EAAU,KAAK;AAAA,EAAK,MAAM;AAAA,EAAM,OAAO;AAAA,EAAK,MAAM;AAAA,EAAK,MAAM;AAAA,EAAK,QAAQ;AAAA,EAAK,OAAO;AAAA,EAC5F,KAAK;AAAA,EAAM,SAAS;AAAA,EAAM,MAAM;AAAA,EAAK,MAAM;AAAA,EAAK,KAAK;AAAA,EAAK,QAAQ;AAAA,EAAK,QAAQ;AAAA,EAAK,KAAK;AAC3F;AAEA,SAAS,mBAAmB,OAAO;AAGjC,SAAO,MAAM,QAAQ,iDAAiD,CAAC,OAAO,SAAS;AACrF,QAAI,KAAK,CAAC,MAAM,KAAK;AACnB,YAAM,MAAM,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM;AAC3C,YAAM,OAAO,OAAO,SAAS,MAAM,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,MAAM,KAAK,EAAE;AAC/E,UAAI,CAAC,OAAO,SAAS,IAAI,KAAK,OAAO,KAAK,OAAO,QAAU,QAAO;AAClE,UAAI;AACF,eAAO,OAAO,cAAc,IAAI;AAAA,MAClC,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO,OAAO,UAAU,eAAe,KAAK,gBAAgB,IAAI,IAAI,eAAe,IAAI,IAAI;AAAA,EAC7F,CAAC;AACH;AAEO,IAAM,kBAAN,MAAsB;AAAA,EAC3B;AAAA,EACA;AAAA,EAEA,YAAY,KAAK;AACf,SAAK,OAAO;AACZ,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,QAAQ,WAAW;AACjB,QAAI,CAAC,aAAa,cAAc,SAAS;AACvC,aAAO,CAAC,IAAI;AAAA,IACd;AAEA,QAAI,cAAc,SAAS;AACzB,aAAO,CAAC,IAAI;AAAA,IACd;AAEA,QAAI,cAAc,OAAO;AACvB,aAAO,KAAK,YAAY;AAAA,IAC1B;AAEA,QAAI,cAAc,OAAO;AACvB,aAAO,KAAK,YAAY;AAAA,IAC1B;AAEA,UAAM,WAAW,UAAU,QAAQ,GAAG;AACtC,QAAI,aAAa,IAAI;AACnB,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,SAAS,UAAU,UAAU,GAAG,QAAQ;AAC9C,UAAM,MAAM,UAAU,UAAU,WAAW,CAAC;AAE5C,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,eAAO,KAAK,YAAY,GAAG;AAAA,MAC7B,KAAK;AACH,eAAO,KAAK,aAAa,GAAG;AAAA,MAC9B,KAAK;AACH,eAAO,KAAK,gBAAgB,GAAG;AAAA,MACjC,KAAK;AACH,eAAO,KAAK,eAAe,GAAG;AAAA,MAChC,KAAK;AACH,eAAO,KAAK,eAAe,GAAG;AAAA,MAChC,KAAK;AACH,eAAO,KAAK,cAAc,GAAG;AAAA,MAC/B,KAAK;AACH,eAAO,KAAK,iBAAiB,GAAG;AAAA,MAClC,KAAK;AACH,eAAO,KAAK,eAAe,GAAG;AAAA,MAChC;AACE,eAAO,CAAC;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA,EAIA,iBAAiB,KAAK;AACpB,UAAM,OAAO,KAAK,KAAK;AACvB,QAAI,CAAC,MAAM;AACT,aAAO,CAAC;AAAA,IACV;AACA,QAAI,QAAQ,UAAU;AACpB,aAAO,KAAK,WAAW,SAAY,CAAC,OAAO,KAAK,MAAM,CAAC,IAAI,CAAC;AAAA,IAC9D;AACA,QAAI,QAAQ,QAAQ;AAClB,aAAO,KAAK,QAAQ,QAAQ,KAAK,SAAS,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC;AAAA,IAChE;AACA,QAAI,QAAQ,WAAW;AACrB,YAAM,UAAU,KAAK,WAAW,CAAC;AACjC,aAAO,CAAC,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA,IAC1E;AACA,QAAI,IAAI,WAAW,SAAS,GAAG;AAC7B,YAAM,OAAO,IAAI,MAAM,UAAU,MAAM,EAAE,YAAY;AACrD,YAAM,SAAS,KAAK,WAAW,CAAC,GAAG,IAAI;AACvC,aAAO,UAAU,SAAY,CAAC,KAAK,IAAI,CAAC;AAAA,IAC1C;AACA,WAAO,CAAC;AAAA,EACV;AAAA;AAAA;AAAA,EAIA,eAAe,KAAK;AAClB,UAAM,KAAK,KAAK,KAAK;AACrB,QAAI,CAAC,IAAI;AACP,aAAO,CAAC;AAAA,IACV;AACA,QAAI,QAAQ,MAAO,QAAO,GAAG,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC;AAC/C,QAAI,QAAQ,OAAQ,QAAO,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC;AAClD,QAAI,QAAQ,SAAU,QAAO,GAAG,SAAS,CAAC,GAAG,MAAM,IAAI,CAAC;AACxD,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,eAAe,WAAW,OAAO;AAC/B,QAAI,CAAC,aAAa,CAAC,MAAM,QAAQ,SAAS,GAAG;AAC3C,aAAO;AAAA,IACT;AAEA,QAAI,SAAS;AAEb,eAAW,YAAY,WAAW;AAChC,eAAS,KAAK,eAAe,UAAU,MAAM;AAAA,IAC/C;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,UAAU,OAAO;AAC9B,QAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,aAAO;AAAA,IACT;AAEA,YAAQ,UAAU;AAAA,MAChB,KAAK;AACH,YAAI;AACF,iBAAO,iBAAiB,KAAK;AAAA,QAC/B,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MAEF,KAAK;AACH,YAAI;AACF,iBAAO,KAAK,MAAM,OAAO,KAAK,CAAC;AAAA,QACjC,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MAEF,KAAK;AACH,YAAI;AACF,iBAAO,KAAK,UAAU,KAAK;AAAA,QAC7B,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MAEF,KAAK;AACH,YAAI;AACF,iBAAO,mBAAmB,OAAO,KAAK,CAAC;AAAA,QACzC,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MAEF,KAAK;AACH,eAAO,mBAAmB,OAAO,KAAK,CAAC;AAAA,MAEzC,KAAK;AACH,eAAO,SAAS,OAAO,KAAK,GAAG,EAAE,KAAK;AAAA,MAExC,KAAK;AACH,YAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,iBAAO,OAAO,OAAO,KAAK;AAAA,QAC5B;AACA,eAAO;AAAA,MAET;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA,EAEA,YAAY,KAAK;AACf,UAAM,QAAQ,KAAK,KAAK,SAAS,CAAC;AAElC,QAAI,IAAI,SAAS,GAAG,GAAG;AACrB,aAAO,KAAK,iBAAiB,OAAO,GAAG;AAAA,IACzC;AAEA,UAAM,QAAQ,KAAK,gBAAgB,OAAO,GAAG;AAC7C,WAAO,UAAU,SAAY,CAAC,KAAK,IAAI,CAAC;AAAA,EAC1C;AAAA,EAEA,aAAa,KAAK;AAChB,UAAM,OAAO,KAAK,KAAK,QAAQ,CAAC;AAEhC,QAAI,IAAI,SAAS,GAAG,GAAG;AACrB,aAAO,KAAK,iBAAiB,MAAM,GAAG;AAAA,IACxC;AAEA,UAAM,QAAQ,KAAK,gBAAgB,MAAM,GAAG;AAC5C,WAAO,UAAU,SAAY,CAAC,KAAK,IAAI,CAAC;AAAA,EAC1C;AAAA,EAEA,gBAAgB,KAAK;AACnB,UAAM,QAAQ,KAAK,KAAK,SAAS,CAAC;AAClC,UAAM,OAAO,KAAK,KAAK,QAAQ,CAAC;AAChC,UAAM,UAAU,KAAK,cAAc;AAEnC,QAAI,IAAI,SAAS,GAAG,GAAG;AACrB,aAAO;AAAA,QACL,GAAG,KAAK,iBAAiB,OAAO,GAAG;AAAA,QACnC,GAAG,KAAK,iBAAiB,MAAM,GAAG;AAAA,QAClC,GAAG,KAAK,iBAAiB,SAAS,GAAG;AAAA,MACvC;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,gBAAgB,OAAO,GAAG,KACxC,KAAK,gBAAgB,MAAM,GAAG,KAC9B,QAAQ,GAAG;AAEhB,WAAO,UAAU,SAAY,CAAC,KAAK,IAAI,CAAC;AAAA,EAC1C;AAAA,EAEA,eAAe,KAAK;AAClB,UAAM,UAAU,KAAK,cAAc;AAEnC,QAAI,IAAI,SAAS,GAAG,GAAG;AACrB,aAAO,KAAK,iBAAiB,SAAS,GAAG;AAAA,IAC3C;AAEA,UAAM,QAAQ,QAAQ,GAAG;AACzB,WAAO,UAAU,SAAY,CAAC,KAAK,IAAI,CAAC;AAAA,EAC1C;AAAA,EAEA,eAAe,KAAK;AAClB,UAAM,MAAM,KAAK;AAEjB,YAAQ,KAAK;AAAA,MACX,KAAK;AACH,eAAO,CAAC,IAAI,eAAe,IAAI,OAAO,GAAG;AAAA,MAC3C,KAAK;AACH,eAAO,CAAC,IAAI,UAAU,KAAK;AAAA,MAC7B,KAAK;AACH,eAAO,IAAI,UAAU,YAAY,IAAI,CAAC,IAAI,QAAQ,YAAY,CAAC,IAAI,CAAC;AAAA,MACtE,KAAK;AACH,eAAO,IAAI,SAAS,UAAU,CAAC,IAAI,QAAQ,OAAO,IAAI,CAAC;AAAA,MACzD,KAAK;AACH,eAAO,IAAI,SAAS,OAAO,CAAC,IAAI,QAAQ,IAAI,IAAI,CAAC;AAAA,MACnD,KAAK;AAAA,MACL,KAAK;AACH,eAAO,CAAC,IAAI,MAAM,IAAI,QAAQ,iBAAiB,EAAE;AAAA,MACnD,KAAK;AACH,eAAO,IAAI,UAAU,cAAc,IAAI,CAAC,IAAI,QAAQ,cAAc,CAAC,IAAI,CAAC;AAAA,MAC1E,KAAK;AACH,eAAO,IAAI,UAAU,gBAAgB,IAAI,CAAC,IAAI,QAAQ,gBAAgB,CAAC,IAAI,CAAC;AAAA,MAC9E,SAAS;AACP,YAAI,IAAI,WAAW,OAAO,GAAG;AAC3B,gBAAM,aAAa,IAAI,UAAU,CAAC,EAAE,YAAY,EAAE,QAAQ,MAAM,GAAG;AACnE,gBAAM,UAAU,IAAI;AAMpB,cAAI,YAAY,QAAQ,OAAO,YAAY,SAAU,QAAO,CAAC;AAC7D,iBAAO,OAAO,UAAU,eAAe,KAAK,SAAS,UAAU,IAAI,CAAC,QAAQ,UAAU,CAAC,IAAI,CAAC;AAAA,QAC9F;AACA,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EAEA,cAAc,KAAK;AACjB,UAAM,QAAQ,KAAK,KAAK;AACxB,QAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,aAAO,CAAC;AAAA,IACV;AAIA,UAAM,MAAM,IAAI,YAAY,GAAG;AAC/B,QAAI,QAAQ,MAAM,WAAW,IAAI,IAAI,MAAM,MAAM,CAAC,CAAC,KAAK,OAAO,UAAU,eAAe,KAAK,OAAO,IAAI,MAAM,GAAG,GAAG,CAAC,GAAG;AACtH,YAAM,OAAO,IAAI,MAAM,MAAM,CAAC;AAC9B,YAAM,QAAQ,MAAM,IAAI,MAAM,GAAG,GAAG,CAAC;AACrC,YAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAClD,YAAM,MAAM,CAAC;AACb,iBAAW,KAAK,MAAM;AACpB,cAAM,IAAI,cAAc,GAAG,IAAI;AAC/B,YAAI,MAAM,UAAa,MAAM,GAAI,KAAI,KAAK,CAAC;AAAA,MAC7C;AACA,aAAO;AAAA,IACT;AAIA,UAAM,cAAc,CAAC,UAAW,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,YAAY,IAAI,CAAC,aAAa,KAAK,CAAC;AACrG,QAAI,IAAI,SAAS,GAAG,GAAG;AACrB,YAAM,SAAS,IAAI,MAAM,GAAG,EAAE;AAC9B,YAAM,MAAM,CAAC;AACb,iBAAW,CAAC,GAAG,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC9C,YAAI,EAAE,WAAW,MAAM,EAAG,KAAI,KAAK,GAAG,YAAY,KAAK,CAAC;AAAA,MAC1D;AACA,aAAO,IAAI,OAAO,CAAC,MAAM,MAAM,MAAS;AAAA,IAC1C;AACA,QAAI,CAAC,OAAO,UAAU,eAAe,KAAK,OAAO,GAAG,EAAG,QAAO,CAAC;AAC/D,WAAO,YAAY,MAAM,GAAG,CAAC,EAAE,OAAO,CAAC,MAAM,MAAM,MAAS;AAAA,EAC9D;AAAA,EAEA,cAAc;AAKZ,QAAI,OAAO,KAAK,KAAK,aAAa,UAAU;AAC1C,aAAO,KAAK,KAAK,WAAW,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC;AAAA,IACtD;AAEA,UAAM,OAAO,KAAK,KAAK;AAEvB,QAAI,SAAS,UAAa,SAAS,MAAM;AACvC,aAAO,CAAC;AAAA,IACV;AAEA,QAAI,OAAO,SAAS,UAAU;AAC5B,aAAO,CAAC,IAAI;AAAA,IACd;AAEA,QAAI;AACF,aAAO,CAAC,KAAK,UAAU,IAAI,CAAC;AAAA,IAC9B,QAAQ;AACN,aAAO,CAAC,OAAO,IAAI,CAAC;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,cAAc;AACZ,UAAM,QAAQ,CAAC;AAEf,UAAM,MAAM,KAAK,KAAK,eAAe,KAAK,KAAK,OAAO;AACtD,UAAM,KAAK,GAAG;AAEd,UAAM,cAAc,IAAI,SAAS,GAAG,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI;AAC5D,QAAI,aAAa;AACf,YAAM,KAAK,WAAW;AAAA,IACxB;AAEA,UAAM,OAAO,KAAK,KAAK;AACvB,QAAI,MAAM;AACR,YAAM,KAAK,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI,CAAC;AAAA,IACnE;AAIA,QAAI,OAAO,KAAK,KAAK,aAAa,YAAY,KAAK,KAAK,UAAU;AAChE,YAAM,KAAK,KAAK,KAAK,QAAQ;AAAA,IAC/B;AAEA,UAAM,UAAU,KAAK,KAAK,WAAW,CAAC;AACtC,UAAM,kBAAkB,oBAAI,IAAI;AAAA,MAC9B;AAAA,MAAQ;AAAA,MAAc;AAAA,MAAiB;AAAA,MAAU;AAAA,MACjD;AAAA,MAAmB;AAAA,MAAY;AAAA,MAAa;AAAA,MAC5C;AAAA,MAAsB;AAAA,MAAkB;AAAA,MACxC;AAAA,MAAkB;AAAA,MAAkB;AAAA,IACtC,CAAC;AAED,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AACnD,UAAI,CAAC,gBAAgB,IAAI,IAAI,GAAG;AAC9B,cAAM,KAAK,GAAG,IAAI,KAAK,KAAK,EAAE;AAAA,MAChC;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,cAAc;AACnC,UAAM,YAAY,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,IAAI;AAChF,QAAI,WAAW;AACb,YAAM,KAAK,SAAS;AAAA,IACtB;AAEA,WAAO,CAAC,MAAM,KAAK,GAAG,CAAC;AAAA,EACzB;AAAA,EAEA,iBAAiB,KAAK,SAAS;AAC7B,QAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC3C,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,SAAS,QAAQ,MAAM,GAAG,EAAE;AAClC,UAAM,SAAS,CAAC;AAEhB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,UAAI,IAAI,WAAW,MAAM,GAAG;AAC1B,eAAO,KAAK,KAAK;AAAA,MACnB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,gBAAgB,KAAK,KAAK;AACxB,QAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC3C,aAAO;AAAA,IACT;AAKA,UAAM,MAAM,CAAC,GAAG,MAAM,MAAM,QAAQ,OAAO,MAAM,YAAY,OAAO,UAAU,eAAe,KAAK,GAAG,CAAC;AAEtG,QAAI,IAAI,KAAK,GAAG,GAAG;AACjB,aAAO,IAAI,GAAG;AAAA,IAChB;AAEA,UAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,QAAI,UAAU;AAEd,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,IAAI,SAAS,IAAI,GAAG;AACvB,eAAO;AAAA,MACT;AACA,gBAAU,QAAQ,IAAI;AAAA,IACxB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,gBAAgB;AACd,QAAI,KAAK,aAAa,MAAM;AAC1B,aAAO,KAAK;AAAA,IACd;AAEA,QAAI,KAAK,KAAK,SAAS;AACrB,WAAK,WAAW,KAAK,KAAK;AAC1B,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,SAAS,KAAK,KAAK,SAAS;AAClC,QAAI,CAAC,QAAQ;AACX,WAAK,WAAW,CAAC;AACjB,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,UAAU,CAAC;AAEjB,eAAW,QAAQ,OAAO,MAAM,GAAG,GAAG;AACpC,YAAM,UAAU,KAAK,QAAQ,GAAG;AAChC,UAAI,YAAY,IAAI;AAClB;AAAA,MACF;AACA,YAAM,OAAO,KAAK,UAAU,GAAG,OAAO,EAAE,KAAK;AAC7C,YAAM,QAAQ,KAAK,UAAU,UAAU,CAAC,EAAE,KAAK;AAC/C,cAAQ,IAAI,IAAI;AAAA,IAClB;AAEA,SAAK,WAAW;AAChB,WAAO,KAAK;AAAA,EACd;AACF;;;ACldA,IAAM,WAAW,oBAAI,IAAI;AAElB,SAAS,OAAO,IAAI,KAAK,OAAO;AACrC,MAAI,OAAO,OAAO,WAAY,QAAO;AAErC,MAAI;AACF,UAAM,SAAS,GAAG,GAAG;AAMrB,QAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,OAAO,OAAO,SAAS,YAAY;AACtF,UAAI;AACF,eAAO,KAAK,QAAW,CAAC,QAAQA,UAAS,OAAO,GAAG,CAAC;AAAA,MACtD,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,IAAAA,UAAS,OAAO,GAAG;AAEnB,WAAO;AAAA,EACT;AACF;AAQA,SAASA,UAAS,OAAO,KAAK;AAC5B,MAAI,SAAS,IAAI,KAAK,EAAG;AACzB,WAAS,IAAI,KAAK;AAClB,MAAI;AAGF,YAAQ;AAAA,MACN,mBAAmB,KAAK,oIAEZ,OAAO,IAAI,UAAU,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC5D;AAAA,EACF,QAAQ;AAAA,EAER;AACF;;;ACpEA,IAAM,gBAAgB;AAAA,EAClB,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AACb;AAEA,IAAM,wBAAwB;AAEvB,SAAS,UAAU,OAAO,UAAU,CAAC,GAAG;AAC3C,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO;AAAA,EACX;AAEA,QAAM,OAAO;AAAA,IACT,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,GAAG;AAAA,EACP;AAEA,MAAI,SAAS;AAEb,MAAI,KAAK,WAAW;AAChB,aAAS,gBAAgB,MAAM;AAAA,EACnC;AAEA,MAAI,KAAK,WAAW;AAChB,aAAS,UAAU,MAAM;AAAA,EAC7B;AAEA,MAAI,KAAK,YAAY;AACjB,aAAS,iBAAiB,MAAM;AAAA,EACpC;AAEA,MAAI,KAAK,aAAa;AAClB,aAAS,kBAAkB,MAAM;AAAA,EACrC;AAEA,MAAI,KAAK,YAAY;AACjB,aAAS,oBAAoB,MAAM;AAAA,EACvC;AAEA,SAAO;AACX;AAEO,SAAS,UAAU,OAAO;AAC7B,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO;AAAA,EACX;AAEA,MAAI,SAAS;AACb,MAAI,WAAW;AACf,MAAI,aAAa;AAEjB,SAAO,WAAW,YAAY,aAAa,uBAAuB;AAC9D,eAAW;AACX;AAEA,QAAI;AACA,eAAS,mBAAmB,MAAM;AAAA,IACtC,QAAQ;AACJ,eAAS,cAAc,MAAM;AAC7B;AAAA,IACJ;AAAA,EACJ;AAEA,SAAO;AACX;AAEA,SAAS,cAAc,OAAO;AAC1B,SAAO,MAAM,QAAQ,sBAAsB,CAAC,OAAO,QAAQ;AACvD,QAAI;AACA,aAAO,OAAO,aAAa,SAAS,KAAK,EAAE,CAAC;AAAA,IAChD,QAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ,CAAC;AACL;AAEO,SAAS,iBAAiB,OAAO;AACpC,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO;AAAA,EACX;AAEA,MAAI,SAAS;AAEb,aAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,aAAa,GAAG;AACxD,aAAS,OAAO,MAAM,MAAM,EAAE,KAAK,IAAI;AAAA,EAC3C;AAEA,WAAS,OAAO,QAAQ,aAAa,CAAC,OAAO,SAAS;AAClD,UAAM,MAAM,SAAS,MAAM,EAAE;AAC7B,WAAO,MAAM,KAAK,MAAM,QAAQ,OAAO,aAAa,GAAG,IAAI;AAAA,EAC/D,CAAC;AAED,WAAS,OAAO,QAAQ,uBAAuB,CAAC,OAAO,QAAQ;AAC3D,UAAM,MAAM,SAAS,KAAK,EAAE;AAC5B,WAAO,MAAM,KAAK,MAAM,QAAQ,OAAO,aAAa,GAAG,IAAI;AAAA,EAC/D,CAAC;AAED,SAAO;AACX;AAEO,SAAS,kBAAkB,OAAO;AACrC,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO;AAAA,EACX;AAQA,MAAI,SAAS;AAEb,WAAS,OAAO,QAAQ,qBAAqB,GAAG;AAChD,WAAS,OAAO,QAAQ,sBAAsB,GAAG;AAEjD,SAAO;AACX;AAEO,SAAS,gBAAgB,OAAO;AACnC,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO;AAAA,EACX;AAEA,MAAI,SAAS,MAAM,QAAQ,SAAS,EAAE;AAEtC,WAAS,OAAO,QAAQ,iCAAiC,EAAE;AAE3D,SAAO;AACX;AAEO,SAAS,oBAAoB,OAAO;AACvC,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO;AAAA,EACX;AAEA,MAAI,SAAS,MAAM,QAAQ,kBAAkB,GAAG;AAEhD,WAAS,OAAO,QAAQ,UAAU,GAAG;AAErC,SAAO;AACX;AAEA,SAAS,yBAAyB,MAAM,UAAU,oBAAI,IAAI,GAAG,SAAS,MAAM;AACxE,MAAI,SAAS,QAAQ,SAAS,QAAW;AACrC,WAAO,SAAS,KAAK;AAAA,EACzB;AAEA,MAAI,OAAO,SAAS,UAAU;AAC1B,WAAO,SAAS,OAAO,KAAK,UAAU,IAAI;AAAA,EAC9C;AAEA,MAAI,OAAO,SAAS,UAAU;AAC1B,WAAO,OAAO,IAAI;AAAA,EACtB;AAEA,MAAI,QAAQ,IAAI,IAAI,GAAG;AACnB,WAAO;AAAA,EACX;AACA,UAAQ,IAAI,IAAI;AAEhB,MAAI,MAAM,QAAQ,IAAI,GAAG;AACrB,UAAM,QAAQ,KAAK,IAAI,UAAQ,yBAAyB,MAAM,SAAS,KAAK,CAAC;AAC7E,WAAO,MAAM,MAAM,KAAK,GAAG,IAAI;AAAA,EACnC;AAIA,QAAM,OAAO,OAAO,oBAAoB,IAAI;AAC5C,QAAM,QAAQ,KAAK,IAAI,SAAO;AAC1B,UAAM,MAAM,yBAAyB,KAAK,GAAG,GAAG,SAAS,KAAK;AAC9D,WAAO,KAAK,UAAU,GAAG,IAAI,MAAM;AAAA,EACvC,CAAC;AAED,SAAO,MAAM,MAAM,KAAK,GAAG,IAAI;AACnC;AAEO,SAAS,iBAAiB,KAAK,UAAU,CAAC,GAAG;AAKhD,QAAM,UAAU,OAAO,IAAI,aAAa,WAClC,IAAI,WACJ,yBAAyB,IAAI,QAAQ,IAAI;AAE/C,SAAO;AAAA,IACH,OAAO,gBAAgB,IAAI,SAAS,CAAC,GAAG,OAAO;AAAA,IAC/C,MAAM,gBAAgB,IAAI,QAAQ,CAAC,GAAG,OAAO;AAAA,IAC7C,SAAS,gBAAgB,IAAI,WAAW,CAAC,GAAG,OAAO;AAAA,IACnD,KAAK,UAAU,IAAI,OAAO,IAAI,OAAO;AAAA,IACrC,aAAa,UAAU,IAAI,eAAe,IAAI,OAAO,IAAI,OAAO;AAAA,IAChE,UAAU;AAAA,EACd;AACJ;AAKA,IAAM,sBAAsB;AAErB,SAAS,gBAAgB,OAAO,UAAU,CAAC,GAAG,QAAQ,GAAG;AAC5D,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO,UAAU,OAAO,OAAO;AAAA,EACnC;AAEA,MAAI,SAAS,qBAAqB;AAC9B,WAAO;AAAA,EACX;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACtB,WAAO,MAAM,IAAI,UAAQ,gBAAgB,MAAM,SAAS,QAAQ,CAAC,CAAC;AAAA,EACtE;AAEA,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC7C,UAAM,SAAS,CAAC;AAEhB,eAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC5C,aAAO,GAAG,IAAI,gBAAgB,KAAK,SAAS,QAAQ,CAAC;AAAA,IACzD;AAEA,WAAO;AAAA,EACX;AAEA,SAAO;AACX;;;AC/OA,IAAM,MAAM;AACZ,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA,IAAI,OAAO,QAAQ,MAAM,UAAU,MAAM,aAAa;AAAA;AAAA,EACtD,IAAI,OAAO,QAAQ,MAAM,QAAQ,MAAM,aAAa;AAAA;AACtD;AAIA,IAAM,yBAAyB,oBAAI,IAAI;AACvC,SAAS,wBAAwB,SAAS;AACxC,QAAM,MAAM,OAAO,OAAO;AAC1B,MAAI,uBAAuB,IAAI,GAAG,EAAG;AACrC,yBAAuB,IAAI,GAAG;AAC9B,UAAQ;AAAA,IACN,iJAC6C,GAAG;AAAA,EAClD;AACF;AAKA,IAAM,qBAAqB;AAEpB,SAAS,WAAW,SAAS;AAClC,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AACA,MAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,oBAAoB;AACtE,WAAO;AAAA,EACT;AAEA,aAAW,aAAa,gBAAgB;AACtC,QAAI,UAAU,KAAK,OAAO,GAAG;AAC3B,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,QAAQ,QAAQ,MAAM,yBAAyB;AACrD,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,MAAI;AACF,WAAO,IAAI,OAAO,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;AAAA,EACtC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,SAAS,YAAY,UAAU,SAAS,UAAU;AAChD,MAAI,aAAa,IAAI;AACnB,WAAO;AAAA,EACT;AACA,QAAM,WAAW,OAAO,aAAa,YAAY,WAAW;AAC5D,SAAO,YAAY;AACrB;AAMO,SAAS,WAAW,MAAM,UAAU,IAAI;AAC7C,MAAI,SAAS,QAAQ,OAAO,SAAS,SAAU;AAC/C,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,eAAW,MAAM,KAAM,YAAW,IAAI,UAAU,EAAE;AAClD;AAAA,EACF;AACA,QAAM,CAAC,MAAM,GAAG,IAAI,IAAI;AACxB,MAAI,SAAS,UAAa,CAAC,OAAO,UAAU,eAAe,KAAK,MAAM,IAAI,EAAG;AAC7E,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,OAAO,KAAK,IAAI;AACtB,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,IAAG,EAAE,QAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,EAAE,CAAC;AAAA,IACnF,OAAO;AACL,SAAG,EAAE,QAAQ,MAAM,KAAK,MAAM,OAAO,KAAK,CAAC;AAAA,IAC7C;AAAA,EACF,OAAO;AACL,eAAW,KAAK,IAAI,GAAG,MAAM,EAAE;AAAA,EACjC;AACF;AAKA,SAAS,cAAc,OAAO,UAAU;AACtC,MAAI,CAAC,YAAY,CAAC,SAAS,SAAS,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC/E,WAAO;AAAA,EACT;AACA,QAAM,OAAO,MAAM,QAAQ,SAAS,GAAG,IAAI,SAAS,MAAM,CAAC,SAAS,GAAG;AACvE,QAAM,MAAM,SAAS;AAErB,aAAW,OAAO,MAAM;AACtB,QAAI,UAAU;AACd,eAAW,OAAO,OAAO,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,EAAE,OAAO,KAAK,MAAM;AAC7D,UAAI,CAAC,WAAW,WAAW,IAAI,MAAM,MAAM,IAAI,OAAO,GAAG,EAAG,WAAU;AAAA,IACxE,CAAC;AACD,QAAI,QAAS,QAAO;AAAA,EACtB;AACA,SAAO;AACT;AAKA,IAAM,0BAA0B,oBAAI,IAAI,CAAC,SAAS,kBAAkB,iBAAiB,CAAC;AAKtF,SAAS,kBAAkB,MAAM,UAAU,KAAO,WAAW,KAAM;AACjE,QAAM,MAAM,CAAC;AACb,QAAM,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AACxB,MAAI,UAAU;AACd,SAAO,MAAM,QAAQ;AACnB,UAAM,CAAC,MAAM,KAAK,IAAI,MAAM,IAAI;AAChC,QAAI,SAAS,QAAQ,SAAS,OAAW;AACzC,QAAI,OAAO,SAAS,UAAU;AAC5B,UAAI,KAAK,IAAI;AACb;AAAA,IACF;AACA,QAAI,SAAS,YAAY,WAAW,QAAS;AAC7C;AACA,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,eAAS,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,IAAK,OAAM,KAAK,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,CAAC;AAAA,IAC5E,OAAO;AACL,iBAAW,KAAK,OAAO,KAAK,IAAI,EAAG,OAAM,KAAK,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,CAAC;AAAA,IACpE;AAAA,EACF;AACA,SAAO;AACT;AAGA,IAAM,aAAa,oBAAI,IAAI;AAC3B,SAASC,UAAS,KAAK,SAAS;AAC9B,MAAI,WAAW,IAAI,GAAG,EAAG;AACzB,aAAW,IAAI,GAAG;AAClB,UAAQ,KAAK,OAAO;AACtB;AAIA,IAAM,mBAAmB,oBAAI,IAAI;AACjC,SAAS,yBAAyB,MAAM;AACtC,MAAI,QAAQ,QAAQ,iBAAiB,IAAI,IAAI,GAAG;AAC9C;AAAA,EACF;AACA,mBAAiB,IAAI,IAAI;AACzB,UAAQ;AAAA,IACN,gDAAgD,IAAI;AAAA,EAEtD;AACF;AAsBA,SAAS,cAAc,OAAO;AAC5B,QAAM,MAAM,OAAO,SAAS,EAAE,EAAE,KAAK;AACrC,MAAI,QAAQ,GAAI,QAAO;AAKvB,MAAI,2BAA2B,KAAK,GAAG,KAAK,IAAI,WAAW,IAAI,GAAG;AAChE,QAAI;AACF,aAAO,IAAI,IAAI,IAAI,WAAW,IAAI,IAAI,QAAQ,GAAG,KAAK,GAAG,EAAE;AAAA,IAC7D,QAAQ;AAEN,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,IAAI,WAAW,GAAG,GAAG;AACvB,UAAM,MAAM,IAAI,QAAQ,GAAG;AAC3B,QAAI,MAAM,EAAG,QAAO,IAAI,MAAM,GAAG,GAAG;AAAA,EACtC;AAIA,QAAM,QAAQ,IAAI,QAAQ,GAAG;AAC7B,MAAI,QAAQ,KAAK,IAAI,QAAQ,KAAK,QAAQ,CAAC,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,CAAC,CAAC,GAAG;AACzF,WAAO,IAAI,MAAM,GAAG,KAAK;AAAA,EAC3B;AAEA,SAAO;AACT;AAEA,SAAS,eAAe,UAAU;AAChC,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,OAAO,OAAO,QAAQ,EAAE,YAAY,EAAE,QAAQ,YAAY,EAAE;AAChE,SAAO,KAAK,QAAQ,YAAY,EAAE;AAClC,SAAO,KAAK,QAAQ,OAAO,EAAE;AAE7B,MAAI,SAAS,eAAe,KAAK,SAAS,YAAY,KAAK,KAAK,SAAS,QAAQ,EAAG,QAAO;AAC3F,MAAI,SAAS,2BAA4B,QAAO;AAGhD,MAAI,KAAK,SAAS,GAAG,GAAG;AACtB,UAAM,IAAI,WAAW,IAAI;AACzB,QAAI,CAAC,EAAG,QAAO;AACf,UAAM,YAAY,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM;AACnF,QAAI,aAAa,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM,MAAM,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM,GAAI,QAAO;AAChF,SAAK,EAAE,CAAC,IAAI,WAAY,MAAQ,QAAO;AACvC,SAAK,EAAE,CAAC,IAAI,WAAY,MAAQ,QAAO;AACvC,QAAI,cAAc,EAAE,CAAC,MAAM,SAAU,EAAE,CAAC,MAAM,IAAI;AAEhD,aAAO,gBAAkB,EAAE,CAAC,KAAK,OAAQ,IAAK,EAAE,CAAC,OAAO,CAAC;AAAA,IAC3D;AACA,WAAO;AAAA,EACT;AAGA,QAAM,KAAK,eAAe,IAAI;AAC9B,MAAI,OAAO,KAAM,QAAO,eAAe,EAAE;AACzC,SAAO;AACT;AAGA,SAAS,eAAe,GAAG;AACzB,QAAM,IAAK,MAAM,KAAM;AACvB,QAAM,IAAK,MAAM,KAAM;AACvB,MAAI,MAAM,OAAO,MAAM,MAAM,MAAM,EAAG,QAAO;AAC7C,MAAI,MAAM,OAAO,MAAM,IAAK,QAAO;AACnC,MAAI,MAAM,OAAO,KAAK,MAAM,KAAK,GAAI,QAAO;AAC5C,MAAI,MAAM,OAAO,MAAM,IAAK,QAAO;AACnC,MAAI,MAAM,OAAO,KAAK,MAAM,KAAK,IAAK,QAAO;AAC7C,SAAO;AACT;AAKA,SAAS,eAAe,MAAM;AAC5B,MAAI,CAAC,+BAA+B,KAAK,IAAI,EAAG,QAAO;AACvD,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,QAAM,OAAO,CAAC;AACd,aAAW,KAAK,OAAO;AACrB,QAAI;AACJ,QAAI,iBAAiB,KAAK,CAAC,EAAG,KAAI,SAAS,GAAG,EAAE;AAAA,aACvC,YAAY,KAAK,CAAC,EAAG,KAAI,SAAS,GAAG,CAAC;AAAA,aACtC,WAAW,KAAK,CAAC,EAAG,KAAI,SAAS,GAAG,EAAE;AAAA,QAC1C,QAAO;AACZ,QAAI,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,EAAG,QAAO;AAC1C,SAAK,KAAK,CAAC;AAAA,EACb;AACA,QAAM,OAAO,KAAK,SAAS;AAC3B,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC7B,QAAI,KAAK,CAAC,IAAI,IAAM,QAAO;AAC3B,aAAS,KAAK,CAAC,IAAI,MAAM,KAAK,IAAI;AAAA,EACpC;AACA,QAAM,UAAU,MAAM,KAAK,IAAI,KAAK,WAAW;AAC/C,MAAI,KAAK,IAAI,IAAI,QAAS,QAAO;AACjC,WAAS,KAAK,IAAI;AAClB,MAAI,QAAQ,KAAK,QAAQ,WAAY,QAAO;AAC5C,SAAO,UAAU;AACnB;AAIA,SAAS,WAAW,MAAM;AACxB,MAAI,CAAC,KAAK,SAAS,GAAG,EAAG,QAAO;AAChC,MAAI,IAAI;AAER,QAAM,YAAY,EAAE,YAAY,GAAG;AACnC,QAAM,OAAO,EAAE,MAAM,YAAY,CAAC;AAClC,MAAI,KAAK,SAAS,GAAG,GAAG;AACtB,UAAM,KAAK,eAAe,IAAI;AAC9B,QAAI,OAAO,KAAM,QAAO;AACxB,QAAI,EAAE,MAAM,GAAG,YAAY,CAAC,KAAM,OAAO,KAAM,OAAQ,SAAS,EAAE,IAAI,OAAO,KAAK,OAAQ,SAAS,EAAE;AAAA,EACvG;AACA,QAAM,SAAS,EAAE,MAAM,IAAI;AAC3B,MAAI,OAAO,SAAS,EAAG,QAAO;AAC9B,QAAM,OAAO,OAAO,CAAC,IAAI,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;AACjD,QAAM,OAAO,OAAO,WAAW,IAAK,OAAO,CAAC,IAAI,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,IAAK;AAC7E,MAAI;AACJ,MAAI,SAAS,MAAM;AACjB,aAAS;AAAA,EACX,OAAO;AACL,UAAM,OAAO,IAAI,KAAK,SAAS,KAAK;AACpC,QAAI,OAAO,EAAG,QAAO;AACrB,aAAS,CAAC,GAAG,MAAM,GAAG,MAAM,IAAI,EAAE,KAAK,GAAG,GAAG,GAAG,IAAI;AAAA,EACtD;AACA,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,QAAM,MAAM,CAAC;AACb,aAAW,KAAK,QAAQ;AACtB,QAAI,CAAC,mBAAmB,KAAK,CAAC,EAAG,QAAO;AACxC,QAAI,KAAK,SAAS,GAAG,EAAE,CAAC;AAAA,EAC1B;AACA,SAAO;AACT;AAeA,IAAM,gBACJ;AAKF,SAAS,kBAAkB,SAAS;AAClC,QAAM,SAAS,QAAQ,SAAS,MAAM,IAAI,UAAU,UAAU,IAAI,OAAO,IAAK,QAAQ,SAAS,CAAE;AACjG,SAAO,OAAO,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AACpD;AAkBO,SAAS,cAAc,MAAM,OAAO,OAAO;AAChD,QAAM,QAAQ,oBAAI,IAAI;AACtB,MAAI,OAAO,SAAS,YAAY,OAAO,UAAU,YAAY,UAAU,GAAI,QAAO,CAAC;AACnF,QAAM,SAAS,OAAO,KAAK;AAE3B,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS,aAAa,GAAG;AAClD,UAAM,QAAQ,MAAM,MAAM,GAAG;AAE7B,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,iBAAiB,kBAAkB,MAAM,CAAC,CAAC,CAAC,CAAC;AAAA,IACpE,QAAQ;AACN;AAAA,IACF;AAIA,QAAI,YAAY,QAAQ,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,EAAG;AAI/E,QAAI,CAAC,OAAO,UAAU,eAAe,KAAK,SAAS,KAAK,EAAG;AAE3D,UAAM,SAAS,QAAQ,KAAK;AAC5B,QAAI,OAAO,WAAW,YAAY,WAAW,OAAQ,OAAM,IAAI,KAAK;AAAA,EACtE;AAEA,SAAO,CAAC,GAAG,KAAK;AAClB;AAMA,IAAM,eAAe,oBAAI,IAAI;AAC7B,SAAS,sBAAsB,MAAM;AACnC,QAAM,MAAM,OAAO,KAAK,IAAI,EAAE,KAAK,EAAE,KAAK,GAAG;AAC7C,MAAI,aAAa,IAAI,GAAG,EAAG;AAC3B,eAAa,IAAI,GAAG;AACpB,UAAQ;AAAA,IACN,qCAAqC,GAAG;AAAA,EAE1C;AACF;AAIA,SAAS,cAAc,MAAM,UAAU;AACrC,MAAI;AACF,QAAI,KAAK,WAAW,UAAa,KAAK,SAAS,UAAa,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AACxF,4BAAsB,IAAI;AAAA,IAC5B;AACA,QAAI,KAAK,QAAQ;AACf,YAAM,WAAW,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,SAAS,CAAC,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,YAAY,CAAC;AAC7G,YAAM,SAAS,OAAO,SAAS,QAAQ,uBAAuB,EAAE,CAAC,KAAK,KAAK,EAAE,YAAY;AACzF,UAAI,CAAC,QAAQ,SAAS,MAAM,EAAG,QAAO;AAAA,IACxC;AACA,QAAI,KAAK,MAAM;AACb,YAAM,MAAM,OAAO,SAAS,QAAQ,oBAAoB,EAAE,CAAC,KAAK,GAAG;AACnE,UAAI,CAAC,YAAY,OAAO,KAAK,IAAI,GAAG,IAAI,MAAM,GAAG,EAAE,CAAC,CAAC,EAAG,QAAO;AAAA,IACjE;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAwBA,SAAS,iBAAiB,OAAO;AAC/B,QAAM,MAAM,OAAO,KAAK,EAAE,YAAY;AAGtC,SAAO,IAAI,SAAS,GAAG,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI;AAChD;AAUA,IAAM,eAAe;AAMrB,IAAM,aAAa;AAEnB,SAAS,YAAY,SAAS;AAC5B,QAAM,QAAQ,WAAW,KAAK,OAAO;AAKrC,SAAO,UAAU,QAAQ,aAAa,KAAK,MAAM,CAAC,CAAC;AACrD;AAEA,SAAS,YAAY,SAAS,MAAM;AAGlC,MAAI,YAAY,OAAO,GAAG;AACxB,UAAM,KAAK,WAAW,OAAO;AAC7B,WAAO,KAAK,GAAG,KAAK,IAAI,IAAI;AAAA,EAC9B;AACA,QAAM,SAAS,iBAAiB,IAAI;AACpC,QAAM,SAAS,iBAAiB,OAAO;AACvC,MAAI,OAAO,SAAS,GAAG,GAAG;AAMxB,UAAM,WAAW,OAAO,SAAS,IAAI;AACrC,UAAM,QAAQ,WAAW,OAAO,MAAM,GAAG,EAAE,IAAI,QAAQ,QAAQ,sBAAsB,MAAM,EAAE,QAAQ,OAAO,OAAO;AACnH,UAAM,KAAK,WAAW,OAAO,QAAQ,WAAW,gBAAgB,MAAM,IAAI;AAC1E,WAAO,KAAK,GAAG,KAAK,MAAM,IAAI;AAAA,EAChC;AACA,SAAO,WAAW;AACpB;AAIA,SAAS,qBAAqB,MAAM;AAClC,SAAO,OAAO,IAAI,EAAE,YAAY,EAAE,QAAQ,gBAAgB,EAAE;AAC9D;AAMA,SAAS,cAAc,UAAU;AAC/B,MAAI;AACF,UAAM,OAAO,qBAAqB,OAAO,SAAS,QAAQ,kBAAkB,EAAE,CAAC,KAAK,EAAE,CAAC;AACvF,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,YAAY,SAAS,QAAQ,oBAAoB,EAAE,CAAC;AAC1D,UAAM,YAAY,cAAc,UAAa,cAAc;AAC3D,UAAM,MAAM,YAAY,YAAY,SAAS,QAAQ,qBAAqB,EAAE,CAAC;AAC7E,QAAI,QAAQ,UAAa,QAAQ,KAAM,QAAO;AAC9C,UAAM,IAAI,OAAO,GAAG,EAAE,KAAK;AAC3B,QAAI,cAAc,MAAM,MAAM,EAAE,YAAY,MAAM,QAAS,QAAO;AAClE,UAAM,UAAU,YAAY,CAAC;AAC7B,QAAI,YAAY,KAAM,QAAO;AAC7B,WAAO,qBAAqB,OAAO,MAAM;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,OAAO;AAC1B,MAAI;AACF,WAAO,IAAI,IAAI,KAAK,EAAE,KAAK,YAAY;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,SAAS,oBAAoB,UAAU;AACrC,MAAI;AACF,UAAM,SAAS,OAAO,SAAS,QAAQ,iBAAiB,EAAE,CAAC,KAAK,CAAC;AACjE,QAAI,SAAS,OAAO,UAAU,IAAK,QAAO;AAC1C,UAAM,WAAW,SAAS,QAAQ,0BAA0B,EAAE,CAAC;AAC/D,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,OAAO,OAAO,SAAS,QAAQ,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE,YAAY;AAC/E,QAAI,CAAC,KAAM,QAAO;AAMlB,UAAM,MAAM,OAAO,QAAQ,EAAE,QAAQ,aAAa,EAAE,EAAE,QAAQ,OAAO,GAAG;AACxE,QAAI;AACJ,QAAI;AACF,eAAS,IAAI,IAAI,KAAK,YAAY,IAAI,EAAE,KAAK,YAAY;AAAA,IAC3D,QAAQ;AACN,aAAO;AAAA,IACT;AACA,WAAO,qBAAqB,MAAM,MAAM,qBAAqB,IAAI;AAAA,EACnE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,SAAS,+BAA+B,UAAU;AAChD,MAAI;AACF,UAAM,OAAO,OAAO,SAAS,QAAQ,kDAAkD,EAAE,CAAC,KAAK,EAAE,EAAE,YAAY;AAC/G,QAAI,SAAS,OAAQ,QAAO;AAC5B,UAAM,OAAO,OAAO,SAAS,QAAQ,6CAA6C,EAAE,CAAC,KAAK,EAAE;AAC5F,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI,SAAS,IAAK,QAAO;AACzB,QAAI,KAAK,YAAY,MAAM,OAAQ,QAAO;AAC1C,UAAM,SAAS,OAAO,SAAS,QAAQ,oBAAoB,EAAE,CAAC,KAAK,EAAE;AACrE,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO,SAAS;AAAA,EAClB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,WAAW,MAAM,OAAO,UAAU,UAAU;AAC1D,MAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,QAAI,SAAS,SAAS;AACpB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAIA,MAAI;AACJ,MAAI,OAAO,UAAU,UAAU;AAC7B,eAAW;AAAA,EACb,OAAO;AACL,QAAI;AACF,iBAAW,OAAO,KAAK;AAAA,IACzB,QAAQ;AACN,iBAAW;AAAA,IACb;AAAA,EACF;AAEA,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,YAAY;AAAA,IAErB,KAAK;AAEH,aAAO,aAAa;AAAA,IAEtB,KAAK;AAAA;AAAA,IAEL,KAAK,WAAW;AACd,YAAM,QAAQ,SAAS,YAAY;AACnC,YAAM,SAAS,OAAO,QAAQ,EAAE,YAAY;AAC5C,aAAO,MAAM,SAAS,MAAM;AAAA,IAC9B;AAAA,IAEA,KAAK,gBAAgB;AACnB,YAAM,QAAQ,SAAS,YAAY;AACnC,YAAM,SAAS,OAAO,QAAQ,EAAE,YAAY;AAC5C,aAAO,CAAC,MAAM,SAAS,MAAM;AAAA,IAC/B;AAAA,IAEA,KAAK,SAAS;AACZ,YAAM,QAAQ,WAAW,QAAQ;AACjC,UAAI,CAAC,OAAO;AACV,gCAAwB,QAAQ;AAChC,eAAO;AAAA,MACT;AACA,aAAO,MAAM,KAAK,QAAQ;AAAA,IAC5B;AAAA,IAEA,KAAK;AACH,aAAO,OAAO,QAAQ,IAAI,OAAO,QAAQ;AAAA,IAE3C,KAAK;AACH,aAAO,OAAO,QAAQ,IAAI,OAAO,QAAQ;AAAA,IAE3C,KAAK;AACH,aAAO,YAAY,UAAU,QAAQ,KAAK,QAAQ,GAAG,QAAQ;AAAA,IAE/D,KAAK;AACH,aAAO,YAAY,UAAU,wBAAwB,KAAK,QAAQ,GAAG,QAAQ;AAAA,IAE/E,KAAK;AACH,aAAO,YAAY,UAAU,CAAC,MAAM,QAAQ,KAAK,SAAS,KAAK,MAAM,IAAI,QAAQ;AAAA,IAEnF,KAAK;AACH,aAAO;AAAA,IAET,KAAK,YAAY;AACf,YAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC;AAClF,aAAO,IAAI,SAAS,QAAQ;AAAA,IAC9B;AAAA,IAEA,KAAK,gBAAgB;AACnB,YAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC;AAClF,aAAO,CAAC,IAAI,SAAS,QAAQ;AAAA,IAC/B;AAAA,IAEA,KAAK,kBAAkB;AACrB,UAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,QAAQ,QAAQ,GAAG;AACrD,eAAO;AAAA,MACT;AACA,YAAM,SAAS,SAAS,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC;AAC5C,aAAO,MAAM,KAAK,CAAC,MAAM,OAAO,SAAS,OAAO,CAAC,CAAC,CAAC;AAAA,IACrD;AAAA,IAEA,KAAK,YAAY;AACf,UAAI;AACF,cAAM,MAAM,IAAI,IAAI,QAAQ;AAC5B,eAAO,IAAI,aAAa;AAAA,MAC1B,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,KAAK;AAGH,aAAO,eAAe,cAAc,QAAQ,CAAC;AAAA,IAE/C,KAAK;AAAA;AAAA,IAEL,KAAK;AACH,aAAO,OAAO,KAAK,QAAQ;AAAA,IAE7B,KAAK;AAEH,aAAO,OAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,QAAQ;AAAA,IAEtD,KAAK,iBAAiB;AAGpB,YAAM,WAAW,SAAS,QAAQ,YAAY,EAAE;AAChD,aAAO,YAAY,UAAU,cAAc,KAAK,QAAQ,GAAG,QAAQ;AAAA,IACrE;AAAA,IAEA,KAAK;AAGH,aAAO,cAAc,UAAU,UAAU,OAAO,QAAQ,EAAE,SAAS;AAAA,IAErE,KAAK;AAIH,aAAO,cAAc,OAAO,QAAQ;AAAA;AAAA;AAAA;AAAA,IAKtC,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAMT;AACE,+BAAyB,IAAI;AAC7B,aAAO;AAAA,EACX;AACF;AAEO,IAAM,aAAN,MAAiB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB,oBAAI,IAAI;AAAA,EAE1B,YAAY,EAAE,WAAW,CAAC,GAAG,aAAa,CAAC,GAAG,iBAAiB,CAAC,GAAG,QAAQ,IAAI,CAAC,GAAG;AACjF,SAAK,SAAS;AACd,SAAK,cAAc;AACnB,SAAK,iBAAiB;AACtB,SAAK,WAAW;AAIhB,QAAI,MAAM,QAAQ,UAAU,KAAK,WAAW,KAAK,CAAC,MAAM,KAAK,MAAM,QAAQ,EAAE,OAAO,KAAK,CAAC,EAAE,OAAO,GAAG;AACpG,MAAAA;AAAA,QACE;AAAA,QACA;AAAA,MAEF;AAAA,IACF;AACA,QAAI,kBAAkB,OAAO,mBAAmB,YAAY,OAAO,KAAK,cAAc,EAAE,SAAS,GAAG;AAClG,MAAAA,UAAS,gCAAgC,qEAAqE;AAAA,IAChH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,KAAK;AAGhB,QAAI,OAAO,KAAK,UAAU,KAAK,SAAS,GAAG;AACzC;AAAA,IACF;AAGA,UAAM,MAAM,OAAO,IAAI,UAAU,IAAI,UAAU,OAAO,GAAG;AACzD,QAAI,KAAK,gBAAgB,IAAI,GAAG,GAAG;AACjC;AAAA,IACF;AACA,SAAK,gBAAgB,IAAI,GAAG;AAC5B,YAAQ,MAAM,uEAAuE,GAAG;AAAA,EAC1F;AAAA,EAEA,SAAS,KAAK;AACZ,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,sBAAgB,EAAE,GAAG,KAAK,GAAG,iBAAiB,GAAG,EAAE;AACnD,iBAAW,IAAI,gBAAgB,aAAa;AAAA,IAC9C,SAAS,KAAK;AACZ,WAAK,aAAa,GAAG;AACrB,aAAO,EAAE,SAAS,OAAO,MAAM,MAAM,SAAS,KAAK;AAAA,IACrD;AAEA,eAAW,QAAQ,KAAK,QAAQ;AAC9B,UAAI;AAGF,YAAI,KAAK,QAAQ,CAAC,cAAc,KAAK,MAAM,QAAQ,GAAG;AACpD;AAAA,QACF;AAEA,cAAM,aAAa,KAAK;AAExB,YAAI,CAAC,MAAM,QAAQ,UAAU,KAAK,WAAW,WAAW,GAAG;AACzD;AAAA,QACF;AAEA,YAAI,KAAK,cAAc,YAAY,QAAQ,GAAG;AAC5C,cAAI,KAAK,eAAe,eAAe,IAAI,GAAG;AAC5C;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS;AAAA,YACT;AAAA,YACA,SAAS,KAAK,WAAW,mCAAmC,KAAK,SAAS,KAAK,EAAE;AAAA,UACnF;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AAGZ,aAAK,aAAa,GAAG;AAAA,MACvB;AAAA,IACF;AAEA,WAAO,EAAE,SAAS,OAAO,MAAM,MAAM,SAAS,KAAK;AAAA,EACrD;AAAA,EAEA,cAAc,YAAY,UAAU;AAClC,UAAM,sBAAsB,WAAW,OAAO,OAAK,EAAE,SAAS;AAC9D,UAAM,yBAAyB,WAAW,OAAO,OAAK,CAAC,EAAE,SAAS;AAElE,QAAI,oBAAoB,SAAS,GAAG;AAClC,UAAI,gBAAgB;AAEpB,iBAAW,aAAa,qBAAqB;AAC3C,YAAI,KAAK,mBAAmB,WAAW,QAAQ,GAAG;AAChD;AAAA,QACF;AAAA,MACF;AAEA,UAAI,kBAAkB,oBAAoB,QAAQ;AAChD,eAAO;AAAA,MACT;AAAA,IACF;AAEA,eAAW,aAAa,wBAAwB;AAC9C,UAAI,KAAK,mBAAmB,WAAW,QAAQ,GAAG;AAChD,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,mBAAmB,WAAW,UAAU;AACtC,UAAM,EAAE,WAAW,OAAO,UAAU,IAAI;AAExC,QAAI,cAAc,WAAW,MAAM,QAAQ,UAAU,KAAK,GAAG;AAC3D,aAAO,KAAK,cAAc,UAAU,OAAO,QAAQ;AAAA,IACrD;AAIA,QAAI,SAAS,MAAM,SAAS,gBAAgB;AAC1C,aAAO,cAAc,QAAQ;AAAA,IAC/B;AAIA,QAAI,SAAS,MAAM,SAAS,cAAc;AACxC,aAAO,oBAAoB,QAAQ;AAAA,IACrC;AAKA,QAAI,SAAS,MAAM,SAAS,kBAAkB;AAC5C,aAAO,+BAA+B,QAAQ;AAAA,IAChD;AAKA,UAAM,SAAS,MAAM,QAAQ,SAAS,IAAI,YAAY,CAAC,SAAS;AAChE,UAAM,SAAS,OAAO,QAAQ,CAAC,MAAM,SAAS,QAAQ,CAAC,CAAC;AAExD,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO;AAAA,IACT;AAEA,aAAS,SAAS,QAAQ;AACxB,UAAI,WAAW;AACb,gBAAQ,SAAS,eAAe,WAAW,KAAK;AAAA,MAClD;AAIA,UAAI,MAAM,SAAS,mBAAmB;AACpC,YAAI,WAAW,MAAM,MAAM,OAAO,MAAM,OAAO,KAAK,GAAG;AACrD,iBAAO;AAAA,QACT;AACA;AAAA,MACF;AAMA,UAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,YAAI,wBAAwB,IAAI,MAAM,IAAI,GAAG;AAC3C,cAAI,WAAW,MAAM,MAAM,OAAO,MAAM,OAAO,KAAK,EAAG,QAAO;AAAA,QAChE,OAAO;AACL,qBAAW,QAAQ,kBAAkB,KAAK,GAAG;AAC3C,gBAAI,WAAW,MAAM,MAAM,MAAM,MAAM,OAAO,KAAK,EAAG,QAAO;AAAA,UAC/D;AAAA,QACF;AACA;AAAA,MACF;AAEA,UAAI,WAAW,MAAM,MAAM,OAAO,MAAM,OAAO,KAAK,GAAG;AACrD,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,KAAK,MAAM;AACxB,QAAI,CAAC,KAAK,eAAe,KAAK,YAAY,WAAW,GAAG;AACtD,aAAO;AAAA,IACT;AAEA,eAAW,aAAa,KAAK,aAAa;AACxC,UAAI,CAAC,MAAM,QAAQ,UAAU,OAAO,GAAG;AACrC;AAAA,MACF;AAEA,UAAI,UAAU,WAAW,UAAU,YAAY,KAAK,IAAI;AACtD;AAAA,MACF;AAEA,YAAM,WAAW,IAAI,gBAAgB,GAAG;AACxC,UAAI,KAAK,cAAc,UAAU,SAAS,QAAQ,GAAG;AACnD,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;ACn8BA,IAAMC,oBAAmB;AACzB,IAAMC,qBAAoB;AAG1B,IAAMC,mBAAkB;AAYjB,IAAM,kBAAN,MAAsB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EAEA;AAAA,EAEA,YAAY,EAAE,UAAU,SAAS,UAAU,MAAM,WAAW,WAAW,kBAAkB,IAAI,CAAC,GAAG;AAG/F,SAAK,aAAa,OAAO,SAAS,IAAI,IAAI,OAAO,SAAS,IAAI;AAC9D,SAAK,YAAY,YAAY,QAAQ,IAAI;AAEzC,SAAK,WAAW,YAAY,WAAW,QAAQ,IAAI,4BAA4BF,mBAAkB,eAAe;AAChH,SAAK,YAAY,OAAO,SAAS,QAAQ,KAAK,WAAW,IAAI,WAAWC;AACxE,SAAK,QAAQ,QAAQ;AACrB,SAAK,aAAa,aAAa;AAU/B,SAAK,qBAAqB,sBAAsB;AAChD,QAAI,CAAC,KAAK,WAAW;AACnB,YAAM,IAAI,MAAM,kFAAkF;AAAA,IACpG;AAAA,EACF;AAAA,EAEA,MAAM,WAAW;AACf,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,KAAK,UAAU,KAAK,eAAe,QAAQ,MAAM,KAAK,aAAa,KAAK,eAAe;AACzF,aAAO,KAAK;AAAA,IACd;AACA,UAAM,MAAM,GAAG,KAAK,QAAQ,UAAU,mBAAmB,KAAK,SAAS,CAAC;AACxE,QAAI;AAKF,YAAM,OAAO,MAAM;AAAA,QACjB,EAAE,WAAW,KAAK,YAAY,UAAU,KAAK,UAAU,WAAW,KAAK,WAAW;AAAA,QAClF;AAAA,MACF;AACA,YAAM,UAAU,EAAE,QAAQ,oBAAoB,GAAG,KAAK;AAQtD,UAAI,KAAK,sBAAsB,OAAO,KAAK,kBAAkB,UAAU;AACrE,gBAAQ,yBAAyB,IAAI;AAAA,MACvC;AACA,UAAI,KAAK,MAAO,SAAQ,eAAe,IAAI,KAAK;AAChD,YAAM,WAAW,MAAM,MAAM,KAAK,EAAE,QAAQ,OAAO,SAAS,QAAQ,YAAY,QAAQ,KAAK,UAAU,EAAE,CAAC;AAE1G,UAAI,SAAS,WAAW,KAAK;AAC3B,aAAK,OAAO,GAAG;AACf,eAAO,KAAK,UAAU,EAAE,SAAS,MAAM,aAAa,MAAM,MAAM,KAAK,OAAO,UAAU,CAAC,GAAG,YAAY,CAAC,GAAG,gBAAgB,CAAC,EAAE;AAAA,MAC/H;AACA,UAAI,CAAC,SAAS,IAAI;AAChB,eAAO,EAAE,SAAS,OAAO,OAAO,gBAAgB,SAAS,MAAM,IAAI,UAAU,CAAC,GAAG,YAAY,CAAC,GAAG,gBAAgB,CAAC,EAAE;AAAA,MACtH;AACA,YAAM,OAAO,MAAM,SAAS,KAAK;AAIjC,UAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAC1C,eAAO,EAAE,SAAS,OAAO,OAAO,iDAAiD,UAAU,CAAC,GAAG,YAAY,CAAC,GAAG,gBAAgB,CAAC,EAAE;AAAA,MACpI;AACA,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT,aAAa;AAAA,QACb,MAAM,SAAS,SAAS,MAAM,MAAM,KAAK;AAAA,QACzC,UAAU,KAAK;AAAA,QACf,YAAY,MAAM,QAAQ,KAAK,UAAU,IAAI,KAAK,aAAa,CAAC;AAAA,QAChE,gBAAgB,KAAK,kBAAkB,CAAC;AAAA,QACxC,GAAG,iBAAiB,IAAI;AAAA,MAC1B;AACA,WAAK,SAAS;AACd,WAAK,QAAQ,OAAO;AACpB,WAAK,OAAO,GAAG;AACf,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,IAAI,SAAS,iBAAiB,sBAAsB,IAAI;AAAA,QAC/D,UAAU,CAAC;AAAA,QAAG,YAAY,CAAC;AAAA,QAAG,gBAAgB,CAAC;AAAA,MACjD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAO,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,gBAAgB,KAAK,YAAY,KAAK,MAAM,KAAK,OAAO,IAAI,KAAK,YAAYC,gBAAe;AAAA,EACnG;AAAA,EAEA,aAAa;AACX,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,QAAQ;AAAA,EACf;AACF;AAGA,SAAS,iBAAiB,MAAM;AAC9B,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO,CAAC;AAC/C,QAAM,IAAI,KAAK,eAAe,KAAK;AACnC,SAAO,MAAM,WAAW,MAAM,YAAY,EAAE,aAAa,EAAE,IAAI,CAAC;AAClE;;;AC9HA,IAAM,iBAAiB,OAAO;AAI9B,eAAsB,iBAAiB,SAAS,UAAU,CAAC,GAAG;AAC5D,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,QAAM,UAAU,QAAQ,UAAU,OAAO,YAAY;AAErD,QAAM,UAAU,CAAC;AACjB,UAAQ,QAAQ,QAAQ,CAAC,OAAO,QAAQ;AACtC,YAAQ,IAAI,YAAY,CAAC,IAAI;AAAA,EAC/B,CAAC;AAED,QAAM,QAAQ,CAAC;AACf,aAAW,CAAC,KAAK,KAAK,KAAK,IAAI,cAAc;AAC3C,QAAI,OAAO,OAAO;AAChB,YAAM,GAAG,IAAI,MAAM,QAAQ,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,GAAG,KAAK,IAAI,CAAC,MAAM,GAAG,GAAG,KAAK;AAAA,IACtF,OAAO;AACL,YAAM,GAAG,IAAI;AAAA,IACf;AAAA,EACF;AAEA,MAAI,UAAU;AACd,MAAI,WAAW,SAAS,WAAW,QAAQ;AACzC,cAAU,MAAM,eAAe,SAAS,YAAY;AAAA,EACtD;AAEA,QAAM,cAAc,QAAQ,cAAc,KAAK;AAC/C,MAAI,OAAO,CAAC;AACZ,MAAI;AACJ,MAAI,SAAS;AACX,UAAM,SAAS,UAAU,SAAS,WAAW;AAC7C,WAAO,OAAO;AACd,YAAQ,OAAO;AAAA,EACjB;AAEA,QAAM,MAAM,IAAI,WAAW,IAAI;AAC/B,QAAM,YACJ,QAAQ,kBAAkB,KAAK,QAAQ,iBAAiB,KAAK,QAAQ,WAAW,KAAK;AAEvF,SAAO;AAAA,IACL;AAAA,IACA,KAAK;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,IAAI,UAAU,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK;AAAA,IACjC,SAAS,aAAa,QAAQ,MAAM;AAAA;AAAA;AAAA,IAGpC,UAAU;AAAA,EACZ;AACF;AAQA,eAAe,eAAe,SAAS,KAAK;AAK1C,MAAI;AACJ,MAAI;AACF,YAAQ,QAAQ,MAAM;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AAMA,QAAM,OAAO,MAAM;AACnB,MAAI,QAAQ,OAAO,KAAK,cAAc,YAAY;AAChD,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,SAAS,CAAC;AAChB,QAAI,WAAW;AACf,QAAI;AACF,iBAAS;AACP,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AACV,YAAI,CAAC,SAAS,YAAY,IAAK;AAC/B,cAAM,OAAO,KAAK,IAAI,MAAM,YAAY,MAAM,QAAQ;AACtD,eAAO,KAAK,SAAS,MAAM,aAAa,QAAQ,MAAM,SAAS,GAAG,IAAI,CAAC;AACvE,oBAAY;AAAA,MACd;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI;AACF,aAAO,IAAI,YAAY,EAAE,OAAO,aAAa,QAAQ,QAAQ,CAAC;AAAA,IAChE,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI;AACF,UAAM,OAAO,MAAM,MAAM,KAAK;AAC9B,WAAO,KAAK,SAAS,MAAM,KAAK,MAAM,GAAG,GAAG,IAAI;AAAA,EAClD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,QAAQ,OAAO;AACnC,QAAM,MAAM,IAAI,WAAW,KAAK;AAChC,MAAI,SAAS;AACb,aAAW,KAAK,QAAQ;AACtB,QAAI,IAAI,GAAG,MAAM;AACjB,cAAU,EAAE;AAAA,EACd;AACA,SAAO;AACT;AAWO,SAAS,UAAU,SAAS,aAAa;AAC9C,QAAM,KAAK,OAAO,eAAe,EAAE,EAAE,YAAY;AACjD,QAAM,SAAS,GAAG,SAAS,kBAAkB,KAAK,WAAW,KAAK,EAAE;AACpE,QAAM,SAAS,GAAG,SAAS,mCAAmC;AAC9D,QAAM,cAAc,GAAG,SAAS,qBAAqB;AAErD,QAAM,cAAc,OAAO,MAAM,GAAG,WAAW,YAAY,KAAK,GAAG,SAAS,YAAY,KAAK,GAAG,SAAS,OAAO;AAEhH,MAAI,aAAa;AACf,UAAM,WAAW,2BAA2B,KAAK,WAAW,IAAI,CAAC;AACjE,QAAI,SAAU,QAAO,eAAe,SAAS,QAAQ;AACrD,WAAO,EAAE,MAAM,CAAC,GAAG,OAAO,OAAU;AAAA,EACtC;AACA,MAAI,QAAQ;AACV,UAAM,OAAO,CAAC;AACd,eAAW,CAAC,GAAG,CAAC,KAAK,IAAI,gBAAgB,OAAO,EAAG,MAAK,CAAC,IAAI,KAAK,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI;AACjG,WAAO,EAAE,MAAM,OAAO,OAAU;AAAA,EAClC;AACA,MAAI,UAAU,aAAa;AACzB,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,OAAO;AACjC,UAAI,UAAU,OAAO,WAAW,SAAU,QAAO,EAAE,MAAM,QAAQ,OAAO,OAAU;AAAA,IACpF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO,EAAE,MAAM,CAAC,GAAG,OAAO,OAAU;AACtC;AAEO,SAAS,eAAe,SAAS,UAAU;AAChD,QAAM,OAAO,CAAC;AACd,QAAM,QAAQ,CAAC;AACf,aAAW,QAAQ,QAAQ,MAAM,OAAO,QAAQ,GAAG;AAEjD,UAAM,MAAM,aAAa,KAAK,IAAI;AAClC,QAAI,CAAC,IAAK;AACV,UAAM,aAAa,KAAK,MAAM,GAAG,IAAI,KAAK;AAC1C,UAAM,cAAc,gCAAgC,KAAK,UAAU,IAAI,CAAC;AACxE,QAAI,CAAC,YAAa;AAClB,UAAM,OAAO,kBAAkB,KAAK,WAAW,IAAI,CAAC;AACpD,QAAI,QAAQ,KAAM;AAClB,UAAM,UAAU,KAAK,MAAM,IAAI,QAAQ,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,UAAU,EAAE;AAC1E,UAAM,WAAW,sBAAsB,KAAK,WAAW,IAAI,CAAC;AAC5D,QAAI,aAAa,QAAW;AAI1B,YAAM,WAAW,+BAA+B,KAAK,UAAU,IAAI,CAAC,GAAG,KAAK,KAAK;AACjF,YAAM,OAAO,EAAE,UAAU,MAAM,UAAU,QAAQ;AACjD,YAAM,IAAI,IAAI,QAAQ,QAAQ,CAAC,EAAE,OAAO,MAAM,IAAI,GAAG,IAAI,IAAI;AAAA,IAC/D,OAAO;AACL,WAAK,IAAI,IAAI,QAAQ,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,IAAI;AAAA,IAC/D;AAAA,EACF;AACA,SAAO,EAAE,MAAM,MAAM;AACvB;AAEA,SAAS,aAAa,QAAQ;AAC5B,QAAM,UAAU,CAAC;AACjB,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AACA,aAAW,QAAQ,OAAO,MAAM,GAAG,GAAG;AACpC,UAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,QAAI,QAAQ,IAAI;AACd;AAAA,IACF;AACA,YAAQ,KAAK,MAAM,GAAG,GAAG,EAAE,KAAK,CAAC,IAAI,KAAK,MAAM,MAAM,CAAC,EAAE,KAAK;AAAA,EAChE;AACA,SAAO;AACT;;;ACzMO,SAAS,gBAAgB,KAAK,UAAU,IAAI;AACjD,QAAM,UAAU,IAAI,UAAU,OAAO,YAAY;AAEjD,QAAM,UAAU,CAAC;AACjB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,WAAW,CAAC,CAAC,GAAG;AAC5D,YAAQ,IAAI,YAAY,CAAC,IAAI,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI;AAAA,EACzE;AAIA,QAAM,OAAO,QAAQ,QAAQ;AAC7B,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,EAAE;AAAA,EAChD,QAAQ;AACN,QAAI;AACF,YAAM,IAAI,IAAI,IAAI,OAAO,KAAK,kBAAkB;AAAA,IAClD,QAAQ;AACN,YAAM,IAAI,IAAI,mBAAmB;AAAA,IACnC;AAAA,EACF;AAEA,QAAM,QAAQ,CAAC;AACf,aAAW,CAAC,KAAK,KAAK,KAAK,IAAI,cAAc;AAC3C,QAAI,OAAO,OAAO;AAChB,YAAM,GAAG,IAAI,MAAM,QAAQ,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,GAAG,KAAK,IAAI,CAAC,MAAM,GAAG,GAAG,KAAK;AAAA,IACtF,OAAO;AACL,YAAM,GAAG,IAAI;AAAA,IACf;AAAA,EACF;AAEA,QAAM,cAAc,QAAQ,cAAc,KAAK;AAC/C,MAAI,OAAO,CAAC;AACZ,MAAI;AACJ,MAAI,SAAS;AAGX,UAAM,SAAS,UAAU,SAAS,WAAW;AAC7C,WAAO,OAAO;AACd,YAAQ,OAAO;AAAA,EACjB;AAEA,QAAM,MAAM,IAAI,WAAW,IAAI;AAC/B,QAAM,YACJ,QAAQ,kBAAkB,KAAK,QAAQ,iBAAiB,KAAK,QAAQ,WAAW,KAAK;AAEvF,SAAO;AAAA,IACL;AAAA,IACA,KAAK;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,IAAI,UAAU,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK,KAAM,IAAI,UAAU,IAAI,OAAO,iBAAkB;AAAA,IAClF,SAASC,cAAa,QAAQ,MAAM;AAAA;AAAA,IAEpC,UAAU;AAAA,EACZ;AACF;AAEA,SAASA,cAAa,QAAQ;AAC5B,QAAM,UAAU,CAAC;AACjB,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AACA,aAAW,QAAQ,OAAO,MAAM,GAAG,GAAG;AACpC,UAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,QAAI,QAAQ,IAAI;AACd;AAAA,IACF;AACA,YAAQ,KAAK,MAAM,GAAG,GAAG,EAAE,KAAK,CAAC,IAAI,KAAK,MAAM,MAAM,CAAC,EAAE,KAAK;AAAA,EAChE;AACA,SAAO;AACT;;;ACvEA,eAAsB,mBAAmB,EAAE,aAAa,SAAS,QAAQ,YAAY,MAAM,QAAQ,WAAW,IAAI,CAAC,GAAG;AACpH,QAAM,WAAW,CAAC;AAClB,MAAI,OAAO,gBAAgB,WAAY,QAAO,MAAM;AAAA,EAAC;AACrD,QAAM,SAAS,IAAI,KAAK,cAAc,CAAC,GAAG,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC;AAE7E,QAAM,QAAQ,CAAC,KAAK,MAAM,WAAW;AACnC,QAAI,CAAC,YAAY,KAAK,MAAM,MAAM,EAAG,QAAO;AAI5C,WAAO,SAAS,EAAE,KAAK,MAAM,OAAO,GAAG,eAAe;AACtD,WAAO;AAAA,EACT;AAOA,MAAI,SAAS;AACb,MAAI,WAAW;AACb,QAAI;AACF,YAAM,gBAAgB,WAAW,MAAM,OAAO,KAAU,GAAG;AAC3D,YAAM,EAAE,KAAK,IAAI,MAAM,OAAO,KAAU;AACxC,UAAI,OAAO,kBAAkB,cAAc,OAAO,SAAS,YAAY;AACrE,iBAAS,EAAE,QAAQ,eAAe,MAAM,UAAU,CAAC,MAAM,OAAO,IAAI,OAAO,CAAC,EAAE,YAAY,CAAC,EAAE;AAAA,MAC/F;AAAA,IACF,QAAQ;AACN,eAAS;AAAA,IACX;AAAA,EACF;AAKA,QAAM,OAAO,CAAC,QAAQ,WAAW,OAAO,QAAQ,EAAE,OAAO,UAAU,QAAQ,OAAO,GAAG,QAAQ;AAG7F,QAAM,uBAAuB,CAAC,KAAK,MAAM,WACvC,IAAI,QAAQ,CAAC,YAAY;AACvB,QAAI,CAAC,QAAQ;AAEX,UAAI,QAAQ,UAAW,MAAK,wBAAwB,EAAE,KAAK,CAAC;AAC5D,aAAO,QAAQ,KAAK;AAAA,IACtB;AACA,QAAI,CAAC,QAAQ,OAAO,KAAK,IAAI,MAAM,KAAK,OAAO,SAAS,IAAI,EAAG,QAAO,QAAQ,KAAK;AACnF,QAAI;AACF,aAAO,OAAO,MAAM,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,cAAc;AACrD,YAAI,OAAO,CAAC,MAAM,QAAQ,SAAS,GAAG;AAAE,eAAK,mBAAmB,EAAE,KAAK,CAAC;AAAG,iBAAO,QAAQ,KAAK;AAAA,QAAG;AAClG,mBAAW,KAAK,WAAW;AACzB,gBAAM,KAAK,KAAK,OAAO,MAAM,WAAW,EAAE,UAAU;AACpD,cAAI,MAAM,MAAM,KAAK,IAAI,MAAM,EAAG,QAAO,QAAQ,IAAI;AAAA,QACvD;AACA,gBAAQ,KAAK;AAAA,MACf,CAAC;AAAA,IACH,QAAQ;AACN,WAAK,mBAAmB,EAAE,KAAK,CAAC;AAChC,cAAQ,KAAK;AAAA,IACf;AAAA,EACF,CAAC;AAGH,QAAM,gBAAgB,WAAW;AACjC,MAAI,OAAO,kBAAkB,cAAc,CAAC,cAAc,qBAAqB;AAC7E,UAAM,oBAAoB,oBAAI,IAAI,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG,CAAC;AAC3D,UAAM,gBAAgB;AAItB,UAAM,YAAY,OAAO,GAAG,WAAW;AACrC,UAAI,OAAO;AACX,UAAI;AACF,eAAO,IAAI,IAAI,CAAC,EAAE;AAAA,MACpB,QAAQ;AACN,eAAO;AAAA,MACT;AACA,UAAI,MAAM,GAAG,MAAM,MAAM,KAAM,MAAM,qBAAqB,GAAG,MAAM,MAAM,GAAI;AAC3E,cAAM,IAAI,MAAM,mEAAmE,QAAQ,CAAC,EAAE;AAAA,MAChG;AAAA,IACF;AAEA,UAAM,UAAU,OAAO,OAAO,SAAS;AACrC,UAAI;AACJ,UAAI;AACF,cAAM,IAAI,QAAQ,OAAO,EAAE,GAAI,QAAQ,CAAC,GAAI,UAAU,SAAS,CAAC;AAAA,MAClE,QAAQ;AACN,eAAO,cAAc,OAAO,IAAI;AAAA,MAClC;AACA,YAAM,iBAAkB,QAAQ,KAAK,YAAc,SAAS,MAAM,YAAa;AAE/E,UAAI,MAAM,IAAI;AACd,UAAI,SAAS,IAAI;AACjB,YAAM,UAAU,KAAK,MAAM;AAG3B,UAAI,mBAAmB,SAAU,QAAO,cAAc,OAAO,IAAI;AAKjE,YAAM,UAAU,IAAI,QAAQ,IAAI,OAAO;AACvC,YAAM,SAAS,IAAI;AACnB,UAAI,OAAO,WAAW,SAAS,WAAW,SAAS,SAAY,MAAM,IAAI,MAAM,EAAE,YAAY;AAE7F,eAAS,MAAM,KAAK,OAAO;AACzB,cAAM,OAAO,MAAM;AAAA,UACjB,QAAQ,IAAI,MAAM,IAAI,QAAQ,KAAK,EAAE,QAAQ,SAAS,MAAM,UAAU,UAAU,OAAO,CAAC;AAAA,QAC1F;AACA,cAAM,WAAW,kBAAkB,IAAI,KAAK,MAAM,IAAI,KAAK,QAAQ,IAAI,UAAU,IAAI;AACrF,YAAI,CAAC,SAAU,QAAO;AACtB,YAAI,OAAO,cAAe,OAAM,IAAI,MAAM,4DAA4D;AAEtG,cAAM,OAAO,IAAI,IAAI,UAAU,GAAG,EAAE;AAEpC,YAAI,KAAK,WAAW,QAAS,KAAK,WAAW,OAAO,KAAK,WAAW,QAAQ,WAAW,QAAS;AAC9F,mBAAS;AACT,iBAAO;AACP,kBAAQ,OAAO,cAAc;AAC7B,kBAAQ,OAAO,gBAAgB;AAAA,QACjC;AAEA,YAAI,IAAI,IAAI,IAAI,EAAE,WAAW,IAAI,IAAI,GAAG,EAAE,QAAQ;AAChD,kBAAQ,OAAO,eAAe;AAC9B,kBAAQ,OAAO,QAAQ;AAAA,QACzB;AACA,cAAM,UAAU,MAAM,MAAM;AAC5B,cAAM;AAAA,MACR;AAAA,IACF;AACA,YAAQ,sBAAsB;AAC9B,eAAW,QAAQ;AACnB,aAAS,KAAK,MAAM;AAClB,UAAI,WAAW,UAAU,QAAS,YAAW,QAAQ;AAAA,IACvD,CAAC;AAAA,EACH;AAGA,aAAW,cAAc,CAAC,aAAa,YAAY,GAAG;AACpD,QAAI;AACF,YAAM,MAAM,MAAM,OAAO;AACzB,YAAM,UAAU,gBAAgB,IAAI,WAAW,KAAK,OAAO,MAAM;AACjE,UAAI,QAAS,UAAS,KAAK,OAAO;AAAA,IACpC,QAAQ;AAAA,IAER;AAAA,EACF;AASA,SAAO,MAAM;AACX,eAAW,WAAW,UAAU;AAC9B,UAAI;AACF,gBAAQ;AAAA,MACV,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAGA,SAAS,gBAAgB,MAAM,OAAO,QAAQ;AAC5C,MAAI,CAAC,QAAQ,OAAO,KAAK,YAAY,cAAc,KAAK,oBAAqB,QAAO;AACpF,QAAM,kBAAkB,KAAK;AAC7B,QAAM,cAAc,KAAK;AAEzB,QAAM,OAAO,CAAC,aACZ,YAAa,MAAM;AACjB,UAAM,SAAS,kBAAkB,IAAI;AACrC,QAAI,UAAU,MAAM,OAAO,KAAK,OAAO,MAAM,OAAO,MAAM,GAAG;AAC3D,YAAM,IAAI,MAAM,mEAAmE,OAAO,QAAQ,OAAO,GAAG,EAAE;AAAA,IAChH;AAGA,QAAI,UAAU,UAAU,OAAO,QAAQ,OAAO,KAAK,OAAO,IAAI,MAAM,KAAK,CAAC,OAAO,SAAS,OAAO,IAAI,GAAG;AACtG,UAAI;AACF,eAAO,oBAAoB,MAAM,QAAQ,OAAO,OAAO,MAAM;AAAA,MAC/D,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO,SAAS,MAAM,MAAM,IAAI;AAAA,EAClC;AAEF,QAAM,iBAAiB,KAAK,eAAe;AAC3C,OAAK,UAAU;AACf,MAAI;AACJ,MAAI,OAAO,gBAAgB,YAAY;AACrC,iBAAa,KAAK,WAAW;AAC7B,SAAK,MAAM;AAAA,EACb;AACA,OAAK,sBAAsB;AAE3B,SAAO,MAAM;AAGX,QAAI,KAAK,YAAY,eAAgB,MAAK,UAAU;AACpD,QAAI,cAAc,KAAK,QAAQ,WAAY,MAAK,MAAM;AACtD,WAAO,KAAK;AAAA,EACd;AACF;AAGA,SAAS,kBAAkB,MAAM;AAC/B,QAAM,QAAQ,KAAK,CAAC;AACpB,MAAI;AACF,QAAI,OAAO,UAAU,YAAY,iBAAiB,KAAK;AACrD,YAAM,MAAM,IAAI,IAAI,OAAO,KAAK,CAAC;AACjC,YAAM,OAAO,KAAK,KAAK,CAAC,MAAM,KAAK,OAAO,MAAM,YAAY,EAAE,aAAa,IAAI;AAC/E,aAAO,EAAE,KAAK,IAAI,MAAM,MAAM,IAAI,UAAU,QAAS,QAAQ,KAAK,UAAW,MAAM;AAAA,IACrF;AACA,QAAI,SAAS,OAAO,UAAU,UAAU;AAEtC,YAAM,OAAO,cAAc,MAAM,YAAY,MAAM,IAAI,KAAK;AAC5D,YAAM,WAAW,MAAM,YAAY;AACnC,YAAM,OAAO,MAAM,OAAO,IAAI,MAAM,IAAI,KAAK;AAC7C,YAAM,OAAO,MAAM,QAAQ;AAC3B,aAAO,EAAE,KAAK,GAAG,QAAQ,KAAK,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,MAAM,QAAQ,MAAM,UAAU,MAAM;AAAA,IAC1F;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAKA,SAAS,cAAc,KAAK;AAC1B,QAAM,OAAO,OAAO,OAAO,EAAE,EAAE,KAAK;AACpC,QAAM,YAAY,gBAAgB,KAAK,IAAI;AAC3C,MAAI,UAAW,QAAO,UAAU,CAAC;AACjC,OAAK,KAAK,MAAM,IAAI,KAAK,CAAC,GAAG,SAAS,EAAG,QAAO;AAChD,SAAO,KAAK,MAAM,GAAG,EAAE,CAAC;AAC1B;AAKO,SAAS,eAAe,WAAW,QAAQ,OAAO;AACvD,aAAW,KAAK,aAAa,CAAC,GAAG;AAC/B,UAAM,KAAK,KAAK,OAAO,MAAM,WAAW,EAAE,UAAU;AACpD,QAAI,MAAM,MAAM,OAAO,KAAK,IAAI,OAAO,MAAM,EAAG,QAAO;AAAA,EACzD;AACA,SAAO;AACT;AAKA,SAAS,oBAAoB,MAAM,QAAQ,OAAO,QAAQ;AACxD,QAAM,kBAAkB,CAAC,UAAU,SAAS,aAAa;AACvD,QAAI,OAAO;AACX,QAAI,KAAK;AACT,QAAI,OAAO,SAAS,YAAY;AAC9B,WAAK;AACL,aAAO,CAAC;AAAA,IACV;AACA,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO,CAAC;AAC/C,QAAI;AACF,aAAO,UAAU,EAAE,GAAG,MAAM,KAAK,KAAK,GAAG,CAAC,KAAK,cAAc;AAC3D,YAAI,IAAK,QAAO,GAAG,GAAG;AACtB,cAAM,OAAO,MAAM,QAAQ,SAAS,IAAI,YAAY,CAAC;AACrD,cAAM,UAAU,eAAe,MAAM,QAAQ,KAAK;AAClD,YAAI,SAAS;AACX,iBAAO,GAAG,IAAI,MAAM,mEAAmE,OAAO,IAAI,gBAAgB,OAAO,EAAE,CAAC;AAAA,QAC9H;AACA,YAAI,KAAK,IAAK,QAAO,GAAG,MAAM,IAAI;AAClC,cAAM,QAAQ,KAAK,CAAC;AACpB,YAAI,CAAC,MAAO,QAAO,GAAG,IAAI,MAAM,iCAAiC,QAAQ,EAAE,CAAC;AAC5E,eAAO,GAAG,MAAM,MAAM,SAAS,MAAM,MAAM;AAAA,MAC7C,CAAC;AAAA,IACH,QAAQ;AAEN,UAAI;AACF,eAAO,UAAU,MAAM,EAAE;AAAA,MAC3B,QAAQ;AACN,WAAG,IAAI,MAAM,iCAAiC,QAAQ,EAAE,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AACA,SAAO,mBAAmB,MAAM,eAAe;AACjD;AAIA,SAAS,mBAAmB,MAAM,QAAQ;AACxC,QAAM,QAAQ,KAAK,CAAC;AACpB,MAAI,SAAS,OAAO,UAAU,YAAY,EAAE,iBAAiB,MAAM;AACjE,WAAO,CAAC,EAAE,GAAG,OAAO,OAAO,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,EAChD;AACA,QAAM,OAAO,KAAK,MAAM,CAAC;AACzB,QAAM,SAAS,KAAK,UAAU,CAAC,MAAM,KAAK,OAAO,MAAM,YAAY,EAAE,aAAa,IAAI;AACtF,MAAI,WAAW,IAAI;AACjB,UAAM,OAAO,CAAC,GAAG,IAAI;AACrB,SAAK,MAAM,IAAI,EAAE,GAAG,KAAK,MAAM,GAAG,OAAO;AACzC,WAAO,CAAC,OAAO,GAAG,IAAI;AAAA,EACxB;AACA,QAAM,QAAQ,KAAK,UAAU,CAAC,MAAM,OAAO,MAAM,UAAU;AAC3D,MAAI,UAAU,GAAI,QAAO,CAAC,OAAO,EAAE,OAAO,GAAG,GAAG,IAAI;AACpD,SAAO,CAAC,OAAO,GAAG,KAAK,MAAM,GAAG,KAAK,GAAG,EAAE,OAAO,GAAG,GAAG,KAAK,MAAM,KAAK,CAAC;AAC1E;;;ACnTO,IAAM,yBAAyB;AAAA,EACpC;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,CAAC,aAAa;AAAA,IACzB,SAAS,CAAC,EAAE,WAAW,iBAAiB,OAAO,EAAE,MAAM,SAAS,OAAO,gEAAgE,EAAE,CAAC;AAAA,EAC5I;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,CAAC,QAAQ,MAAM;AAAA,IAC1B,SAAS,CAAC,EAAE,WAAW,iBAAiB,OAAO,EAAE,MAAM,SAAS,OAAO,oCAAoC,EAAE,CAAC;AAAA,EAChH;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,CAAC,MAAM;AAAA,IAClB,SAAS,CAAC,EAAE,WAAW,iBAAiB,OAAO,EAAE,MAAM,SAAS,OAAO,gCAAgC,EAAE,CAAC;AAAA,EAC5G;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKR,WAAW,CAAC,YAAY,YAAY,QAAQ,QAAQ,QAAQ,QAAQ,eAAe,UAAU,OAAO,WAAW,SAAS,MAAM;AAAA,IAC9H,SAAS,CAAC,EAAE,WAAW,iBAAiB,OAAO,EAAE,MAAM,SAAS,OAAO,gQAAgQ,EAAE,CAAC;AAAA,EAC5U;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAYR,WAAW,CAAC,YAAY;AAAA,IACxB,SAAS,CAAC,EAAE,WAAW,iBAAiB,OAAO,EAAE,MAAM,SAAS,OAAO,qEAAqE,EAAE,CAAC;AAAA,EACjJ;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWR,WAAW,CAAC,KAAK;AAAA,IACjB,SAAS,CAAC,EAAE,WAAW,iBAAiB,OAAO,EAAE,MAAM,oBAAoB,OAAO,QAAQ,OAAO,eAAe,EAAE,CAAC;AAAA,EACrH;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,CAAC,WAAW,YAAY,SAAS,SAAS,MAAM;AAAA,IAC3D,SAAS,CAAC,EAAE,WAAW,iBAAiB,OAAO,EAAE,MAAM,SAAS,OAAO,mGAAmG,EAAE,CAAC;AAAA,EAC/K;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA;AAAA,IAGR,SAAS,CAAC,EAAE,WAAW,iBAAiB,OAAO,EAAE,MAAM,SAAS,OAAO,sCAAsC,EAAE,CAAC;AAAA,EAClH;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,CAAC,YAAY,aAAa,OAAO,QAAQ,QAAQ,gBAAgB,YAAY;AAAA,IACxF,SAAS,CAAC,EAAE,WAAW,iBAAiB,OAAO,EAAE,MAAM,SAAS,OAAO,iJAAiJ,EAAE,CAAC;AAAA,EAC7N;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA;AAAA;AAAA,IAIR,WAAW,CAAC,aAAa,uBAAuB,WAAW,cAAc,UAAU,QAAQ,WAAW,QAAQ,QAAQ,MAAM;AAAA,IAC5H,SAAS,CAAC,EAAE,WAAW,iBAAiB,OAAO,EAAE,MAAM,SAAS,OAAO,+KAA+K,EAAE,CAAC;AAAA,EAC3P;AACF;AAGO,IAAM,uBAAuB;AAAA,EAClC;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS,CAAC,EAAE,WAAW,eAAe,OAAO,EAAE,MAAM,gBAAgB,EAAE,CAAC;AAAA,EAC1E;AACF;;;AC5IA,SAAS,WAAW,GAAG;AACrB,SAAO,OAAO,CAAC,EAAE,QAAQ,YAAY,CAAC,OAAO,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ,GAAE,CAAC,CAAE;AAC1H;AAGO,SAAS,gBAAgB,EAAE,MAAM,IAAI,OAAO,aAAa,IAAI,CAAC,GAAG;AACtE,QAAM,UAAU,WAAW,GAAG;AAC9B,QAAM,WAAW,WAAW,QAAQ,YAAY;AAChD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iCAkBwB,OAAO;AAAA;AAAA;AAAA;AAAA,uFAI+C,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoD/F;;;AC1EA,IAAI;AACJ,eAAe,SAAS;AACtB,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI;AACF,UAAM,CAAC,IAAI,IAAI,IAAI,MAAM,QAAQ,IAAI,CAAC,OAAO,IAAS,GAAG,OAAO,MAAW,CAAC,CAAC;AAC7E,YAAQ;AAAA,MACN,cAAc,GAAG;AAAA,MACjB,eAAe,GAAG;AAAA,MAClB,WAAW,GAAG;AAAA,MACd,MAAM,KAAK;AAAA,IACb;AAAA,EACF,QAAQ;AACN,YAAQ;AAAA,EACV;AACA,SAAO;AACT;AAEO,SAAS,UAAU,UAAU,CAAC,GAAG;AACtC,MAAI,MAAM;AACV,QAAM,UAAU,YAAY,OAAO;AACnC,SAAO;AAAA,IACL,MAAM,OAAO;AACX,UAAI,IAAK,QAAO;AAChB,YAAM,MAAM,MAAM,QAAQ,KAAK;AAC/B,UAAI,IAAK,OAAM;AACf,aAAO;AAAA,IACT;AAAA,IACA,MAAM,MAAM,KAAK;AACf,UAAI,IAAK,OAAM;AACf,YAAM,QAAQ,MAAM,GAAG;AAAA,IACzB;AAAA,EACF;AACF;AAIA,SAAS,YAAY,SAAS;AAC5B,QAAM,UAAU,QAAQ;AACxB,MAAI,WAAW,OAAO,QAAQ,SAAS,cAAc,OAAO,QAAQ,UAAU,YAAY;AACxF,WAAO;AAAA,MACL,MAAM,YAAY;AAChB,YAAI;AACF,iBAAO,WAAW,MAAM,QAAQ,KAAK,CAAC;AAAA,QACxC,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MACA,OAAO,OAAO,QAAQ;AACpB,YAAI;AACF,gBAAM,QAAQ,MAAM,GAAG;AAAA,QACzB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,MAAM,QAAQ;AACpB,SAAO;AAAA,IACL,MAAM,YAAY,UAAU,GAAG;AAAA,IAC/B,OAAO,OAAO,QAAQ,WAAW,KAAK,GAAG;AAAA,EAC3C;AACF;AAEA,eAAe,WAAW,KAAK,KAAK;AAClC,MAAI,CAAC,IAAK;AACV,QAAM,KAAK,MAAM,OAAO;AACxB,MAAI,CAAC,GAAI;AACT,MAAI;AACF,OAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACrC,OAAG,cAAc,GAAG,KAAK,KAAK,uBAAuB,GAAG,KAAK,UAAU,GAAG,CAAC;AAAA,EAC7E,QAAQ;AAAA,EAER;AACF;AAEA,eAAe,UAAU,KAAK;AAC5B,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,KAAK,MAAM,OAAO;AACxB,MAAI,CAAC,GAAI,QAAO;AAChB,MAAI;AACF,WAAO,WAAW,KAAK,MAAM,GAAG,aAAa,GAAG,KAAK,KAAK,uBAAuB,GAAG,MAAM,CAAC,CAAC;AAAA,EAC9F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,WAAW,OAAO;AAChC,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,MAAI,MAAM,UAAU,OAAO,MAAM,WAAW,UAAU;AACpD,WAAO,EAAE,QAAQ,MAAM,QAAQ,MAAM,MAAM,QAAQ,KAAK;AAAA,EAC1D;AACA,MAAI,MAAM,QAAQ,MAAM,QAAQ,KAAK,MAAM,QAAQ,MAAM,UAAU,GAAG;AACpE,WAAO,EAAE,QAAQ,OAAO,MAAM,KAAK;AAAA,EACrC;AACA,SAAO;AACT;;;ACpFO,IAAM,UAAU,OAAO,OAAO;AAAA,EACnC,KAAK,OAAO,OAAO,EAAE,OAAO,MAAM,CAAC;AAAA,EACnC,KAAK,OAAO,OAAO,EAAE,OAAO,MAAM,CAAC;AAAA,EACnC,OAAO,OAAO,OAAO,EAAE,OAAO,MAAM,CAAC;AAAA,EACrC,OAAO,OAAO,OAAO,EAAE,OAAO,OAAO,OAAO,KAAK,CAAC;AAAA,EAElD,KAAK,OAAO,OAAO,EAAE,OAAO,MAAM,MAAM,OAAO,UAAU,KAAK,CAAC;AAAA,EAC/D,MAAM,OAAO,OAAO,EAAE,OAAO,MAAM,MAAM,OAAO,UAAU,KAAK,CAAC;AAAA,EAChE,SAAS,OAAO,OAAO,EAAE,OAAO,MAAM,MAAM,OAAO,UAAU,KAAK,CAAC;AAAA,EACnE,QAAQ,OAAO,OAAO,EAAE,OAAO,MAAM,MAAM,OAAO,UAAU,KAAK,CAAC;AAAA;AAAA;AAAA,EAIlE,OAAO,OAAO,OAAO;AAAA,IACnB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,IACV,uBAAuB,OAAO,OAAO,CAAC,WAAW,YAAY,MAAM,CAAC;AAAA,EACtE,CAAC;AAAA,EAED,QAAQ,OAAO,OAAO;AAAA,IACpB,OAAO;AAAA,IACP,MAAM,OAAO,OAAO;AAAA,MAClB;AAAA,MAAe;AAAA,MAAkB;AAAA,MAAmB;AAAA,MAAgB;AAAA,MACpE;AAAA,MAAe;AAAA,MAAM;AAAA,MAAgB;AAAA,IACvC,CAAC;AAAA,IACD,cAAc,OAAO,OAAO,CAAC,OAAO,CAAC;AAAA,EACvC,CAAC;AAAA,EAED,UAAU,OAAO,OAAO;AAAA,IACtB,OAAO;AAAA,IACP,MAAM,OAAO,OAAO,CAAC,UAAU,QAAQ,SAAS,CAAC;AAAA,IACjD,cAAc,OAAO,OAAO,CAAC,SAAS,CAAC;AAAA,EACzC,CAAC;AAAA,EAED,QAAQ,OAAO,OAAO,EAAE,OAAO,MAAM,MAAM,OAAO,OAAO,CAAC,OAAO,QAAQ,QAAQ,CAAC,EAAE,CAAC;AACvF,CAAC;AAQM,IAAM,cAAc,OAAO,OAAO;AAAA,EACvC;AAAA,EAAkB;AAAA,EAAmB;AAAA,EAAY;AAAA,EAAkB;AAAA,EACnE;AAAA,EAAe;AAAA,EAAe;AAAA,EAAiB;AAAA,EAAU;AAAA,EACzD;AAAA,EAAY;AAAA,EAAY;AAAA,EAAiB;AAAA,EAAc;AAAA,EAAiB;AAAA,EACxE;AAAA,EAAS;AAAA,EAAoB;AAAA,EAAa;AAAA,EAAa;AAAA,EAAgB;AAAA,EAAgB;AAAA,EACvF;AAAA,EAAU;AAAA,EAAS;AACrB,CAAC;AAGM,IAAM,4BAA4B,OAAO,OAAO,CAAC,gBAAgB,cAAc,gBAAgB,CAAC;AAGhG,IAAM,0BAA0B,OAAO,OAAO;AAAA,EACnD;AAAA,EAAS;AAAA,EAAU;AAAA,EAAc;AAAA,EAAe;AAAA,EAAe;AAAA,EAC/D;AAAA,EAAc;AAAA,EAAiB;AAAA,EAC/B;AAAA,EAAgB;AAAA,EAAc;AAChC,CAAC;AA4CM,IAAM,iBAAiB,OAAO,OAAO;AAAA,EAC1C;AAAA,EAAmB;AAAA,EAAW;AAAA,EAAoB;AAAA,EAAiB;AAAA,EAAyB;AAAA,EAAU;AAAA,EAAU;AAAA,EAChH;AAAA,EAA4B;AAAA,EAAkB;AAAA,EAAU;AAAA,EAAoB;AAAA,EAC5E;AAAA,EAAmB;AAAA,EAAoB;AACzC,CAAC;AAGM,IAAM,eAAe,OAAO,OAAO,CAAC,YAAY,UAAU,UAAU,CAAC;AACrE,IAAM,mBAAmB,OAAO,OAAO,CAAC,UAAU,OAAO,MAAM,CAAC;AAOhE,IAAM,cAAc;AAGpB,IAAM,gBAAgB;AAEtB,IAAM,iBAAiB,OAAO,OAAO;AAAA,EAC1C,iBAAiB,OAAO,OAAO;AAAA,IAC7B,UAAU,OAAO,OAAO,CAAC,OAAO,OAAO,CAAC;AAAA,IACxC,QAAQ,OAAO,OAAO,EAAE,KAAK,yBAAyB,OAAO,SAAS,CAAC;AAAA,EACzE,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKD,kBAAkB,OAAO,OAAO;AAAA,IAC9B,UAAU,OAAO,OAAO,CAAC,SAAS,OAAO,CAAC;AAAA,IAC1C,QAAQ,OAAO,OAAO,EAAE,OAAO,oBAAoB,OAAO,mBAAmB,CAAC;AAAA,EAChF,CAAC;AAAA,EACD,OAAO,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,gBAAgB,CAAC,EAAE,CAAC;AAAA,EAC9G,UAAU,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,mBAAmB,CAAC,EAAE,CAAC;AAAA,EACpH,SAAS,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,mBAAmB,CAAC,EAAE,CAAC;AAAA,EACnH,cAAc,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,mBAAmB,CAAC,EAAE,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKxH,UAAU,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,mBAAmB,CAAC,EAAE,CAAC;AAAA,EACpH,UAAU,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,2BAA2B,CAAC,EAAE,CAAC;AAAA,EAC5H,cAAc,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,2BAA2B,CAAC,EAAE,CAAC;AAAA,EAChI,gBAAgB,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,iBAAiB,CAAC,EAAE,CAAC;AAAA;AAAA;AAAA;AAAA,EAIxH,QAAQ,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,SAAS,CAAC,EAAE,CAAC;AAAA,EACxG,eAAe,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,SAAS,CAAC,EAAE,CAAC;AAAA,EAC/G,WAAW,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,UAAU,CAAC,EAAE,CAAC;AAAA,EAC5G,WAAW,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,OAAO,EAAE,OAAO,UAAU,CAAC,EAAE,CAAC;AAC9G,CAAC;AAGM,SAAS,oBAAoB,OAAO,OAAO;AAChD,QAAM,iBAAiB,CAAC,MAAM,OAAO,MAAM,YAAY,EAAE,KAAK,MAAM;AACpE,QAAM,WAAW,CAAC,MAAM,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM;AACvF,QAAM,eAAe,CAAC,MAAM,OAAO,MAAM,YAAY,YAAY,KAAK,CAAC;AAEvE,UAAQ,OAAO;AAAA,IACb,KAAK;AAIH,aAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,QAAQ,IAClE,OACA;AAAA,IACN,KAAK;AACH,aAAO,OAAO,UAAU,YAAY,OAAO;AAAA,IAC7C,KAAK;AACH,aAAO,eAAe,KAAK,IAAI,OAAO;AAAA,IACxC,KAAK;AAGH,UAAI,CAAC,eAAe,KAAK,EAAG,QAAO;AACnC,aAAO,cAAc,KAAK,KAAK,IAAI,OAAO;AAAA,IAC5C,KAAK;AACH,UAAI,eAAe,KAAK,EAAG,QAAO;AAClC,UAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,KAAK,MAAM,MAAM,cAAc,EAAG,QAAO;AACpF,aAAO;AAAA,IACT,KAAK;AACH,aAAO,SAAS,KAAK,IAAI,OAAO;AAAA,IAClC,KAAK;AACH,aAAO,OAAO,UAAU,WAAW,OAAO;AAAA,IAC5C,KAAK;AACH,aAAO,SAAS,KAAK,KAAK,OAAO,SAAS,OAAO,KAAK,CAAC,IAAI,OAAO;AAAA,IACpE,KAAK;AACH,UAAI,SAAS,KAAK,EAAG,QAAO;AAC5B,UAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,KAAK,MAAM,MAAM,QAAQ,EAAG,QAAO;AAC9E,aAAO;AAAA,IACT,KAAK;AACH,aAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,KAAK,MAAM,MAAM,QAAQ,IACnE,OACA;AAAA,IACN,KAAK,mBAAmB;AACtB,UAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AAChF,YAAM,QAAQ,OAAO,KAAK,KAAK;AAC/B,UAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,YAAM,MAAM,MAAM,KAAK,CAAC,SAAS,CAAC,aAAa,IAAI,CAAC;AACpD,aAAO,QAAQ,SAAY,OAAO,iCAAiC,KAAK,UAAU,GAAG,CAAC;AAAA,IACxF;AAAA,IACA,KAAK,oBAAoB;AACvB,UAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,EAAG,QAAO;AACxD,YAAM,MAAM,MAAM,KAAK,CAAC,SAAS,CAAC,aAAa,IAAI,CAAC;AACpD,aAAO,QAAQ,SAAY,OAAO,iCAAiC,KAAK,UAAU,GAAG,CAAC;AAAA,IACxF;AAAA,IACA,KAAK,gBAAgB;AACnB,UAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AAIhF,YAAM,UAAU,OAAO,KAAK,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,aAAa,SAAS,GAAG,CAAC;AAC9E,UAAI,QAAQ,SAAS,EAAG,QAAO,qBAAqB,QAAQ,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AAEpG,iBAAW,QAAQ,CAAC,YAAY,QAAQ,GAAG;AACzC,YAAI,MAAM,IAAI,MAAM,UAAa,OAAO,MAAM,IAAI,MAAM,WAAW;AAEjE,iBAAO,SAAS,IAAI;AAAA,QACtB;AAAA,MACF;AAEA,YAAM,WAAW,MAAM;AACvB,YAAM,cAAc,aAAa;AACjC,UAAI,aAAa,UAAa,CAAC,aAAa;AAC1C,YAAI,OAAO,aAAa,YAAY,CAAC,iBAAiB,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,SAAS,YAAY,CAAC,GAAG;AAC7G,iBAAO,4CAA4C,iBAAiB,KAAK,IAAI,CAAC;AAAA,QAChF;AAAA,MACF;AAGA,UAAI,MAAM,aAAa,SAAS,MAAM,WAAW,SAAS,aAAa;AACrE,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,IACA,KAAK;AACH,aAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAI,OAAO;AAAA,IACvF,KAAK;AACH,aAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE,SAAS,IACvG,OACA;AAAA,IACN,KAAK;AACH,aAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,KAAK,MAAM,MAAM,cAAc,IACzE,OACA;AAAA,IACN;AACE,aAAO;AAAA,EACX;AACF;AAGO,IAAM,YAAY,OAAO,OAAO;AAAA,EACrC;AAAA,EAAiB;AAAA,EAAkB;AAAA,EAAoB;AAAA,EAAU;AAAA,EAAe;AAAA,EAAe;AACjG,CAAC;AAEM,IAAM,SAAS,OAAO,OAAO,CAAC,WAAW,YAAY,QAAQ,CAAC;AAE9D,IAAM,UAAU,OAAO,OAAO;AAAA,EACnC;AAAA,EAAS;AAAA,EAAU;AAAA,EAAU;AAAA,EAAc;AAAA,EAAiB;AAC9D,CAAC;AA0CM,IAAM,uBAAuB,OAAO,OAAO;AAAA,EAChD,WAAW;AAAA,EACX,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,WAAW;AACb,CAAC;AASM,IAAM,qBAAqB,OAAO,OAAO,CAAC,SAAS,CAAC;AAEpD,IAAM,YAAY,OAAO,OAAO,CAAC,QAAQ,QAAQ,CAAC;AAGlD,IAAM,kBAAkB,OAAO,OAAO;AAAA,EAC3C;AAAA,EAAM;AAAA,EAAW;AAAA,EAAS;AAAA,EAAY;AAAA,EAAS;AAAA,EAAU;AAAA,EAAW;AAAA,EACpE;AAAA,EAAW;AAAA,EAAe;AAAA,EAAmB;AAAA,EAC7C;AAAA,EAAa;AAAA,EACb;AAAA,EAAe;AAAA,EAAkB;AAAA,EAAgB;AACnD,CAAC;AAYM,IAAM,oBAAoB,OAAO,OAAO;AAAA,EAC7C,OAAO,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,CAAC,GAAG,WAAW,OAAO,OAAO,CAAC,CAAC,GAAG,QAAQ,OAAO,OAAO,CAAC,WAAW,YAAY,QAAQ,CAAC,EAAE,CAAC;AAAA,EAC5I,QAAQ,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,CAAC,GAAG,WAAW,OAAO,OAAO,CAAC,CAAC,GAAG,QAAQ,OAAO,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;AAAA,EACxH,QAAQ,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,CAAC,CAAC,GAAG,WAAW,OAAO,OAAO,CAAC,CAAC,GAAG,QAAQ,OAAO,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;AAAA,EACxH,cAAc,OAAO,OAAO;AAAA,IAC1B,UAAU,OAAO,OAAO,CAAC,aAAa,CAAC;AAAA,IACvC,WAAW,OAAO,OAAO,CAAC,QAAQ,CAAC;AAAA,IACnC,QAAQ,OAAO,OAAO,CAAC,UAAU,CAAC;AAAA,IAClC,QAAQ,OAAO,OAAO,EAAE,aAAa,kBAAkB,CAAC;AAAA,EAC1D,CAAC;AAAA,EACD,iBAAiB,OAAO,OAAO;AAAA,IAC7B,UAAU,OAAO,OAAO,CAAC,gBAAgB,CAAC;AAAA,IAC1C,WAAW,OAAO,OAAO,CAAC,CAAC;AAAA,IAC3B,QAAQ,OAAO,OAAO,CAAC,UAAU,CAAC;AAAA,IAClC,QAAQ,OAAO,OAAO,EAAE,gBAAgB,mBAAmB,CAAC;AAAA,EAC9D,CAAC;AAAA,EACD,iBAAiB,OAAO,OAAO;AAAA,IAC7B,UAAU,OAAO,OAAO,CAAC,CAAC;AAAA,IAC1B,WAAW,OAAO,OAAO,CAAC,cAAc,CAAC;AAAA,IACzC,QAAQ,OAAO,OAAO,CAAC,UAAU,CAAC;AAAA,IAClC,QAAQ,OAAO,OAAO,EAAE,cAAc,eAAe,CAAC;AAAA,EACxD,CAAC;AACH,CAAC;AAEM,IAAM,SAAS,OAAO,OAAO;AAAA,EAClC,UAAU;AAAA,EACV,eAAe;AAAA,EACf,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,gBAAgB;AAClB,CAAC;AAGM,IAAM,kBAAkB;AAGxB,SAAS,QAAQ,WAAW;AACjC,SAAO,WAAW,cAAc,mBAAmB,MAAM,QAAQ,WAAW,KAAK;AACnF;AAUO,SAAS,sBAAsB,WAAW;AAC/C,MAAI,CAAC,aAAa,OAAO,cAAc,SAAU,QAAO;AAExD,QAAM,QAAQ,UAAU,cAAc;AACtC,QAAM,SAAS,MAAM,QAAQ,UAAU,KAAK;AAE5C,MAAI,SAAS,CAAC,QAAQ;AACpB,WAAO,cAAc,eAAe;AAAA,EACtC;AACA,MAAI,UAAU,CAAC,OAAO;AACpB,WAAO,uDAAuD,KAAK,UAAU,UAAU,SAAS,CAAC,mCAAmC,eAAe;AAAA,EACrJ;AACA,MAAI,SAAS,QAAQ;AACnB,QAAI,UAAU,UAAU,OAAW,QAAO;AAC1C,QAAI,UAAU,cAAc,OAAW,QAAO;AAC9C,QAAI,UAAU,MAAM,WAAW,EAAG,QAAO;AAAA,EAC3C;AAEA,SAAO;AACT;AAOO,SAAS,iBAAiB,WAAW;AAC1C,MAAI,cAAc,UAAa,cAAc,KAAM,QAAO;AAE1D,MAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,QAAI,UAAU,WAAW,EAAG,QAAO;AACnC,eAAW,UAAU,WAAW;AAC9B,YAAM,UAAU,iBAAiB,MAAM;AACvC,UAAI,YAAY,KAAM,QAAO;AAAA,IAC/B;AAEA,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,cAAc,YAAY,cAAc,GAAI,QAAO;AAK9D,MAAI,cAAc,iBAAiB;AACjC,WAAO,IAAI,eAAe;AAAA,EAC5B;AAEA,QAAM,MAAM,UAAU,QAAQ,GAAG;AACjC,QAAM,OAAO,QAAQ,KAAK,YAAY,UAAU,MAAM,GAAG,GAAG;AAC5D,QAAM,SAAS,QAAQ,IAAI;AAE3B,MAAI,WAAW,OAAW,QAAO,6BAA6B,IAAI;AAElE,MAAI,QAAQ,IAAI;AACd,WAAO,OAAO,UAAU,OACpB,WAAW,IAAI,wBAAwB,IAAI,gBAC3C;AAAA,EACN;AAEA,MAAI,OAAO,UAAU,KAAM,QAAO,WAAW,IAAI;AAEjD,QAAM,MAAM,UAAU,MAAM,MAAM,CAAC;AACnC,MAAI,QAAQ,GAAI,QAAO,WAAW,IAAI;AAEtC,SAAO,WAAW,MAAM,QAAQ,GAAG;AACrC;AAEA,SAAS,WAAW,MAAM,QAAQ,KAAK;AACrC,MAAI,IAAI,SAAS,GAAG,KAAK,OAAO,aAAa,MAAM;AACjD,WAAO,IAAI,IAAI;AAAA,EACjB;AAEA,QAAM,OAAO,IAAI,SAAS,GAAG,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI;AAGpD,MAAI,OAAO,SAAS,MAAO,QAAO;AAElC,MAAI,MAAM,QAAQ,OAAO,IAAI,KAAK,OAAO,KAAK,SAAS,IAAI,EAAG,QAAO;AAErE,aAAW,UAAU,OAAO,gBAAgB,CAAC,GAAG;AAC9C,QAAI,KAAK,WAAW,MAAM,KAAK,KAAK,SAAS,OAAO,OAAQ,QAAO;AAAA,EACrE;AAEA,QAAM,WAAW;AAAA,IACf,GAAI,MAAM,QAAQ,OAAO,IAAI,IAAI,OAAO,OAAO,CAAC;AAAA,IAChD,IAAI,OAAO,gBAAgB,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,QAAG;AAAA,EACnD,EAAE,KAAK,IAAI;AAEX,SAAO,IAAI,IAAI,iBAAiB,GAAG,uBAAuB,QAAQ;AACpE;AAGO,SAAS,aAAa,OAAO,WAAW;AAC7C,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,MAAI,OAAO,MAAM,SAAS,YAAY,MAAM,SAAS,GAAI,QAAO;AAChE,MAAI,CAAC,YAAY,SAAS,MAAM,IAAI,EAAG,QAAO,uBAAuB,MAAM,IAAI;AAE/E,QAAM,gBAAgB,0BAA0B,SAAS,MAAM,IAAI;AACnE,MAAI,CAAC,kBAAkB,cAAc,UAAa,cAAc,OAAO;AACrE,WAAO,eAAe,MAAM,IAAI;AAAA,EAClC;AACA,MAAI,iBAAiB,cAAc,UAAa,cAAc,MAAM;AAClE,WAAO,eAAe,MAAM,IAAI;AAAA,EAClC;AAEA,QAAM,WAAW,eAAe,MAAM,IAAI;AAC1C,MAAI,UAAU;AACZ,eAAW,YAAY,SAAS,UAAU;AACxC,UAAI,MAAM,QAAQ,MAAM,UAAa,MAAM,QAAQ,MAAM,MAAM;AAC7D,eAAO,eAAe,MAAM,IAAI,YAAY,QAAQ;AAAA,MACtD;AAAA,IACF;AACA,eAAW,CAAC,SAAS,KAAK,KAAK,OAAO,QAAQ,SAAS,UAAU,CAAC,CAAC,GAAG;AACpE,UAAI,MAAM,OAAO,MAAM,OAAW;AAClC,YAAM,UAAU,oBAAoB,OAAO,MAAM,OAAO,CAAC;AACzD,UAAI,QAAS,QAAO,eAAe,MAAM,IAAI,cAAc,OAAO,KAAK,OAAO;AAAA,IAChF;AAAA,EACF,WAAW,CAAC,wBAAwB,SAAS,MAAM,IAAI,KAAK,MAAM,UAAU,QAAW;AACrF,WAAO,eAAe,MAAM,IAAI;AAAA,EAClC;AAEA,SAAO;AACT;AAGO,SAAS,iBAAiB,WAAW;AAC1C,MAAI,cAAc,UAAa,cAAc,KAAM,QAAO;AAC1D,MAAI,CAAC,MAAM,QAAQ,SAAS,EAAG,QAAO;AAEtC,aAAW,YAAY,WAAW;AAChC,QAAI,OAAO,aAAa,YAAY,CAAC,UAAU,SAAS,QAAQ,GAAG;AACjE,aAAO,qBAAqB,OAAO,QAAQ,CAAC;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,YAAY,MAAM;AAChC,MAAI,SAAS,UAAa,SAAS,KAAM,QAAO;AAChD,MAAI,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,EAAG,QAAO;AAE5D,QAAM,OAAO,OAAO,KAAK,IAAI;AAC7B,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,UAAU,KAAK,OAAO,CAAC,QAAQ,CAAC,UAAU,SAAS,GAAG,CAAC;AAC7D,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO,gCAAgC,QAAQ,KAAK,IAAI,CAAC,mCAA8B,UAAU,KAAK,OAAO,CAAC;AAAA,EAEhH;AAKA,MAAI,UAAU,QAAQ,CAAC,iBAAiB,KAAK,IAAI,GAAG;AAClD,WAAO;AAAA,EACT;AAEA,MAAI,YAAY,MAAM;AACpB,UAAM,UAAU,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,SAAS,CAAC,KAAK,MAAM;AACvE,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAI,CAAC,QAAQ,MAAM,gBAAgB,EAAG,QAAO;AAAA,EAC/C;AAEA,SAAO;AACT;AAEA,SAAS,iBAAiB,OAAO;AAC/B,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM;AACvD;AAMO,SAAS,oBAAoB,MAAM;AACxC,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAE9C,aAAW,YAAY,iBAAiB;AACtC,QAAI,YAAY,QAAQ,KAAK,QAAQ,MAAM,MAAM;AAC/C,aAAO,IAAI,QAAQ;AAAA,IAErB;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,oBAAoB,MAAM;AACxC,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAE9C,aAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,oBAAoB,GAAG;AACpE,QAAI,KAAK,QAAQ,MAAM,UAAa,KAAK,QAAQ,MAAM,KAAM;AAC7D,UAAM,UAAU,oBAAoB,OAAO,KAAK,QAAQ,CAAC;AACzD,QAAI,QAAS,QAAO,IAAI,QAAQ,KAAK,OAAO;AAAA,EAC9C;AAcA,SAAO;AACT;AAEO,SAAS,cAAc,QAAQ,MAAM;AAC1C,MAAI,WAAW,UAAa,WAAW,KAAM,QAAO;AACpD,MAAI,CAAC,QAAQ,SAAS,MAAM,EAAG,QAAO,mBAAmB,MAAM;AAE/D,QAAM,OAAO,kBAAkB,MAAM;AAErC,aAAW,YAAY,MAAM,YAAY,CAAC,GAAG;AAC3C,QAAI,OAAO,QAAQ,MAAM,UAAa,OAAO,QAAQ,MAAM,MAAM;AAC/D,aAAO,WAAW,MAAM,YAAY,QAAQ;AAAA,IAC9C;AAAA,EACF;AAEA,aAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,MAAM,UAAU,CAAC,CAAC,GAAG;AAClE,QAAI,OAAO,QAAQ,MAAM,UAAa,OAAO,QAAQ,MAAM,KAAM;AACjE,UAAM,UAAU,oBAAoB,OAAO,KAAK,QAAQ,CAAC;AACzD,QAAI,QAAS,QAAO,WAAW,MAAM,eAAe,QAAQ,KAAK,OAAO;AAAA,EAC1E;AAEA,QAAM,QAAQ,MAAM,SAAS;AAC7B,MAAI,SAAS,UAAa,CAAC,KAAK,OAAO,SAAS,KAAK,GAAG;AACtD,WAAO,WAAW,MAAM,2BAA2B,KAAK,OAAO,KAAK,GAAG,CAAC,eAAe,KAAK;AAAA,EAC9F;AAEA,SAAO;AACT;;;ACrnBO,IAAMC,UAAS;AAEtB,IAAMC,UAAS,IAAI,IAAI,MAAe;AACtC,IAAMC,WAAU,IAAI,IAAI,OAAgB;AAQjC,SAAS,eAAe,QAAQ,OAAO,CAAC,GAAG;AAChD,QAAM,WAAW,CAAC;AAClB,QAAM,aAAa,MAAM,QAAQ,QAAQ,QAAQ,IAAI,OAAO,WAAW,CAAC;AACxE,QAAM,eAAe,MAAM,QAAQ,QAAQ,UAAU,IAAI,OAAO,aAAa,CAAC;AAE9E,QAAM,WAAW,CAAC;AAClB,aAAW,QAAQ,YAAY;AAC7B,QAAI,SAAS,UAAUF,QAAO,UAAU;AACtC,eAAS,KAAK,EAAE,IAAI,KAAK,IAAI,GAAG,QAAQ,4BAA4BA,QAAO,QAAQ,IAAI,CAAC;AACxF;AAAA,IACF;AACA,UAAM,SAAS,YAAY,IAAI;AAC/B,QAAI,OAAQ,UAAS,KAAK,EAAE,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC;AAAA,QAC/C,UAAS,KAAK,IAAI;AAAA,EACzB;AAEA,QAAM,aAAa,CAAC;AACpB,aAAW,MAAM,cAAc;AAC7B,QAAI,WAAW,UAAUA,QAAO,eAAe;AAC7C,eAAS,KAAK,EAAE,IAAI,KAAK,EAAE,GAAG,QAAQ,iCAAiCA,QAAO,aAAa,IAAI,CAAC;AAChG;AAAA,IACF;AAIA,QAAI,CAAC,KAAK,yBAAyB,MAAM,MAAM,QAAQ,GAAG,OAAO,KAAK,CAAC,GAAG,SAAS;AACjF,eAAS,KAAK,EAAE,IAAI,KAAK,EAAE,GAAG,QAAQ,4FAA4F,CAAC;AACnI;AAAA,IACF;AACA,UAAM,SAAS,kBAAkB,IAAI,OAAO;AAC5C,QAAI,OAAQ,UAAS,KAAK,EAAE,IAAI,KAAK,EAAE,GAAG,QAAQ,cAAc,MAAM,GAAG,CAAC;AAAA,QACrE,YAAW,KAAK,EAAE;AAAA,EACzB;AAEA,SAAO;AAAA,IACL,QAAQ,EAAE,GAAG,QAAQ,UAAU,WAAW;AAAA,IAC1C;AAAA,EACF;AACF;AAEA,SAAS,KAAK,MAAM;AAClB,QAAM,KAAK,MAAM,MAAM,MAAM;AAC7B,SAAO,OAAO,UAAa,OAAO,OAAO,mBAAmB,OAAO,EAAE;AACvE;AAGA,SAAS,YAAY,MAAM;AACzB,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAK9C,QAAM,aAAa,oBAAoB,IAAI;AAC3C,MAAI,WAAY,QAAO;AAEvB,MAAI,KAAK,UAAU,UAAa,CAACC,QAAO,IAAI,KAAK,KAAK,EAAG,QAAO,kBAAkB,KAAK,KAAK;AAI5F,QAAM,eAAe,cAAc,KAAK,QAAQ,IAAI;AACpD,MAAI,aAAc,QAAO;AAIzB,QAAM,cAAc,YAAY,KAAK,IAAI;AACzC,MAAI,YAAa,QAAO;AAKxB,QAAM,iBAAiB,oBAAoB,IAAI;AAC/C,MAAI,eAAgB,QAAO;AAE3B,SAAO,kBAAkB,KAAK,OAAO;AACvC;AAEA,SAAS,kBAAkB,YAAY,QAAQ,GAAG;AAChD,MAAI,CAAC,MAAM,QAAQ,UAAU,EAAG,QAAO;AACvC,MAAI,WAAW,WAAW,EAAG,QAAO;AACpC,MAAI,QAAQD,QAAO,gBAAiB,QAAO,uBAAuBA,QAAO,eAAe;AACxF,MAAI,WAAW,SAASA,QAAO,sBAAsB;AACnD,WAAO,aAAaA,QAAO,oBAAoB;AAAA,EACjD;AACA,aAAW,KAAK,YAAY;AAC1B,UAAM,cAAc,sBAAsB,CAAC;AAC3C,QAAI,YAAa,QAAO;AAExB,QAAI,QAAQ,CAAC,GAAG;AACd,YAAM,SAAS,kBAAkB,EAAE,OAAO,QAAQ,CAAC;AACnD,UAAI,OAAQ,QAAO;AACnB;AAAA,IACF;AAIA,UAAM,kBAAkB,iBAAiB,EAAE,SAAS;AACpD,QAAI,gBAAiB,QAAO;AAE5B,UAAM,iBAAiB,iBAAiB,EAAE,SAAS;AACnD,QAAI,eAAgB,QAAO;AAE3B,UAAM,cAAc,aAAa,EAAE,OAAO,EAAE,SAAS;AACrD,QAAI,YAAa,QAAO;AAExB,UAAM,IAAI,EAAE;AACZ,QAAI,EAAE,SAAS,SAAS;AACtB,UAAI,OAAO,EAAE,UAAU,SAAU,QAAO;AACxC,UAAI,EAAE,MAAM,SAASA,QAAO,eAAgB,QAAO,qBAAqBA,QAAO,cAAc;AAAA,IAC/F,WAAW,OAAO,EAAE,UAAU,YAAY,EAAE,MAAM,SAASA,QAAO,gBAAgB;AAChF,aAAO,2BAA2BA,QAAO,cAAc;AAAA,IACzD;AACA,QAAI,EAAE,OAAO;AAMX,YAAM,SAAS,kBAAkB,CAAC,EAAE,WAAW,EAAE,WAAW,OAAO,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC;AACxF,UAAI,OAAQ,QAAO;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;;;AChJA,SAAS,qBAAqB,KAAK,SAAS;AAC1C,MAAI,QAAQ,oBAAqB,QAAO,CAAC;AACzC,QAAM,EAAE,SAAS,IAAI;AAAA,IACnB,EAAE,UAAU,MAAM,QAAQ,IAAI,QAAQ,IAAI,IAAI,WAAW,CAAC,GAAG,YAAY,MAAM,QAAQ,IAAI,UAAU,IAAI,IAAI,aAAa,CAAC,EAAE;AAAA,IAC7H,EAAE,uBAAuB,QAAQ,sBAAsB;AAAA,EACzD;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,UAAU,SAAS,OAAO;AAClD,QAAM,SAAS,QAAQ;AACvB,aAAW,KAAK,UAAU;AACxB,QAAI,OAAO,WAAW,YAAY;AAChC,aAAO,QAAQ,EAAE,GAAG,GAAG,UAAU,MAAM,GAAG,gBAAgB;AAAA,IAC5D;AAAA,EACF;AACA,QAAM,SAAS,SAAS,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,KAAK,IAAI;AACjF,SAAO,QAAQ,SAAS,IAAI;AAAA,IAC1B,GAAG,KAAK,wCAAwC,SAAS,MAAM,iGACA,MAAM,GAAG,SAAS,SAAS,IAAI,aAAQ,EAAE;AAAA,EAC1G,GAAG,SAAS;AACd;AAGA,SAAS,WAAW,QAAQ,QAAQ;AAClC,SAAO,WAAW,SAAY,EAAE,GAAG,QAAQ,QAAQ,EAAE,IAAI,KAAK,EAAE,IAAI,EAAE,GAAG,QAAQ,QAAQ,EAAE,IAAI,OAAO,OAAO,EAAE;AACjH;AAEA,eAAsB,aAAa,SAAS,OAAO,MAAM,CAAC,GAAG;AAI3D,QAAM,YAAY,IAAI;AACtB,MAAI,QAAQ,UAAU;AACpB,UAAM,QAAQ,MAAM,MAAM,KAAK;AAC/B,UAAM,SAAS,IAAI,gBAAgB,EAAE,UAAU,QAAQ,UAAU,SAAS,QAAQ,eAAe,MAAM,OAAO,MAAM,WAAW,WAAW,IAAI,WAAW,mBAAmB,QAAQ,qBAAqB,KAAK,CAAC;AAC/M,UAAM,MAAM,MAAM,OAAO,SAAS;AAClC,QAAI,IAAI,WAAW,IAAI,eAAe,OAAO,OAAQ,QAAO,WAAW,gBAAgB,MAAM,QAAQ,OAAO,CAAC;AAC7G,QAAI,IAAI,WAAW,CAAC,IAAI,aAAa;AACnC,YAAM,WAAW,qBAAqB,KAAK,OAAO;AAClD,UAAI,SAAS,SAAS,GAAG;AACvB,yBAAiB,UAAU,SAAS,sBAAsB;AAG1D,YAAI,OAAO,OAAQ,QAAO,WAAW,gBAAgB,MAAM,QAAQ,OAAO,GAAG,iBAAiB;AAC9F,YAAI,QAAQ,MAAO,QAAO,WAAW,gBAAgB,QAAQ,OAAO,OAAO,GAAG,iBAAiB;AAC/F,eAAO,WAAW,YAAY,GAAG,iBAAiB;AAAA,MACpD;AACA,YAAM,SAAS,gBAAgB,KAAK,OAAO;AAC3C,YAAM,MAAM,MAAM,EAAE,QAAQ,MAAM,IAAI,QAAQ,KAAK,CAAC;AACpD,aAAO,WAAW,MAAM;AAAA,IAC1B;AACA,QAAI,OAAO,QAAQ;AACjB,aAAO,QAAQ,SAAS,IAAI,MAAM,4BAA4B,IAAI,SAAS,oBAAoB,wBAAwB,GAAG,SAAS;AACnI,aAAO,WAAW,gBAAgB,MAAM,QAAQ,OAAO,GAAG,IAAI,SAAS,oBAAoB;AAAA,IAC7F;AACA,QAAI,QAAQ,OAAO;AACjB,aAAO,QAAQ,SAAS,IAAI,MAAM,4BAA4B,IAAI,SAAS,oBAAoB,2BAA2B,GAAG,SAAS;AACtI,aAAO,WAAW,gBAAgB,QAAQ,OAAO,OAAO,GAAG,IAAI,SAAS,oBAAoB;AAAA,IAC9F;AACA,WAAO,QAAQ,SAAS,IAAI,MAAM,4BAA4B,IAAI,SAAS,oBAAoB,0CAAqC,GAAG,SAAS;AAChJ,WAAO,WAAW,YAAY,GAAG,IAAI,SAAS,oBAAoB;AAAA,EACpE;AAEA,MAAI,QAAQ,OAAO;AACjB,UAAM,QAAQ,MAAM,MAAM,KAAK;AAC/B,UAAM,SAAS,IAAI,qBAAqB,EAAE,OAAO,QAAQ,OAAO,SAAS,QAAQ,SAAS,MAAM,OAAO,MAAM,UAAU,CAAC;AACxH,UAAM,MAAM,MAAM,OAAO,SAAS;AAClC,QAAI,IAAI,WAAW,IAAI,eAAe,OAAO,OAAQ,QAAO,WAAW,gBAAgB,MAAM,QAAQ,OAAO,CAAC;AAC7G,QAAI,IAAI,WAAW,CAAC,IAAI,aAAa;AACnC,YAAM,WAAW,qBAAqB,KAAK,OAAO;AAClD,UAAI,SAAS,SAAS,GAAG;AACvB,yBAAiB,UAAU,SAAS,sBAAsB;AAG1D,YAAI,OAAO,OAAQ,QAAO,WAAW,gBAAgB,MAAM,QAAQ,OAAO,GAAG,iBAAiB;AAC9F,YAAI,QAAQ,MAAO,QAAO,WAAW,gBAAgB,QAAQ,OAAO,OAAO,GAAG,iBAAiB;AAC/F,eAAO,WAAW,YAAY,GAAG,iBAAiB;AAAA,MACpD;AACA,YAAM,SAAS,gBAAgB,KAAK,OAAO;AAC3C,YAAM,MAAM,MAAM,EAAE,QAAQ,MAAM,IAAI,QAAQ,KAAK,CAAC;AACpD,aAAO,WAAW,MAAM;AAAA,IAC1B;AACA,QAAI,OAAO,QAAQ;AACjB,aAAO,QAAQ,SAAS,IAAI,MAAM,sBAAsB,IAAI,SAAS,oBAAoB,wBAAwB,GAAG,SAAS;AAC7H,aAAO,WAAW,gBAAgB,MAAM,QAAQ,OAAO,GAAG,IAAI,SAAS,oBAAoB;AAAA,IAC7F;AACA,WAAO,QAAQ,SAAS,IAAI,MAAM,sBAAsB,IAAI,SAAS,oBAAoB,0CAAqC,GAAG,SAAS;AAC1I,WAAO,WAAW,YAAY,GAAG,IAAI,SAAS,oBAAoB;AAAA,EACpE;AAGA,MAAI,QAAQ,OAAO;AACjB,WAAO,WAAW,gBAAgB,QAAQ,OAAO,OAAO,CAAC;AAAA,EAC3D;AAEA,SAAO,WAAW,YAAY,CAAC;AACjC;AAMO,SAAS,gBAAgB,GAAG,UAAU,CAAC,GAAG;AAC/C,QAAM,cAAc,GAAG,eAAe,GAAG;AACzC,QAAM,EAAE,QAAQ,SAAS,SAAS,IAAI;AAAA,IACpC;AAAA,MACE,UAAU,MAAM,QAAQ,EAAE,QAAQ,IAAI,EAAE,WAAW,CAAC;AAAA,MACpD,YAAY,MAAM,QAAQ,EAAE,UAAU,IAAI,EAAE,aAAa,CAAC;AAAA,IAC5D;AAAA,IACA,EAAE,uBAAuB,QAAQ,sBAAsB;AAAA,EACzD;AACA,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,SAAS,QAAQ;AACvB,QAAI,OAAO,WAAW,YAAY;AAChC,iBAAW,KAAK,UAAU;AACxB,eAAO,QAAQ,GAAG,gBAAgB;AAAA,MACpC;AAAA,IACF,OAAO;AACL,YAAM,SAAS,SAAS,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,KAAK,IAAI;AAEjF,cAAQ;AAAA,QACN,gBAAgB,SAAS,MAAM,0EAA0E,MAAM,MAC5G,SAAS,SAAS,IAAI,aAAQ;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AACA,SAAO;AAAA,IACL,UAAU,QAAQ;AAAA,IAClB,YAAY,QAAQ;AAAA,IACpB,gBAAgB,EAAE,kBAAkB,CAAC;AAAA,IACrC,GAAI,gBAAgB,WAAW,gBAAgB,YAAY,EAAE,YAAY,IAAI,CAAC;AAAA,EAChF;AACF;AAEO,SAAS,cAAc;AAC5B,SAAO,EAAE,UAAU,CAAC,GAAG,YAAY,CAAC,GAAG,gBAAgB,CAAC,EAAE;AAC5D;;;AC/IA,IAAMG,mBAAkB;AACxB,IAAM,yBAAyB;AAExB,SAAS,aAAa,MAAM,EAAE,WAAW,QAAQ,IAAI,CAAC,GAAG;AAC9D,MAAI,UAAU;AACd,MAAI,WAAW;AACf,MAAI,QAAQ;AAEZ,QAAM,WAAW,MAAM;AACrB,QAAI,QAAS;AACb,UAAM,UAAU,KAAK,IAAI,KAAK,UAAU,sBAAsB;AAC9D,UAAM,SAAS,IAAI,KAAK,OAAO,IAAIA;AACnC,UAAM,QAAQ,YAAY,UAAU;AACpC,YAAQ,WAAW,KAAK,KAAK;AAC7B,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,MAAM,YAAY;AACtB,QAAI,QAAS;AACb,QAAI;AACF,YAAM,SAAS,MAAM,KAAK;AAC1B,UAAI,UAAU,OAAO,OAAO,MAAO;AAAA,UAC9B,YAAW;AAAA,IAClB,SAAS,KAAK;AACZ;AACA,aAAO,SAAS,KAAK,SAAS;AAAA,IAChC;AACA,aAAS;AAAA,EACX;AAEA,WAAS;AACT,SAAO;AAAA,IACL,OAAO;AACL,gBAAU;AACV,UAAI,MAAO,cAAa,KAAK;AAAA,IAC/B;AAAA,EACF;AACF;AAEO,SAAS,mBAAmB,MAAM,QAAQ;AAC/C,SAAO,OAAO,YAAY;AAExB,QAAI,CAAC,OAAQ,QAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;AAC7D,QAAI,WAAW;AACf,QAAI;AACF,iBAAW,SAAS,SAAS,MAAM,sBAAsB,KAAK,IAAI,IAAI,QAAQ,GAAG,EAAE,aAAa,IAAI,OAAO;AAAA,IAC7G,QAAQ;AACN,iBAAW;AAAA,IACb;AACA,QAAI,aAAa,OAAQ,QAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;AACzE,QAAI,YAAY;AAChB,QAAI;AAGF,YAAM,SAAS,MAAM,KAAK;AAC1B,UAAI,UAAU,OAAO,OAAO,MAAO,aAAY;AAAA,IACjD,QAAQ;AACN,kBAAY;AAAA,IACd;AACA,WAAO,IAAI,SAAS,KAAK,UAAU,EAAE,UAAU,CAAC,GAAG,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE,CAAC;AAAA,EACrH;AACF;;;AC9CA,IAAMC,oBAAmB;AACzB,IAAM,mBAAmB;AACzB,IAAM,YAAY;AAElB,IAAM,YAAY;AAaX,SAAS,eAAe,MAAM;AACnC,QAAM,MAAM,oBAAI,IAAI;AACpB,QAAM,OAAO,CAAC,eAAe;AAC3B,QAAI,CAAC,MAAM,QAAQ,UAAU,EAAG;AAChC,eAAW,aAAa,YAAY;AAClC,UAAI,CAAC,aAAa,OAAO,cAAc,SAAU;AACjD,UAAI,OAAO,UAAU,cAAc,YAAY,UAAU,cAAc,SAAS;AAC9E,YAAI,IAAI,UAAU,SAAS;AAAA,MAC7B;AACA,UAAI,MAAM,QAAQ,UAAU,KAAK,EAAG,MAAK,UAAU,KAAK;AAAA,IAC1D;AAAA,EACF;AACA,OAAK,MAAM,OAAO;AAElB,SAAO,CAAC,GAAG,GAAG;AAChB;AAYO,SAAS,WAAW,MAAM;AAC/B,QAAM,WAAW,MAAM;AACvB,MAAI,OAAO,aAAa,YAAY,aAAa,GAAI,QAAO;AAC5D,MAAI,OAAO,aAAa,YAAY,OAAO,SAAS,QAAQ,EAAG,QAAO,OAAO,QAAQ;AAErF,SAAO;AACT;AAWO,SAAS,QAAQ,MAAM;AAC5B,MAAI,OAAO,SAAS,YAAY,SAAS,GAAI,QAAO;AACpD,QAAM,MAAM,KAAK,OAAO,MAAM;AAE9B,SAAO,QAAQ,KAAK,OAAO,KAAK,MAAM,GAAG,GAAG;AAC9C;AAaO,SAAS,wBAAwB,MAAM;AAC5C,QAAM,WAAW,KAAK,YAAY,QAAQ,KAAK;AAC/C,MAAI,CAAC,UAAU;AAGb,WAAO;AAAA,MACL,SAAS;AAAA,MAAC;AAAA,MAAG,QAAQ;AAAA,MAAC;AAAA,MAAG,OAAO;AAAA,MAAC;AAAA,MAAG,eAAe;AAAA,MAAC;AAAA,MAAG,SAAS,MAAM;AAAA,MACtE,QAAQ,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,iBAAiB,KAAK;AAAA,IACvF;AAAA,EACF;AAEA,QAAM,aAAa,KAAK,WAAW,QAAQ,KAAK;AAChD,QAAM,UAAU,OAAO,eAAe,YAAY,aAAa,UAAU,IACrE,WAAW,QAAQ,OAAO,EAAE,IAC5BA;AACJ,QAAM,YAAY,KAAK,aAAa,WAAW;AAC/C,QAAM,UAAU,OAAO,SAAS,KAAK,OAAO,KAAK,KAAK,UAAU,IAAI,KAAK,UAAU;AACnF,QAAM,WAAW,OAAO,SAAS,KAAK,QAAQ,KAAK,KAAK,WAAW,IAAI,KAAK,WAAW;AAMvF,MAAI,YAAY,KAAK,aAAa;AAKlC,MAAI,OAAO;AACX,MAAI,YAAY;AAChB,MAAI,SAAS;AACb,MAAI,eAAe;AAEnB,MAAI,kBAAkB;AAGtB,MAAI,QAAQ,CAAC;AAEb,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,MAAI,UAAU;AAEd,QAAM,QAAQ,MAAM;AAClB,QAAI,OAAO;AACT,mBAAa,KAAK;AAClB,cAAQ;AAAA,IACV;AACA,QAAI,MAAM,WAAW,KAAK,OAAO,cAAc,WAAY;AAE3D,UAAM,QAAQ,MAAM,OAAO,GAAG,SAAS;AAIvC,UAAM,cAAc;AACpB,cAAU;AACV,oBAAgB;AAChB,YAAQ,MAAM;AAEd,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,MAAM,MAAM,UAAU,GAAG,OAAO,eAAe,mBAAmB,QAAQ,CAAC,IAAI;AAAA,UACnF,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,YACR,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,YAKd,GAAI,MAAM,gBAAgB,EAAE,WAAW,KAAK,WAAW,UAAU,QAAQ,GAAG,SAAS;AAAA,UACvF;AAAA,UACA,MAAM,KAAK,UAAU,EAAE,YAAY,OAAO,SAAS,YAAY,CAAC;AAAA,QAClE,CAAC;AAKD,YAAI,OAAO,IAAI,IAAI;AACjB,uBAAa,MAAM;AACnB,6BAAkB,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC3C,OAAO;AACL,oBAAU,MAAM;AAAA,QAClB;AAAA,MACF,QAAQ;AACN,kBAAU,MAAM;AAAA,MAClB;AAAA,IACF,GAAG;AAAA,EACL;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASL,OAAO,WAAW;AAChB,UAAI,QAAS;AACb,YAAM,SAAS,WAAW,MAAM;AAChC,UAAI,WAAW,UAAa,WAAW,QAAQ,WAAW,GAAI;AAE9D,UAAI,MAAM,UAAU,UAAU;AAC5B,cAAM,MAAM;AACZ;AAAA,MACF;AAEA,YAAM,KAAK;AAAA,QACT,SAAS;AAAA,QACT,OAAO,QAAQ,UAAU,IAAI;AAAA,QAC7B,YAAY,eAAe,UAAU,IAAI;AAAA,QACzC,OAAO,UAAU,SAAS;AAAA;AAAA;AAAA,QAG1B,UAAU,UAAU,SAAS;AAAA,QAC7B,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,QAKZ,eAAe,WAAW,UAAU,IAAI;AAAA,QACxC,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,MACtC,CAAC;AAED,UAAI,MAAM,UAAU,WAAW;AAC7B,cAAM;AAEN;AAAA,MACF;AACA,UAAI,CAAC,MAAO,SAAQ,WAAW,OAAO,OAAO;AAAA,IAC/C;AAAA,IACA;AAAA,IACA,OAAO;AACL,gBAAU;AACV,YAAM;AAAA,IACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,aAAa,MAAM;AACjB,kBAAY,QAAQ;AAAA,IACtB;AAAA,IACA,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,IAKf,QAAQ,OAAO,EAAE,MAAM,WAAW,QAAQ,SAAS,eAAe,SAAS,gBAAgB;AAAA,EAC7F;AACF;;;ACnQA,IAAM,mBAAmB;AACzB,IAAMC,oBAAmB;AACzB,IAAMC,aAAY;AAClB,IAAM,gBAAgB;AAOf,SAAS,YAAY,QAAQ;AAClC,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,EAAG,QAAO;AAC9D,QAAM,MAAM,OAAO,YAAY,GAAG;AAClC,MAAI,OAAO,KAAK,QAAQ,OAAO,SAAS,EAAG,QAAO;AAClD,QAAM,eAAe,OAAO,MAAM,GAAG,GAAG;AACxC,QAAM,WAAW,OAAO,MAAM,MAAM,CAAC;AACrC,MAAI,CAAC,QAAQ,KAAK,QAAQ,KAAK,aAAa,WAAW,EAAG,QAAO;AACjE,SAAO,EAAE,UAAU,aAAa;AAClC;AAMO,SAAS,eAAe,oBAAoB;AACjD,QAAM,UAAU,OAAO,YAAY,cAAc,QAAQ,KAAK,sBAAsB;AACpF,MAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,GAAG;AAIrD,UAAM,YAAY,QAAQ,QAAQ,OAAO,EAAE;AAC3C,QAAI,aAAa,SAAS,EAAG,QAAO;AAEpC,YAAQ;AAAA,MACN;AAAA,IAEF;AAAA,EACF;AACA,MAAI,OAAO,uBAAuB,YAAY,mBAAmB,SAAS,GAAG;AAC3E,QAAI;AACF,aAAO,IAAI,IAAI,kBAAkB,EAAE;AAAA,IACrC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,QAAM,WAAW,OAAO,YAAY,cAAc,QAAQ,KAAK,sBAAsB;AACrF,MAAI,OAAO,aAAa,YAAY,SAAS,SAAS,GAAG;AACvD,QAAI;AACF,aAAO,IAAI,IAAI,QAAQ,EAAE;AAAA,IAC3B,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAWO,SAAS,0BAA0B,MAAM;AAC9C,QAAM,QAAQ,YAAY,KAAK,MAAM;AACrC,MAAI,CAAC,OAAO;AACV,WAAO,EAAE,SAAS;AAAA,IAAC,GAAG,QAAQ;AAAA,IAAC,GAAG,OAAO;AAAA,IAAC,EAAE;AAAA,EAC9C;AAEA,QAAM,WAAW,KAAK,WAAW,kBAAkB,QAAQ,OAAO,EAAE;AACpE,QAAM,YAAY,KAAK,aAAa,WAAW;AAC/C,QAAM,UAAU,OAAO,SAAS,KAAK,OAAO,IAAI,KAAK,UAAUD;AAC/D,QAAM,aAAa,OAAO,KAAK,eAAe,WAAW,KAAK,aAAa;AAG3E,MAAI,QAAQ,CAAC;AAEb,MAAI,QAAQ;AACZ,MAAI,UAAU;AAGd,MAAI,cAAc;AAElB,MAAI,gBAAgB;AAEpB,QAAM,mBAAmB,YAAY;AACnC,QAAI,eAAe,KAAK,IAAI,IAAI,YAAY,YAAY,eAAe;AACrE,aAAO,YAAY;AAAA,IACrB;AACA,QAAI,cAAe,QAAO;AAE1B,qBAAiB,YAAY;AAC3B,UAAI;AACF,cAAM,MAAM,MAAM,UAAU,GAAG,OAAO,gBAAgB;AAAA,UACpD,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,YACR,cAAc;AAAA,UAChB;AAAA,UACA,MAAM,KAAK,UAAU;AAAA,YACnB,YAAY;AAAA,YACZ,WAAW,MAAM;AAAA,YACjB,eAAe,MAAM;AAAA,UACvB,CAAC;AAAA,QACH,CAAC;AACD,YAAI,CAAC,OAAO,CAAC,IAAI,GAAI,QAAO;AAC5B,cAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,cAAM,QAAQ,MAAM;AACpB,YAAI,OAAO,UAAU,YAAY,MAAM,WAAW,EAAG,QAAO;AAC5D,cAAM,YAAY,OAAO,MAAM,UAAU;AACzC,cAAM,QAAQ,OAAO,SAAS,SAAS,KAAK,YAAY,IAAI,YAAY,MAAO;AAC/E,sBAAc,EAAE,OAAO,WAAW,KAAK,IAAI,IAAI,MAAM;AACrD,eAAO;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT,UAAE;AACA,wBAAgB;AAAA,MAClB;AAAA,IACF,GAAG;AAEH,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,MAAM;AAClB,QAAI,OAAO;AACT,mBAAa,KAAK;AAClB,cAAQ;AAAA,IACV;AACA,QAAI,MAAM,WAAW,KAAK,OAAO,cAAc,WAAY;AAE3D,UAAM,QAAQ,MAAM,OAAO,GAAGC,UAAS;AACvC,UAAM,YAAY;AAChB,YAAM,QAAQ,MAAM,iBAAiB;AACrC,UAAI,CAAC,MAAO;AAEZ,YAAM,OAAO,IAAI,gBAAgB;AACjC,WAAK,IAAI,QAAQ,UAAU;AAC3B,WAAK,IAAI,QAAQ,KAAK,UAAU,KAAK,CAAC;AAEtC,UAAI;AACF,cAAM,IAAI,UAAU,GAAG,OAAO,iBAAiB;AAAA,UAC7C,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,eAAe,UAAU,KAAK;AAAA,YAC9B,QAAQ;AAAA,YACR,gBAAgB;AAAA,YAChB,cAAc;AAAA,YACd,GAAI,aAAa,EAAE,eAAe,WAAW,IAAI,CAAC;AAAA,UACpD;AAAA,UACA;AAAA,QACF,CAAC;AACD,YAAI,KAAK,OAAO,EAAE,SAAS,WAAY,GAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MACzD,QAAQ;AAAA,MAER;AAAA,IACF,GAAG;AAAA,EACL;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUL,OAAO,OAAO;AACZ,UAAI,QAAS;AACb,YAAM,MAAM,OAAO,MAAM;AACzB,UAAI,QAAQ,UAAa,QAAQ,QAAQ,QAAQ,GAAI;AAErD,YAAM,KAAK;AAAA,QACT;AAAA,QACA,QAAQ,MAAM,UAAU;AAAA,QACxB,aAAa,MAAM,QAAQ;AAAA,QAC3B,IAAI,MAAM,MAAM;AAAA,QAChB,YAAY,MAAM,aAAa;AAAA,QAC/B,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,MACnC,CAAC;AAED,UAAI,MAAM,UAAUA,YAAW;AAC7B,cAAM;AACN;AAAA,MACF;AACA,UAAI,CAAC,MAAO,SAAQ,WAAW,OAAO,OAAO;AAAA,IAC/C;AAAA,IACA;AAAA,IACA,OAAO;AACL,gBAAU;AACV,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAGO,SAAS,mBAAmB;AACjC,QAAM,IAAI,OAAO,YAAY,cAAc,QAAQ,KAAK,uBAAuB;AAC/E,MAAI,MAAM,UAAa,MAAM,GAAI,QAAO;AACxC,SAAO,CAAC,sBAAsB,KAAK,OAAO,CAAC,CAAC;AAC9C;;;ACtMO,IAAM,aAAa;AAG1B,IAAM,aAAa,oBAAI,IAAI,CAAC,oBAAoB,kBAAkB,qBAAqB,YAAY,CAAC;AAS7F,SAAS,oBAAoB,EAAE,YAAY,aAAa,YAAY,MAAM,GAAG;AAClF,QAAM,QAAQ,WAAW,WAAW;AACpC,QAAM,gBAAgB,cAAc,IAAI,IAAI,WAAW,EAAE,SAAS;AAElE,SAAO,eAAe,mBAAmB,SAAS;AAChD,UAAM,SAAS,QAAQ,QAAQ,IAAI,aAAa;AAChD,QAAI,CAAC,OAAQ,QAAO,IAAI,SAAS,mCAAmC,EAAE,QAAQ,IAAI,CAAC;AAEnF,QAAI;AACJ,QAAI;AACF,kBAAY,IAAI,IAAI,MAAM;AAAA,IAC5B,QAAQ;AACN,aAAO,IAAI,SAAS,8BAA8B,EAAE,QAAQ,IAAI,CAAC;AAAA,IACnE;AAKA,QAAI,CAAC,iBAAiB,UAAU,aAAa,YAAY,UAAU,WAAW,eAAe;AAC3F,aAAO,IAAI,SAAS,kCAAkC,EAAE,QAAQ,IAAI,CAAC;AAAA,IACvE;AAEA,UAAM,UAAU,QAAQ,WAAW,SAAS,QAAQ,WAAW;AAC/D,UAAM,WAAW,UAAU,MAAM,QAAQ,KAAK,IAAI;AAIlD,UAAM,UAAU,IAAI,QAAQ,UAAU,SAAS,GAAG;AAAA,MAChD,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ;AAAA,MACjB,MAAM,UAAU,WAAW;AAAA,IAC7B,CAAC;AACD,UAAM,UAAU,MAAM,MAAM,OAAO;AACnC,QAAI,QAAS,QAAO;AAGpB,UAAM,iBAAiB,IAAI,QAAQ,QAAQ,OAAO;AAClD,mBAAe,OAAO,aAAa;AACnC,mBAAe,OAAO,MAAM;AAC5B,UAAM,WAAW,MAAM,UAAU,UAAU,SAAS,GAAG;AAAA,MACrD,QAAQ,QAAQ;AAAA,MAChB,SAAS;AAAA,MACT,MAAM,UAAU,WAAW;AAAA,MAC3B,UAAU;AAAA,IACZ,CAAC;AAED,UAAM,aAAa,IAAI,QAAQ;AAC/B,aAAS,QAAQ,QAAQ,CAAC,OAAO,QAAQ;AACvC,UAAI,CAAC,WAAW,IAAI,IAAI,YAAY,CAAC,EAAG,YAAW,IAAI,KAAK,KAAK;AAAA,IACnE,CAAC;AACD,UAAM,MAAM,MAAM,SAAS,YAAY;AACvC,UAAM,YAAY,IAAI,SAAS,KAAK,EAAE,QAAQ,SAAS,QAAQ,YAAY,SAAS,YAAY,SAAS,WAAW,CAAC;AAIrH,WAAO,WAAW,iBAAiB,WAAW,eAAe,SAAS,IAAI;AAAA,EAC5E;AACF;;;AClCO,SAAS,oBAAoB,EAAE,WAAW,GAAG;AAClD,QAAM,QAAQ,WAAW,WAAW;AACpC,SAAO,OAAO,SAAS;AACrB,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,IAAI,QAAQ,sCAAsC;AAAA,QAC5D,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC;AAAA,MACjC,CAAC;AACD,YAAM,MAAM,MAAM,GAAG;AAAA,IACvB,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI,OAAO,CAAC;AACZ,QAAI;AACF,aAAO,MAAM,IAAI,MAAM,EAAE,KAAK;AAAA,IAChC,QAAQ;AAAA,IAER;AACA,WAAO,EAAE,MAAM,KAAK,MAAM,SAAS,KAAK,WAAW,wBAAwB;AAAA,EAC7E;AACF;AAEA,eAAsB,iBAAiB,UAAU,CAAC,GAAG;AACnD,QAAM,UAAU,QAAQ;AACxB,QAAM,eAAe,QAAQ,YAAY;AAKzC,QAAM,SAAS,MAAM,cAAc,OAAO;AAC1C,QAAM,cACJ,UAAU,iBAAiB,KAAK,QAAQ,sBAAsB,QAC1D,0BAA0B;AAAA,IACxB;AAAA,IACA,SAAS,eAAe,QAAQ,iBAAiB,QAAQ,OAAO;AAAA,IAChE,YAAY,QAAQ;AAAA,IACpB,WAAW,QAAQ;AAAA,EACrB,CAAC,IACD;AAMN,MAAI,aAAa;AAEjB,QAAM,WAAW,CAAC,cAAc;AAC9B,WAAO,cAAc,WAAW,UAAU;AAC1C,QAAI,WAAY,YAAW,OAAO,SAAS;AAC3C,QAAI,eAAe,WAAW,SAAS,SAAS;AAC9C,kBAAY,OAAO;AAAA,QACjB,MAAM,UAAU;AAAA,QAChB,QAAQ,UAAU;AAAA,QAClB,MAAM,UAAU;AAAA,QAChB,IAAI,UAAU;AAAA,QACd,WAAW,UAAU;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,QAAQ,UAAU,OAAO;AAI/B,QAAM,gBAAgB,OAAO,QAAQ,aAAa,IAAI,IAAI,OAAO,QAAQ,aAAa,IAAI;AAG1F,QAAM,YAAY,MAAM,iBAAiB,OAAO;AAgBhD,MAAI,QAAQ,YAAY,CAAC,WAAW;AAClC,UAAM,UACJ,qDACA,QAAQ,WACR;AAEF,WAAO,SAAS,IAAI,MAAM,OAAO,GAAG,SAAS;AAC7C,YAAQ,KAAK,OAAO;AAAA,EACtB;AACA,QAAM,SAAS,MAAM,aAAa,SAAS,OAAO,EAAE,WAAW,eAAe,UAAU,CAAC;AAWzF,MAAI,qBAAqB;AACzB,MAAI,QAAQ,qBAAqB,QAAQ,QAAQ,YAAY,iBAAiB,GAAG;AAC/E,QAAI,CAAC,WAAW;AACd,2BAAqB;AACrB,YAAM,UACJ,yDACA,QAAQ,WACR;AACF,aAAO,SAAS,IAAI,MAAM,OAAO,GAAG,SAAS;AAC7C,cAAQ,KAAK,OAAO;AAAA,IACtB,OAAO;AACL,2BAAqB;AACrB,mBAAa,wBAAwB;AAAA,QACnC,UAAU,QAAQ;AAAA,QAClB,SAAS,QAAQ;AAAA,QACjB;AAAA;AAAA;AAAA;AAAA,QAIA,YAAY,MAAM,MAAM,KAAK,IAAI,QAAQ;AAAA,QACzC,WAAW,QAAQ;AAAA,QACnB,SAAS,QAAQ;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,OAAO,YAAY,SAAS,MAAM;AAItC,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AAIJ,QAAM,cAAc,CAAC,cAAc;AACjC,UAAM,WAAW,UAAU,YAAY,CAAC;AACxC,mBAAe,QAAQ,UAAU,SAAS;AAC1C,oBAAgB,CAAC,GAAI,QAAQ,iBAAiB,wBAAyB,GAAG,QAAQ,UAAU,UAAU,CAAC;AACvG,gBAAY,kBAAkB,aAAa;AAC3C,kBAAc,CAAC,GAAI,QAAQ,eAAe,sBAAuB,GAAG,QAAQ,UAAU,QAAQ,CAAC;AAC/F,aAAS,IAAI,WAAW;AAAA,MACtB,UAAU;AAAA,MACV,YAAY,UAAU;AAAA,MACtB,gBAAgB,UAAU;AAAA,MAC1B;AAAA,IACF,CAAC;AAGD,sBAAkB,cAAc,IAAI,CAAC,UAAU;AAAA,MAC7C;AAAA,MACA,QAAQ,IAAI,WAAW,EAAE,UAAU,CAAC,IAAI,GAAG,QAAQ,CAAC;AAAA,MACpD,WAAW,KAAK,WAAW,YAAY,KAAK,WAAW,WAAW,iBAAiB,IAAI,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,MAK3F,cAAc,KAAK,WAAW,YAAY,KAAK,WAAW,aAAa,iBAAiB,IAAI;AAAA;AAAA;AAAA;AAAA,MAI5F,WAAW,MAAM,QAAQ,KAAK,SAAS,KAAK,KAAK,UAAU,SACvD,KAAK,UAAU,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,YAAY,CAAC,IACjD;AAAA,IACN,EAAE;AACF,mBAAe,IAAI,WAAW,EAAE,UAAU,aAAa,QAAQ,CAAC;AAAA,EAClE;AAEA,cAAY,MAAM;AASlB,QAAM,aAAa,uBAAO,OAAO,IAAI;AACrC,QAAM,SAAS,OAAO,QAAQ,WAAW,aAAa,QAAQ,SAAS;AACvE,QAAM,aAAa,CAAC,OAAO,QAAQ,WAAW;AAC5C,UAAM,MAAM,GAAG,KAAK,IAAI,MAAM;AAC9B,eAAW,GAAG,KAAK,WAAW,GAAG,KAAK,KAAK;AAG3C,WAAO,QAAQ,EAAE,OAAO,QAAQ,QAAQ,OAAO,WAAW,GAAG,EAAE,GAAG,QAAQ;AAAA,EAC5E;AAEA,QAAM,SACJ,OAAO,QAAQ,aAAa,aACxB,QAAQ,WACR,MAAO,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW;AASvE,QAAM,WAAW,CAAC,SAAU,MAAM,gBAAgB,YAAY,YAAY;AAE1E,QAAM,SAAS,CAAC,OAAO,QAAQ,OAAOC,QAAO,MAAM,CAAC,MAAM;AACxD,QAAI,CAAC,UAAU,CAAC,OAAO,QAAS,QAAOA,OAAM;AAC7C,UAAM,gBAAgB,SAAS,OAAO,IAAI;AAC1C,aAAS;AAAA,MACP;AAAA;AAAA;AAAA,MAGA,MAAM;AAAA,MACN,UAAU,OAAO,MAAM;AAAA,MACvB,MAAM,OAAO;AAAA,MACb,SAAS,OAAO;AAAA,MAChB,QAAQ,IAAI;AAAA,MACZ,MAAM,IAAI;AAAA,MACV,IAAI,IAAI;AAAA,MACR,WAAW,IAAI;AAAA,IACjB,CAAC;AACD,WAAO,kBAAkB,UAAU,MAAM,IAAIA,OAAM;AAAA,EACrD;AAKA,QAAM,aAAa,CAAC,MAAM,MAAM,WAAW;AACzC,QAAI,YAAY;AAChB,UAAM,aAAa,CAAC;AACpB,UAAM,kBAAkB,CAAC;AACzB,QAAI,YAAY;AAChB,eAAW,EAAE,MAAM,QAAQ,IAAI,WAAW,WAAW,YAAY,KAAK,iBAAiB;AAGrF,UAAI,WAAW;AACb,YAAI,cAAc,KAAM,aAAY,KAAK,YAAY;AACrD,YAAI,CAAC,UAAU,KAAK,CAAC,MAAM,UAAU,SAAS,CAAC,CAAC,EAAG;AAAA,MACrD;AACA,UAAI;AACJ,UAAI;AAKF,iBAAS,GAAG,SAAS,EAAE,GAAI,UAAU,CAAC,GAAI,WAAW,EAAE,GAAG,MAAM,MAAM,KAAK,EAAE,CAAC;AAAA,MAChF,SAAS,KAAK;AACZ,eAAO,SAAS,KAAK,SAAS;AAC9B;AAAA,MACF;AACA,UAAI,CAAC,OAAO,QAAS;AAIrB,YAAM,eAAe,SAAS,IAAI;AAClC,eAAS,EAAE,OAAO,YAAY,MAAM,cAAc,UAAU,KAAK,UAAU,MAAM,SAAS,OAAO,QAAQ,CAAC;AAC1G,UAAI,iBAAiB,QAAS;AAC9B,UAAI,aAAa,UAAU,QAAQ;AAEjC,cAAM,gBAAgB,UAAU,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ;AACzD,YAAI,eAAe,cAAc,QAAQ;AACvC,cAAI,CAAC,UAAW,aAAY;AAAA,QAC9B,OAAO;AACL,qBAAW,KAAK,EAAE,MAAM,UAAU,CAAC;AAAA,QACrC;AAAA,MACF,WAAW,iBAAiB,KAAK,MAAM,EAAG,iBAAgB,KAAK,IAAI;AAAA,eAC1D,CAAC,UAAW,aAAY;AAAA,IACnC;AACA,QAAI,SAAS,WAAY,CAAC,aAAa,CAAC,WAAW,UAAU,CAAC,gBAAgB,OAAS,QAAO,EAAE,SAAS,OAAO;AAChH,QAAI,UAAW,QAAO,EAAE,SAAS,QAAQ;AACzC,QAAI,OAAO;AAIX,UAAM,UAAU,EAAE,GAAI,KAAK,WAAW,CAAC,EAAG;AAC1C,eAAW,EAAE,MAAM,UAAU,KAAK,YAAY;AAC5C,YAAM,OAAO,OAAO,KAAK,QAAQ;AAIjC,YAAM,YAAY,KAAK,WAAW,WAAW,aAAa;AAC1D,YAAM,gBAAgB,UAAU,OAAO,CAAC,MAAM,EAAE,QAAQ;AACxD,YAAM,gBAAgB,UAAU,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ;AACzD,UAAI,cAAc,OAAQ,QAAO,mBAAmB,MAAM,eAAe,MAAM,WAAW,SAAS;AACnG,UAAI,CAAC,cAAc,OAAQ;AAC3B,aAAO,eAAe,MAAM,eAAe,MAAM,SAAS;AAC1D,UAAI,UAAW;AACf,iBAAW,QAAQ,OAAO,KAAK,OAAO,GAAG;AACvC,cAAM,QAAQ,QAAQ,IAAI;AAC1B,YAAI,OAAO,UAAU,UAAU;AAC7B,kBAAQ,IAAI,IAAI,eAAe,OAAO,eAAe,IAAI;AAAA,QAC3D,WAAW,MAAM,QAAQ,KAAK,GAAG;AAE/B,kBAAQ,IAAI,IAAI,MAAM,IAAI,CAAC,SAAU,OAAO,SAAS,WAAW,eAAe,MAAM,eAAe,IAAI,IAAI,IAAK;AAAA,QACnH;AAAA,MACF;AAAA,IACF;AACA,eAAW,QAAQ,gBAAiB,qBAAoB,SAAS,IAAI;AACrE,WAAO,EAAE,SAAS,UAAU,MAAM,QAAQ;AAAA,EAC5C;AAIA,QAAM,sBAAsB,CAAC,YAAY;AACvC,QAAI;AACF,YAAM,IAAI,IAAI,IAAI,QAAQ,GAAG;AAC7B,YAAM,UAAU,aAAa,QAAQ,OAAO;AAG5C,UAAI,CAAC,QAAQ,KAAM,SAAQ,OAAO,EAAE;AACpC,aAAO,EAAE,QAAQ,QAAQ,QAAQ,aAAa,EAAE,WAAW,EAAE,QAAQ,QAAQ;AAAA,IAC/E,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,qBAAqB,CAAC,QAAS,MAAM,EAAE,QAAQ,IAAI,QAAQ,aAAa,IAAI,KAAK,SAAS,IAAI,WAAW,CAAC,EAAE,IAAI;AAGtH,QAAM,aAAa,OAAO,UAAU,WAAW;AAC7C,UAAM,OAAO,MAAM,iBAAiB,UAAU,SAAS;AACvD,QAAI,KAAK,MAAM;AAGb,UAAI,KAAK,SAAS,iBAAkB,YAAW,YAAY,KAAK,MAAM,EAAE,QAAQ,UAAU,OAAO,CAAC;AAClG,aAAO;AAAA,IACT;AACA,UAAM,OAAO,KAAK;AAClB,UAAM,IAAI,WAAW,MAAM,EAAE,QAAQ,SAAS,QAAQ,SAAS,aAAa,SAAS,OAAO,EAAE,GAAG,MAAM;AACvG,QAAI,EAAE,YAAY,QAAS,QAAO,aAAa;AAC/C,QAAI,EAAE,YAAY,SAAU,QAAO,gBAAgB,UAAU,EAAE,MAAM,EAAE,OAAO;AAC9E,WAAO;AAAA,EACT;AAKA,QAAM,mBAAmB,CAAC,KAAK,WAAW;AACxC,UAAM,YAAY,IAAI,MAAM,KAAK,GAAG;AACpC,UAAM,UAAU,IAAI,IAAI,KAAK,GAAG;AAChC,UAAM,SAAS,CAAC;AAChB,QAAI,OAAO;AACX,QAAI,WAAW;AACf,UAAM,MAAM;AACZ,UAAM,UAAU,CAAC,OAAO,QAAQ;AAC9B,UAAI,SAAS,KAAM;AACnB,YAAM,MAAM,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,OAAO,OAAO,QAAQ,WAAW,MAAM,MAAM;AACtG,cAAQ,IAAI;AACZ,UAAI,OAAO,KAAK;AAId,mBAAW,KAAK,OAAQ,WAAU,CAAC;AACnC,eAAO,SAAS;AAChB,kBAAU,GAAG;AACb,mBAAW;AACX,mBAAW,YAAY,YAAY,EAAE,OAAO,KAAK,CAAC;AAClD;AAAA,MACF;AACA,aAAO,KAAK,GAAG;AAAA,IACjB;AACA,QAAI,QAAQ,SAAU,OAAO,KAAK,IAAI;AACpC,UAAI,SAAU,QAAO,UAAU,OAAO,KAAK,EAAE;AAC7C,cAAQ,OAAO,GAAG;AAClB,UAAI,OAAO,QAAQ,WAAY,KAAI;AAAA,eAC1B,OAAO,OAAO,WAAY,IAAG;AACtC,aAAO;AAAA,IACT;AACA,QAAI,MAAM,SAAU,OAAO,KAAK,IAAI;AAClC,UAAI,OAAO,UAAU,YAAY;AAAE,aAAK;AAAO,gBAAQ;AAAW,cAAM;AAAA,MAAW,WAC1E,OAAO,QAAQ,YAAY;AAAE,aAAK;AAAK,cAAM;AAAA,MAAW;AACjE,UAAI,UAAU;AAAE,YAAI,SAAS,KAAM,WAAU,OAAO,GAAG;AAAG,eAAO,QAAQ,EAAE;AAAA,MAAG;AAC9E,cAAQ,OAAO,GAAG;AAClB,UAAI,SAAU,QAAO,QAAQ,EAAE;AAC/B,YAAM,SAAS,OAAO,OAAO,MAAM;AACnC,UAAI,KAAK,IAAI,YAAY,IAAI,UAAU,cAAc,IAAI;AACzD,UAAI,MAAM,QAAQ,EAAE,EAAG,MAAK,GAAG,CAAC;AAChC,YAAM,OAAO,sBAAsB,EAAE;AAErC,UAAI,SAAS,UAAW,SAAS,WAAW,YAAY,MAAM,GAAI;AAChE,mBAAW,YAAY,SAAS,SAAU,gBAAgB,EAAE,MAAM,sBAAsB,gBAAgB,0BAA2B,aAAa;AAChJ,mBAAW,KAAK,OAAQ,WAAU,CAAC;AACnC,eAAO,QAAQ,EAAE;AAAA,MACnB;AACA,YAAM,OAAO,OAAO,SAAS,MAAM;AACnC,UAAI;AACJ,UAAI;AACF,YAAI,WAAW,MAAM,EAAE,QAAQ,IAAI,YAAY,SAAS,IAAI,aAAa,IAAI,WAAW,IAAI,CAAC,EAAE,GAAG,MAAM;AAAA,MAC1G,SAAS,KAAK;AACZ,eAAO,SAAS,KAAK,SAAS;AAC9B,mBAAW,KAAK,OAAQ,WAAU,CAAC;AACnC,eAAO,QAAQ,EAAE;AAAA,MACnB;AACA,UAAI,EAAE,YAAY,SAAS;AACzB,YAAI,aAAa;AACjB,YAAI;AAAE,cAAI,UAAU,gBAAgB,kBAAkB;AAAA,QAAG,QAAQ;AAAA,QAAqB;AACtF,eAAO,QAAQ,KAAK,UAAU,EAAE,OAAO,4DAA4D,CAAC,GAAG,EAAE;AAAA,MAC3G;AACA,UAAI,EAAE,YAAY,UAAU;AAC1B,YAAI;AAAE,cAAI,gBAAgB,IAAI,aAAa,gBAAgB;AAAA,QAAG,QAAQ;AAAA,QAAe;AACrF,YAAI,EAAE,WAAW,IAAI,WAAW;AAC9B,gBAAM,UAAU,IAAI,aAAa,IAAI,WAAW,IAAI,CAAC;AACrD,qBAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,EAAE,OAAO,GAAG;AAGrD,gBAAI,KAAK,YAAY,MAAM,iBAAkB;AAC7C,gBAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,kBAAI;AAAE,oBAAI,gBAAgB,IAAI,aAAa,IAAI;AAAA,cAAG,QAAQ;AAAA,cAAe;AAAA,YAC3E,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,kBAAI;AAAE,oBAAI,UAAU,MAAM,KAAK;AAAA,cAAG,QAAQ;AAAA,cAA8B;AAAA,YAC1E,WAAW,OAAO,UAAU,YAAY,QAAQ,IAAI,MAAM,OAAO;AAC/D,kBAAI;AAAE,oBAAI,UAAU,MAAM,KAAK;AAAA,cAAG,QAAQ;AAAA,cAA8B;AAAA,YAC1E;AAAA,UACF;AAAA,QACF;AACA,eAAO,QAAQ,EAAE,MAAM,EAAE;AAAA,MAC3B;AACA,iBAAW,KAAK,OAAQ,WAAU,CAAC;AACnC,aAAO,QAAQ,EAAE;AAAA,IACnB;AAAA,EACF;AAGA,QAAM,QAAQ,IAAI,KAAK,QAAQ,cAAc,CAAC,GAAG,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC;AACpF,QAAM,oBAAoB,CAAC,KAAK,MAAM,WAAW;AAC/C,QAAI,QAAQ,MAAM,IAAI,KAAK,YAAY,CAAC,EAAG,QAAO;AAClD,QAAI;AACJ,QAAI;AACF,eAAS,aAAa,SAAS,EAAE,SAAS,EAAE,KAAK,MAAM,OAAO,EAAE,CAAC;AAAA,IACnE,SAAS,KAAK;AACZ,aAAO,SAAS,KAAK,SAAS;AAC9B,aAAO;AAAA,IACT;AACA,QAAI,CAAC,OAAO,QAAS,QAAO;AAG5B,UAAM,aAAa,SAAS,OAAO,IAAI;AACvC,aAAS,EAAE,OAAO,UAAU,MAAM,YAAY,UAAU,OAAO,MAAM,UAAU,MAAM,OAAO,MAAM,SAAS,OAAO,QAAQ,CAAC;AAC3H,WAAO,eAAe;AAAA,EACxB;AAEA,QAAM,aAAa;AAAA,IACjB,IAAI,OAAO;AACT,aAAO;AAAA,IACT;AAAA,IACA,IAAI,QAAQ;AACV,aAAO,EAAE,SAAS,cAAc,UAAU,eAAe,QAAQ,YAAY;AAAA,IAC/E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,WAAW;AACT,aAAO,EAAE,SAAS,EAAE,GAAG,WAAW,EAAE;AAAA,IACtC;AAAA;AAAA;AAAA,IAIA,gBAAgB,CAAC,UAAU,YAAY,WAAW,UAAU,UAAU,oBAAoB,OAAO,IAAI,MAAS;AAAA;AAAA,IAG9G,aAAa;AACX,aAAO,OAAO,YAAY;AACxB,YAAI;AACJ,YAAI;AACF,mBAAS,OAAO,SAAS,MAAM,iBAAiB,OAAO,CAAC;AAAA,QAC1D,SAAS,KAAK;AACZ,iBAAO,SAAS,KAAK,SAAS;AAC9B,iBAAO;AAAA,QACT;AACA,eAAO,OAAO,WAAW,QAAQ,MAAM,cAAc,QAAQ,OAAO,GAAG,MAAM,MAAM,iBAAiB,OAAO,CAAC;AAAA,MAC9G;AAAA,IACF;AAAA;AAAA,IAGA,MAAM,SAAS;AACb,YAAM,QAAQ,WAAW,WAAW;AACpC,aAAO,OAAO,YAAY,SAAS;AACjC,cAAM,UAAU,MAAM,MAAM,OAAO;AACnC,YAAI,QAAS,QAAO;AACpB,cAAM,WAAW,MAAM,QAAQ,SAAS,GAAG,IAAI;AAC/C,eAAO,WAAW,UAAU,oBAAoB,OAAO,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA;AAAA;AAAA,IAIA,QAAQ,cAAc,CAAC,GAAG;AACxB,aAAO,CAAC,KAAK,KAAK,SAAS;AACzB,YAAI;AACJ,YAAI;AACF,mBAAS,OAAO,SAAS,GAAG;AAAA,QAC9B,SAAS,KAAK;AACZ,iBAAO,SAAS,KAAK,SAAS;AAC9B,cAAI,YAAY,gBAAiB,kBAAiB,KAAK,mBAAmB,GAAG,CAAC;AAC9E,iBAAO,KAAK;AAAA,QACd;AACA;AAAA,UACE;AAAA,UACA;AAAA,UACA,MAAM;AACJ,gBAAI,qBAAqB,CAAC,MAAM,IAAI,UAAU,CAAC,CAAC,GAAG;AACjD,kBAAI,OAAO,GAAG,EAAE,KAAK,MAAM,EAAE,KAAK,gBAAgB,EAAE,KAAK,IAAI,eAAe,IAAI,OAAO,KAAK,MAAM,QAAQ,MAAM,GAAG,CAAC,CAAC;AAAA,YACvH,OAAO;AACL,kBAAI,OAAO,GAAG,EAAE,KAAK,UAAU,MAAM,CAAC;AAAA,YACxC;AAAA,UACF;AAAA,UACA,MAAM;AACJ,gBAAI,YAAY,gBAAiB,kBAAiB,KAAK,mBAAmB,GAAG,CAAC;AAC9E,iBAAK;AAAA,UACP;AAAA,UACA,gBAAgB,GAAG;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,KAAK,cAAc,CAAC,GAAG;AACrB,YAAM,WAAW,YAAY,gBAAgB,OAAO;AACpD,aAAO,CAAC,KAAK,KAAK,SAAS;AAKzB,YAAI,IAAI,iBAAiB,IAAI,SAAS,QAAW;AAC/C,4BAAkB,KAAK,KAAK,MAAM,IAAI,IAAI,IAAI;AAC9C;AAAA,QACF;AAEA,cAAM,SAAS,CAAC;AAChB,YAAI,OAAO;AACX,YAAI,WAAW;AACf,YAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,kBAAQ,MAAM;AACd,cAAI,OAAO,UAAU;AACnB,uBAAW;AACX;AAAA,UACF;AACA,iBAAO,KAAK,KAAK;AAAA,QACnB,CAAC;AACD,YAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,iBAAO,SAAS,KAAK,SAAS;AAC9B,eAAK;AAAA,QACP,CAAC;AACD,YAAI,GAAG,OAAO,MAAM;AAClB,cAAI,SAAU,YAAW,WAAW,YAAY,EAAE,OAAO,MAAM,OAAO,SAAS,CAAC;AAChF,4BAAkB,KAAK,KAAK,MAAM,WAAW,KAAK,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM,CAAC;AAAA,QAC1F,CAAC;AAAA,MACH;AAKA,eAAS,kBAAkB,KAAK,KAAK,MAAM,SAAS,YAAY;AAC9D,YAAI;AACJ,YAAI;AACJ,YAAI;AACF,mBAAS,gBAAgB,KAAK,OAAO;AACrC,cAAI,eAAe,UAAa,eAAe,KAAM,QAAO,OAAO;AACnE,mBAAS,OAAO,SAAS,MAAM;AAAA,QACjC,SAAS,KAAK;AACZ,iBAAO,SAAS,KAAK,SAAS;AAC9B,iBAAO,KAAK;AAAA,QACd;AACA;AAAA,UACE;AAAA,UACA;AAAA,UACA,MAAM;AACJ,gBAAI,aAAa;AACjB,gBAAI,qBAAqB,CAAC,MAAM,IAAI,UAAU,CAAC,CAAC,GAAG;AACjD,kBAAI,UAAU,gBAAgB,0BAA0B;AACxD,kBAAI,IAAI,gBAAgB,EAAE,KAAK,IAAI,OAAO,KAAK,MAAM,QAAQ,MAAM,GAAG,CAAC,CAAC;AAAA,YAC1E,OAAO;AACL,kBAAI,UAAU,gBAAgB,kBAAkB;AAChD,kBAAI,IAAI,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,YAC3C;AAAA,UACF;AAAA,UACA,MAAM;AAGJ,gBAAI,IAAI,SAAS,OAAW,KAAI,OAAO,OAAO;AAC9C,gBAAI,YAAY,gBAAiB,kBAAiB,KAAK,mBAAmB,GAAG,CAAC;AAC9E,iBAAK;AAAA,UACP;AAAA,UACA,gBAAgB,GAAG;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,QAAQ,QAAQ;AAClB,eAAW,kBAAkB,MAAM,mBAAmB;AAAA,MACpD,aAAa;AAAA,MACb,SAAS,QAAQ;AAAA;AAAA;AAAA,MAGjB,QAAQ,CAAC,EAAE,QAAQ,OAAO,MAAM,WAAW,UAAU,QAAQ,MAAM;AAAA,MACnE,WAAW,QAAQ,cAAc;AAAA,MACjC,YAAY,QAAQ;AAAA,IACtB,CAAC;AAAA,EACH;AAOA,QAAM,MAAM,QAAQ,QAAQ,OAAO,YAAY,cAAc,QAAQ,IAAI,IAAI;AAC7E,QAAM,OAAO,QAAQ,QAAQ,YAAY,QAAQ,KAAK;AACtD,QAAM,gBAAgB,QAAQ,kBAAkB,OAAO,YAAY,cAAc,QAAQ,IAAI,4BAA4B;AACzH,QAAM,cAAc,SAAS,QAAQ,YAAY,KAAK,QAAQ,aAAa;AAO3E,MAAI,WAAW;AACf,MAAI,eAAe,QAAQ,YAAY,QAAQ,mBAAmB,SAAS,KAAK;AAC9E,QAAI;AACF,OAAC,EAAE,gBAAgB,SAAS,IAAI,MAAM,OAAO,gCAAuB;AAAA,IACtE,SAAS,KAAK;AACZ,aAAO,SAAS,KAAK,SAAS;AAAA,IAChC;AAAA,EACF;AAEA,QAAM,iBAAiB,YAAY;AACjC,QAAI,UAAU;AACZ,UAAI;AACF,cAAM,SAAS,GAAG;AAAA,MACpB,SAAS,KAAK;AACZ,eAAO,SAAS,KAAK,SAAS;AAAA,MAChC;AAAA,IACF;AACA,UAAM,OAAO,MAAM,aAAa,SAAS,OAAO,EAAE,WAAW,QAAQ,kBAAkB,UAAU,CAAC;AAClG,WAAO,YAAY,SAAS,IAAI;AAChC,gBAAY,IAAI;AAIhB,QAAI,WAAY,YAAW,cAAc,MAAM,MAAM,KAAK,IAAI,QAAQ,IAAI;AAO1E,WAAO,KAAK,UAAU,EAAE,IAAI,KAAK;AAAA,EACnC;AAEA,MAAI,MAAM;AAER,eAAW,UAAU,MAAM,eAAe;AAG1C,eAAW,iBAAiB,MAAM,mBAAmB,gBAAgB,aAAa;AAAA,EACpF;AAEA,QAAM,OAAO,QAAQ,YAAY,KAAK,OAClC,aAAa,gBAAgB,EAAE,WAAW,QAAQ,WAAW,QAAQ,CAAC,IACtE;AAMJ,aAAW,OAAO,MAAM;AACtB,UAAM,KAAK;AACX,iBAAa,KAAK;AAClB,gBAAY,KAAK;AAAA,EACnB;AAEA,aAAW,cAAc,WAAW;AAGpC,aAAW,qBAAqB;AAEhC,MAAI,WAAY,YAAW,kBAAkB,MAAM,WAAW,OAAO;AAErE,SAAO;AACT;AAMA,SAAS,YAAY,SAAS,QAAQ;AACpC,QAAM,MAAM,OAAO,YAAY,cAAc,QAAQ,KAAK,kBAAkB;AAC5E,MAAI,QAAQ,WAAW,QAAQ,UAAW,QAAO;AACjD,MAAI,QAAQ,gBAAgB,WAAW,QAAQ,gBAAgB,UAAW,QAAO,OAAO;AACxF,MAAI,SAAS,SAAS,QAAS,QAAO;AACtC,MAAI,SAAS,SAAS,UAAW,QAAO;AACxC,SAAO;AACT;AAUA,IAAM,mBAAmB,CAAC,4BAA4B,oBAAoB;AAS1E,eAAe,oBAAoB,SAAS,OAAO;AACjD,MAAI,OAAO,YAAY,eAAe,OAAO,QAAQ,QAAQ,WAAY,QAAO;AAEhF,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,UAAM,CAAC,IAAI,OAAO,IAAI,MAAM,QAAQ,IAAI,CAAC,OAAO,IAAS,GAAG,OAAO,MAAW,CAAC,CAAC;AAChF,mBAAe,GAAG;AAClB,WAAO,QAAQ;AAAA,EACjB,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,SAAS,OAAO,QAAQ,IAAI;AACxC,aAAW,YAAY,kBAAkB;AACvC,QAAI;AACF,YAAM,QAAQ,KAAK,MAAM,aAAa,KAAK,KAAK,QAAQ,GAAG,MAAM,CAAC,IAAI,KAAK;AAC3E,UAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAAG,QAAO;AAAA,IAC5D,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAGA,eAAe,cAAc,SAAS;AACpC,MAAI,OAAO,SAAS,WAAW,YAAY,QAAQ,OAAO,SAAS,EAAG,QAAO,QAAQ;AACrF,MAAI,OAAO,YAAY,aAAa;AAClC,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,EAAG,QAAO;AAAA,EAChE;AAEA,SAAO,oBAAoB,SAAS,QAAQ;AAC9C;AAWA,eAAe,iBAAiB,SAAS;AACvC,MAAI,OAAO,SAAS,cAAc,YAAY,QAAQ,UAAU,SAAS,EAAG,QAAO,QAAQ;AAC3F,MAAI,OAAO,YAAY,aAAa;AAClC,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,EAAG,QAAO;AAAA,EAChE;AACA,QAAM,WAAW,MAAM,oBAAoB,SAAS,WAAW;AAC/D,MAAI,aAAa,OAAW,QAAO;AAEnC,SAAO,cAAc,OAAO;AAC9B;AAIA,SAAS,QAAQ,OAAO,OAAO;AAC7B,UAAQ,SAAS,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,SAAS,eAAe,KAAK;AACrE;AAMA,SAAS,gBAAgB,IAAI;AAC3B,SAAO,OAAO,MAAM,EAAE,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK;AAC3D;AACA,SAAS,sBAAsB,IAAI;AACjC,QAAM,OAAO,gBAAgB,EAAE;AAC/B,MAAI,SAAS,oBAAqB,QAAO;AACzC,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,iEAAiE,KAAK,IAAI,EAAG,QAAO;AACxF,MAAI,SAAS,2BAA4B,QAAO;AAChD,SAAO;AACT;AAEA,SAAS,YAAY,OAAO;AAC1B,QAAM,IAAI,KAAK,IAAI,MAAM,QAAQ,GAAG;AACpC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,IAAI,MAAM,CAAC;AACjB,QAAI,MAAM,EAAG,QAAO;AACpB,QAAI,IAAI,KAAM,IAAI,MAAM,IAAI,GAAK;AAAA,EACnC;AACA,SAAO,IAAI,KAAK,OAAO,IAAI;AAC7B;AAKA,eAAe,iBAAiB,UAAU,MAAM,oBAAoB;AAClE,MAAI,CAAC,YAAY,OAAO,SAAS,UAAU,WAAY,QAAO,EAAE,MAAM,iBAAiB;AACvF,QAAM,KAAK,SAAS,SAAS,MAAM,cAAc,KAAK;AACtD,QAAM,OAAO,sBAAsB,EAAE;AACrC,MAAI,SAAS,OAAQ,QAAO,EAAE,MAAM,gBAAgB,EAAE,MAAM,sBAAsB,gBAAgB,wBAAwB;AAC1H,QAAM,QAAQ,SAAS;AACvB,QAAM,MAAM,OAAO,SAAS,SAAS,MAAM,gBAAgB,KAAK,CAAC;AACjE,MAAI,OAAO,MAAM,IAAK,QAAO,EAAE,MAAM,WAAW;AAChD,MAAI;AACJ,MAAI;AACF,YAAQ,SAAS,MAAM;AAAA,EACzB,QAAQ;AACN,WAAO,EAAE,MAAM,eAAe;AAAA,EAChC;AAIA,QAAM,OAAO,MAAM;AACnB,MAAI,QAAQ,OAAO,KAAK,cAAc,YAAY;AAChD,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,SAAS,CAAC;AAChB,QAAI,OAAO;AACX,QAAI,OAAO;AACX,QAAI,UAAU,CAAC;AACf,QAAI;AACF,iBAAS;AACP,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AACV,YAAI,CAAC,MAAO;AACZ,YAAI,CAAC,SAAS;AACZ,oBAAU;AACV,cAAI,YAAY,KAAK,EAAG,QAAO,EAAE,MAAM,cAAc;AAAA,QACvD;AACA,gBAAQ,MAAM;AACd,YAAI,KAAM;AACV,YAAI,OAAO,KAAK;AAAE,iBAAO;AAAM;AAAA,QAAU;AACzC,eAAO,KAAK,KAAK;AAAA,MACnB;AAAA,IACF,QAAQ;AACN,aAAO,EAAE,MAAM,cAAc;AAAA,IAC/B;AACA,QAAI,KAAM,QAAO,EAAE,MAAM,WAAW;AACpC,QAAI;AACF,aAAO,EAAE,MAAM,IAAI,YAAY,EAAE,OAAO,YAAY,QAAQ,IAAI,CAAC,EAAE;AAAA,IACrE,QAAQ;AACN,aAAO,EAAE,MAAM,gBAAgB;AAAA,IACjC;AAAA,EACF;AAEA,MAAI;AACF,UAAM,OAAO,MAAM,MAAM,KAAK;AAC9B,QAAI,KAAK,SAAS,IAAK,QAAO,EAAE,MAAM,WAAW;AACjD,QAAI,SAAS,YAAY,IAAI,YAAY,EAAE,OAAO,KAAK,MAAM,GAAG,GAAG,CAAC,CAAC,EAAG,QAAO,EAAE,MAAM,cAAc;AACrG,WAAO,EAAE,KAAK;AAAA,EAChB,QAAQ;AACN,WAAO,EAAE,MAAM,cAAc;AAAA,EAC/B;AACF;AAEA,SAAS,YAAY,QAAQ,OAAO;AAClC,QAAM,MAAM,IAAI,WAAW,KAAK;AAChC,MAAI,SAAS;AACb,aAAW,KAAK,QAAQ;AACtB,QAAI,IAAI,GAAG,MAAM;AACjB,cAAU,EAAE;AAAA,EACd;AACA,SAAO;AACT;AAEA,SAAS,aAAa,SAAS;AAC7B,QAAM,MAAM,CAAC;AACb,WAAS,UAAU,CAAC,GAAG,MAAM;AAAE,QAAI,EAAE,YAAY,CAAC,IAAI;AAAA,EAAG,CAAC;AAG1D,QAAM,aAAa,SAAS,eAAe;AAC3C,MAAI,cAAc,WAAW,OAAQ,KAAI,YAAY,IAAI;AACzD,SAAO;AACT;AAMA,SAAS,iBAAiB,MAAM;AAC9B,MAAI,QAAQ;AACZ,QAAM,OAAO,CAAC,UAAU;AACtB,eAAW,KAAK,SAAS,CAAC,GAAG;AAC3B,UAAI,MAAO;AACX,UAAI,MAAM,QAAQ,EAAE,KAAK,EAAG,MAAK,EAAE,KAAK;AACxC,YAAM,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,WAAW,EAAE,MAAM,SAAS,cAAc,EAAE,MAAM,SAAS;AACvG,UAAI,UAAU,MAAM,QAAQ,EAAE,SAAS,KAAK,EAAE,UAAU,OAAQ,SAAQ;AAAA,IAC1E;AAAA,EACF;AACA,OAAK,KAAK,OAAO;AACjB,SAAO;AACT;AAKA,SAAS,iBAAiB,MAAM;AAC9B,QAAM,MAAM,CAAC;AACb,QAAM,OAAO,CAAC,UAAU;AACtB,eAAW,KAAK,SAAS,CAAC,GAAG;AAC3B,UAAI,MAAM,QAAQ,EAAE,KAAK,EAAG,MAAK,EAAE,KAAK;AACxC,YAAM,IAAI,EAAE;AACZ,UAAI,CAAC,EAAG;AACR,UAAI,EAAE,SAAS,WAAW,OAAO,EAAE,UAAU,UAAU;AAGrD,cAAM,OAAO,WAAW,EAAE,KAAK;AAC/B,YAAI,MAAM;AACR,gBAAM,QAAQ,KAAK,MAAM,SAAS,GAAG,IAAI,KAAK,QAAQ,KAAK,QAAQ;AACnE,cAAI;AACF,gBAAI,KAAK,EAAE,IAAI,IAAI,OAAO,KAAK,QAAQ,KAAK,EAAE,CAAC;AAAA,UACjD,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF,YAAY,EAAE,SAAS,cAAc,EAAE,SAAS,cAAc,EAAE,SAAS,MAAM;AAC7E,YAAI,KAAK,EAAE,SAAS,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,MACvC,WAAW,EAAE,SAAS,sBAAsB,OAAO,EAAE,UAAU,UAAU;AAKvE,YAAI,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,OAAO,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AAAA,MACzE,WAAW,EAAE,SAAS,qBAAqB,EAAE,SAAS,YAAY,EAAE,SAAS,GAAG;AAG9E,cAAM,OAAO,MAAM,QAAQ,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,GAAG;AAClD,mBAAW,OAAO,MAAM;AACtB,cAAI,KAAK,EAAE,UAAU,OAAO,GAAG,EAAE,MAAM,GAAG,GAAG,WAAW,EAAE,MAAM,CAAC;AAAA,QACnE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,OAAK,KAAK,OAAO;AACjB,SAAO;AACT;AAMA,SAAS,WAAW,KAAK;AACvB,SAAO,OAAO,GAAG,EAAE,QAAQ,YAAY,CAAC,OAAO,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ,GAAG,CAAC,CAAC;AAC5H;AAIA,SAAS,eAAe,MAAM,WAAW,MAAM,WAAW;AACxD,MAAI,MAAM;AACV,aAAW,KAAK,WAAW;AACzB,QAAI,EAAE,GAAI,OAAM,IAAI,QAAQ,EAAE,IAAI,YAAY,CAAC,MAAM,UAAU,CAAC,IAAI,IAAI;AAAA,aAC/D,EAAE,UAAU;AAInB,iBAAW,SAAS,cAAc,KAAK,EAAE,SAAS,OAAO,EAAE,SAAS,KAAK,GAAG;AAC1E,cAAM,IAAI,MAAM,KAAK,EAAE,KAAK,YAAY,UAAU,KAAK,IAAI,IAAI;AAAA,MACjE;AAAA,IACF,WAAW,EAAE,SAAS;AAIpB,YAAM,KAAK,IAAI,OAAO,EAAE,QAAQ,QAAQ,uBAAuB,MAAM,GAAG,IAAI;AAC5E,YAAM,IAAI,QAAQ,IAAI,CAAC,MAAO,YAAY,UAAU,CAAC,IAAI,IAAK;AAAA,IAChE;AAAA,EACF;AACA,SAAO;AACT;AAIA,SAAS,YAAY,WAAW;AAC9B,SAAO,aAAa,QAAQ,cAAc,mBAAmB,cAAc,SAAS,cAAc;AACpG;AAIA,SAAS,mBAAmB,WAAW;AACrC,MAAI,CAAC,aAAa,CAAC,UAAU,KAAM,QAAO,MAAM;AAChD,SAAO,CAAC,UAAU;AAChB,QAAI;AACF,aAAO,WAAW,UAAU,MAAM,OAAO,UAAU,OAAO,SAAS;AAAA,IACrE,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,IAAM,qBAAqB,MAAM;AAOjC,SAAS,kBAAkB,OAAO;AAChC,MAAI,MAAM;AACV,aAAW,KAAK,SAAS,CAAC,GAAG;AAC3B,QAAI,KAAK,EAAE,iBAAiB,KAAM,QAAO;AACzC,UAAM,IAAI,OAAO,KAAK,EAAE,SAAS;AACjC,QAAI,OAAO,SAAS,CAAC,KAAK,IAAI,IAAK,OAAM;AAAA,EAC3C;AACA,SAAO;AACT;AAKA,SAAS,mBAAmB,MAAM,eAAe,MAAM,KAAK,WAAW;AACrE,MAAI,CAAC,cAAc,UAAU,OAAO,SAAS,YAAY,KAAK,SAAS,IAAK,QAAO;AACnF,QAAM,OAAO,KAAK,UAAU,EAAE,CAAC;AAC/B,MAAI,SAAS,OAAO,SAAS,IAAK,QAAO;AAIzC,QAAM,YAAY,gBAAgB,IAAI;AACtC,MAAI;AACJ,MAAI;AACF,UAAM,KAAK,MAAM,SAAS;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,UAAU;AACd,aAAW,KAAK,eAAe;AAC7B,UAAM,OAAO,mBAAmB,EAAE,SAAS;AAC3C,eAAW,KAAK,EAAE,UAAU,CAAC,QAAQ;AACnC,UAAI;AACF,YAAI,KAAK,IAAI,KAAK,GAAG;AAEnB,cAAI,OAAO,IAAI,GAAG,IAAI,YAAY,UAAU,OAAO,IAAI,KAAK,CAAC,IAAI;AACjE,oBAAU;AAAA,QACZ;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO,UAAU,eAAe,KAAK,UAAU,GAAG,CAAC,IAAI;AACzD;AAEA,IAAM,cAAc;AACpB,IAAM,eAAe;AAKrB,SAAS,gBAAgB,MAAM;AAC7B,MAAI,MAAM;AACV,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,KAAK,UAAU;AACjC,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,OAAO;AACT,aAAO;AACP,UAAI,OAAO,MAAM;AAAE,eAAO,KAAK,IAAI,CAAC,KAAK;AAAI,aAAK;AAAG;AAAA,MAAU;AAC/D,UAAI,OAAO,IAAK,SAAQ;AACxB;AACA;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AAAE,cAAQ;AAAM,aAAO;AAAI;AAAK;AAAA,IAAU;AAC1D,QAAI,OAAO,OAAQ,MAAM,OAAO,MAAM,KAAM;AAC1C,UAAI,IAAI,OAAO,MAAM,IAAI,IAAI;AAC7B,UAAI,SAAS;AACb,aAAO,IAAI,KAAK,UAAU,KAAK,CAAC,KAAK,OAAO,KAAK,CAAC,KAAK,KAAK;AAAE;AAAU;AAAA,MAAK;AAC7E,YAAM,OAAO,KAAK,CAAC;AACnB,YAAM,aAAa,SAAS,KAAK,SAAS,OAAO,SAAS,OAAO,SAAS;AAC1E,UAAI,cAAc,UAAU,IAAI;AAC9B,eAAO,IAAI,WAAW,GAAG,KAAK,MAAM,GAAG,CAAC,CAAC,GAAG,YAAY;AAAA,MAC1D,OAAO;AACL,eAAO,KAAK,MAAM,GAAG,KAAK,IAAI,CAAC;AAAA,MACjC;AACA,UAAI,IAAI,IAAI,IAAI,IAAI;AACpB;AAAA,IACF;AACA,WAAO;AACP;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,eAAe,MAAM;AAC5B,MAAI,CAAC,KAAK,SAAS,WAAW,EAAG,QAAO;AACxC,SAAO,KAAK,QAAQ,IAAI,OAAO,IAAI,WAAW,WAAW,YAAY,KAAK,GAAG,GAAG,IAAI;AACtF;AAKA,SAAS,iBAAiB,QAAQ;AAChC,SAAO,WAAW,gBAAgB,WAAW,mBAAmB,WAAW;AAC7E;AAEA,SAAS,oBAAoB,SAAS,MAAM;AAC1C,MAAI,KAAK,WAAW,iBAAiB;AACnC,eAAW,QAAQ,KAAK,kBAAkB,CAAC,EAAG,SAAQ,OAAO,IAAI,EAAE,YAAY,CAAC,IAAI;AACpF;AAAA,EACF;AACA,MAAI,KAAK,WAAW,cAAc;AAChC,UAAM,SAAS,KAAK,WAAW;AAC/B,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,KAAK,eAAe,CAAC,CAAC,GAAG;AAClE,YAAM,MAAM,OAAO,IAAI,EAAE,YAAY;AACrC,YAAM,UAAU,QAAQ,GAAG,KAAK,QAAQ,QAAQ,GAAG,MAAM;AACzD,UAAI,UAAU,QAAS;AACvB,cAAQ,GAAG,IAAI,OAAO,KAAK;AAAA,IAC7B;AACA;AAAA,EACF;AACA,MAAI,KAAK,WAAW,iBAAiB;AACnC,UAAM,SAAS,QAAQ,YAAY;AACnC,UAAM,QAAQ,KAAK,gBAAgB,CAAC;AACpC,QAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,cAAQ,YAAY,IAAI,OAAO,IAAI,CAAC,MAAO,OAAO,MAAM,WAAW,aAAa,GAAG,KAAK,IAAI,CAAE;AAAA,IAChG,WAAW,OAAO,WAAW,UAAU;AACrC,cAAQ,YAAY,IAAI,aAAa,QAAQ,KAAK;AAAA,IACpD;AAAA,EACF;AACF;AAKA,IAAM,YAAY,CAAC,UAAU,OAAO,MAAM;AAE1C,SAAS,aAAa,QAAQ,EAAE,WAAW,MAAM,SAAS,MAAM,WAAW,MAAM,IAAI,CAAC,GAAG;AACvF,MAAI,MAAM,OAAO,MAAM;AACvB,MAAI,YAAY,CAAC,gBAAgB,KAAK,GAAG,EAAG,QAAO;AACnD,MAAI,UAAU,CAAC,cAAc,KAAK,GAAG,EAAG,QAAO;AAC/C,QAAM,YAAY,UAAU,KAAK,CAAC,MAAM,OAAO,aAAa,YAAY,EAAE,YAAY,MAAM,SAAS,YAAY,CAAC;AAClH,MAAI,aAAa,CAAC,oBAAoB,KAAK,GAAG,EAAG,QAAO,cAAc,SAAS;AAC/E,SAAO;AACT;AAEA,SAAS,gBAAgB,UAAU,MAAM,iBAAiB;AACxD,QAAM,UAAU,IAAI,QAAQ,SAAS,OAAO;AAC5C,UAAQ,OAAO,gBAAgB;AAC/B,MAAI,iBAAiB;AACnB,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,eAAe,GAAG;AAC3D,UAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,YAAI;AAAE,kBAAQ,OAAO,IAAI;AAAA,QAAG,QAAQ;AAAA,QAAa;AAAA,MACnD,WAAW,OAAO,UAAU,UAAU;AAIpC,YAAI;AACF,cAAI,QAAQ,IAAI,IAAI,MAAM,MAAO,SAAQ,IAAI,MAAM,KAAK;AAAA,QAC1D,QAAQ;AAAA,QAAmC;AAAA,MAC7C,WAAW,MAAM,QAAQ,KAAK,GAAG;AAE/B,YAAI;AACF,kBAAQ,OAAO,IAAI;AACnB,qBAAW,QAAQ,MAAO,SAAQ,OAAO,MAAM,OAAO,IAAI,CAAC;AAAA,QAC7D,QAAQ;AAAA,QAAa;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,SAAS,WAAW,OAAO,SAAS,WAAW,OAAO,SAAS,WAAW,OAAO,SAAS,WAAW;AACtH,SAAO,IAAI,SAAS,WAAW,OAAO,MAAM,EAAE,QAAQ,SAAS,QAAQ,YAAY,SAAS,YAAY,QAAQ,CAAC;AACnH;AAEA,SAAS,eAAe;AACtB,SAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,4DAA4D,CAAC,GAAG;AAAA,IAC1G,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,EAChD,CAAC;AACH;AAQA,IAAM,gBAAgB;AAEtB,SAAS,UAAU,QAAQ;AAGzB,SAAO,EAAE,OAAO,eAAe,SAAS,eAAe,MAAM,QAAQ,MAAM,GAAG;AAChF;AAIA,SAAS,qBAAqB,WAAW;AACvC,QAAM,OAAO,UAAU,gBAAgB;AACvC,MAAI,KAAM,QAAO,SAAS;AAC1B,UAAQ,UAAU,QAAQ,KAAK,IAAI,SAAS,WAAW;AACzD;AAIA,SAAS,cAAc,QAAQ,SAAS;AACtC,MAAI,WAAW,qBAAqB,CAAC,MAAM,QAAQ,QAAQ,IAAI,CAAC,CAAC,GAAG;AAClE,WAAO,IAAI,SAAS,gBAAgB,EAAE,KAAK,QAAQ,KAAK,MAAM,QAAQ,MAAM,GAAG,CAAC,GAAG;AAAA,MACjF,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,2BAA2B;AAAA,IACxD,CAAC;AAAA,EACH;AACA,SAAO,IAAI,SAAS,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG;AAAA,IACrD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,EAChD,CAAC;AACH;AAEA,SAAS,gBAAgB,EAAE,OAAO,MAAM,UAAU,MAAM,QAAQ,GAAG;AACjE,QAAM,MAAM,SAAS,UAAU,UAAU;AACzC,UAAQ,KAAK,gBAAgB,GAAG,UAAU,SAAS,SAAS,aAAa,YAAY,GAAG,SAAS,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,GAAG,KAAK,CAAC;AAC5I;AAGA,SAAS,iBAAiB,SAAS;AACjC,MAAI,CAAC,QAAS,QAAO,CAAC;AACtB,MAAI,OAAO;AACX,MAAI;AACF,WAAO,IAAI,IAAI,QAAQ,GAAG,EAAE;AAAA,EAC9B,QAAQ;AACN,WAAO,OAAO,QAAQ,QAAQ,WAAW,QAAQ,MAAM;AAAA,EACzD;AACA,SAAO;AAAA,IACL,QAAQ,QAAQ,UAAU;AAAA,IAC1B;AAAA,IACA,IAAI,QAAQ,SAAS,MAAM,iBAAiB,KAAK;AAAA,IACjD,WAAW,QAAQ,SAAS,MAAM,YAAY,KAAK;AAAA,EACrD;AACF;AAGA,SAAS,gBAAgB,KAAK;AAC5B,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,UAAU,IAAI,WAAW,CAAC;AAChC,QAAM,KAAK,QAAQ,YAAY,KAAK,QAAQ,YAAY;AACxD,QAAM,MAAM,QAAQ,iBAAiB,KAAK,QAAQ,iBAAiB;AACnE,SAAO;AAAA,IACL,QAAQ,IAAI,UAAU;AAAA,IACtB,MAAM,IAAI,eAAe,IAAI,OAAO;AAAA,IACpC,IAAI,IAAI,OAAO,OAAO,QAAQ,WAAW,MAAM;AAAA,IAC/C,WAAW,OAAO,OAAO,WAAW,KAAK,MAAM,QAAQ,EAAE,IAAI,GAAG,CAAC,IAAI;AAAA,EACvE;AACF;","names":["warnOnce","warnOnce","DEFAULT_BASE_URL","DEFAULT_CACHE_TTL","JITTER_FRACTION","parseCookies","LIMITS","PHASES","ACTIONS","JITTER_FRACTION","DEFAULT_BASE_URL","DEFAULT_FLUSH_MS","MAX_BATCH","allow"]}