dns-worker 1.1.0 → 1.1.1-canary.0b671ea

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/utils/cache.ts", "../src/utils/geo.ts", "../src/utils/crypto.ts", "../src/lib/cookies.ts", "../src/models/session.ts", "../src/lib/jwt.ts", "../src/lib/session.ts", "../src/lib/preauth.ts", "../src/lib/auth.ts", "../src/lib/middleware.ts", "../src/serverfull/index.ts", "../src/serverfull/cache.ts", "../src/serverfull/config.ts", "../src/serverfull/db.ts", "../src/serverfull/udp.ts", "../src/utils/dns/constants.ts", "../src/utils/dns/decoder.ts", "../src/utils/ech/constants.ts", "../src/utils/ech/codec.ts", "../src/utils/ech/httpsRdata.ts", "../src/utils/cidr.ts", "../src/utils/ech/cloudflareIp.ts", "../src/models/systemSettings.ts", "../src/utils/ech/sync.ts", "../src/utils/dns/rdata.ts", "../src/utils/dns/encoder.ts", "../src/utils/dns/injectEcs.ts", "../src/pipeline/cache.ts", "../src/models/profile.ts", "../src/models/rule.ts", "../src/utils/bloom.ts", "../src/models/profileBloom.ts", "../src/pipeline/config.ts", "../src/utils/dnsStamp.ts", "../src/utils/validator.ts", "../src/utils/sockets.ts", "../src/pipeline/resolver/types.ts", "../src/pipeline/resolver/transport.ts", "../src/pipeline/resolver/ecs.ts", "../src/pipeline/resolver/ech.ts", "../src/models/log/core.ts", "../src/models/log/aggregation.ts", "../src/models/kemKey.ts", "../src/models/log/retention.ts", "../src/models/log/analytics/traffic.ts", "../src/models/log/analytics/domains.ts", "../src/models/log/analytics/clients.ts", "../src/models/log/analytics/destinations.ts", "../src/models/log/analytics/index.ts", "../src/models/log/index.ts", "../src/pipeline/logBatcher.ts", "../node_modules/@noble/hashes/utils.js", "../node_modules/@noble/curves/utils.js", "../node_modules/@noble/curves/abstract/modular.js", "../node_modules/@noble/curves/abstract/curve.js", "../node_modules/@noble/hashes/hmac.js", "../node_modules/@noble/curves/abstract/der.js", "../node_modules/@noble/curves/abstract/weierstrass.js", "../node_modules/@noble/hashes/_u64.js", "../node_modules/@noble/hashes/_md.js", "../node_modules/@noble/hashes/sha2.js", "../node_modules/@noble/curves/abstract/fft.js", "../node_modules/@noble/curves/nist.js", "../node_modules/@noble/hashes/sha3.js", "../node_modules/@noble/post-quantum/utils.js", "../node_modules/@noble/post-quantum/_crystals.js", "../node_modules/@noble/post-quantum/ml-kem.js", "../node_modules/@noble/post-quantum/hybrid.js", "../src/lib/crypto/e2ee.ts", "../src/lib/crypto/dekManager.ts", "../src/pipeline/resolver/block.ts", "../src/utils/geoip.ts", "../src/pipeline/resolver/observability.ts", "../src/pipeline/resolver/index.ts", "../src/lib/filtering.ts", "../src/pipeline/filter.ts", "../src/pipeline/index.ts", "../src/utils/envelope.ts", "../src/models/user/secrets.ts", "../src/models/user/core.ts", "../src/models/user/security.ts", "../src/models/user/mfa.ts", "../src/models/user/lifecycle.ts", "../src/models/user/index.ts", "../src/api/doh.ts", "../src/serverfull/format.ts", "../src/serverfull/dot.ts", "../src/serverfull/http.ts", "../src/index.ts", "../src/utils/linuxSetup.ts", "../src/api/auth/config.ts", "../src/api/auth/register.ts", "../src/models/activityLog.ts", "../src/lib/totp.ts", "../src/api/auth/utils.ts", "../src/api/auth/login.ts", "../src/models/passkey.ts", "../src/lib/webauthn/encoding.ts", "../src/lib/webauthn/cbor.ts", "../src/lib/webauthn/attestation.ts", "../src/lib/webauthn/assertion.ts", "../src/api/auth/refresh.ts", "../src/api/auth/logout.ts", "../src/api/auth/lock.ts", "../src/api/auth/forgotPassword.ts", "../src/api/auth.ts", "../src/lib/rbac.ts", "../src/api/profiles/core.ts", "../src/models/accessPoint.ts", "../src/api/profiles/accessPoints.ts", "../src/api/profiles/rules.ts", "../src/models/list.ts", "../src/models/listBloom.ts", "../src/utils/sync/combine.ts", "../src/utils/parser.ts", "../src/utils/listFetcher.ts", "../src/utils/sync/singleListSync.ts", "../src/utils/sync/orchestrator.ts", "../src/api/profiles/lists.ts", "../src/api/profiles/logs.ts", "../src/api/profiles/logsWs.ts", "../src/api/profiles/e2ee.ts", "../src/api/profiles.ts", "../src/api/account/me.ts", "../src/api/account/security/password.ts", "../src/api/account/security/reauth.ts", "../src/api/account/security/totp.ts", "../src/api/account/security/pin.ts", "../src/api/account/security/passkeys.ts", "../src/api/account/security/recoveryKeys.ts", "../src/api/account/security/index.ts", "../src/api/account/sessions.ts", "../src/api/account/e2ee.ts", "../src/api/account/personal.ts", "../src/api/account/admin.ts", "../src/api/account.ts", "../src/api/system.ts", "../src/cron.ts", "../src/api/mapData.ts", "../src/serverfull/status.ts", "../src/serverfull/service/env.ts", "../src/serverfull/service/systemd.ts", "../src/serverfull/service/schtasks.ts", "../src/serverfull/service/index.ts"],
4
+ "sourcesContent": ["export const cacheUtils = {\r\n /**\r\n * \u751F\u6210\u89C4\u8303\u7684\u7F13\u5B58 URL\r\n */\r\n generateCacheUrl(key: string): string {\r\n return `https://obex.local/cache/${encodeURIComponent(key)}`;\r\n },\r\n\r\n async get<T>(cache: Cache | null | undefined, key: string): Promise<T | null> {\r\n if (!cache) return null;\r\n const url = this.generateCacheUrl(key);\r\n const response = await cache.match(url);\r\n if (!response) return null;\r\n return response.json();\r\n },\r\n\r\n async set(cache: Cache | null | undefined, key: string, data: any, ttlSeconds: number, tags: string[] = []): Promise<void> {\r\n if (!cache) return;\r\n const url = this.generateCacheUrl(key);\r\n const headers: Record<string, string> = {\r\n \"Content-Type\": \"application/json\",\r\n \"Cache-Control\": `public, max-age=${ttlSeconds}`\r\n };\r\n if (tags && tags.length > 0) {\r\n headers[\"Cache-Tag\"] = tags.join(\",\");\r\n }\r\n const response = new Response(JSON.stringify(data), {\r\n headers\r\n });\r\n return cache.put(url, response);\r\n },\r\n\r\n async delete(cache: Cache | null | undefined, key: string): Promise<boolean> {\r\n if (!cache) return false;\r\n const url = this.generateCacheUrl(key);\r\n return cache.delete(url);\r\n },\r\n\r\n /**\r\n * \u901F\u7387\u9650\u5236\u68C0\u67E5 (\u6ED1\u52A8\u7A97\u53E3)\r\n */\r\n async isRateLimited(cache: Cache | null | undefined, key: string, limit: number, windowSec: number): Promise<boolean> {\r\n if (!cache) return false;\r\n const cacheKey = `ratelimit:${key}`;\r\n const current = await this.get<{ count: number, reset: number }>(cache, cacheKey);\r\n \r\n const now = Math.floor(Date.now() / 1000);\r\n if (!current || now > current.reset) {\r\n await this.set(cache, cacheKey, { count: 1, reset: now + windowSec }, windowSec);\r\n return false;\r\n }\r\n\r\n if (current.count >= limit) return true;\r\n\r\n await this.set(cache, cacheKey, { count: current.count + 1, reset: current.reset }, Math.max(1, current.reset - now));\r\n return false;\r\n }\r\n};\r\n", "/**\n * Extracts coordinates from the request strictly using Cloudflare geo-IP.\n * Client headers are ignored to prevent geolocation spoofing.\n */\nexport function getRequestCoordinates(request: Request): { latitude: number | null; longitude: number | null } {\n // Try Cloudflare Workers context cf object\n const cf = (request as any).cf;\n if (cf && cf.latitude && cf.longitude) {\n const lat = parseFloat(cf.latitude);\n const lon = parseFloat(cf.longitude);\n if (!isNaN(lat) && !isNaN(lon)) {\n return { latitude: lat, longitude: lon };\n }\n }\n\n // Fallback to Cloudflare-injected headers\n const headerLat = request.headers.get(\"CF-IPLatitude\");\n const headerLon = request.headers.get(\"CF-IPLongitude\");\n if (headerLat && headerLon) {\n const lat = parseFloat(headerLat);\n const lon = parseFloat(headerLon);\n if (!isNaN(lat) && !isNaN(lon)) {\n return { latitude: lat, longitude: lon };\n }\n }\n\n return { latitude: null, longitude: null };\n}\n\n/**\n * Calculates the Haversine distance between two coordinates in kilometers.\n */\nexport function calculateDistanceInKm(lat1: number, lon1: number, lat2: number, lon2: number): number {\n const R = 6371; // Earth radius in km\n const dLat = ((lat2 - lat1) * Math.PI) / 180;\n const dLon = ((lon2 - lon1) * Math.PI) / 180;\n const a =\n Math.sin(dLat / 2) * Math.sin(dLat / 2) +\n Math.cos((lat1 * Math.PI) / 180) *\n Math.cos((lat2 * Math.PI) / 180) *\n Math.sin(dLon / 2) *\n Math.sin(dLon / 2);\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n return R * c;\n}\n", "/**\n * Low-level PBKDF2 key derivation using Web Crypto API.\n * Single responsibility: performing the raw cryptographic derivation.\n */\nexport async function pbkdf2(\n data: string,\n salt: Uint8Array,\n iterations: number,\n keyLength: number = 256\n): Promise<Uint8Array> {\n const encoder = new TextEncoder();\n const dataBuffer = encoder.encode(data);\n\n const baseKey = await crypto.subtle.importKey(\n \"raw\",\n dataBuffer,\n \"PBKDF2\",\n false,\n [\"deriveBits\"]\n );\n\n const hashBuffer = await crypto.subtle.deriveBits(\n {\n name: \"PBKDF2\",\n salt: salt,\n iterations: iterations,\n hash: \"SHA-256\"\n },\n baseKey,\n keyLength\n );\n\n return new Uint8Array(hashBuffer);\n}\n\n/**\n * Hashes a password/string using PBKDF2 and formats the result as a hex string.\n * @param data - The data to hash.\n * @param salt - The optional salt. If not provided, a random 16-byte salt is generated.\n * @param iterations - The number of iterations (default 100,000).\n * @param keyLength - The derived key length in bits (default 256).\n */\nexport async function hashWithPBKDF2(\n data: string,\n salt?: Uint8Array,\n iterations: number = 100000,\n keyLength: number = 256\n): Promise<string> {\n const actualSalt = salt ?? crypto.getRandomValues(new Uint8Array(16));\n const derived = await pbkdf2(data, actualSalt, iterations, keyLength);\n\n return Array.from(derived)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * Simple PBKDF2 implementation using Web Crypto API for Cloudflare Workers\n * - Uses SHA-256 with 100,000 iterations for password hashing (configurable)\n * - Generates a random 16-byte salt for each password\n * - Stores the salt and hash together in a base64-encoded string\n * - Provides functions for hashing and verifying passwords\n *\n * Note: In a production environment, consider using a well-established library like bcrypt or argon2. \n * @param password - The plaintext password to hash.\n * @param version - The hashing version to use (default 1). Version 2 uses fewer iterations for faster hashing.\n * @returns A base64-encoded string containing the salt and hash for storage.\n */\nexport async function hashPassword(\n password: string,\n version: number = 1\n): Promise<string> {\n const salt = crypto.getRandomValues(new Uint8Array(16));\n const iterations = version === 2 ? 100000 : 100000;\n \n const derived = await pbkdf2(password, salt, iterations, 256);\n \n const combinedBytes = new Uint8Array(salt.length + derived.length);\n combinedBytes.set(salt);\n combinedBytes.set(derived, salt.length);\n return btoa(String.fromCharCode(...combinedBytes));\n}\n\nexport async function verifyPassword(password: string, storedHash: string, version: number = 1): Promise<boolean> {\n // Decode the stored hash\n const combined = new Uint8Array(\n atob(storedHash)\n .split(\"\")\n .map((c) => c.charCodeAt(0))\n );\n \n const salt = combined.slice(0, 16);\n const originalHash = combined.slice(16);\n \n const iterations = version === 2 ? 100000 : 100000;\n \n const testHashArray = await pbkdf2(password, salt, iterations, 256);\n \n if (testHashArray.length !== originalHash.length) return false;\n \n // Constant-time comparison\n let result = 0;\n for (let i = 0; i < testHashArray.length; i++) {\n result |= testHashArray[i] ^ originalHash[i];\n }\n return result === 0;\n}\n\n/**\n * Standard z-base-32 alphabet (Tahoe-LAFS human-oriented base-32 encoding).\n * 32 characters designed for high legibility and reduced transcription errors:\n * excludes 0, l, v, 2 to avoid visual ambiguity.\n */\nexport const ZBASE32_ALPHABET: string = \"ybndrfg8ejkmcpqxot1uwisza345h769\";\n\n/**\n * Generates a cryptographically secure random token of the specified length\n * using the z-base-32 character set, derived directly from binary randomness.\n *\n * Each character is sampled from a cryptographically secure random byte via\n * `crypto.getRandomValues`. Because 256 is an exact integer multiple of 32 (256 % 32 === 0),\n * masking each byte with `0x1f` (31) partitions the 256 possible byte values into 32\n * equally probable buckets (8 byte values per character), guaranteeing zero modulo bias\n * and uniform entropy.\n *\n * @param length - Number of characters to generate (defaults to 5).\n * @returns A string of z-base-32 encoded characters.\n */\nexport function generateZBase32Token(length: number = 5): string {\n const bytes = new Uint8Array(length);\n crypto.getRandomValues(bytes);\n let token = \"\";\n for (let i = 0; i < length; i++) {\n token += ZBASE32_ALPHABET[bytes[i] & 0x1f];\n }\n return token;\n}\n\n/**\n * Generates a secure random ID of the specified length.\n */\nexport function generateId(length: number): string {\n const byteLength = Math.ceil(length / 2);\n const bytes = new Uint8Array(byteLength);\n \n crypto.getRandomValues(bytes);\n \n return Array.from(bytes)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('')\n .slice(0, length);\n}\n\n/**\n * Extracts the 16-byte salt from the stored base64 hash and returns it as a hex string.\n */\nexport function extractSaltHex(storedHash: string): string {\n const combined = new Uint8Array(\n atob(storedHash)\n .split(\"\")\n .map((c) => c.charCodeAt(0))\n );\n const salt = combined.slice(0, 16);\n return Array.from(salt).map(b => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Computes the HMAC-SHA256 signature of the given data using the specified key.\n * Key and data can be strings (UTF-8) or Uint8Arrays. Returns a hex-encoded signature.\n */\nexport async function hmacSha256(key: string | Uint8Array, data: string | Uint8Array): Promise<string> {\n const encoder = new TextEncoder();\n const keyBuffer = typeof key === \"string\" ? encoder.encode(key) : key;\n const dataBuffer = typeof data === \"string\" ? encoder.encode(data) : data;\n\n const cryptoKey = await crypto.subtle.importKey(\n \"raw\",\n keyBuffer,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"]\n );\n\n const signature = await crypto.subtle.sign(\"HMAC\", cryptoKey, dataBuffer);\n return Array.from(new Uint8Array(signature))\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Hashes the PIN using PBKDF2 with 600,000 iterations.\n * This is equivalent to the client-side hashPin/hashPasswordClient.\n */\nexport async function hashPin(pin: string, salt: string): Promise<string> {\n const saltBytes = new TextEncoder().encode(salt.toLowerCase());\n const derived = await pbkdf2(pin, saltBytes, 600000, 256);\n return Array.from(derived)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * Hashes the session ID using PBKDF2-HMAC-SHA256 with the user ID as a fixed salt.\n * Uses 100 iterations for deterministic hashing.\n * Returns a 64-character hex string.\n */\nexport async function generateSessionHash(sessionId: string, userId: string): Promise<string> {\n const salt = new TextEncoder().encode(userId);\n return hashWithPBKDF2(sessionId, salt, 100, 256);\n}\n\n/**\n * Hashes the client pinHash with a nonce challenge.\n * Uses 10,000 iterations to be fully compatible with Cloudflare Workers limits.\n */\nexport async function hashChallenge(pinHash: string, nonce: string): Promise<string> {\n const saltBytes = new TextEncoder().encode(nonce.toLowerCase());\n const derived = await pbkdf2(pinHash, saltBytes, 10000, 256);\n return Array.from(derived)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n", "import { Env } from '../types';\n\nexport const REFRESH_TOKEN_COOKIE_NAME = \"auth_refresh\";\nexport const PREAUTH_COOKIE_NAME = 'preauth_session';\n\n/**\n * Returns a serialized Set-Cookie header string for a new refresh token.\n */\nexport function createRefreshTokenCookie(refreshToken: string, env: Env, keepLoggedIn?: boolean): string {\n let maxAge: number;\n if (keepLoggedIn) {\n const expirationDays = Number(env.OPTIONAL_SESSION_EXPIRATION_DAYS) || 7;\n maxAge = expirationDays * 24 * 60 * 60;\n } else {\n const expirationMinutes = Number(env.DEFAULT_SESSION_EXPIRATION_MINUTES) || 1440;\n maxAge = expirationMinutes * 60;\n }\n return `${REFRESH_TOKEN_COOKIE_NAME}=${refreshToken}; HttpOnly; SameSite=Lax; Path=/; Max-Age=${maxAge}; Secure`;\n}\n\n/**\n * Returns a serialized Set-Cookie header string to clear the refresh token cookie.\n */\nexport function createBlankRefreshTokenCookie(): string {\n return `${REFRESH_TOKEN_COOKIE_NAME}=; HttpOnly; SameSite=Lax; Path=/; Max-Age=0; Secure`;\n}\n\n/**\n * Parses the Cookie header and returns the refresh token if present.\n */\nexport function readRefreshTokenCookie(cookieHeader: string | null): string | null {\n if (!cookieHeader) return null;\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${REFRESH_TOKEN_COOKIE_NAME}=([^;]*)`));\n return match ? match[1] : null;\n}\n\n/**\n * Returns a serialized Set-Cookie header string for a new CSRF token.\n * This cookie is not HttpOnly so that client-side JavaScript can read it to append the header.\n */\nexport function createCsrfCookie(token: string, env?: Env, keepLoggedIn?: boolean): string {\n let maxAgeStr = \"\";\n if (keepLoggedIn && env) {\n const expirationDays = Number(env.OPTIONAL_SESSION_EXPIRATION_DAYS) || 7;\n const maxAge = expirationDays * 24 * 60 * 60;\n maxAgeStr = `; Max-Age=${maxAge}`;\n }\n return `csrf_token=${token}; SameSite=Lax; Path=/; Secure${maxAgeStr}`;\n}\n\n/**\n * Parses the Cookie header and returns the CSRF token if present.\n */\nexport function readCsrfCookie(cookieHeader: string | null): string | null {\n if (!cookieHeader) return null;\n const match = cookieHeader.match(/(?:^|;\\s*)csrf_token=([^;]*)/);\n return match ? match[1] : null;\n}\n\n/**\n * Returns a Set-Cookie string for the pre-auth token.\n */\nexport function createPreauthCookie(token: string, env: Env): string {\n const preauthTtl = Number(env.PREAUTH_TTL_SECONDS) || 300;\n return `${PREAUTH_COOKIE_NAME}=${token}; HttpOnly; SameSite=Lax; Path=/; Max-Age=${preauthTtl}; Secure`;\n}\n\n/**\n * Returns a Set-Cookie string to clear the pre-auth cookie.\n */\nexport function clearPreauthCookie(): string {\n return `${PREAUTH_COOKIE_NAME}=; HttpOnly; SameSite=Lax; Path=/; Max-Age=0; Secure`;\n}\n\n/**\n * Reads the pre-auth token from the Cookie header.\n */\nexport function readPreauthCookie(cookieHeader: string | null): string | null {\n if (!cookieHeader) return null;\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${PREAUTH_COOKIE_NAME}=([^;]*)`));\n return match ? match[1] : null;\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\n\nexport class SessionModel {\n constructor(private db: D1Database) {}\n\n async getSessionUserId(sessionId: string): Promise<string | null> {\n const session = await this.db.prepare(\"SELECT user_id, expires_at FROM sessions WHERE id = ?\").bind(sessionId).first<{ user_id: string; expires_at: number }>();\n if (!session) return null;\n if (session.expires_at <= Math.floor(Date.now() / 1000)) {\n await this.deleteSession(sessionId);\n return null;\n }\n return session.user_id;\n }\n\n async getSession(sessionId: string): Promise<any> {\n const session = await this.db.prepare(\"SELECT * FROM sessions WHERE id = ?\").bind(sessionId).first<any>();\n if (!session) return null;\n if (session.expires_at <= Math.floor(Date.now() / 1000)) {\n await this.deleteSession(sessionId);\n return null;\n }\n return session;\n }\n\n async getSessionWithUser(sessionId: string): Promise<any> {\n const session = await this.db.prepare(`\n SELECT sessions.id as id, sessions.id as session_id, sessions.user_id, sessions.created_at, sessions.expires_at, sessions.ip_address, sessions.user_agent, sessions.latitude, sessions.longitude, sessions.rotation_counter, sessions.last_active_at, sessions.is_paused,\n users.id as u_id, users.username, users.role, users.pin_hash, users.session_lock_timeout\n FROM sessions\n INNER JOIN users ON sessions.user_id = users.id\n WHERE sessions.id = ?\n `).bind(sessionId).first<any>();\n\n if (!session) return null;\n if (session.expires_at <= Math.floor(Date.now() / 1000)) {\n await this.deleteSession(sessionId);\n return null;\n }\n return session;\n }\n\n async createSession(\n id: string,\n userId: string,\n createdAt: number,\n expiresAt: number,\n ipAddress: string | null = null,\n userAgent: string | null = null,\n latitude: number | null = null,\n longitude: number | null = null,\n lastActiveAt: number | null = null\n ): Promise<boolean> {\n const result = await this.db\n .prepare(\n \"INSERT INTO sessions (id, user_id, created_at, expires_at, ip_address, user_agent, latitude, longitude, rotation_counter, last_active_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)\"\n )\n .bind(id, userId, createdAt, expiresAt, ipAddress, userAgent, latitude, longitude, lastActiveAt || createdAt)\n .run();\n return result.success;\n }\n\n async updateLastActive(id: string, lastActiveAt: number): Promise<boolean> {\n const result = await this.db.prepare(\"UPDATE sessions SET last_active_at = ? WHERE id = ?\").bind(lastActiveAt, id).run();\n return result.success;\n }\n\n async pauseSession(id: string): Promise<boolean> {\n const result = await this.db.prepare(\"UPDATE sessions SET is_paused = 1 WHERE id = ?\").bind(id).run();\n return result.success;\n }\n\n async resumeSession(id: string, lastActiveAt: number): Promise<boolean> {\n const result = await this.db.prepare(\"UPDATE sessions SET is_paused = 0, last_active_at = ? WHERE id = ?\").bind(lastActiveAt, id).run();\n return result.success;\n }\n\n async getSessionsByUser(userId: string, now: number = Math.floor(Date.now() / 1000)): Promise<any[]> {\n // Proactively delete expired sessions for this user\n await this.db.prepare(\"DELETE FROM sessions WHERE user_id = ? AND expires_at <= ?\").bind(userId, now).run();\n\n const { results } = await this.db.prepare(`\n SELECT id, ip_address, user_agent, created_at, expires_at, latitude, longitude\n FROM sessions\n WHERE user_id = ? AND expires_at > ?\n ORDER BY expires_at DESC\n `).bind(userId, now).all();\n return results;\n }\n\n async deleteExpiredSessions(now: number = Math.floor(Date.now() / 1000)): Promise<{ deletedSessions: number; deletedPending: number }> {\n const res1 = await this.db.prepare(\"DELETE FROM sessions WHERE expires_at <= ?\").bind(now).run();\n const res2 = await this.db.prepare(\"DELETE FROM pending_totp_sessions WHERE expires_at <= ?\").bind(now).run();\n return {\n deletedSessions: res1.meta?.changes || 0,\n deletedPending: res2.meta?.changes || 0,\n };\n }\n\n async extendSession(id: string, expiresAt: number): Promise<boolean> {\n const result = await this.db.prepare(\"UPDATE sessions SET expires_at = ? WHERE id = ?\").bind(expiresAt, id).run();\n return result.success;\n }\n\n async incrementRotationCounter(id: string): Promise<boolean> {\n const result = await this.db.prepare(\"UPDATE sessions SET rotation_counter = rotation_counter + 1 WHERE id = ?\").bind(id).run();\n return result.success;\n }\n\n async deleteSession(id: string): Promise<boolean> {\n const result = await this.db.prepare(\"DELETE FROM sessions WHERE id = ?\").bind(id).run();\n return result.success;\n }\n\n async deleteAllByUserId(userId: string): Promise<boolean> {\n const result = await this.db.prepare(\"DELETE FROM sessions WHERE user_id = ?\").bind(userId).run();\n return result.success;\n }\n\n async createPendingTotpSession(id: string, userId: string, expiresAt: number): Promise<boolean> {\n const result = await this.db.prepare(\"INSERT INTO pending_totp_sessions (id, user_id, expires_at) VALUES (?, ?, ?)\").bind(id, userId, expiresAt).run();\n return result.success;\n }\n\n async getPendingTotpSession(id: string): Promise<{ user_id: string; expires_at: number } | null> {\n const row = await this.db.prepare(\"SELECT * FROM pending_totp_sessions WHERE id = ?\").bind(id).first<{ user_id: string; expires_at: number }>();\n if (!row) return null;\n if (row.expires_at <= Math.floor(Date.now() / 1000)) {\n await this.deletePendingTotpSession(id);\n return null;\n }\n return row;\n }\n\n async deletePendingTotpSession(id: string): Promise<boolean> {\n const result = await this.db.prepare(\"DELETE FROM pending_totp_sessions WHERE id = ?\").bind(id).run();\n return result.success;\n }\n}\n", "export function base64urlEncode(buf: ArrayBuffer | Uint8Array | string): string {\n let stringToEncode = \"\";\n if (typeof buf === \"string\") {\n stringToEncode = buf;\n } else {\n const bytes = new Uint8Array(buf);\n for (let i = 0; i < bytes.byteLength; i++) {\n stringToEncode += String.fromCharCode(bytes[i]);\n }\n }\n return btoa(stringToEncode)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nexport function base64urlDecode(str: string): Uint8Array {\n let base64 = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4) {\n base64 += \"=\";\n }\n const binaryString = atob(base64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\n\nexport function stringToUint8Array(str: string): Uint8Array {\n return new TextEncoder().encode(str);\n}\n\n/** Minimum accepted JWT_SECRET length. The built-in generator produces 64 characters. */\nexport const MIN_JWT_SECRET_LENGTH = 32;\n\n/**\n * Placeholder values from documentation, wrangler.toml, and template files.\n * A deployment that kept one of these has a publicly known secret.\n */\nexport const PLACEHOLDER_JWT_SECRETS = new Set([\n \"your_secure_random_string_here\",\n \"\u60A8\u7684\u968F\u673A\u5B89\u5168JWT\u5BC6\u94A5\",\n \"\u60A8\u7684\u96A8\u6A5F\u5B89\u5168JWT\u91D1\u9470\",\n \"replace_with_a_secure_random_jwt_secret_string_32chars_min\",\n]);\n\n/**\n * Checks whether the JWT secret is missing or empty.\n */\nexport function isMissingJwtSecret(secret: string | undefined | null): boolean {\n if (typeof secret !== \"string\") return true;\n return secret.trim().length === 0;\n}\n\n/**\n * Checks whether the JWT secret is a default preset or documentation placeholder.\n */\nexport function isPresetJwtSecret(secret: string | undefined | null): boolean {\n if (typeof secret !== \"string\") return false;\n return PLACEHOLDER_JWT_SECRETS.has(secret.trim());\n}\n\n/**\n * Returns detailed status of JWT_SECRET configuration:\n * - \"valid\": Configured with a non-preset string\n * - \"preset\": Configured with a documentation or default template preset string\n * - \"missing\": Not configured or empty string\n */\nexport function getJwtSecretStatus(secret: string | undefined | null): \"valid\" | \"preset\" | \"missing\" {\n if (isMissingJwtSecret(secret)) return \"missing\";\n if (isPresetJwtSecret(secret)) return \"preset\";\n return \"valid\";\n}\n\n/**\n * Whether JWT_SECRET is present and is not a documentation placeholder.\n * Does not block on string length to allow legacy deployments to continue operating.\n */\nexport function isUsableJwtSecret(secret: string | undefined | null): secret is string {\n return getJwtSecretStatus(secret) === \"valid\";\n}\n\n/**\n * Checks whether JWT_SECRET meets the recommended cryptographic length (>= 32 characters).\n * Shorter secrets are still usable to avoid breaking legacy deployments, but will trigger\n * a non-blocking warning in the administration dashboard and startup logs.\n */\nexport function isStrongJwtSecret(secret: string | undefined | null): boolean {\n if (!isUsableJwtSecret(secret)) return false;\n return secret.trim().length >= MIN_JWT_SECRET_LENGTH;\n}\n\nconst JWT_KEY_SALT = \"DNS_WORKER_JWT_KEY_SALT_v1\";\n\n/**\n * Derives a CryptoKey for JWT signing and verification.\n * Fixes hex parsing vulnerabilities where invalid hex characters evaluated to NaN (and implicitly 0 in Uint8Array),\n * or non-hex inputs could cause length mismatches / null pointer exceptions.\n * Derives a consistent 256-bit raw key by hashing secret + fixed salt using SHA-256.\n *\n * @param secret - Raw secret string or hex secret configured in environment\n * @returns CryptoKey for HMAC-SHA512 signing/verification\n */\nexport async function importJwtSecret(secret: string): Promise<CryptoKey> {\n const saltedData = new TextEncoder().encode(`${secret}:${JWT_KEY_SALT}`);\n const secretBytes = await crypto.subtle.digest(\"SHA-256\", saltedData);\n\n return crypto.subtle.importKey(\n \"raw\",\n secretBytes,\n { name: \"HMAC\", hash: \"SHA-512\" },\n false,\n [\"sign\", \"verify\"]\n );\n}\n\nexport async function signJWT(payload: any, key: CryptoKey): Promise<string> {\n const header = { alg: \"HS512\", typ: \"JWT\" };\n const encodedHeader = base64urlEncode(JSON.stringify(header));\n const encodedPayload = base64urlEncode(JSON.stringify(payload));\n \n const dataToSign = `${encodedHeader}.${encodedPayload}`;\n const signatureBuffer = await crypto.subtle.sign(\n \"HMAC\",\n key,\n stringToUint8Array(dataToSign)\n );\n \n const encodedSignature = base64urlEncode(signatureBuffer);\n return `${dataToSign}.${encodedSignature}`;\n}\n\nexport async function verifyJWT<T = any>(token: string, key: CryptoKey): Promise<T | null> {\n const parts = token.split(\".\");\n if (parts.length !== 3) return null;\n \n const [encodedHeader, encodedPayload, encodedSignature] = parts;\n const dataToVerify = `${encodedHeader}.${encodedPayload}`;\n \n const signatureBytes = base64urlDecode(encodedSignature);\n \n const isValid = await crypto.subtle.verify(\n \"HMAC\",\n key,\n signatureBytes,\n stringToUint8Array(dataToVerify)\n );\n \n if (!isValid) return null;\n \n try {\n const payloadBytes = base64urlDecode(encodedPayload);\n const payloadStr = new TextDecoder().decode(payloadBytes);\n const payload = JSON.parse(payloadStr) as any;\n \n // Check expiry\n if (payload.exp && Math.floor(Date.now() / 1000) > payload.exp) {\n return null;\n }\n \n return payload as T;\n } catch (e) {\n return null;\n }\n}\n", "import { Env, User } from '../types';\nimport { SessionModel } from '../models/session';\nimport { generateId } from '../utils/crypto';\nimport { base64urlEncode, base64urlDecode } from './jwt';\nimport { calculateDistanceInKm } from '../utils/geo';\n\nexport interface Session {\n id: string;\n user_id: string;\n created_at: number;\n expires_at: number;\n ip_address?: string | null;\n user_agent?: string | null;\n latitude?: number | null;\n longitude?: number | null;\n rotation_counter?: number;\n last_active_at?: number | null;\n is_paused?: number;\n}\n\nexport interface SessionValidationResult {\n session: Session | null;\n user: User | null;\n}\n\nexport const SESSION_ID_LENGTH = 80;\n\n/**\n * Creates a refresh token string containing session ID, rotation version, and timestamp.\n */\nexport function createRefreshTokenString(sessionId: string, version: number): string {\n const payload = {\n sid: sessionId,\n v: version,\n ts: Date.now()\n };\n return base64urlEncode(JSON.stringify(payload));\n}\n\n/**\n * Parses a refresh token string.\n */\nexport function parseRefreshTokenString(token: string): { sid: string; v: number; ts: number } | null {\n try {\n const jsonStr = new TextDecoder().decode(base64urlDecode(token));\n return JSON.parse(jsonStr);\n } catch (e) {\n return null;\n }\n}\n\n/**\n * Creates a new session in the database and returns it along with the initial refresh token.\n */\nexport async function createSession(\n env: Env,\n userId: string,\n ipAddress: string | null = null,\n userAgent: string | null = null,\n latitude: number | null = null,\n longitude: number | null = null,\n keepLoggedIn?: boolean\n): Promise<{ session: Session; refreshToken: string }> {\n const prefix = keepLoggedIn ? \"k_\" : \"s_\";\n const sessionId = prefix + generateId(SESSION_ID_LENGTH - 2);\n const now = Math.floor(Date.now() / 1000);\n let expiresAt: number;\n if (keepLoggedIn) {\n const expirationDays = Number(env.OPTIONAL_SESSION_EXPIRATION_DAYS) || 7;\n expiresAt = now + expirationDays * 24 * 60 * 60;\n } else {\n const expirationMinutes = Number(env.DEFAULT_SESSION_EXPIRATION_MINUTES) || 1440;\n expiresAt = now + expirationMinutes * 60;\n }\n\n const session: Session = {\n id: sessionId,\n user_id: userId,\n created_at: now,\n expires_at: expiresAt,\n ip_address: ipAddress,\n user_agent: userAgent,\n latitude: latitude,\n longitude: longitude,\n last_active_at: now\n };\n\n const sessionModel = new SessionModel(env.DB);\n await sessionModel.createSession(\n session.id,\n session.user_id,\n session.created_at,\n session.expires_at,\n session.ip_address,\n session.user_agent,\n session.latitude,\n session.longitude,\n session.last_active_at\n );\n\n const refreshToken = createRefreshTokenString(session.id, 0);\n\n return { session, refreshToken };\n}\n\n/**\n * Rotates a refresh token. Validates anti-reuse, geographic distance, and expiry.\n * Returns the session, user, and a newly rotated refresh token.\n */\nexport async function rotateSession(\n env: Env,\n refreshTokenString: string,\n currentLat: number | null = null,\n currentLon: number | null = null\n): Promise<{ session: Session | null; user: User | null; newRefreshToken: string | null; reason?: string }> {\n const parsed = parseRefreshTokenString(refreshTokenString);\n if (!parsed) {\n return { session: null, user: null, newRefreshToken: null, reason: \"invalid_token\" };\n }\n\n const sessionModel = new SessionModel(env.DB);\n const result = await sessionModel.getSessionWithUser(parsed.sid);\n\n if (!result) {\n return { session: null, user: null, newRefreshToken: null, reason: \"session_not_found\" };\n }\n\n const session: Session = {\n id: result.session_id,\n user_id: result.user_id,\n created_at: result.created_at,\n expires_at: result.expires_at,\n ip_address: result.ip_address,\n user_agent: result.user_agent,\n latitude: result.latitude,\n longitude: result.longitude,\n rotation_counter: result.rotation_counter,\n last_active_at: result.last_active_at,\n is_paused: result.is_paused\n };\n\n const user: User = {\n id: result.u_id,\n username: result.username,\n role: result.role as 'admin' | 'user',\n pin_hash: result.pin_hash,\n session_lock_timeout: result.session_lock_timeout\n };\n\n // Anti-reuse mechanism\n if (session.rotation_counter !== parsed.v) {\n // Token reuse detected. Terminate the session immediately.\n await invalidateSession(env, session.id);\n return { session: null, user, newRefreshToken: null, reason: \"token_reuse\" };\n }\n\n // Expiration check\n if (Math.floor(Date.now() / 1000) >= session.expires_at) {\n await invalidateSession(env, session.id);\n return { session: null, user, newRefreshToken: null, reason: \"expired\" };\n }\n\n const now = Math.floor(Date.now() / 1000);\n\n // Strict Geolocation Check\n if (\n session.latitude === null || session.latitude === undefined ||\n session.longitude === null || session.longitude === undefined ||\n currentLat === null || currentLon === null\n ) {\n await invalidateSession(env, session.id);\n return { session: null, user, newRefreshToken: null, reason: \"geolocation_missing\" };\n }\n\n const distance = calculateDistanceInKm(session.latitude, session.longitude, currentLat, currentLon);\n const maxDistance = Number(env.SESSION_GEO_DISTANCE_KM) || 50;\n if (distance > maxDistance) {\n await invalidateSession(env, session.id);\n return { session: null, user, newRefreshToken: null, reason: \"geolocation_mismatch\" };\n }\n\n // Inactivity timeout check during refresh rotation\n const lastActive = session.last_active_at || session.created_at;\n if (user.pin_hash && !session.is_paused) {\n const timeoutSeconds = (user.session_lock_timeout || 15) * 60;\n if (now - lastActive > timeoutSeconds) {\n await sessionModel.pauseSession(session.id);\n session.is_paused = 1;\n }\n }\n\n // Session extension (if close to expiration)\n const timeRemaining = session.expires_at - Math.floor(Date.now() / 1000);\n const isKeepLoggedIn = session.id.startsWith(\"k_\");\n const totalDurationInSeconds = isKeepLoggedIn\n ? (Number(env.OPTIONAL_SESSION_EXPIRATION_DAYS) || 7) * 24 * 60 * 60\n : (Number(env.DEFAULT_SESSION_EXPIRATION_MINUTES) || 1440) * 60;\n const extensionThreshold = Math.floor(totalDurationInSeconds / 2);\n if (timeRemaining < extensionThreshold) {\n session.expires_at = Math.floor(Date.now() / 1000) + totalDurationInSeconds;\n await sessionModel.extendSession(session.id, session.expires_at);\n }\n\n // Rotate the token\n await sessionModel.incrementRotationCounter(session.id);\n if (!session.is_paused) {\n await sessionModel.updateLastActive(session.id, now);\n }\n const newCounter = (session.rotation_counter || 0) + 1;\n const newRefreshToken = createRefreshTokenString(session.id, newCounter);\n\n return { session, user, newRefreshToken };\n}\n\n/**\n * Deletes a session from the database.\n */\nexport async function invalidateSession(env: Env, sessionId: string): Promise<void> {\n const sessionModel = new SessionModel(env.DB);\n await sessionModel.deleteSession(sessionId);\n}\n", "import { Env } from '../types';\nimport { SessionModel } from '../models/session';\nimport { generateId } from '../utils/crypto';\nimport { SESSION_ID_LENGTH } from './session';\n\nimport { cacheUtils } from '../utils/cache';\n\n/**\n * Creates a short-lived pre-auth session after Step 1 (username + Turnstile).\n * @returns The pre-auth session token string\n */\nexport async function createPreauthSession(env: Env, userId: string): Promise<string> {\n const token = generateId(SESSION_ID_LENGTH);\n const preauthTtl = Number(env.PREAUTH_TTL_SECONDS) || 300;\n const expiresAt = Math.floor(Date.now() / 1000) + preauthTtl;\n const sessionModel = new SessionModel(env.DB);\n await sessionModel.createPendingTotpSession(token, userId, expiresAt);\n return token;\n}\n\n/**\n * Validates a pre-auth token and returns the associated userId, or null if invalid/expired.\n * Does NOT delete the session \u2014 call invalidatePreauthSession after successful verification.\n */\nexport async function validatePreauthSession(env: Env, token: string): Promise<string | null> {\n const sessionModel = new SessionModel(env.DB);\n const row = await sessionModel.getPendingTotpSession(token);\n\n if (!row) return null;\n if (Math.floor(Date.now() / 1000) >= row.expires_at) {\n await sessionModel.deletePendingTotpSession(token);\n return null;\n }\n return row.user_id;\n}\n\n/**\n * Deletes a pre-auth session after it has been used (successfully or failed terminally).\n */\nexport async function invalidatePreauthSession(env: Env, token: string): Promise<void> {\n const sessionModel = new SessionModel(env.DB);\n await sessionModel.deletePendingTotpSession(token);\n}\n\n/**\n * Increments failed attempts for a pre-auth session. If it reaches 3 attempts, deletes it.\n * Returns the number of attempts remaining (from 3 down to 0).\n */\nexport async function recordFailedPreauthAttempt(cache: Cache, token: string, env: Env): Promise<number> {\n const cacheKey = `preauth_state:${token}`;\n const state = await cacheUtils.get<{ nonce: string, failedAttempts: number }>(cache, cacheKey);\n if (!state) return 0;\n \n const failedAttempts = (state.failedAttempts || 0) + 1;\n \n if (failedAttempts >= 3) {\n const sessionModel = new SessionModel(env.DB);\n await sessionModel.deletePendingTotpSession(token);\n await cacheUtils.delete(cache, cacheKey);\n return 0;\n }\n \n const preauthTtl = Number(env.PREAUTH_TTL_SECONDS) || 300;\n await cacheUtils.set(cache, cacheKey, { ...state, failedAttempts }, preauthTtl);\n return 3 - failedAttempts;\n}\n", "import { Env } from \"../types\";\n\n// Re-export Geo Utilities\nexport { getRequestCoordinates, calculateDistanceInKm } from \"../utils/geo\";\n\n// Re-export Crypto Utilities\nexport {\n generateId,\n generateZBase32Token,\n ZBASE32_ALPHABET,\n extractSaltHex,\n hmacSha256,\n generateSessionHash\n} from \"../utils/crypto\";\n\n// Re-export Cookie Management\nexport {\n REFRESH_TOKEN_COOKIE_NAME,\n PREAUTH_COOKIE_NAME,\n createRefreshTokenCookie,\n createBlankRefreshTokenCookie,\n readRefreshTokenCookie,\n createCsrfCookie,\n readCsrfCookie,\n createPreauthCookie,\n clearPreauthCookie,\n readPreauthCookie\n} from \"./cookies\";\n\n// Re-export Session Management (Values)\nexport {\n SESSION_ID_LENGTH,\n createRefreshTokenString,\n parseRefreshTokenString,\n createSession,\n rotateSession,\n invalidateSession\n} from \"./session\";\n\n// Re-export Session Management (Types)\nexport type {\n Session,\n SessionValidationResult\n} from \"./session\";\n\n// Re-export Pre-auth Management\nexport {\n createPreauthSession,\n validatePreauthSession,\n invalidatePreauthSession,\n recordFailedPreauthAttempt\n} from \"./preauth\";\nimport {\n isUsableJwtSecret,\n isStrongJwtSecret,\n isPresetJwtSecret,\n isMissingJwtSecret,\n getJwtSecretStatus,\n MIN_JWT_SECRET_LENGTH\n} from \"./jwt\";\nexport {\n isUsableJwtSecret,\n isStrongJwtSecret,\n isPresetJwtSecret,\n isMissingJwtSecret,\n getJwtSecretStatus,\n MIN_JWT_SECRET_LENGTH\n};\n\n/**\n * Gets or creates the JWT secret from environment configuration.\n * Differentiates between missing JWT_SECRET and unedited preset JWT_SECRET.\n */\nexport async function getOrCreateJwtSecret(env: Env): Promise<string> {\n const status = getJwtSecretStatus(env.JWT_SECRET);\n if (status === \"missing\") {\n throw new Error(\"jwt_secret_missing\");\n }\n if (status === \"preset\") {\n throw new Error(\"jwt_secret_preset\");\n }\n return env.JWT_SECRET!;\n}\n", "import { Env, ExecutionContext, User } from '../types';\nimport { getOrCreateJwtSecret, readCsrfCookie } from './auth';\nimport { importJwtSecret, verifyJWT } from './jwt';\nimport { SessionModel } from '../models/session';\n\n/**\n * Applies standard security headers and Content-Security-Policy (CSP) with a nonce.\n */\nexport function applySecurityHeaders(response: Response, nonce: string): Response {\n const newHeaders = new Headers(response.headers);\n newHeaders.set('X-Content-Type-Options', 'nosniff');\n newHeaders.set('X-Frame-Options', 'DENY');\n newHeaders.set('X-XSS-Protection', '1; mode=block');\n newHeaders.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');\n newHeaders.set('Cross-Origin-Opener-Policy', 'same-origin');\n newHeaders.set('Cross-Origin-Embedder-Policy', 'credentialless');\n \n if (!newHeaders.has('Content-Security-Policy')) {\n newHeaders.set(\n 'Content-Security-Policy',\n `default-src 'self'; script-src 'self' 'nonce-${nonce}' https://challenges.cloudflare.com https://static.cloudflareinsights.com 'wasm-unsafe-eval'; worker-src 'self' blob:; script-src-attr 'unsafe-inline'; frame-src 'self' https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://icons.duckduckgo.com; connect-src 'self' https://challenges.cloudflare.com https://cloudflare-dns.com https://1.1.1.1;`\n );\n }\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders\n });\n}\n\n/**\n * Parses authorization header and verifies JWT to get the current authenticated user.\n */\ninterface CachedAuthUser {\n user: User;\n expiresAt: number;\n}\nconst authUserMemoryCache = new Map<string, CachedAuthUser>();\n\n/**\n * In-memory activity tracking to prevent premature PIN locking\n * and throttle D1 writes even when DB writes fail or are delayed.\n */\ninterface SessionActivityRecord {\n lastObserved: number;\n lastWriteAttempt: number;\n}\nconst sessionActivityMemory = new Map<string, SessionActivityRecord>();\n\nexport function invalidateAuthUserCache(sessionId: string): void {\n authUserMemoryCache.delete(sessionId);\n sessionActivityMemory.delete(sessionId);\n}\n\nexport async function getCurrentUser(request: Request, env: Env, ctx?: ExecutionContext): Promise<User | null> {\n const authHeader = request.headers.get(\"Authorization\") || \"\";\n let accessToken = \"\";\n if (authHeader.startsWith(\"Bearer \")) {\n accessToken = authHeader.slice(7);\n } else if (\n request.headers.get(\"Upgrade\")?.toLowerCase().includes(\"websocket\") ||\n new URL(request.url).pathname.endsWith(\"/ws\")\n ) {\n const url = new URL(request.url);\n accessToken = url.searchParams.get(\"token\") || url.searchParams.get(\"access_token\") || \"\";\n }\n\n if (!accessToken) {\n return null;\n }\n\n try {\n const secret = await getOrCreateJwtSecret(env);\n const jwtKey = await importJwtSecret(secret);\n const payload = await verifyJWT<{ userId: string; role: string; sessionId: string; exp: number }>(\n accessToken,\n jwtKey\n );\n if (payload) {\n // Check 10-second micro-cache to collapse parallel dashboard requests into 1 D1 read\n const cached = authUserMemoryCache.get(payload.sessionId);\n if (cached && cached.expiresAt > Date.now()) {\n const activity = sessionActivityMemory.get(payload.sessionId);\n if (activity) {\n activity.lastObserved = Math.floor(Date.now() / 1000);\n }\n return cached.user;\n }\n\n // Validate session & user in database via single atomic JOIN query (halves D1 reads)\n const sessionModel = new SessionModel(env.DB);\n const session = await sessionModel.getSessionWithUser(payload.sessionId);\n if (!session) {\n authUserMemoryCache.delete(payload.sessionId);\n sessionActivityMemory.delete(payload.sessionId);\n return null;\n }\n\n const sessionId = session.id || session.session_id || payload.sessionId;\n const now = Math.floor(Date.now() / 1000);\n const lastActive = session.last_active_at || session.created_at;\n const activityRecord = sessionActivityMemory.get(payload.sessionId);\n const effectiveLastActive = Math.max(lastActive, activityRecord?.lastObserved ?? 0);\n\n // Single Source of Truth inactivity check on server (fields from JOINed users table)\n if (session.pin_hash && !session.is_paused) {\n const timeoutSeconds = (session.session_lock_timeout || 15) * 60;\n if (now - effectiveLastActive > timeoutSeconds) {\n try {\n await sessionModel.pauseSession(sessionId);\n } catch (e) {\n console.error(\"[Auth] Failed to update pauseSession in D1:\", e);\n }\n session.is_paused = 1;\n }\n }\n\n if (session.is_paused) {\n const pausedUser: User = { id: payload.userId, username: session.username || \"\", role: (session.role || payload.role) as any, isPaused: true, sessionId: payload.sessionId };\n return pausedUser;\n }\n\n // Throttle DB updates: only update if at least intervalSec have elapsed since last write attempt\n const lastWriteAttempt = activityRecord?.lastWriteAttempt ?? lastActive;\n const intervalSec = parseInt(String(env.SESSION_LAST_ACTIVE_UPDATE_INTERVAL || \"60\"), 10) || 60;\n\n let newWriteAttempt = lastWriteAttempt;\n if (now - lastWriteAttempt > intervalSec) {\n newWriteAttempt = now;\n const writePromise = sessionModel\n .updateLastActive(sessionId, now)\n .catch((e) => console.error(\"[Auth] session last-active write failed:\", e));\n\n if (ctx && typeof ctx.waitUntil === \"function\") {\n ctx.waitUntil(writePromise);\n }\n }\n\n // Update in-memory activity tracking (cap size at 1000)\n if (sessionActivityMemory.size > 1000) {\n const oldestKey = sessionActivityMemory.keys().next().value;\n if (oldestKey) sessionActivityMemory.delete(oldestKey);\n }\n sessionActivityMemory.set(payload.sessionId, {\n lastObserved: now,\n lastWriteAttempt: newWriteAttempt\n });\n\n const validatedUser: User = { id: payload.userId, username: session.username || \"\", role: (session.role || payload.role) as any, sessionId: payload.sessionId };\n\n // Micro-cache user authentication (default 30 seconds, configurable via AUTH_CACHE_TTL_SEC)\n const cacheTtlSec = parseInt(String(env.AUTH_CACHE_TTL_SEC || \"30\"), 10) || 30;\n if (authUserMemoryCache.size > 100) {\n const oldestKey = authUserMemoryCache.keys().next().value;\n if (oldestKey) authUserMemoryCache.delete(oldestKey);\n }\n authUserMemoryCache.set(payload.sessionId, {\n user: validatedUser,\n expiresAt: Date.now() + cacheTtlSec * 1000\n });\n\n return validatedUser;\n }\n } catch (e) {\n // Ignore verification errors and return null\n }\n return null;\n}\n\n/**\n * Validates CSRF double submit cookie against the request header.\n */\nexport function validateCsrf(request: Request): boolean {\n const cookieHeader = request.headers.get(\"Cookie\") || \"\";\n const csrfCookie = readCsrfCookie(cookieHeader);\n const csrfHeader = request.headers.get(\"X-CSRF-Token\");\n return !!csrfCookie && !!csrfHeader && csrfCookie === csrfHeader;\n}\n", "/**\n * @file index.ts\n * @description Master entry point for DNS Worker Serverfull mode.\n * Starts Classic UDP DNS, DoT (DNS over TLS), HTTP Web Dashboard & DoH, and scheduled cron jobs.\n */\n\nimport { parseArgs } from 'node:util';\nimport { initNodeGlobals } from './cache';\nimport { getPackageVersion, getServerfullConfig, ServerfullCliArgs } from './config';\nimport { initServerfullDb } from './db';\nimport { UdpDnsServer } from './udp';\nimport { DotDnsServer } from './dot';\nimport { HttpServer } from './http';\nimport { flushLogBatch } from '../pipeline/logBatcher';\nimport worker from '../index';\nimport { ExecutionContext } from '../types';\nimport { isUsableJwtSecret, isStrongJwtSecret } from '../lib/jwt';\nimport {\n formatBanner,\n formatDiagnostic,\n formatHelpMenu,\n formatKeyValueSection\n} from './format';\nimport { showServerfullStatus } from './status';\nimport { handleServiceAction } from './service';\n\nfunction checkNodeVersion(): void {\n const [major, minor] = process.versions.node.split('.').map(Number);\n if (major < 22 || (major === 22 && minor < 5)) {\n console.error(formatDiagnostic({\n level: 'error',\n title: 'DNS Worker - Environment Error',\n message: `Node.js >= 22.5.0 is required (found v${process.versions.node}).`,\n solutions: [\n 'Please upgrade Node.js to version 22.5.0 or higher to use built-in SQLite (node:sqlite).'\n ]\n }));\n process.exit(1);\n }\n}\n\nfunction printHelp(): void {\n const version = getPackageVersion();\n\n const menu = formatHelpMenu({\n name: `DNS Worker v${version} (Serverfull Mode)`,\n description: 'Privacy-first DNS & DoH Resolver',\n usage: [\n 'dns-worker [options]',\n 'dns-worker status [options]',\n 'dns-worker service <action>',\n 'npx dns-worker [options]'\n ],\n commands: [\n { label: 'status', desc: 'Inspect service runtime status and database health' },\n { label: 'service <action>', desc: 'Manage background service (install, start, stop, restart, status, logs, uninstall)' }\n ],\n options: [\n { label: '-s, --status', desc: 'Display service and database runtime status' },\n { label: '-p, --port <number>', desc: 'Web Dashboard & DoH HTTP port (default: 3000)' },\n { label: '--dns-port <number>', desc: 'Classic UDP DNS port (default: 53)' },\n { label: '--dot-port <number>', desc: 'DoT (DNS over TLS) port (default: 853)' },\n { label: '-h, --host <address>', desc: 'Network address to bind (default: 0.0.0.0)' },\n { label: '--db <path>', desc: 'SQLite database file path (default: ./data/dns_worker.sqlite)' },\n { label: '--default-profile <key>', desc: 'Default Profile Key or Access Point Token for standard queries' },\n { label: '--disable-udp', desc: 'Disable Classic UDP DNS server' },\n { label: '--disable-dot', desc: 'Disable DoT server' },\n { label: '-v, --version', desc: 'Display version number' },\n { label: '--help', desc: 'Display this help message' }\n ],\n envVars: [\n { label: 'PORT / SERVERFULL_HTTP_PORT', desc: 'Web Dashboard & DoH port' },\n { label: 'DNS_PORT / SERVERFULL_UDP_PORT', desc: 'Classic UDP DNS port' },\n { label: 'DOT_PORT / SERVERFULL_DOT_PORT', desc: 'DoT port' },\n { label: 'DB_PATH / SERVERFULL_DB_PATH', desc: 'SQLite database file path' },\n { label: 'JWT_SECRET', desc: 'JWT secret key (recommended >= 32 characters)' },\n { label: 'SERVERFULL_TLS_KEY_PATH', desc: 'Path to TLS private key for DoT' },\n { label: 'SERVERFULL_TLS_CERT_PATH', desc: 'Path to TLS certificate for DoT' }\n ],\n tip: [\n \"Run 'dns-worker status' to inspect active runtime status, port availability, and database health.\",\n process.platform === 'win32'\n ? \"Run 'dns-worker service install' (in Administrator terminal) to run as a persistent Windows background service.\"\n : \"Run 'sudo dns-worker service install' to run as a persistent background Linux systemd service.\"\n ]\n });\n\n console.log(menu);\n}\n\nasync function parseCli(): Promise<ServerfullCliArgs> {\n try {\n const { values, positionals } = parseArgs({\n options: {\n port: { type: 'string', short: 'p' },\n 'dns-port': { type: 'string' },\n 'dot-port': { type: 'string' },\n host: { type: 'string', short: 'h' },\n db: { type: 'string' },\n 'default-profile': { type: 'string' },\n 'disable-udp': { type: 'boolean' },\n 'disable-dot': { type: 'boolean' },\n status: { type: 'boolean', short: 's' },\n help: { type: 'boolean' },\n version: { type: 'boolean', short: 'v' }\n },\n allowPositionals: true\n });\n\n if (values.help || positionals[0]?.toLowerCase() === 'help') {\n printHelp();\n process.exit(0);\n }\n\n if (values.version) {\n console.log(`dns-worker v${getPackageVersion()}`);\n process.exit(0);\n }\n\n if (values.status || positionals[0]?.toLowerCase() === 'status') {\n const { config, env } = getServerfullConfig(values as ServerfullCliArgs);\n await showServerfullStatus(config, env.JWT_SECRET);\n process.exit(0);\n }\n\n if (positionals[0]?.toLowerCase() === 'service') {\n const action = positionals[1]?.toLowerCase() || 'status';\n await handleServiceAction(action);\n process.exit(0);\n }\n\n const serviceInstallHint = process.platform === 'win32'\n ? \"Run 'dns-worker service install' in Administrator terminal to install Windows service.\"\n : \"Run 'sudo dns-worker service install' to install as a Linux systemd service.\";\n\n if (positionals.length > 0) {\n console.error(formatDiagnostic({\n level: 'error',\n title: 'DNS Worker - CLI Argument Error',\n message: `Unrecognized command or argument \"${positionals.join(' ')}\".`,\n solutions: [\n \"Run 'dns-worker status' to view runtime status.\",\n serviceInstallHint,\n \"Run 'dns-worker --help' to inspect supported options and usage.\"\n ]\n }));\n printHelp();\n process.exit(1);\n }\n\n return values as ServerfullCliArgs;\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n const serviceInstallHint = process.platform === 'win32'\n ? \"Run 'dns-worker service install' in Administrator terminal to install Windows service.\"\n : \"Run 'sudo dns-worker service install' to install as a Linux systemd service.\";\n\n console.error(formatDiagnostic({\n level: 'error',\n title: 'DNS Worker - CLI Argument Error',\n message: errorMsg,\n solutions: [\n \"Run 'dns-worker status' to view runtime status.\",\n serviceInstallHint,\n \"Run 'dns-worker --help' to inspect supported options and usage.\"\n ]\n }));\n printHelp();\n process.exit(1);\n }\n}\n\nasync function bootstrap(): Promise<void> {\n checkNodeVersion();\n const cliArgs = await parseCli();\n\n console.log(formatBanner({\n title: `Initializing DNS Worker v${getPackageVersion()} (Serverfull)`,\n borderChar: '-'\n }));\n\n // 1. Initialize in-memory Web Cache and HTMLRewriter polyfills\n initNodeGlobals();\n\n // 2. Load environment variables & configurations\n const { config, env } = getServerfullConfig(cliArgs);\n\n console.log(formatKeyValueSection({\n title: '[Services] Configured Ports & Transports:',\n items: [\n { label: 'Web Dashboard & DoH', value: `http://${config.host}:${config.httpPort}` },\n { label: 'Classic UDP DNS', value: config.disableUdp ? 'Disabled' : `udp://${config.host}:${config.udpPort}` },\n { label: 'DNS over TLS (DoT)', value: config.disableDot ? 'Disabled' : `tls://${config.host}:${config.dotPort}` }\n ]\n }));\n\n // Non-blocking security check for legacy short JWT_SECRET\n if (isUsableJwtSecret(env.JWT_SECRET) && !isStrongJwtSecret(env.JWT_SECRET)) {\n console.warn(formatDiagnostic({\n level: 'warning',\n title: 'SECURITY WARNING',\n message: 'JWT_SECRET is shorter than 32 characters.',\n solutions: [\n 'Please configure KEK_v1 in your environment before rotating JWT_SECRET to prevent existing encrypted credentials from becoming unrecoverable.'\n ]\n }));\n }\n\n // 3. Initialize SQLite D1 adapter and execute schema migrations\n const db = initServerfullDb(config.dbPath);\n env.DB = db;\n\n // 4. Initialize servers\n const udpServer = !config.disableUdp ? new UdpDnsServer({\n port: config.udpPort,\n host: config.host,\n defaultProfileKey: config.defaultProfileKey,\n env\n }) : null;\n\n const dotServer = !config.disableDot ? new DotDnsServer({\n port: config.dotPort,\n host: config.host,\n tlsKeyPath: config.tlsKeyPath,\n tlsCertPath: config.tlsCertPath,\n defaultProfileKey: config.defaultProfileKey,\n env\n }) : null;\n\n const httpServer = new HttpServer({\n port: config.httpPort,\n host: config.host,\n env\n });\n\n // 5. Start all server transports\n if (udpServer) await udpServer.start();\n if (dotServer) await dotServer.start();\n await httpServer.start();\n\n // 6. Start scheduled cron jobs (every 60 seconds)\n const cronTimer = setInterval(async () => {\n try {\n const scheduledEvent = {\n cron: '* * * * *',\n scheduledTime: Date.now(),\n type: 'scheduled' as const,\n noRetry() {}\n };\n const ctx: ExecutionContext = {\n waitUntil(promise: Promise<any>) {\n promise.catch((err) => console.error('[Cron Task Error]:', err));\n },\n passThroughOnException() {}\n } as any;\n\n await worker.scheduled(scheduledEvent as any, env, ctx);\n } catch (err) {\n console.error('[Cron Scheduler Error]:', err);\n }\n }, 60000);\n\n const hasTls = !!(dotServer && config.tlsKeyPath && config.tlsCertPath);\n console.log(formatBanner({\n title: 'DNS Worker (Serverfull Mode) Started Successfully',\n borderChar: '=',\n bullet: '\u2022 ',\n items: [\n {\n label: 'Classic UDP DNS',\n value: udpServer ? `udp://${config.host}:${config.udpPort}` : 'Disabled (--disable-udp)'\n },\n {\n label: 'DoT (TLS DNS)',\n value: hasTls ? `tls://${config.host}:${config.dotPort}` : 'Disabled / Not Configured'\n },\n {\n label: 'Web UI & DoH',\n value: `http://${config.host}:${config.httpPort}`\n },\n {\n label: 'SQLite Database',\n value: config.dbPath\n }\n ]\n }));\n\n // 7. Handle graceful shutdown\n const shutdown = async (signal: string) => {\n console.log(`\\n[Serverfull] Received ${signal}. Shutting down gracefully...`);\n clearInterval(cronTimer);\n\n await Promise.all([\n udpServer?.stop(),\n dotServer?.stop(),\n httpServer.stop()\n ]);\n\n try {\n await flushLogBatch(env);\n } catch {\n /* ignore */\n }\n\n try {\n db.rawDb.close();\n console.log('[Serverfull] Database connection closed.');\n } catch (e) {\n /* ignore */\n }\n\n console.log('[Serverfull] All services stopped. Goodbye.');\n process.exit(0);\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n}\n\nbootstrap().catch((err) => {\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Serverfull - Fatal Startup Error',\n message: err?.message || String(err)\n }));\n process.exit(1);\n});\n", "/**\n * @file cache.ts\n * @description In-memory Cache API and HTMLRewriter polyfill for Node.js runtime.\n */\n\nexport class MemoryCache {\n private store = new Map<string, { body: Uint8Array; headers: Headers; status: number; statusText: string; expires: number }>();\n\n async match(request: string | Request): Promise<Response | undefined> {\n const url = typeof request === 'string' ? request : request.url;\n const entry = this.store.get(url);\n if (!entry) return undefined;\n if (Date.now() > entry.expires) {\n this.store.delete(url);\n return undefined;\n }\n return new Response(entry.body, {\n status: entry.status,\n statusText: entry.statusText,\n headers: new Headers(entry.headers)\n });\n }\n\n async put(request: string | Request, response: Response): Promise<void> {\n const url = typeof request === 'string' ? request : request.url;\n const cc = response.headers.get('Cache-Control') || '';\n const maxAgeMatch = cc.match(/max-age=(\\d+)/i);\n const ttl = maxAgeMatch ? parseInt(maxAgeMatch[1], 10) : 3600;\n const bodyBuf = await response.clone().arrayBuffer();\n\n this.store.set(url, {\n body: new Uint8Array(bodyBuf),\n headers: new Headers(response.headers),\n status: response.status,\n statusText: response.statusText,\n expires: Date.now() + ttl * 1000\n });\n }\n\n async delete(request: string | Request): Promise<boolean> {\n const url = typeof request === 'string' ? request : request.url;\n return this.store.delete(url);\n }\n}\n\n/**\n * Ensures global Cache API and HTMLRewriter are initialized in Node.js.\n */\nexport function initNodeGlobals(): void {\n if (typeof (globalThis as any).caches === 'undefined') {\n const defaultCache = new MemoryCache();\n (globalThis as any).caches = {\n default: defaultCache,\n open: async () => new MemoryCache()\n };\n }\n\n if (typeof (globalThis as any).HTMLRewriter === 'undefined') {\n (globalThis as any).HTMLRewriter = class {\n private handlers: Array<{ selector: string; fn: (el: any) => void }> = [];\n\n on(selector: string, handler: { element?: (el: any) => void }) {\n if (handler.element) {\n this.handlers.push({ selector, fn: handler.element });\n }\n return this;\n }\n\n transform(response: Response): Response {\n const handlers = this.handlers;\n const originalTextPromise = response.text();\n\n const stream = new ReadableStream({\n async start(controller) {\n let html = await originalTextPromise;\n for (const h of handlers) {\n if (h.selector === 'head') {\n h.fn({\n prepend(content: string) {\n html = html.replace(/<head[^>]*>/i, `$&${content}`);\n },\n append(content: string) {\n html = html.replace(/<\\/head>/i, `${content}$&`);\n }\n });\n }\n }\n controller.enqueue(new TextEncoder().encode(html));\n controller.close();\n }\n });\n\n const newHeaders = new Headers(response.headers);\n newHeaders.delete('content-length');\n\n return new Response(stream, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders\n });\n }\n };\n }\n}\n", "/**\n * @file config.ts\n * @description Configuration loader and environment variable manager for Serverfull mode.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport { fileURLToPath } from 'node:url';\nimport { Env } from '../types';\n\nexport interface ServerfullCliArgs {\n port?: string;\n 'dns-port'?: string;\n 'dot-port'?: string;\n host?: string;\n db?: string;\n 'default-profile'?: string;\n 'disable-udp'?: boolean;\n 'disable-dot'?: boolean;\n status?: boolean;\n version?: boolean;\n help?: boolean;\n}\n\nexport interface ServerfullConfig {\n tlsKeyPath: string;\n tlsCertPath: string;\n udpPort: number;\n dotPort: number;\n httpPort: number;\n host: string;\n dbPath: string;\n defaultProfileKey: string;\n disableUdp: boolean;\n disableDot: boolean;\n packageRoot: string;\n}\n\n/**\n * Resolves the root directory of the installed package (where static/ and migrations/ live).\n */\nexport function getPackageRoot(): string {\n try {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n // 1. If running bundled (e.g. dist/serverfull.mjs -> root has migrations and static)\n if (fs.existsSync(path.join(currentDir, 'migrations')) && fs.existsSync(path.join(currentDir, 'static'))) {\n return currentDir;\n }\n // 2. One level up from dist/\n const parent = path.resolve(currentDir, '..');\n if (fs.existsSync(path.join(parent, 'migrations')) && fs.existsSync(path.join(parent, 'static'))) {\n return parent;\n }\n // 3. Two levels up (from src/serverfull/)\n const grandParent = path.resolve(currentDir, '../..');\n if (fs.existsSync(path.join(grandParent, 'migrations')) && fs.existsSync(path.join(grandParent, 'static'))) {\n return grandParent;\n }\n } catch {}\n return process.cwd();\n}\n\n/**\n * Resolves package version from package.json if available.\n */\nexport function getPackageVersion(): string {\n try {\n const pkgPath = path.join(getPackageRoot(), 'package.json');\n if (fs.existsSync(pkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n if (pkg.version) return pkg.version;\n }\n } catch {}\n return '1.0.0';\n}\n\n/**\n * Parses simple KEY=VALUE dotenv files.\n */\nfunction loadDotEnv(filePath: string): void {\n try {\n if (fs.existsSync(filePath)) {\n const content = fs.readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n');\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eqIdx = trimmed.indexOf('=');\n if (eqIdx !== -1) {\n const key = trimmed.slice(0, eqIdx).trim();\n let val = trimmed.slice(eqIdx + 1).trim();\n if ((val.startsWith('\"') && val.endsWith('\"')) || (val.startsWith(\"'\") && val.endsWith(\"'\"))) {\n val = val.slice(1, -1);\n }\n if (!(key in process.env)) {\n process.env[key] = val;\n }\n }\n }\n }\n } catch (err) {\n console.warn(`[Config] Failed to read ${filePath}:`, err);\n }\n}\n\n/**\n * Loads configuration files from root workspace directory.\n */\nexport function loadEnvFiles(rootDir: string = process.cwd()): void {\n loadDotEnv(path.join(rootDir, '.dev.vars'));\n loadDotEnv(path.join(rootDir, '.env'));\n loadDotEnv(path.join(rootDir, '.env.serverfull'));\n}\n\n/**\n * Loads Serverfull-specific options and builds the standard Env interface.\n */\nexport function getServerfullConfig(cliArgs?: ServerfullCliArgs): { config: ServerfullConfig; env: Env } {\n loadEnvFiles();\n\n const packageRoot = getPackageRoot();\n\n // Support both SERVERFULL_TLS_KEY_PATH and SERFULL_TLS_KEY_PATH\n const tlsKeyPath = process.env.SERVERFULL_TLS_KEY_PATH || process.env.SERFULL_TLS_KEY_PATH || '';\n // Support SERVERFULL_TLS_CERT_PATH and public key aliases\n const tlsCertPath = process.env.SERVERFULL_TLS_CERT_PATH || \n process.env.SERVERFULL_TLS_PUB_PATH || \n process.env.SERVERFULL_TLS_PUBLIC_KEY_PATH ||\n process.env.SERFULL_TLS_CERT_PATH ||\n process.env.SERFULL_TLS_PUB_PATH || '';\n\n const udpPort = parseInt(cliArgs?.['dns-port'] || process.env.SERVERFULL_UDP_PORT || process.env.DNS_PORT || '53', 10);\n const dotPort = parseInt(cliArgs?.['dot-port'] || process.env.SERVERFULL_DOT_PORT || process.env.DOT_PORT || '853', 10);\n const httpPort = parseInt(cliArgs?.port || process.env.SERVERFULL_HTTP_PORT || process.env.PORT || '3000', 10);\n const host = cliArgs?.host || process.env.SERVERFULL_HOST || process.env.SERVERFULL_BIND_ADDRESS || '0.0.0.0';\n const dbPath = cliArgs?.db || process.env.SERVERFULL_DB_PATH || process.env.DB_PATH || path.join(process.cwd(), 'data', 'dns_worker.sqlite');\n const defaultProfileKey = cliArgs?.['default-profile'] || process.env.SERVERFULL_DEFAULT_PROFILE_KEY || process.env.DEFAULT_PROFILE_KEY || '';\n const disableUdp = Boolean(cliArgs?.['disable-udp'] || process.env.SERVERFULL_DISABLE_UDP === 'true');\n const disableDot = Boolean(cliArgs?.['disable-dot'] || process.env.SERVERFULL_DISABLE_DOT === 'true');\n\n const config: ServerfullConfig = {\n tlsKeyPath,\n tlsCertPath,\n udpPort,\n dotPort,\n httpPort,\n host,\n dbPath,\n defaultProfileKey,\n disableUdp,\n disableDot,\n packageRoot\n };\n\n const env: Env = {\n DB: null as any, // Set by db.ts\n ASSETS: null as any, // Set by http.ts\n JWT_SECRET: process.env.JWT_SECRET || 'dns_worker_serverfull_secret_' + crypto.randomUUID().replace(/-/g, '') + crypto.randomUUID().replace(/-/g, ''),\n FAIL_OPEN_UPSTREAM: process.env.FAIL_OPEN_UPSTREAM || 'https://freedns.controld.com/no-ads-malware-typo',\n MAX_ACCESS_POINTS_PER_PROFILE: process.env.MAX_ACCESS_POINTS_PER_PROFILE || 100,\n MAX_PROFILES_PER_USER: process.env.MAX_PROFILES_PER_USER || 10,\n DEFAULT_SESSION_EXPIRATION_MINUTES: process.env.DEFAULT_SESSION_EXPIRATION_MINUTES || 1440,\n OPTIONAL_SESSION_EXPIRATION_DAYS: process.env.OPTIONAL_SESSION_EXPIRATION_DAYS || 7,\n ACCESS_TOKEN_EXPIRATION_MINUTES: process.env.ACCESS_TOKEN_EXPIRATION_MINUTES || 1,\n SESSION_GEO_DISTANCE_KM: process.env.SESSION_GEO_DISTANCE_KM || 50,\n PREAUTH_TTL_SECONDS: process.env.PREAUTH_TTL_SECONDS || 300,\n BLOOM_MEM_TTL: process.env.BLOOM_MEM_TTL || 600000,\n SYNC_TIMEOUT_MS: process.env.SYNC_TIMEOUT_MS || 30000,\n INACTIVITY_THRESHOLD_DAYS: process.env.INACTIVITY_THRESHOLD_DAYS || 180,\n THROTTLE_ACTIVE_SEC: process.env.THROTTLE_ACTIVE_SEC || 3600,\n SYNC_PROFILE_INTERVAL_SEC: process.env.SYNC_PROFILE_INTERVAL_SEC || 86400,\n BLOOM_FALSE_POSITIVE_RATE: process.env.BLOOM_FALSE_POSITIVE_RATE || 0.0001,\n MAX_SYNC_DOMAINS: process.env.MAX_SYNC_DOMAINS || 1000000,\n MAX_LIST_DOMAINS: process.env.MAX_LIST_DOMAINS || 500000,\n MAX_LOG_RETENTION_DAYS: process.env.MAX_LOG_RETENTION_DAYS || 30,\n DEFAULT_LOG_RETENTION_DAYS: process.env.DEFAULT_LOG_RETENTION_DAYS || 7,\n NORMAL_USER_MAX_LOG_RETENTION_DAYS: process.env.NORMAL_USER_MAX_LOG_RETENTION_DAYS || 7,\n NORMAL_USER_DEFAULT_LOG_RETENTION_DAYS: process.env.NORMAL_USER_DEFAULT_LOG_RETENTION_DAYS || 1,\n MAX_LOGS_PER_PROFILE: process.env.MAX_LOGS_PER_PROFILE || 500000,\n SERVERFULL_DEFAULT_PROFILE_KEY: defaultProfileKey,\n ...process.env\n };\n\n return { config, env };\n}\n", "/**\n * @file db.ts\n * @description SQLite D1Database adapter and automatic schema migration runner for Serverfull mode.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport { D1Database, D1PreparedStatement, D1Result, D1ExecResult, D1DatabaseSession } from '@cloudflare/workers-types';\nimport { getPackageRoot } from './config';\n\nexport class NodeD1PreparedStatement implements D1PreparedStatement {\n private params: any[] = [];\n\n constructor(\n public readonly db: DatabaseSync,\n public readonly sql: string\n ) {}\n\n bind(...values: any[]): D1PreparedStatement {\n this.params = values.map(v => (typeof v === 'boolean' ? (v ? 1 : 0) : v));\n return this as unknown as D1PreparedStatement;\n }\n\n async first<T = unknown>(colName?: string): Promise<T | null> {\n try {\n const stmt = this.db.prepare(this.sql);\n const row = stmt.get(...this.params) as Record<string, any> | undefined;\n if (!row) return null;\n if (colName) {\n return (row[colName] ?? null) as T;\n }\n return row as T;\n } catch (err: any) {\n console.warn(`[SQLite D1] Query error in first(): ${err.message}\\nSQL: ${this.sql}`);\n throw err;\n }\n }\n\n async all<T = unknown>(): Promise<D1Result<T>> {\n try {\n const stmt = this.db.prepare(this.sql);\n const rows = stmt.all(...this.params) as T[];\n return {\n results: rows,\n success: true,\n meta: {\n duration: 0,\n size_after: 0,\n rows_read: rows.length,\n rows_written: 0,\n last_row_id: 0,\n changed_db: false,\n changes: 0\n }\n };\n } catch (err: any) {\n console.warn(`[SQLite D1] Query error in all(): ${err.message}\\nSQL: ${this.sql}`);\n throw err;\n }\n }\n\n async run<T = unknown>(): Promise<D1Result<T>> {\n try {\n const stmt = this.db.prepare(this.sql);\n const result = stmt.run(...this.params);\n const changes = Number(result.changes);\n const lastInsertRowid = Number(result.lastInsertRowid);\n return {\n results: [],\n success: true,\n meta: {\n duration: 0,\n size_after: 0,\n rows_read: 0,\n rows_written: changes,\n last_row_id: lastInsertRowid,\n changed_db: changes > 0,\n changes: changes\n }\n };\n } catch (err: any) {\n console.warn(`[SQLite D1] Query error in run(): ${err.message}\\nSQL: ${this.sql}`);\n throw err;\n }\n }\n\n async raw<T = unknown[]>(options?: { columnNames?: boolean }): Promise<any> {\n try {\n const stmt = this.db.prepare(this.sql);\n const rows = stmt.all(...this.params) as Record<string, any>[];\n const rawRows = rows.map(r => Object.values(r));\n if (options?.columnNames && rows.length > 0) {\n return [Object.keys(rows[0]), ...rawRows];\n }\n return rawRows;\n } catch (err: any) {\n console.warn(`[SQLite D1] Query error in raw(): ${err.message}\\nSQL: ${this.sql}`);\n throw err;\n }\n }\n}\n\nexport class NodeD1Database implements D1Database {\n constructor(public readonly rawDb: DatabaseSync) {}\n\n prepare(query: string): D1PreparedStatement {\n return new NodeD1PreparedStatement(this.rawDb, query) as unknown as D1PreparedStatement;\n }\n\n async batch<T = unknown>(statements: D1PreparedStatement[]): Promise<D1Result<T>[]> {\n this.rawDb.exec('BEGIN IMMEDIATE');\n try {\n const results: D1Result<T>[] = [];\n for (const stmt of statements) {\n const res = await stmt.run<T>();\n results.push(res);\n }\n this.rawDb.exec('COMMIT');\n return results;\n } catch (err) {\n this.rawDb.exec('ROLLBACK');\n throw err;\n }\n }\n\n async exec(query: string): Promise<D1ExecResult> {\n this.rawDb.exec(query);\n return { count: 0, duration: 0 };\n }\n\n async dump(): Promise<ArrayBuffer> {\n throw new Error('dump() is not supported in NodeD1Database');\n }\n\n withSession(_constraintOrBookmark?: string): D1DatabaseSession {\n const session = Object.assign(this, {\n getBookmark: () => null\n });\n return session as unknown as D1DatabaseSession;\n }\n}\n\n/**\n * Runs all pending migrations from migrations/ directory.\n */\nexport function runMigrations(db: DatabaseSync, migrationsDir: string): void {\n // Ensure migrations tracking table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS _cf_d1_migrations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT UNIQUE NOT NULL,\n applied_at INTEGER NOT NULL\n );\n `);\n\n if (!fs.existsSync(migrationsDir)) {\n console.warn(`[Migrations] Directory not found: ${migrationsDir}`);\n return;\n }\n\n const files = fs.readdirSync(migrationsDir)\n .filter((f: string) => f.endsWith('.sql'))\n .sort();\n\n const stmtGet = db.prepare('SELECT name FROM _cf_d1_migrations WHERE name = ?');\n const stmtInsert = db.prepare('INSERT INTO _cf_d1_migrations (name, applied_at) VALUES (?, ?)');\n\n let appliedCount = 0;\n for (const file of files) {\n const existing = stmtGet.get(file);\n if (existing) continue;\n\n const filePath = path.join(migrationsDir, file);\n const sql = fs.readFileSync(filePath, 'utf-8');\n\n console.log(`[Migrations] Applying ${file}...`);\n db.exec('BEGIN IMMEDIATE');\n try {\n db.exec(sql);\n stmtInsert.run(file, Math.floor(Date.now() / 1000));\n db.exec('COMMIT');\n appliedCount++;\n } catch (err: any) {\n db.exec('ROLLBACK');\n console.error(`[Migrations] Failed to apply ${file}:`, err.message || err);\n throw err;\n }\n }\n\n if (appliedCount > 0) {\n console.log(`[Migrations] Successfully applied ${appliedCount} migration(s).`);\n } else {\n console.log('[Migrations] Database schema is up-to-date.');\n }\n}\n\n/**\n * Initializes SQLite database for Serverfull mode with WAL mode and runs pending migrations.\n */\nexport function initServerfullDb(dbPath: string, rootDir: string = getPackageRoot()): NodeD1Database {\n const dir = path.dirname(dbPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n console.log(`[SQLite D1] Connecting to database at: ${dbPath}`);\n const rawDb = new DatabaseSync(dbPath);\n\n // Configure SQLite for high concurrency, zero-copy memory reads & integrity\n rawDb.exec('PRAGMA journal_mode = WAL;');\n rawDb.exec('PRAGMA synchronous = NORMAL;');\n rawDb.exec('PRAGMA foreign_keys = ON;');\n rawDb.exec('PRAGMA temp_store = MEMORY;');\n rawDb.exec('PRAGMA cache_size = -64000;'); // 64 MB in-memory page cache\n rawDb.exec('PRAGMA mmap_size = 268435456;'); // 256 MB zero-copy memory-mapped I/O\n\n // Run migrations\n const migrationsDir = path.join(rootDir, 'migrations');\n runMigrations(rawDb, migrationsDir);\n\n return new NodeD1Database(rawDb);\n}\n", "/**\n * @file udp.ts\n * @description Classic UDP DNS server (RFC 1035 / RFC 6891) for Serverfull mode.\n */\n\nimport dgram from 'node:dgram';\nimport { Buffer } from 'node:buffer';\nimport { Env, Context, ExecutionContext } from '../types';\nimport { parseDNSQueryFromRaw } from '../utils/dns';\nimport { pipeline } from '../pipeline';\nimport { resolveDefaultProfile, resolveProfileByKey } from '../api/doh';\nimport { resolveUpstreamEndpoint, fetchFromUpstream } from '../pipeline/resolver/transport';\nimport { formatPortError } from './format';\n\nexport interface UdpServerOptions {\n port: number;\n host: string;\n defaultProfileKey?: string;\n env: Env;\n}\n\nexport class UdpDnsServer {\n private socket: dgram.Socket | null = null;\n private isRunning: boolean = false;\n\n constructor(private options: UdpServerOptions) {}\n\n start(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.socket = dgram.createSocket('udp4');\n\n const startupErrorHandler = (err: any) => {\n try { this.socket?.close(); } catch {}\n this.socket = null;\n\n console.error(formatPortError({\n serviceName: 'UDP DNS',\n protocol: 'UDP',\n port: this.options.port,\n err,\n alternateOption: '--dns-port <port> (e.g. --dns-port 5353)',\n disableOption: '--disable-udp'\n }));\n\n reject(err);\n };\n\n this.socket.once('error', startupErrorHandler);\n\n this.socket.on('message', async (msg: Buffer, rinfo: dgram.RemoteInfo) => {\n await this.handleMessage(msg, rinfo);\n });\n\n this.socket.bind(this.options.port, this.options.host, () => {\n this.socket?.removeListener('error', startupErrorHandler);\n this.socket?.on('error', (err: Error) => {\n console.error('[UDP DNS] Socket runtime error:', err);\n });\n // Boost OS UDP socket buffers to reduce packet drops under high DNS load\n try {\n this.socket?.setRecvBufferSize(4 * 1024 * 1024);\n this.socket?.setSendBufferSize(4 * 1024 * 1024);\n } catch {\n /* Gracefully ignore if OS limits prevent buffer expansion */\n }\n this.isRunning = true;\n console.log(`[UDP DNS] Classic DNS listening on udp://${this.options.host}:${this.options.port}`);\n resolve();\n });\n } catch (err) {\n reject(err);\n }\n });\n }\n\n private async handleMessage(msg: Buffer, rinfo: dgram.RemoteInfo): Promise<void> {\n try {\n const rawBytes = new Uint8Array(msg.buffer, msg.byteOffset, msg.byteLength);\n const query = parseDNSQueryFromRaw(rawBytes);\n if (!query) {\n return; // Drop invalid / malformed packets\n }\n\n const env = this.options.env;\n const ctx: ExecutionContext = {\n waitUntil(promise: Promise<any>) {\n promise.catch((err) => console.error('[UDP Background Task Error]:', err));\n },\n passThroughOnException() {}\n } as any;\n\n // Determine profile:\n // 1. Configured default profile key\n // 2. Database default / first profile fallback\n let profile = null;\n if (this.options.defaultProfileKey) {\n profile = await resolveProfileByKey(this.options.defaultProfileKey, env, ctx);\n }\n if (!profile) {\n profile = await resolveDefaultProfile(env, ctx);\n }\n if (!profile) {\n return;\n }\n\n const context: Context = {\n profileId: profile.id,\n accessPointId: profile.access_point_id,\n accessPointName: profile.access_point_name,\n startTime: Date.now(),\n env,\n ctx\n };\n\n const host = rinfo.address.includes(':') ? `[${rinfo.address}]` : rinfo.address;\n const request = new Request(`http://${host}/dns-query`, {\n headers: {\n 'CF-Connecting-IP': rinfo.address,\n 'Accept': 'application/dns-message',\n 'Content-Type': 'application/dns-message'\n }\n });\n\n const result = await pipeline.process(request, query, context);\n if (!result || !result.answer || result.answer.length === 0) {\n return;\n }\n\n let answer = result.answer;\n\n // RFC 1035 UDP 512 bytes limit check (if no EDNS or response > 512, truncate)\n if (answer.length > 512) {\n const truncated = new Uint8Array(answer);\n // Set TC (Truncation) bit: flags byte 1 (offset 2) bit 1 (0x02)\n truncated[2] |= 0x02;\n answer = truncated.slice(0, 512);\n }\n\n this.socket?.send(answer, rinfo.port, rinfo.address, (err: Error | null) => {\n if (err) {\n console.warn(`[UDP DNS] Failed to send answer to ${rinfo.address}:${rinfo.port}:`, err.message);\n }\n });\n } catch (err) {\n console.error('[UDP DNS] Message handling exception:', err);\n try {\n const failOpenUpstream = this.options.env.FAIL_OPEN_UPSTREAM || 'https://freedns.controld.com/no-ads-malware-typo';\n const rawBytes = new Uint8Array(msg.buffer, msg.byteOffset, msg.byteLength);\n const endpoint = resolveUpstreamEndpoint(failOpenUpstream);\n const transportRes = await fetchFromUpstream(endpoint, rawBytes);\n let answer = transportRes.answer;\n if (answer.length > 512) {\n const truncated = new Uint8Array(answer);\n truncated[2] |= 0x02;\n answer = truncated.slice(0, 512);\n }\n this.socket?.send(answer, rinfo.port, rinfo.address);\n } catch (fallbackErr) {\n console.error('[UDP DNS] Fail-open upstream fallback failed:', fallbackErr);\n }\n }\n }\n\n stop(): Promise<void> {\n return new Promise((resolve) => {\n if (this.socket && this.isRunning) {\n this.socket.close(() => {\n this.isRunning = false;\n console.log('[UDP DNS] Service stopped.');\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n}\n", "/**\n * Dictionary mapping DNS record type codes to their uppercase string representations.\n */\nexport const DNS_TYPES: Record<number, string> = {\n 1: \"A\",\n 2: \"NS\",\n 5: \"CNAME\",\n 6: \"SOA\",\n 12: \"PTR\",\n 13: \"HINFO\",\n 15: \"MX\",\n 16: \"TXT\",\n 17: \"RP\",\n 24: \"SIG\",\n 25: \"KEY\",\n 28: \"AAAA\",\n 29: \"LOC\",\n 33: \"SRV\",\n 35: \"NAPTR\",\n 36: \"KX\",\n 37: \"CERT\",\n 39: \"DNAME\",\n 41: \"OPT\",\n 43: \"DS\",\n 44: \"SSHFP\",\n 45: \"IPSECKEY\",\n 46: \"RRSIG\",\n 47: \"NSEC\",\n 48: \"DNSKEY\",\n 49: \"DHCID\",\n 50: \"NSEC3\",\n 51: \"NSEC3PARAM\",\n 52: \"TLSA\",\n 53: \"SMIMEA\",\n 59: \"CDS\",\n 64: \"SVCB\",\n 65: \"HTTPS\",\n 99: \"SPF\",\n 255: \"ANY\",\n 256: \"URI\",\n 257: \"CAA\",\n 32769: \"DLV\"\n};\n\n/**\n * Dictionary mapping DNS record type strings to their numeric codes.\n */\nexport const DNS_TYPE_TO_CODE: Record<string, number> = Object.fromEntries(\n Object.entries(DNS_TYPES).map(([code, name]) => [name, Number(code)])\n);\n", "import { DNSQuery } from \"../../types\";\nimport { DNS_TYPES } from \"./constants\";\n\n/**\n * Decodes a DNS domain name from the raw packet buffer (with support for compression pointers 0xC0).\n *\n * @param buffer - The raw DNS packet buffer.\n * @param offset - The byte offset where the name starts.\n * @returns An object containing the decoded domain name and the number of bytes read.\n */\nexport function decodeName(\n buffer: Uint8Array,\n offset: number\n): { name: string; read: number } {\n let name = \"\";\n let curr = offset;\n let jumped = false;\n let consumed = 0;\n let iterations = 0;\n let terminated = false;\n\n while (iterations < 128) {\n if (curr >= buffer.length) break; // Boundary check\n const len = buffer[curr];\n\n if (len === 0) {\n if (!jumped) consumed++;\n terminated = true;\n break;\n }\n\n if ((len & 0xc0) === 0xc0) {\n if (curr + 1 >= buffer.length) break; // Pointer bytes check\n const pointer = ((len & 0x3f) << 8) | buffer[curr + 1];\n if (!jumped) {\n consumed += 2;\n jumped = true;\n }\n curr = pointer;\n iterations++;\n continue;\n }\n\n // RFC 1035: Label length top 2 bits must be 00 for uncompressed labels (max 63 octets)\n if ((len & 0xc0) !== 0) {\n break;\n }\n\n if (name.length > 0) name += \".\";\n // Check if label content overflows the buffer\n if (curr + 1 + len > buffer.length) break;\n\n for (let i = 0; i < len; i++) {\n name += String.fromCharCode(buffer[curr + 1 + i]);\n }\n if (!jumped) consumed += len + 1;\n curr += len + 1;\n iterations++;\n }\n\n if (!terminated) {\n return { name: \"\", read: 0 };\n }\n\n return { name, read: consumed };\n}\n\n/**\n * Maps a numeric DNS query type code to its string representation.\n *\n * @param type - The numeric DNS query type code.\n * @returns The string representation of the record type (e.g. \"A\", \"AAAA\").\n */\nexport function getQTypeName(type: number): string {\n return DNS_TYPES[type] || `TYPE${type}`;\n}\n\n/**\n * Parses an incoming HTTP Request (GET or POST) containing a DNS query packet.\n *\n/**\n * Parses raw DNS query bytes into a structured DNSQuery object.\n *\n * @param raw - The raw DNS packet buffer.\n * @returns Parsed DNSQuery or null if invalid/truncated.\n */\nexport function parseDNSQueryFromRaw(raw: Uint8Array): DNSQuery | null {\n try {\n // Minimum header length: Header(12) + QTYPE(2) + QCLASS(2) = 16\n if (raw.length < 16) return null;\n\n const { name, read } = decodeName(raw, 12);\n if (read === 0) return null;\n const qtypeOffset = 12 + read;\n\n // Boundary check for query type and class\n if (qtypeOffset + 4 > raw.length) return null;\n\n const qtypeCode = (raw[qtypeOffset] << 8) | raw[qtypeOffset + 1];\n\n return {\n name,\n type: getQTypeName(qtypeCode),\n raw\n };\n } catch (e) {\n console.error(\"DNS Parse Error:\", e);\n return null;\n }\n}\n\n/**\n * Parses an incoming HTTP Request (GET or POST) containing a DNS query packet.\n *\n * @param request - The incoming Cloudflare Worker Request object.\n * @returns A promise resolving to the parsed DNSQuery or null if invalid.\n */\nexport async function parseDNSQuery(\n request: Request\n): Promise<DNSQuery | null> {\n try {\n let raw: Uint8Array;\n\n if (request.method === \"GET\") {\n const url = new URL(request.url);\n const dnsParam = url.searchParams.get(\"dns\");\n if (!dnsParam) return null;\n\n let base64 = dnsParam.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4) base64 += \"=\";\n\n const binary = atob(base64);\n raw = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n raw[i] = binary.charCodeAt(i);\n }\n } else if (request.method === \"POST\") {\n const buffer = await request.arrayBuffer();\n raw = new Uint8Array(buffer);\n } else {\n return null;\n }\n\n return parseDNSQueryFromRaw(raw);\n } catch (e) {\n console.error(\"DNS Parse Error:\", e);\n return null;\n }\n}\n\n/**\n * Parses raw DNS answer packets into structured answer record details.\n *\n * @param raw - The raw DNS response packet buffer.\n * @returns An array of parsed DNS answer objects containing name, type, data, and ttl.\n */\nexport function parseDNSAnswer(\n raw: Uint8Array\n): { name: string; type: string; data: string; ttl: number }[] {\n if (raw.length < 12) return [];\n const ansCount = (raw[6] << 8) | raw[7];\n if (ansCount === 0) return [];\n\n const results: { name: string; type: string; data: string; ttl: number }[] = [];\n let offset = 12;\n\n // Skip question records safely\n const qCount = (raw[4] << 8) | raw[5];\n for (let i = 0; i < qCount; i++) {\n if (offset >= raw.length) return [];\n const { read } = decodeName(raw, offset);\n if (read === 0) return [];\n offset += read + 4; // QNAME + QTYPE(2) + QCLASS(2)\n if (offset > raw.length) return [];\n }\n\n for (let i = 0; i < ansCount; i++) {\n if (offset >= raw.length) break;\n\n const { name, read: nameRead } = decodeName(raw, offset);\n if (nameRead === 0) break;\n offset += nameRead;\n\n // Boundary check 1: Ensure enough bytes for fixed RR header\n // TYPE(2) + CLASS(2) + TTL(4) + RDLENGTH(2) = 10 bytes\n if (offset + 10 > raw.length) {\n break;\n }\n\n const typeCode = (raw[offset] << 8) | raw[offset + 1];\n const ttl =\n (raw[offset + 4] << 24) |\n (raw[offset + 5] << 16) |\n (raw[offset + 6] << 8) |\n raw[offset + 7];\n const rdLength = (raw[offset + 8] << 8) | raw[offset + 9];\n offset += 10;\n\n // Boundary check 2: Ensure the buffer contains the full RDATA bytes declared by rdLength.\n // A truncated or malformed packet can declare an rdLength whose bytes are not in the buffer;\n // indexing past the buffer yields undefined, which would render as\n // \"undefined.undefined.undefined.undefined\" for A records. Drop the record and stop parsing\n // rather than emitting invented data.\n if (offset + rdLength > raw.length) {\n break;\n }\n\n const type = getQTypeName(typeCode);\n let data = \"\";\n\n if (type === \"A\" && rdLength === 4) {\n data = `${raw[offset]}.${raw[offset + 1]}.${raw[offset + 2]}.${raw[offset + 3]}`;\n } else if (type === \"AAAA\" && rdLength === 16) {\n const parts: string[] = [];\n for (let j = 0; j < 16; j += 2) {\n parts.push(\n ((raw[offset + j] << 8) | raw[offset + j + 1]).toString(16)\n );\n }\n // Locate the longest contiguous sequence of \"0\"s for IPv6 compression\n let bestStart = -1,\n bestLen = 0;\n let currentStart = -1,\n currentLen = 0;\n for (let j = 0; j < parts.length; j++) {\n if (parts[j] === \"0\") {\n if (currentStart === -1) currentStart = j;\n currentLen++;\n } else {\n if (currentLen > bestLen) {\n bestStart = currentStart;\n bestLen = currentLen;\n }\n currentStart = -1;\n currentLen = 0;\n }\n }\n if (currentLen > bestLen) {\n bestStart = currentStart;\n bestLen = currentLen;\n }\n\n if (bestLen > 1) {\n // Replace the longest \"0\" sequence with \"::\"\n const replacement =\n bestStart === 0 || bestStart + bestLen === parts.length ? \":\" : \"\";\n parts.splice(bestStart, bestLen, replacement);\n }\n data = parts.join(\":\").replace(\":::\", \"::\");\n } else if (type === \"CNAME\" || type === \"NS\" || type === \"PTR\") {\n data = decodeName(raw, offset).name;\n } else if (type === \"TXT\") {\n // Process TXT records (which can contain multiple substrings)\n let txtOffset = offset;\n const txtParts: string[] = [];\n const txtEnd = offset + rdLength;\n while (txtOffset < txtEnd) {\n const len = raw[txtOffset];\n if (txtOffset + 1 + len > txtEnd) break;\n txtParts.push(\n String.fromCharCode(\n ...raw.slice(txtOffset + 1, txtOffset + 1 + len)\n )\n );\n txtOffset += len + 1;\n }\n data = txtParts.join(\"\");\n } else if (type === \"HTTPS\" || type === \"SVCB\") {\n // HTTPS/SVCB format: priority (2 bytes) + target name (variable) + parameters (variable)\n if (rdLength >= 2) {\n const priority = (raw[offset] << 8) | raw[offset + 1];\n const { name: target, read: targetRead } = decodeName(raw, offset + 2);\n let curr = offset + 2 + targetRead;\n const end = offset + rdLength;\n const params: string[] = [];\n\n while (curr + 4 <= end) {\n const key = (raw[curr] << 8) | raw[curr + 1];\n const valLen = (raw[curr + 2] << 8) | raw[curr + 3];\n curr += 4;\n\n if (curr + valLen > end) break;\n\n if (key === 1) { // alpn\n let pCurr = curr;\n const pEnd = curr + valLen;\n const alpns: string[] = [];\n while (pCurr < pEnd) {\n const aLen = raw[pCurr];\n if (pCurr + 1 + aLen > pEnd) break;\n let aStr = \"\";\n for (let k = 0; k < aLen; k++) {\n aStr += String.fromCharCode(raw[pCurr + 1 + k]);\n }\n alpns.push(aStr);\n pCurr += aLen + 1;\n }\n params.push(`alpn=${alpns.join(\",\")}`);\n } else if (key === 4) { // ipv4hint\n const ips: string[] = [];\n for (let k = 0; k + 4 <= valLen; k += 4) {\n ips.push(`${raw[curr + k]}.${raw[curr + k + 1]}.${raw[curr + k + 2]}.${raw[curr + k + 3]}`);\n }\n params.push(`ipv4hint=${ips.join(\",\")}`);\n } else if (key === 5) { // ech\n let binStr = \"\";\n for (let k = 0; k < valLen; k++) {\n binStr += String.fromCharCode(raw[curr + k]);\n }\n params.push(`ech=${btoa(binStr)}`);\n } else if (key === 6) { // ipv6hint\n const ips: string[] = [];\n for (let k = 0; k + 16 <= valLen; k += 16) {\n const parts: string[] = [];\n for (let j = 0; j < 16; j += 2) {\n parts.push(((raw[curr + k + j] << 8) | raw[curr + k + j + 1]).toString(16));\n }\n ips.push(parts.join(\":\"));\n }\n params.push(`ipv6hint=${ips.join(\",\")}`);\n } else {\n params.push(`key${key}=[${valLen}B]`);\n }\n\n curr += valLen;\n }\n\n data = `${priority} ${target || \".\"}${params.length > 0 ? \" \" + params.join(\" \") : \"\"}`.trim();\n } else {\n data = `[Raw: ${rdLength} bytes]`;\n }\n } else {\n data = `[Raw: ${rdLength} bytes]`;\n }\n\n results.push({ name, type, data, ttl });\n offset += rdLength;\n }\n\n return results;\n}\n", "/**\n * Default fallback preset ECH Fronting (outer SNI) domains when not configured in env.\n */\nexport const DEFAULT_PRESET_ECH_FRONTING_DOMAINS: readonly string[] = [\n \"cloudflare-ech.com\",\n \"crypto.cloudflare.com\",\n \"one.one.one.one\",\n \"www.cloudflare.com\",\n \"encryptedsni.com\",\n \"cdnjs.com\"\n];\n\n/**\n * Supported preset ECH Fronting (outer SNI) domains (alias of default preset).\n * For runtime configured domains, use `getPresetEchFrontingDomains(env)`.\n */\nexport const PRESET_ECH_FRONTING_DOMAINS: readonly string[] = DEFAULT_PRESET_ECH_FRONTING_DOMAINS;\n\n\n/**\n * Default preferred ECH fronting domain.\n */\nexport const DEFAULT_ECH_FRONTING_DOMAIN = \"cloudflare-ech.com\";\n\n/**\n * Fallback Cloudflare active ECH public key & parameters (RFC draft-13 / RFC 9460).\n */\nexport const CLOUDFLARE_DEFAULT_ECH_BASE64 =\n \"AEX+DQBBawAgACAUjjcEHSf2wlThCqPLx//d+m3qlWUe3nwuQaUVaVwQBgAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA=\";\n\n/**\n * Resolves preset ECH fronting domains from environment variables or falls back to default presets.\n *\n * @param env - Worker or Serverfull environment bindings.\n * @returns Array of fronting domain strings.\n */\nexport function getPresetEchFrontingDomains(env?: { PRESET_ECH_FRONTING_DOMAINS?: string }): string[] {\n if (env?.PRESET_ECH_FRONTING_DOMAINS) {\n try {\n const parsed = JSON.parse(env.PRESET_ECH_FRONTING_DOMAINS);\n if (Array.isArray(parsed) && parsed.length > 0) {\n return parsed\n .map((item: unknown) => (typeof item === \"string\" ? item : (item as { domain?: string })?.domain || \"\"))\n .filter(Boolean);\n }\n } catch (e) {\n console.warn(\"[ECH] Failed to parse PRESET_ECH_FRONTING_DOMAINS from env:\", e);\n }\n }\n return [...DEFAULT_PRESET_ECH_FRONTING_DOMAINS];\n}\n\n", "import { DEFAULT_ECH_FRONTING_DOMAIN, CLOUDFLARE_DEFAULT_ECH_BASE64 } from \"./constants\";\n\n/**\n * Rebuilds an existing RFC draft-13 ECHConfigList by replacing its public_name (outer SNI)\n * with the specified fronting domain, keeping Cloudflare's exact public key, config_id, and cipher suites.\n *\n * @param rawBase64 - Existing Base64 ECHConfigList from Cloudflare.\n * @param newFrontingDomain - Target fronting domain (e.g. \"crypto.cloudflare.com\").\n * @returns Base64 string of the updated ECHConfigList.\n */\nexport function rebuildEchWithFronting(\n rawBase64: string,\n newFrontingDomain: string = DEFAULT_ECH_FRONTING_DOMAIN\n): string {\n try {\n const binary = atob(rawBase64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n\n if (bytes.length < 16) return rawBase64;\n\n // uint16 list_len: bytes[0..1]\n // uint16 version: bytes[2..3] (0xfe0d)\n const version = (bytes[2] << 8) | bytes[3];\n if (version !== 0xfe0d) return rawBase64;\n\n // ECHConfigContents:\n // uint8 config_id: bytes[6]\n // uint16 kem_id: bytes[7..8]\n // uint16 pk_len: bytes[9..10]\n const configId = bytes[6];\n const kemId = (bytes[7] << 8) | bytes[8];\n const pkLen = (bytes[9] << 8) | bytes[10];\n const pkStart = 11;\n const pkEnd = pkStart + pkLen;\n if (pkEnd + 4 > bytes.length) return rawBase64;\n const publicKey = bytes.slice(pkStart, pkEnd);\n\n let offset = pkEnd;\n const csLen = (bytes[offset] << 8) | bytes[offset + 1];\n offset += 2;\n if (offset + csLen + 2 > bytes.length) return rawBase64;\n const cipherSuites = bytes.slice(offset, offset + csLen);\n offset += csLen;\n\n const maxNameLen = bytes[offset++];\n const oldNameLen = bytes[offset++];\n if (offset + oldNameLen + 2 > bytes.length) return rawBase64;\n offset += oldNameLen;\n\n const extLen = (bytes[offset] << 8) | bytes[offset + 1];\n offset += 2;\n if (offset + extLen > bytes.length) return rawBase64;\n const extensions = bytes.slice(offset, offset + extLen);\n\n const targetName = (newFrontingDomain || DEFAULT_ECH_FRONTING_DOMAIN).trim().toLowerCase();\n const nameBytes = new Uint8Array(targetName.length);\n for (let i = 0; i < targetName.length; i++) {\n nameBytes[i] = targetName.charCodeAt(i);\n }\n\n // Assemble new ECHConfigContents\n const contents: number[] = [\n configId,\n (kemId >> 8) & 0xff,\n kemId & 0xff,\n (pkLen >> 8) & 0xff,\n pkLen & 0xff,\n ...Array.from(publicKey),\n (csLen >> 8) & 0xff,\n csLen & 0xff,\n ...Array.from(cipherSuites),\n maxNameLen,\n nameBytes.length,\n ...Array.from(nameBytes),\n (extLen >> 8) & 0xff,\n extLen & 0xff,\n ...Array.from(extensions)\n ];\n\n // ECHConfig\n const config: number[] = [\n 0xfe,\n 0x0d,\n (contents.length >> 8) & 0xff,\n contents.length & 0xff,\n ...contents\n ];\n\n // ECHConfigList\n const list: number[] = [\n (config.length >> 8) & 0xff,\n config.length & 0xff,\n ...config\n ];\n\n let resultBinary = \"\";\n for (let i = 0; i < list.length; i++) {\n resultBinary += String.fromCharCode(list[i]);\n }\n return btoa(resultBinary);\n } catch (e) {\n console.error(\"Failed to rebuild ECH with fronting domain:\", e);\n return rawBase64;\n }\n}\n\n/**\n * Generates an RFC draft-13 ECHConfigList base64 string for Cloudflare's edge,\n * embedding the custom or preset public_name (outer SNI / ECH Fronting domain).\n *\n * @param publicName - The fronting outer domain name (defaults to \"crypto.cloudflare.com\").\n * @param sourceEchBase64 - Optional raw ECH template from Cloudflare upstream.\n * @returns A base64-encoded ECHConfigList string.\n */\nexport function buildCloudflareEchConfig(\n publicName: string = DEFAULT_ECH_FRONTING_DOMAIN,\n sourceEchBase64?: string\n): string {\n const base = sourceEchBase64 || CLOUDFLARE_DEFAULT_ECH_BASE64;\n return rebuildEchWithFronting(base, publicName);\n}\n", "/**\n * Parses an IPv6 address string into an array of 16 bytes.\n */\nexport function parseIPv6Bytes(ip: string): number[] {\n const bytes = new Uint8Array(16);\n if (ip.includes(\"::\")) {\n const [left, right] = ip.split(\"::\");\n const leftParts = left ? left.split(\":\").filter(Boolean) : [];\n const rightParts = right ? right.split(\":\").filter(Boolean) : [];\n\n let i = 0;\n for (const part of leftParts) {\n const v = parseInt(part, 16);\n bytes[i++] = (v >> 8) & 0xff;\n bytes[i++] = v & 0xff;\n }\n\n i = 16 - rightParts.length * 2;\n for (const part of rightParts) {\n const v = parseInt(part, 16);\n bytes[i++] = (v >> 8) & 0xff;\n bytes[i++] = v & 0xff;\n }\n } else {\n const parts = ip.split(\":\");\n let i = 0;\n for (const part of parts) {\n const v = parseInt(part, 16);\n bytes[i++] = (v >> 8) & 0xff;\n bytes[i++] = v & 0xff;\n }\n }\n return Array.from(bytes);\n}\n\n/**\n * Encodes structured HTTPS/SVCB parameters into standard RFC 9460 RDATA byte sequence.\n */\nexport function encodeHttpsRDataFromParams(options: {\n priority?: number;\n target?: string;\n alpn?: string[];\n ipv4hints?: string[];\n ipv6hints?: string[];\n echBase64?: string;\n}): number[] {\n const {\n priority = 1,\n target = \".\",\n alpn = [\"h3\", \"h2\"],\n ipv4hints = [],\n ipv6hints = [],\n echBase64 = null\n } = options;\n\n const bytes: number[] = [];\n\n // 1. Priority (uint16)\n bytes.push((priority >> 8) & 0xff, priority & 0xff);\n\n // 2. Target Name (wire format DNS name, 0x00 for root \".\")\n if (!target || target === \".\") {\n bytes.push(0x00);\n } else {\n const labels = target.split(\".\");\n for (const label of labels) {\n bytes.push(label.length);\n for (let i = 0; i < label.length; i++) {\n bytes.push(label.charCodeAt(i));\n }\n }\n bytes.push(0x00);\n }\n\n // 3. SvcParams strictly ordered by key in ascending order (RFC 9460 Section 2.2):\n // Key 1: alpn\n if (alpn && alpn.length > 0) {\n const alpnVal: number[] = [];\n for (const proto of alpn) {\n alpnVal.push(proto.length);\n for (let i = 0; i < proto.length; i++) {\n alpnVal.push(proto.charCodeAt(i));\n }\n }\n bytes.push(0x00, 0x01); // Key 1\n bytes.push((alpnVal.length >> 8) & 0xff, alpnVal.length & 0xff);\n bytes.push(...alpnVal);\n }\n\n // Key 4: ipv4hint\n if (ipv4hints && ipv4hints.length > 0) {\n const ipBytes: number[] = [];\n for (const ip of ipv4hints) {\n const parts = ip.split(\".\").map(Number);\n if (parts.length === 4 && parts.every((p) => !isNaN(p) && p >= 0 && p <= 255)) {\n ipBytes.push(...parts);\n }\n }\n if (ipBytes.length > 0) {\n bytes.push(0x00, 0x04); // Key 4\n bytes.push((ipBytes.length >> 8) & 0xff, ipBytes.length & 0xff);\n bytes.push(...ipBytes);\n }\n }\n\n // Key 5: ech\n if (echBase64) {\n try {\n const binaryStr = atob(echBase64);\n const echBytes: number[] = [];\n for (let i = 0; i < binaryStr.length; i++) {\n echBytes.push(binaryStr.charCodeAt(i));\n }\n bytes.push(0x00, 0x05); // Key 5\n bytes.push((echBytes.length >> 8) & 0xff, echBytes.length & 0xff);\n bytes.push(...echBytes);\n } catch (e) {\n console.error(\"Failed to decode base64 ech config:\", e);\n }\n }\n\n // Key 6: ipv6hint\n if (ipv6hints && ipv6hints.length > 0) {\n const ip6Bytes: number[] = [];\n for (const ip of ipv6hints) {\n const raw16 = parseIPv6Bytes(ip);\n if (raw16 && raw16.length === 16) {\n ip6Bytes.push(...raw16);\n }\n }\n if (ip6Bytes.length > 0) {\n bytes.push(0x00, 0x06); // Key 6\n bytes.push((ip6Bytes.length >> 8) & 0xff, ip6Bytes.length & 0xff);\n bytes.push(...ip6Bytes);\n }\n }\n\n return bytes;\n}\n\n/**\n * Encodes a standard textual HTTPS/SVCB presentation string (e.g. \"1 . alpn=h3,h2 ipv4hint=... ech=...\")\n * into RFC 9460 RDATA binary bytes.\n *\n * @param value - The textual presentation string.\n * @returns Array of RDATA bytes.\n */\nexport function encodeHttpsRDataFromString(value: string): number[] {\n const tokens = value.trim().split(/\\s+/);\n const priority = parseInt(tokens[0], 10) || 1;\n const target = tokens[1] || \".\";\n\n let alpn: string[] | undefined;\n let ipv4hints: string[] | undefined;\n let echBase64: string | undefined;\n let ipv6hints: string[] | undefined;\n\n for (let i = 2; i < tokens.length; i++) {\n const token = tokens[i];\n const eqIdx = token.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const k = token.substring(0, eqIdx);\n const v = token.substring(eqIdx + 1);\n if (k === \"alpn\") {\n alpn = v.split(\",\");\n } else if (k === \"ipv4hint\") {\n ipv4hints = v.split(\",\");\n } else if (k === \"ech\") {\n echBase64 = v;\n } else if (k === \"ipv6hint\") {\n ipv6hints = v.split(\",\");\n }\n }\n\n return encodeHttpsRDataFromParams({\n priority,\n target,\n alpn,\n ipv4hints,\n ipv6hints,\n echBase64\n });\n}\n", "/**\n * @file cidr.ts\n * @description High-performance IPv4 and IPv6 CIDR parsing, matching, and validation utilities.\n */\n\n/**\n * Pre-compiled IPv4 CIDR range structure.\n */\nexport interface ParsedCidrV4 {\n version: 4;\n net: number;\n mask: number;\n}\n\n/**\n * Pre-compiled IPv6 CIDR range structure.\n */\nexport interface ParsedCidrV6 {\n version: 6;\n net: bigint;\n mask: bigint;\n}\n\nexport type ParsedCidr = ParsedCidrV4 | ParsedCidrV6;\n\n/**\n * Converts an IPv4 address string into a 32-bit unsigned integer, or null if invalid.\n *\n * @param ip - The IPv4 string to convert.\n * @returns 32-bit unsigned integer or null.\n */\nexport function ipv4ToNumeric(ip: string): number | null {\n const parts = ip.split(\".\");\n if (parts.length !== 4) return null;\n\n let hasError = false;\n const val = parts.reduce((ipInt, octet) => {\n const num = Number(octet);\n if (octet === \"\" || isNaN(num) || num < 0 || num > 255 || String(num) !== octet) {\n hasError = true;\n }\n return (ipInt << 8) + num;\n }, 0) >>> 0;\n\n return hasError ? null : val;\n}\n\n/**\n * Converts an IPv6 address string into a 128-bit BigInt, or null if invalid.\n *\n * @param ip - The IPv6 string to convert.\n * @returns 128-bit BigInt or null.\n */\nexport function ipv6ToBigInt(ip: string): bigint | null {\n let cleanIp = ip.trim().toLowerCase();\n if (cleanIp.startsWith(\"[\") && cleanIp.endsWith(\"]\")) {\n cleanIp = cleanIp.slice(1, -1);\n }\n\n // Extract mapped IPv4 part if present\n let ipv4Part = \"\";\n if (cleanIp.includes(\".\")) {\n const lastColon = cleanIp.lastIndexOf(\":\");\n if (lastColon === -1) return null;\n ipv4Part = cleanIp.substring(lastColon + 1);\n cleanIp = cleanIp.substring(0, lastColon) + \":0:0\";\n }\n\n // A valid IPv6 can only have at most one \"::\"\n const doubleColonParts = cleanIp.split(\"::\");\n if (doubleColonParts.length > 2) return null;\n\n let fullIp = cleanIp;\n if (doubleColonParts.length === 2) {\n const leftParts = doubleColonParts[0] ? doubleColonParts[0].split(\":\") : [];\n const rightParts = doubleColonParts[1] ? doubleColonParts[1].split(\":\") : [];\n if (leftParts.includes(\"\") || rightParts.includes(\"\")) return null;\n\n const missingLength = 8 - (leftParts.length + rightParts.length);\n if (missingLength < 0) return null;\n\n const middle = new Array(missingLength).fill(\"0\").join(\":\");\n fullIp = [...leftParts, middle, ...rightParts].filter(Boolean).join(\":\");\n }\n\n const parts = fullIp.split(\":\");\n if (parts.length !== 8) return null;\n\n if (ipv4Part) {\n const ipv4Val = ipv4ToNumeric(ipv4Part);\n if (ipv4Val === null) return null;\n parts[6] = (ipv4Val >>> 16).toString(16);\n parts[7] = (ipv4Val & 0xffff).toString(16);\n }\n\n let hexString = \"\";\n for (const part of parts) {\n if (part === \"\" || part.length > 4) return null;\n const parsed = parseInt(part, 16);\n if (\n isNaN(parsed) ||\n parsed < 0 ||\n parsed > 0xffff ||\n part.toLowerCase() !== parsed.toString(16).padStart(part.length, \"0\")\n ) {\n return null;\n }\n hexString += part.padStart(4, \"0\");\n }\n\n try {\n return BigInt(\"0x\" + hexString);\n } catch {\n return null;\n }\n}\n\n/**\n * Checks if a string is a valid IPv4 address.\n *\n * @param ip - String to test.\n * @returns True if valid IPv4.\n */\nexport function isIPv4(ip: string): boolean {\n return ipv4ToNumeric(ip) !== null;\n}\n\n/**\n * Checks if a string is a valid IPv6 address.\n *\n * @param ip - String to test.\n * @returns True if valid IPv6.\n */\nexport function isIPv6(ip: string): boolean {\n return ipv6ToBigInt(ip) !== null;\n}\n\n/**\n * Parses an IPv4 or IPv6 CIDR string into numeric net and mask representations.\n *\n * @param cidr - The CIDR string (e.g. \"192.168.0.0/16\" or \"2606:4700::/32\").\n * @returns Parsed CIDR structure, or null if invalid format.\n */\nexport function parseCidr(cidr: string): ParsedCidr | null {\n if (!cidr || typeof cidr !== \"string\") return null;\n const slashIdx = cidr.lastIndexOf(\"/\");\n if (slashIdx === -1) return null;\n\n const ipPart = cidr.slice(0, slashIdx).trim();\n const prefixStr = cidr.slice(slashIdx + 1).trim();\n const prefixLen = parseInt(prefixStr, 10);\n if (isNaN(prefixLen) || prefixLen < 0) return null;\n\n if (ipPart.includes(\":\")) {\n if (prefixLen > 128) return null;\n const ipVal = ipv6ToBigInt(ipPart);\n if (ipVal === null) return null;\n const mask = prefixLen === 0 ? 0n : ((~0n << BigInt(128 - prefixLen)) & ((1n << 128n) - 1n));\n const net = ipVal & mask;\n return { version: 6, net, mask };\n } else {\n if (prefixLen > 32) return null;\n const ipVal = ipv4ToNumeric(ipPart);\n if (ipVal === null) return null;\n const mask = prefixLen === 0 ? 0 : (~0 << (32 - prefixLen)) >>> 0;\n const net = (ipVal & mask) >>> 0;\n return { version: 4, net, mask };\n }\n}\n\n/**\n * Checks whether an IP address belongs to a specified CIDR range.\n *\n * @param ip - The IP address string to check (IPv4 or IPv6).\n * @param cidr - The CIDR block string (IPv4 or IPv6).\n * @returns True if the IP is contained within the CIDR range.\n */\nexport function ipInCidr(ip: string, cidr: string): boolean {\n const parsed = parseCidr(cidr);\n if (!parsed) return false;\n\n if (parsed.version === 4) {\n const ipVal = ipv4ToNumeric(ip);\n if (ipVal === null) return false;\n return ((ipVal & parsed.mask) >>> 0) === parsed.net;\n } else {\n const ipVal = ipv6ToBigInt(ip);\n if (ipVal === null) return false;\n return (ipVal & parsed.mask) === parsed.net;\n }\n}\n\n/**\n * Checks whether an IP address belongs to any of the specified CIDR ranges.\n *\n * @param ip - The IP address string to check.\n * @param cidrs - Array of CIDR strings.\n * @returns True if the IP matches any CIDR range.\n */\nexport function ipInCidrs(ip: string, cidrs: readonly string[]): boolean {\n return cidrs.some((cidr) => ipInCidr(ip, cidr));\n}\n\n/**\n * Compiles a list of CIDR strings into an optimized high-throughput matcher function.\n *\n * @param cidrs - Array of CIDR strings (can contain both IPv4 and IPv6 ranges).\n * @returns A matcher function (ip: string) => boolean.\n */\nexport function createCidrMatcher(cidrs: readonly string[]): (ip: string) => boolean {\n const v4Ranges: ParsedCidrV4[] = [];\n const v6Ranges: ParsedCidrV6[] = [];\n\n for (const cidr of cidrs) {\n const parsed = parseCidr(cidr);\n if (parsed) {\n if (parsed.version === 4) {\n v4Ranges.push(parsed);\n } else {\n v6Ranges.push(parsed);\n }\n }\n }\n\n return (ip: string): boolean => {\n if (!ip) return false;\n if (ip.includes(\":\")) {\n const ipVal = ipv6ToBigInt(ip);\n if (ipVal === null) return false;\n return v6Ranges.some((r) => (ipVal & r.mask) === r.net);\n } else {\n const ipVal = ipv4ToNumeric(ip);\n if (ipVal === null) return false;\n return v4Ranges.some((r) => ((ipVal & r.mask) >>> 0) === r.net);\n }\n };\n}\n", "import { createCidrMatcher } from \"../cidr\";\n\nlet activeCfProxyCidrs: readonly string[] = [];\nlet activeCfMatcher: ((ip: string) => boolean) | null = null;\n\n/**\n * Updates the in-memory Cloudflare CIDR list and recompiles the fast matcher.\n *\n * @param cidrs - Array of CIDR strings.\n */\nexport function updateActiveCfProxyCidrs(cidrs: readonly string[]): void {\n if (cidrs && cidrs.length > 0) {\n activeCfProxyCidrs = cidrs;\n activeCfMatcher = createCidrMatcher(activeCfProxyCidrs);\n }\n}\n\n/**\n * Returns the currently active in-memory Cloudflare CIDR ranges.\n */\nexport function getActiveCfProxyCidrs(): readonly string[] {\n return activeCfProxyCidrs;\n}\n\n/**\n * High-performance compiled CIDR matcher for Cloudflare CDN/Proxy edge IPs.\n *\n * @param ip - The IP address to check.\n * @returns True if the IP belongs to Cloudflare CDN/Proxy edge.\n */\nexport function isCloudflareIp(ip: string): boolean {\n return activeCfMatcher ? activeCfMatcher(ip) : false;\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\n\ninterface SettingCacheEntry {\n value: string | null;\n expiresAt: number;\n}\n\n/** Global in-memory cache for system settings per Worker isolate (5-minute TTL) */\nconst MEMORY_CACHE = new Map<string, SettingCacheEntry>();\nconst CACHE_TTL_MS = 5 * 60 * 1000;\n\nexport class SystemSettingsModel {\n constructor(private db: D1Database) {}\n\n /**\n * Clears the in-memory cache for a specific key, or all keys if none provided.\n *\n * @param key - Optional setting key to clear. If omitted, clears entire cache.\n */\n static clearMemoryCache(key?: string): void {\n if (key) {\n MEMORY_CACHE.delete(key);\n } else {\n MEMORY_CACHE.clear();\n }\n }\n\n /**\n * Defensively creates system_settings table if it does not already exist.\n */\n private async ensureTable(): Promise<void> {\n try {\n await this.db.prepare(\n \"CREATE TABLE IF NOT EXISTS system_settings (key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at INTEGER NOT NULL)\"\n ).run();\n } catch (e) {\n console.warn(\"[SystemSettingsModel] Failed to ensure system_settings table:\", e);\n }\n }\n\n /**\n * Retrieves all system settings from D1 and populates the in-memory cache.\n *\n * @returns Record containing all setting key-value pairs.\n */\n async getAll(): Promise<Record<string, string>> {\n try {\n const { results } = await this.db.prepare(\"SELECT key, value FROM system_settings\").all<{ key: string; value: string }>();\n const settings: Record<string, string> = {};\n const expiresAt = Date.now() + CACHE_TTL_MS;\n for (const row of results) {\n settings[row.key] = row.value;\n MEMORY_CACHE.set(row.key, { value: row.value, expiresAt });\n }\n return settings;\n } catch (e: unknown) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n if (errorMsg.includes(\"no such table\")) {\n await this.ensureTable();\n return {};\n }\n throw e;\n }\n }\n\n /**\n * Retrieves a single system setting by key, using the in-memory cache when fresh.\n *\n * @param key - The setting key to look up.\n * @returns Setting value string, or null if not found.\n */\n async get(key: string): Promise<string | null> {\n const cached = MEMORY_CACHE.get(key);\n if (cached && cached.expiresAt > Date.now()) {\n return cached.value;\n }\n\n try {\n const res = await this.db.prepare(\"SELECT value FROM system_settings WHERE key = ?\").bind(key).first<{ value: string }>();\n const val = res ? res.value : null;\n MEMORY_CACHE.set(key, { value: val, expiresAt: Date.now() + CACHE_TTL_MS });\n return val;\n } catch (e: unknown) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n if (errorMsg.includes(\"no such table\")) {\n await this.ensureTable();\n return null;\n }\n throw e;\n }\n }\n\n /**\n * Updates or inserts a single system setting in D1 and refreshes the in-memory cache.\n *\n * @param key - Setting key.\n * @param value - Setting value string.\n * @returns Boolean indicating whether the operation succeeded.\n */\n async set(key: string, value: string): Promise<boolean> {\n const now = Math.floor(Date.now() / 1000);\n try {\n const result = await this.db.prepare(\n \"INSERT INTO system_settings (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value, updated_at=excluded.updated_at\"\n ).bind(key, value, now).run();\n\n if (result.success) {\n MEMORY_CACHE.set(key, { value, expiresAt: Date.now() + CACHE_TTL_MS });\n }\n return result.success;\n } catch (e: unknown) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n if (errorMsg.includes(\"no such table\")) {\n await this.ensureTable();\n const retryResult = await this.db.prepare(\n \"INSERT INTO system_settings (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value, updated_at=excluded.updated_at\"\n ).bind(key, value, now).run();\n if (retryResult.success) {\n MEMORY_CACHE.set(key, { value, expiresAt: Date.now() + CACHE_TTL_MS });\n }\n return retryResult.success;\n }\n throw e;\n }\n }\n\n /**\n * Batch updates or inserts multiple system settings and refreshes the in-memory cache.\n *\n * @param settings - Key-value map of settings to persist.\n * @returns Boolean indicating whether the operation succeeded.\n */\n async setMany(settings: Record<string, string>): Promise<boolean> {\n const now = Math.floor(Date.now() / 1000);\n const stmts = Object.entries(settings).map(([key, value]) =>\n this.db.prepare(\"INSERT INTO system_settings (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value, updated_at=excluded.updated_at\").bind(key, String(value), now)\n );\n try {\n if (stmts.length > 0) {\n await this.db.batch(stmts);\n }\n } catch (e: unknown) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n if (errorMsg.includes(\"no such table\")) {\n await this.ensureTable();\n if (stmts.length > 0) {\n await this.db.batch(stmts);\n }\n } else {\n throw e;\n }\n }\n const expiresAt = Date.now() + CACHE_TTL_MS;\n for (const [key, value] of Object.entries(settings)) {\n MEMORY_CACHE.set(key, { value: String(value), expiresAt });\n }\n return true;\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { parseCidr } from \"../cidr\";\nimport { SystemSettingsModel } from \"../../models/systemSettings\";\nimport { updateActiveCfProxyCidrs, getActiveCfProxyCidrs } from \"./cloudflareIp\";\n\nlet cachedActiveCfEchConfig: string | null = null;\nlet loadPromise: Promise<void> | null = null;\n\n/**\n * Updates in-memory active Cloudflare ECH config template.\n *\n * @param echBase64 - Live Base64 ECHConfigList from Cloudflare.\n */\nexport function setActiveCfEchConfig(echBase64: string): void {\n if (echBase64 && echBase64 !== cachedActiveCfEchConfig) {\n cachedActiveCfEchConfig = echBase64;\n }\n}\n\n/**\n * Returns the currently active cached Cloudflare ECH configuration string.\n */\nexport function getActiveCfEchConfig(): string | null {\n return cachedActiveCfEchConfig;\n}\n\n/**\n * Persists live Cloudflare active ECH config to the system_settings table and in-memory cache.\n *\n * @param db - D1Database instance.\n * @param echBase64 - Live Base64 ECHConfigList from Cloudflare.\n */\nexport async function saveActiveCfEchConfig(db: D1Database, echBase64: string): Promise<void> {\n if (!echBase64) return;\n setActiveCfEchConfig(echBase64);\n try {\n const settingsModel = new SystemSettingsModel(db);\n await settingsModel.set(\"cf_active_ech_config\", echBase64);\n } catch (e) {\n console.warn(\"[ECH] Could not persist active ECH config to DB:\", e);\n }\n}\n\n/**\n * Ensures Cloudflare IP ranges and active ECH config are loaded into memory from the database.\n * If database does not have them yet, triggers an initial sync.\n *\n * @param db - D1Database instance.\n */\nexport async function ensureCloudflareIpRangesLoaded(db: D1Database): Promise<void> {\n const currentCidrs = getActiveCfProxyCidrs();\n if (currentCidrs.length > 0) {\n return;\n }\n if (!loadPromise && db) {\n loadPromise = (async () => {\n try {\n const settingsModel = new SystemSettingsModel(db);\n const [cachedCidrs, cachedEch] = await Promise.all([\n settingsModel.get(\"cf_proxy_cidrs\"),\n settingsModel.get(\"cf_active_ech_config\")\n ]);\n\n if (cachedEch) {\n cachedActiveCfEchConfig = cachedEch;\n }\n\n if (cachedCidrs) {\n const parsed = JSON.parse(cachedCidrs);\n if (Array.isArray(parsed) && parsed.length > 0) {\n updateActiveCfProxyCidrs(parsed);\n return;\n }\n }\n // If not in DB, sync from official endpoints\n await syncCloudflareIpRanges(db, true);\n } catch (e) {\n console.warn(\"[ECH] Could not load Cloudflare IP ranges from DB:\", e);\n } finally {\n loadPromise = null;\n }\n })();\n }\n if (loadPromise) {\n await loadPromise;\n }\n}\n\n/**\n * Initializes active Cloudflare IP ranges from D1 system_settings on startup if available.\n *\n * @param db - D1Database instance.\n */\nexport async function initCloudflareIpRanges(db: D1Database): Promise<void> {\n try {\n const settingsModel = new SystemSettingsModel(db);\n const cachedJson = await settingsModel.get(\"cf_proxy_cidrs\");\n if (cachedJson) {\n const parsed = JSON.parse(cachedJson);\n if (Array.isArray(parsed) && parsed.length >= 10) {\n updateActiveCfProxyCidrs(parsed);\n }\n }\n } catch (e) {\n console.warn(\"[ECH] Could not load cached Cloudflare IP ranges from DB:\", e);\n }\n}\n\n/**\n * Syncs official Cloudflare IP ranges and active ECH configuration from Cloudflare.\n * Automatically persists to the system_settings table and updates active in-memory state.\n *\n * @param db - D1Database instance.\n * @param force - Force sync regardless of last update timestamp.\n * @returns Object indicating sync status and count.\n */\nexport async function syncCloudflareIpRanges(\n db: D1Database,\n force: boolean = false\n): Promise<{ success: boolean; updated: boolean; count: number }> {\n const settingsModel = new SystemSettingsModel(db);\n const now = Math.floor(Date.now() / 1000);\n\n if (!force) {\n const lastUpdatedStr = await settingsModel.get(\"cf_proxy_cidrs_updated_at\");\n if (lastUpdatedStr) {\n const lastUpdated = parseInt(lastUpdatedStr, 10);\n if (!isNaN(lastUpdated) && now - lastUpdated < 86400) {\n return { success: true, updated: false, count: getActiveCfProxyCidrs().length };\n }\n }\n }\n\n try {\n const [v4Res, v6Res, echRes] = await Promise.all([\n fetch(\"https://www.cloudflare.com/ips-v4\", {\n headers: { \"User-Agent\": \"Obex-DNS/1.0\" },\n signal: AbortSignal.timeout(5000)\n }),\n fetch(\"https://www.cloudflare.com/ips-v6\", {\n headers: { \"User-Agent\": \"Obex-DNS/1.0\" },\n signal: AbortSignal.timeout(5000)\n }),\n fetch(\"https://cloudflare-dns.com/dns-query?name=cloudflare-ech.com&type=HTTPS\", {\n headers: { \"Accept\": \"application/dns-json\" },\n signal: AbortSignal.timeout(5000)\n }).catch(() => null)\n ]);\n\n if (!v4Res.ok || !v6Res.ok) {\n throw new Error(`Failed to fetch Cloudflare IPs: v4 HTTP ${v4Res.status}, v6 HTTP ${v6Res.status}`);\n }\n\n const v4Text = await v4Res.text();\n const v6Text = await v6Res.text();\n\n const rawList = [...v4Text.split(\"\\n\"), ...v6Text.split(\"\\n\")]\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n\n const validCidrs = rawList.filter((cidr) => parseCidr(cidr) !== null);\n\n let fetchedEch: string | null = null;\n if (echRes && echRes.ok) {\n try {\n const echJson: any = await echRes.json();\n if (echJson.Answer) {\n for (const ans of echJson.Answer) {\n const match = String(ans.data).match(/ech=([A-Za-z0-9+/=]+)/);\n if (match) {\n fetchedEch = match[1];\n break;\n }\n }\n }\n } catch (e) {\n console.warn(\"[ECH] Failed to parse live Cloudflare ECH config:\", e);\n }\n }\n\n if (validCidrs.length >= 10) {\n const updates: Record<string, string> = {\n cf_proxy_cidrs: JSON.stringify(validCidrs),\n cf_proxy_cidrs_updated_at: String(now)\n };\n\n if (fetchedEch) {\n updates.cf_active_ech_config = fetchedEch;\n cachedActiveCfEchConfig = fetchedEch;\n }\n\n await settingsModel.setMany(updates);\n updateActiveCfProxyCidrs(validCidrs);\n console.log(`[ECH] Synced ${validCidrs.length} Cloudflare IP ranges and active ECH config.`);\n return { success: true, updated: true, count: validCidrs.length };\n } else {\n throw new Error(`Received insufficient valid CIDRs: ${validCidrs.length}`);\n }\n } catch (e: any) {\n console.warn(\"[ECH] Cloudflare IP/ECH sync failed:\", e.message || e);\n return { success: false, updated: false, count: getActiveCfProxyCidrs().length };\n }\n}\n", "import { encodeHttpsRDataFromString } from \"../ech\";\n\n/**\n * Encodes string record values (like IPs, domains, texts, HTTPS/SVCB) into standard DNS RDATA byte segments.\n *\n * @param type - The DNS record type.\n * @param value - The payload value to encode.\n * @returns A number array representing encoded RDATA bytes.\n */\nexport function encodeRData(type: string, value: string): number[] {\n let data: number[] = [];\n if (type === \"A\") {\n data = value.split(\".\").map((v) => parseInt(v) || 0);\n } else if (type === \"AAAA\") {\n const bytes = new Uint8Array(16).fill(0);\n if (value.includes(\"::\")) {\n const [left, right] = value.split(\"::\");\n const leftParts = left.split(\":\").filter((p) => p);\n const rightParts = right.split(\":\").filter((p) => p);\n\n let i = 0;\n for (const part of leftParts) {\n const v = parseInt(part, 16);\n bytes[i++] = (v >> 8) & 0xff;\n bytes[i++] = v & 0xff;\n }\n\n i = 16 - rightParts.length * 2;\n for (const part of rightParts) {\n const v = parseInt(part, 16);\n bytes[i++] = (v >> 8) & 0xff;\n bytes[i++] = v & 0xff;\n }\n } else {\n const parts = value.split(\":\");\n let i = 0;\n for (const part of parts) {\n const v = parseInt(part, 16);\n bytes[i++] = (v >> 8) & 0xff;\n bytes[i++] = v & 0xff;\n }\n }\n data = Array.from(bytes);\n } else if (type === \"CNAME\") {\n const labels = value.split(\".\");\n for (const label of labels) {\n data.push(label.length);\n for (let i = 0; i < label.length; i++) data.push(label.charCodeAt(i));\n }\n data.push(0);\n } else if (type === \"TXT\") {\n for (let i = 0; i < value.length; i += 255) {\n const chunk = value.substring(i, i + 255);\n data.push(chunk.length);\n for (let j = 0; j < chunk.length; j++) {\n data.push(chunk.charCodeAt(j));\n }\n }\n } else if (type === \"HTTPS\" || type === \"SVCB\") {\n data = encodeHttpsRDataFromString(value);\n }\n return data;\n}\n", "import { DNS_TYPE_TO_CODE } from \"./constants\";\nimport { decodeName } from \"./decoder\";\nimport { encodeRData } from \"./rdata\";\n\n/**\n * Structured details of a DNS record.\n */\nexport interface DNSRecord {\n /** The owner domain name. If omitted, points to the original query name using compression pointers. */\n name?: string;\n /** The record type, e.g. \"A\", \"AAAA\", \"CNAME\", \"TXT\". */\n type: string;\n /** The value/payload of the record. */\n value: string;\n /** Time-To-Live in seconds. */\n ttl?: number;\n}\n\n/**\n * Builds a raw DNS query packet from a domain name and record type string.\n *\n * @param name - The domain name to query.\n * @param type - The record type string.\n * @returns A Uint8Array representing the raw DNS query packet.\n */\nexport function buildDNSQuery(name: string, type: string): Uint8Array {\n const header = new Uint8Array(12);\n const id = Math.floor(Math.random() * 65535);\n header[0] = id >> 8;\n header[1] = id & 0xff;\n header[2] = 0x01; // QR=0, Opcode=0, AA=0, TC=0, RD=1\n header[3] = 0x00; // RA=0, Z=0, RCODE=0\n header[4] = 0x00; // QDCOUNT (High)\n header[5] = 0x01; // QDCOUNT (Low) - 1 Question\n header[6] = 0x00; // ANCOUNT\n header[7] = 0x00;\n header[8] = 0x00; // NSCOUNT\n header[9] = 0x00;\n header[10] = 0x00; // ARCOUNT\n header[11] = 0x00;\n\n const labels = name.split(\".\");\n const question: number[] = [];\n for (const label of labels) {\n question.push(label.length);\n for (let i = 0; i < label.length; i++) {\n question.push(label.charCodeAt(i));\n }\n }\n question.push(0); // Root\n\n const typeCode = DNS_TYPE_TO_CODE[type] || 1;\n question.push(typeCode >> 8);\n question.push(typeCode & 0xff);\n question.push(0x00); // Class IN\n question.push(0x01);\n\n const raw = new Uint8Array(header.length + question.length);\n raw.set(header);\n raw.set(new Uint8Array(question), header.length);\n return raw;\n}\n\n/**\n * Creates a standard SERVFAIL response packet (RFC 1035).\n *\n * @param queryRaw - Optional raw incoming DNS query to echo ID and flags from.\n * @returns A 12-byte DNS header with RCODE 2 (SERVFAIL).\n */\nexport function createServFail(queryRaw?: Uint8Array): Uint8Array {\n const res = new Uint8Array(12);\n if (queryRaw && queryRaw.length >= 12) {\n res.set(queryRaw.slice(0, 12));\n }\n res[2] = (res[2] & 0x01) | 0x80; // QR=1, inherit RD\n res[3] = (res[3] & 0xf0) | 0x02; // ServFail (RCODE 2)\n return res;\n}\n\n/**\n * Builds a raw DNS response packet containing multiple answer resource records.\n *\n * @param queryRaw - The raw incoming DNS query packet buffer.\n * @param records - The array of DNS records to include in the answers section.\n * @param rcode - The response error code (defaults to 0, i.e. NOERROR).\n * @returns A Uint8Array containing the compiled DNS response packet.\n */\nexport function buildResponseMulti(\n queryRaw: Uint8Array,\n records: DNSRecord[],\n rcode: number = 0\n): Uint8Array {\n try {\n if (!queryRaw || queryRaw.length < 12) {\n return createServFail(queryRaw);\n }\n\n const header = new Uint8Array(12);\n header.set(queryRaw.slice(0, 12));\n header[2] = (header[2] & 0x01) | 0x84; // QR=1, AA=1, inherit RD\n header[3] = 0x80 | (rcode & 0x0f); // RA=1, RCODE\n\n let qEnd = 12;\n const qCount = (queryRaw[4] << 8) | queryRaw[5];\n for (let i = 0; i < qCount; i++) {\n const { read } = decodeName(queryRaw, qEnd);\n if (read === 0) {\n return createServFail(queryRaw);\n }\n qEnd += read + 4;\n if (qEnd > queryRaw.length) {\n return createServFail(queryRaw);\n }\n }\n const questionSection = queryRaw.slice(12, qEnd);\n\n header[4] = (qCount >> 8) & 0xff;\n header[5] = qCount & 0xff;\n header[6] = (records.length >> 8) & 0xff;\n header[7] = records.length & 0xff; // ANCOUNT\n header[8] = 0;\n header[9] = 0; // NSCOUNT\n header[10] = 0;\n header[11] = 0; // ARCOUNT\n\n if (records.length === 0) {\n const res = new Uint8Array(12 + questionSection.length);\n res.set(header);\n res.set(questionSection, 12);\n return res;\n }\n\n const answerRRs: Uint8Array[] = [];\n let totalLength = 12 + questionSection.length;\n\n for (const record of records) {\n const { name, type, value, ttl = 60 } = record;\n\n let nameBytes: number[] = [];\n if (!name) {\n nameBytes = [0xc0, 0x0c]; // Pointer to the first question\n } else {\n const labels = name.split(\".\");\n for (const label of labels) {\n nameBytes.push(label.length);\n for (let i = 0; i < label.length; i++)\n nameBytes.push(label.charCodeAt(i));\n }\n nameBytes.push(0);\n }\n\n const data = encodeRData(type, value);\n const rdlength = data.length;\n\n const rr = new Uint8Array(nameBytes.length + 10 + rdlength);\n rr.set(nameBytes, 0);\n let offset = nameBytes.length;\n\n const tCode = DNS_TYPE_TO_CODE[type] || 1;\n rr[offset++] = tCode >> 8;\n rr[offset++] = tCode & 0xff;\n rr[offset++] = 0;\n rr[offset++] = 1; // Class IN\n rr[offset++] = (ttl >> 24) & 0xff;\n rr[offset++] = (ttl >> 16) & 0xff;\n rr[offset++] = (ttl >> 8) & 0xff;\n rr[offset++] = ttl & 0xff;\n rr[offset++] = (rdlength >> 8) & 0xff;\n rr[offset++] = rdlength & 0xff;\n rr.set(data, offset);\n\n answerRRs.push(rr);\n totalLength += rr.length;\n }\n\n const res = new Uint8Array(totalLength);\n res.set(header, 0);\n res.set(questionSection, 12);\n\n let currentOffset = 12 + questionSection.length;\n for (const rr of answerRRs) {\n res.set(rr, currentOffset);\n currentOffset += rr.length;\n }\n\n return res;\n } catch (e) {\n console.error(\"Critical error in buildResponseMulti:\", e);\n const fallback = new Uint8Array(12);\n fallback.set(queryRaw.slice(0, 12));\n fallback[2] |= 0x80;\n fallback[3] = (fallback[3] & 0xf0) | 0x02; // ServFail\n return fallback;\n }\n}\n\n/**\n * Builds a raw DNS response packet containing a single answer resource record.\n *\n * @param queryRaw - The raw incoming DNS query packet buffer.\n * @param type - The record type.\n * @param value - The record payload value.\n * @param ttl - Time-To-Live in seconds (defaults to 60).\n * @param rcode - The response error code (defaults to 0).\n * @returns A Uint8Array containing the compiled DNS response packet.\n */\nexport function buildResponse(\n queryRaw: Uint8Array,\n type: string,\n value: string,\n ttl: number = 60,\n rcode: number = 0\n): Uint8Array {\n try {\n if (!queryRaw || queryRaw.length < 12) {\n return createServFail(queryRaw);\n }\n\n const header = new Uint8Array(12);\n header.set(queryRaw.slice(0, 12));\n header[2] = (header[2] & 0x01) | 0x84; // QR=1, AA=1, inherit RD\n header[3] = 0x80 | (rcode & 0x0f); // RA=1, RCODE\n\n let qEnd = 12;\n const qCount = (queryRaw[4] << 8) | queryRaw[5];\n for (let i = 0; i < qCount; i++) {\n const { read } = decodeName(queryRaw, qEnd);\n if (read === 0) {\n return createServFail(queryRaw);\n }\n qEnd += read + 4;\n if (qEnd > queryRaw.length) {\n return createServFail(queryRaw);\n }\n }\n const questionSection = queryRaw.slice(12, qEnd);\n\n header[4] = (qCount >> 8) & 0xff;\n header[5] = qCount & 0xff;\n header[6] = 0;\n header[7] = value ? 1 : 0; // ANCOUNT\n header[8] = 0;\n header[9] = 0; // NSCOUNT\n header[10] = 0;\n header[11] = 0; // ARCOUNT\n\n if (!value) {\n const res = new Uint8Array(12 + questionSection.length);\n res.set(header);\n res.set(questionSection, 12);\n return res;\n }\n\n const data = encodeRData(type, value);\n const rdlength = data.length;\n\n const answerRR = new Uint8Array(12 + rdlength);\n answerRR[0] = 0xc0;\n answerRR[1] = 0x0c; // Pointer to original query name\n const tCode = DNS_TYPE_TO_CODE[type] || 1;\n answerRR[2] = tCode >> 8;\n answerRR[3] = tCode & 0xff;\n answerRR[4] = 0;\n answerRR[5] = 1; // Class IN\n answerRR[6] = (ttl >> 24) & 0xff;\n answerRR[7] = (ttl >> 16) & 0xff;\n answerRR[8] = (ttl >> 8) & 0xff;\n answerRR[9] = ttl & 0xff;\n answerRR[10] = (rdlength >> 8) & 0xff;\n answerRR[11] = rdlength & 0xff;\n answerRR.set(data, 12);\n\n const res = new Uint8Array(\n 12 + questionSection.length + answerRR.length\n );\n res.set(header);\n res.set(questionSection, 12);\n res.set(answerRR, 12 + questionSection.length);\n return res;\n } catch (e) {\n console.error(\"Critical error in buildResponse:\", e);\n const fallback = new Uint8Array(12);\n fallback.set(queryRaw.slice(0, 12));\n fallback[2] |= 0x80;\n fallback[3] = (fallback[3] & 0xf0) | 0x02; // ServFail\n return fallback;\n }\n}\n", "/**\n * injectEcs.ts \u2014 RFC 7871 EDNS Client Subnet \u6CE8\u5165\u5DE5\u5177\n *\n * \u5C06 ECS OPT \u8BB0\u5F55\u76F4\u63A5\u5199\u5165 DNS \u67E5\u8BE2\u7684\u7EBF\u683C\u5F0F\uFF08wire format\uFF09\uFF0C\u786E\u4FDD\u6240\u6709\n * \u652F\u6301 EDNS0 \u7684\u4E0A\u6E38\uFF08\u5305\u62EC ControlD\u3001NextDNS \u7B49\u975E Google DoH \u670D\u52A1\uFF09\n * \u5747\u80FD\u6B63\u786E\u8BFB\u53D6\u5230 ECS \u4FE1\u606F\u3002\n *\n * OPT RR \u7ED3\u6784\uFF08RFC 6891 \u00A76.1\uFF09:\n * NAME : 0x00 (root)\n * TYPE : 0x0029 (OPT = 41)\n * CLASS : 0x1000 (UDP payload size = 4096)\n * TTL : 0x00000000 (extended RCODE + flags)\n * RDLENGTH : variable\n * RDATA : option list\n *\n * ECS Option\uFF08RFC 7871 \u00A76\uFF09:\n * OPTION-CODE : 0x0008 (ECS = 8)\n * OPTION-LENGTH : variable\n * FAMILY : 0x0001 (IPv4) | 0x0002 (IPv6)\n * SOURCE PREFIX : prefix length (e.g. 24)\n * SCOPE PREFIX : 0x00 (client must send 0)\n * ADDRESS : network address, truncated to \u2308SOURCE_PREFIX/8\u2309 bytes\n */\n\n/** EDNS0 OPT \u8BB0\u5F55\u7C7B\u578B\u7801 */\nconst OPT_TYPE = 41;\n\n/** ECS OPTION-CODE\uFF08RFC 7871\uFF09 */\nconst ECS_OPTION_CODE = 8;\n\n/**\n * \u89E3\u6790 CIDR \u5B57\u7B26\u4E32\uFF0C\u8FD4\u56DE\u5730\u5740\u65CF\u3001\u524D\u7F00\u957F\u5EA6\u548C\u7F51\u7EDC\u5B57\u8282\u3002\n * \u5BF9\u5730\u5740\u6309\u524D\u7F00\u957F\u5EA6\u622A\u65AD\uFF0C\u4FDD\u8BC1\u4E0D\u53D1\u9001\u8D85\u51FA\u524D\u7F00\u7684\u4E3B\u673A\u4F4D\u3002\n *\n * @param cidr - \u5F62\u5982 \"159.223.80.0/20\" \u6216 \"2400:6180::/48\" \u7684\u5B57\u7B26\u4E32\n * @returns \u89E3\u6790\u7ED3\u679C\uFF0C\u6216 null\uFF08\u683C\u5F0F\u975E\u6CD5\u65F6\uFF09\n */\nfunction parseCidr(cidr: string): { family: 1 | 2; prefixLen: number; addrBytes: Uint8Array } | null {\n const slashIdx = cidr.lastIndexOf('/');\n if (slashIdx === -1) return null;\n\n const addr = cidr.slice(0, slashIdx);\n const prefixLen = parseInt(cidr.slice(slashIdx + 1), 10);\n if (isNaN(prefixLen)) return null;\n\n if (addr.includes(':')) {\n // IPv6\n const bytes = ipv6ToBytes(addr);\n if (!bytes) return null;\n if (prefixLen < 0 || prefixLen > 128) return null;\n const addrBytes = maskBytes(bytes, prefixLen);\n return { family: 2, prefixLen, addrBytes };\n } else {\n // IPv4\n const parts = addr.split('.').map(Number);\n if (parts.length !== 4 || parts.some(p => isNaN(p) || p < 0 || p > 255)) return null;\n if (prefixLen < 0 || prefixLen > 32) return null;\n const bytes = new Uint8Array(parts);\n const addrBytes = maskBytes(bytes, prefixLen);\n return { family: 1, prefixLen, addrBytes };\n }\n}\n\n/** \u5C06\u5B57\u8282\u6570\u7EC4\u622A\u65AD\u5230 \u2308prefixLen/8\u2309 \u5B57\u8282\uFF0C\u4F59\u91CF\u4E3B\u673A\u4F4D\u6E05\u96F6\u3002 */\nfunction maskBytes(bytes: Uint8Array, prefixLen: number): Uint8Array {\n const byteLen = Math.ceil(prefixLen / 8);\n const masked = new Uint8Array(byteLen);\n for (let i = 0; i < byteLen; i++) {\n masked[i] = bytes[i] ?? 0;\n }\n // \u6700\u540E\u4E00\u4E2A\u5B57\u8282\u4E2D\uFF0C\u8D85\u51FA prefixLen \u7684\u4F4D\u6E05\u96F6\n const lastByteBits = prefixLen % 8;\n if (lastByteBits !== 0 && byteLen > 0) {\n masked[byteLen - 1] &= (0xff << (8 - lastByteBits)) & 0xff;\n }\n return masked;\n}\n\n/** \u5C06 IPv6 \u5730\u5740\uFF08\u542B ::\uFF09\u5C55\u5F00\u4E3A 16 \u5B57\u8282 Uint8Array\u3002 */\nfunction ipv6ToBytes(addr: string): Uint8Array | null {\n try {\n // \u5904\u7406 :: \u5C55\u5F00\n const parts = addr.split('::');\n if (parts.length > 2) return null;\n\n const left = parts[0] ? parts[0].split(':') : [];\n const right = parts[1] ? parts[1].split(':') : [];\n const missing = 8 - left.length - right.length;\n const groups = [...left, ...Array(missing).fill('0'), ...right];\n\n if (groups.length !== 8) return null;\n\n const bytes = new Uint8Array(16);\n for (let i = 0; i < 8; i++) {\n const val = parseInt(groups[i], 16);\n if (isNaN(val)) return null;\n bytes[i * 2] = (val >> 8) & 0xff;\n bytes[i * 2 + 1] = val & 0xff;\n }\n return bytes;\n } catch {\n return null;\n }\n}\n\n/**\n * \u6784\u5EFA\u5305\u542B\u5355\u4E2A ECS option \u7684 OPT RR \u5B57\u8282\u5E8F\u5217\u3002\n *\n * @param cidr - \u8981\u6CE8\u5165\u7684 CIDR \u5B57\u7B26\u4E32\n * @returns OPT RR \u7684 Uint8Array\uFF0C\u6216 null\uFF08cidr \u975E\u6CD5\u65F6\uFF09\n */\nfunction buildEcsOptRr(cidr: string): Uint8Array | null {\n const parsed = parseCidr(cidr);\n if (!parsed) return null;\n\n const { family, prefixLen, addrBytes } = parsed;\n\n // ECS option \u5185\u5BB9: OPTION-CODE(2) + OPTION-LEN(2) + FAMILY(2) + SRC-PREFIX(1) + SCOPE-PREFIX(1) + ADDRESS(N)\n const ecsDataLen = 4 + addrBytes.length;\n const ecsOption = new Uint8Array(4 + ecsDataLen);\n let o = 0;\n // OPTION-CODE = 8\n ecsOption[o++] = 0x00;\n ecsOption[o++] = ECS_OPTION_CODE;\n // OPTION-LENGTH\n ecsOption[o++] = (ecsDataLen >> 8) & 0xff;\n ecsOption[o++] = ecsDataLen & 0xff;\n // ADDRESS FAMILY\n ecsOption[o++] = (family >> 8) & 0xff;\n ecsOption[o++] = family & 0xff;\n // SOURCE PREFIX-LENGTH\n ecsOption[o++] = prefixLen;\n // SCOPE PREFIX-LENGTH (always 0 in queries)\n ecsOption[o++] = 0x00;\n // ADDRESS (truncated)\n ecsOption.set(addrBytes, o);\n\n // OPT RR: NAME(1) + TYPE(2) + CLASS(2) + TTL(4) + RDLENGTH(2) + RDATA(N)\n const rdLen = ecsOption.length;\n const optRr = new Uint8Array(11 + rdLen);\n let p = 0;\n optRr[p++] = 0x00; // NAME = root\n optRr[p++] = 0x00; // TYPE high\n optRr[p++] = OPT_TYPE; // TYPE low = 41\n optRr[p++] = 0x10; // CLASS (UDP payload) high = 4096\n optRr[p++] = 0x00; // CLASS low\n optRr[p++] = 0x00; // TTL (extended RCODE)\n optRr[p++] = 0x00;\n optRr[p++] = 0x00;\n optRr[p++] = 0x00;\n optRr[p++] = (rdLen >> 8) & 0xff; // RDLENGTH high\n optRr[p++] = rdLen & 0xff; // RDLENGTH low\n optRr.set(ecsOption, p);\n\n return optRr;\n}\n\n/**\n * \u5C06 ECS OPT RR \u6CE8\u5165\u5230\u73B0\u6709\u7684 DNS \u67E5\u8BE2\u62A5\u6587\u4E2D\uFF0C\u8FD4\u56DE\u4FEE\u6539\u540E\u7684\u65B0\u62A5\u6587\u3002\n *\n * \u7B56\u7565\uFF1A\n * - \u82E5\u62A5\u6587\u5DF2\u5305\u542B OPT RR\uFF08ARCOUNT > 0 \u4E14\u5B58\u5728 TYPE=41 \u8BB0\u5F55\uFF09\uFF0C\u7528\u65B0 ECS \u66FF\u6362\u5176\u4E2D\u7684\n * ECS option\uFF08\u5176\u4ED6 EDNS option \u4FDD\u7559\uFF09\uFF0C\u4EE5\u907F\u514D\u91CD\u590D\u6CE8\u5165\u3002\n * - \u82E5\u62A5\u6587\u6CA1\u6709 OPT RR\uFF0C\u5728 Additional \u533A\u672B\u5C3E\u9644\u52A0\u4E00\u4E2A\u65B0\u7684 OPT RR \u5E76\u5C06 ARCOUNT +1\u3002\n *\n * @param raw - \u539F\u59CB DNS \u67E5\u8BE2\u7684\u7EBF\u683C\u5F0F\u5B57\u8282\n * @param cidr - \u8981\u6CE8\u5165\u7684 ECS \u7F51\u7EDC\u524D\u7F00\uFF0C\u683C\u5F0F \"a.b.c.d/N\" \u6216 \"::x/N\"\n * @returns \u6CE8\u5165 ECS \u540E\u7684\u65B0 DNS \u67E5\u8BE2\u5B57\u8282\uFF0C\u6216\u539F\u59CB\u5B57\u8282\uFF08cidr \u975E\u6CD5\u65F6\u56DE\u9000\uFF09\n */\nexport function injectEcsIntoQuery(raw: Uint8Array, cidr: string): Uint8Array {\n const optRr = buildEcsOptRr(cidr);\n if (!optRr) {\n console.warn(`[ECS] Invalid CIDR \"${cidr}\", skipping injection.`);\n return raw;\n }\n\n if (raw.length < 12) return raw;\n\n // \u8BFB\u53D6\u5F53\u524D ARCOUNT\n const arCount = (raw[10] << 8) | raw[11];\n\n // \u627E\u5230 Additional \u533A\u7684\u8D77\u59CB\u504F\u79FB\uFF08\u8DF3\u8FC7 Header + Question + Answer + Authority\uFF09\n let offset = 12;\n\n // \u8DF3\u8FC7 Question \u533A\n const qdCount = (raw[4] << 8) | raw[5];\n for (let i = 0; i < qdCount && offset < raw.length; i++) {\n // \u8DF3\u8FC7 QNAME\n while (offset < raw.length) {\n const len = raw[offset];\n if (len === 0) { offset++; break; }\n if ((len & 0xc0) === 0xc0) { offset += 2; break; } // compression pointer\n offset += 1 + len;\n }\n offset += 4; // QTYPE + QCLASS\n }\n\n // \u8DF3\u8FC7 Answer \u548C Authority \u533A\uFF08\u5171 ANCOUNT + NSCOUNT \u6761 RR\uFF09\n const rrToSkip = ((raw[6] << 8) | raw[7]) + ((raw[8] << 8) | raw[9]);\n for (let i = 0; i < rrToSkip && offset < raw.length; i++) {\n offset = skipRR(raw, offset);\n }\n\n // Additional \u533A\u8D77\u59CB\n const additionalStart = offset;\n\n // \u5728 Additional \u533A\u4E2D\u67E5\u627E\u5DF2\u6709\u7684 OPT RR\uFF0C\u82E5\u627E\u5230\u5219\u5265\u79BB\u5B83\n let existingOptStart = -1;\n let existingOptEnd = -1;\n let pos = additionalStart;\n for (let i = 0; i < arCount && pos < raw.length; i++) {\n const rrStart = pos;\n // OPT RR \u7684 NAME \u56FA\u5B9A\u4E3A 0x00\uFF08\u5355\u5B57\u8282\uFF09\uFF0CTYPE \u7D27\u968F\u5176\u540E\n if (pos < raw.length && raw[pos] === 0x00) {\n const rrType = pos + 1 < raw.length ? ((raw[pos + 1] << 8) | raw[pos + 2]) : -1;\n if (rrType === OPT_TYPE) {\n existingOptStart = rrStart;\n pos = skipRR(raw, pos);\n existingOptEnd = pos;\n break;\n }\n }\n pos = skipRR(raw, pos);\n }\n\n // \u91CD\u65B0\u7EC4\u88C5\u62A5\u6587\uFF1A\u539F\u59CB\u5185\u5BB9\uFF08\u4E0D\u542B\u65E7 OPT\uFF09 + \u65B0 OPT RR\n let newRaw: Uint8Array;\n\n if (existingOptStart !== -1) {\n // \u66FF\u6362\u5DF2\u6709 OPT RR\n const before = raw.slice(0, existingOptStart);\n const after = raw.slice(existingOptEnd);\n newRaw = new Uint8Array(before.length + optRr.length + after.length);\n newRaw.set(before, 0);\n newRaw.set(optRr, before.length);\n newRaw.set(after, before.length + optRr.length);\n // ARCOUNT \u4E0D\u53D8\uFF08\u66FF\u6362\uFF0C\u4E0D\u589E\u52A0\uFF09\n } else {\n // \u8FFD\u52A0\u65B0 OPT RR\n newRaw = new Uint8Array(raw.length + optRr.length);\n newRaw.set(raw, 0);\n newRaw.set(optRr, raw.length);\n // ARCOUNT + 1\n const newArCount = arCount + 1;\n newRaw[10] = (newArCount >> 8) & 0xff;\n newRaw[11] = newArCount & 0xff;\n }\n\n return newRaw;\n}\n\n/** \u8DF3\u8FC7\u4E00\u6761 RR\uFF0C\u8FD4\u56DE\u4E0B\u4E00\u6761 RR \u7684\u504F\u79FB\u91CF\u3002 */\nfunction skipRR(raw: Uint8Array, offset: number): number {\n if (offset >= raw.length) return offset;\n\n // \u8DF3\u8FC7 NAME\uFF08\u53EF\u80FD\u542B\u538B\u7F29\u6307\u9488\uFF09\n while (offset < raw.length) {\n const len = raw[offset];\n if (len === 0) { offset++; break; }\n if ((len & 0xc0) === 0xc0) { offset += 2; break; }\n offset += 1 + len;\n }\n\n if (offset + 10 > raw.length) return raw.length;\n\n // TYPE(2) + CLASS(2) + TTL(4) + RDLENGTH(2)\n const rdLength = (raw[offset + 8] << 8) | raw[offset + 9];\n offset += 10 + rdLength;\n\n return Math.min(offset, raw.length);\n}\n", "import { Env, ProfileSettings, Rule } from \"../types\";\nimport { BloomFilter } from \"../utils/bloom\";\nimport { cacheUtils } from \"../utils/cache\";\n\nexport interface ConfigCacheEntry {\n settings: ProfileSettings;\n rules: Rule[];\n timestamp: number;\n}\n\n/**\n * \u9AD8\u6027\u80FD\u3001\u96F6\u4F9D\u8D56\u7684\u5BB9\u91CF\u53D7\u9650 Map (LRU \u6DD8\u6C70\u7B56\u7565)\u3002\n * \u5229\u7528 JavaScript Map \u4FDD\u8BC1\u7684\u201C\u6309\u63D2\u5165\u987A\u5E8F\u8FED\u4EE3\u201D\u7279\u6027\uFF0C\u5728 O(1) \u590D\u6742\u5EA6\u5185\u81EA\u52A8\u6DD8\u6C70\u6700\u65E7\u7684\u6570\u636E\u3002\n */\nclass SizeCappedMap<K, V> extends Map<K, V> {\n constructor(private maxEntries: number) {\n super();\n }\n\n override set(key: K, value: V): this {\n // \u82E5\u952E\u4E0D\u5B58\u5728\u4E14\u5373\u5C06\u8D85\u51FA\u5BB9\u91CF\uFF0C\u6DD8\u6C70\u6700\u65E7\u7684\u8BB0\u5F55 (\u7B2C\u4E00\u9879)\n if (!this.has(key) && this.size >= this.maxEntries) {\n const oldestKey = this.keys().next().value;\n if (oldestKey !== undefined) {\n this.delete(oldestKey);\n }\n }\n // \u91CD\u65B0\u66F4\u65B0\u63D2\u5165\u987A\u5E8F\uFF0C\u4F7F\u5176\u4F5C\u4E3A\u6700\u8FD1\u6D3B\u8DC3\u9879\u79FB\u5230\u94FE\u8868\u672B\u5C3E\n this.delete(key);\n super.set(key, value);\n return this;\n }\n}\n\n// --- L1 Memory Cache (Isolate \u5168\u5C40\uFF0C\u5BB9\u91CF\u786C\u6027\u9650\u5236) ---\n// \u5E03\u9686\u8FC7\u6EE4\u5668\u5360\u7528\u8F83\u5927\uFF08\u6BCF\u4E2A ~2.4MB\uFF09\uFF0C\u9650\u5236\u6700\u591A\u7F13\u5B58 3 \u4E2A Profile\uFF0C\u5185\u5B58\u786C\u9876\u4E3A ~7.2MB\uFF0C\u89C4\u907F GC \u6296\u52A8\u5D29\u6E83\nexport const bloomMemoryMap = new SizeCappedMap<string, { bloom: BloomFilter; ts: number }>(3);\n\n// \u57FA\u7840\u914D\u7F6E\u4F53\u79EF\u8F83\u5C0F\uFF0C\u5141\u8BB8\u7F13\u5B58 10 \u4E2A Profile\nexport const configCache = new SizeCappedMap<string, ConfigCacheEntry>(10);\n\n// DNS \u7F13\u5B58\u9650\u5236\u6700\u591A 500 \u6761\u8BB0\u5F55\uFF0C\u9632\u6B62\u906D\u53D7\u6D77\u91CF\u968F\u673A\u5B50\u57DF\u540D\u67E5\u8BE2\uFF08DNS Tunnel / Random Query Attack\uFF09\u65F6\u5BFC\u81F4 Isolate \u5185\u5B58\u8017\u5C3D\nexport const dnsCache = new SizeCappedMap<string, any>(500);\n\n// Profile Key \u5230 Profile \u5143\u6570\u636E\u6620\u5C04\u7F13\u5B58 (L1 \u5185\u5B58\u7F13\u5B58\uFF0C\u9650\u5236\u6700\u591A 100 \u4E2A Key)\nexport const profileKeyMemoryMap = new SizeCappedMap<string, { data: any; ts: number }>(100);\n\n/**\n * \u901A\u8FC7 Cloudflare Zone Cache Purge API \u5168\u7403\u6309 Tag \u4E3B\u52A8\u5931\u6548\u7F13\u5B58\u3002\n * \u9700\u914D\u7F6E CF_ZONE_ID \u4E0E CF_PURGE_TOKEN (\u5177\u5907 Zone -> Cache Purge \u6743\u9650)\u3002\n * \u82E5\u672A\u914D\u7F6E\u6216\u8C03\u7528\u5931\u8D25\uFF0C\u4EC5\u8BB0\u5F55\u65E5\u5FD7\uFF0C\u4E0D\u963B\u65AD\u4E3B\u4E1A\u52A1\u6D41\u7A0B\u3002\n */\nasync function purgeTags(env: Env, tags: string[]): Promise<void> {\n try {\n const res = await fetch(`https://api.cloudflare.com/client/v4/zones/${env.CF_ZONE_ID}/purge_cache`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${env.CF_PURGE_TOKEN}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ tags }),\n });\n if (!res.ok) {\n const errorText = await res.text();\n console.error(`[Cache] Global cache purge failed (HTTP ${res.status}):`, errorText);\n }\n } catch (e: any) {\n console.error(\"[Cache] Global cache purge error:\", e?.message || e);\n }\n}\n\nexport const pipelineCache = {\n /**\n * \u6E05\u7406\u6307\u5B9A Profile \u7684\u5404\u7EA7\u7F13\u5B58\u3002\n * \u5305\u542B L1 \u5185\u5B58\u7F13\u5B58\u3001L2 \u672C\u5730\u6570\u636E\u4E2D\u5FC3 Cache API\uFF0C\u4EE5\u53CA (\u82E5\u914D\u7F6E\u4E86 env) L2 \u5168\u7403\u8FB9\u7F18\u8282\u70B9 Cache-Tag \u5931\u6548\u3002\n *\n * \u7B7E\u540D\u652F\u6301\u591A\u79CD\u91CD\u8F7D\u5F62\u5F0F\uFF0C\u786E\u4FDD\u5411\u540E\u5B8C\u5168\u517C\u5BB9\uFF1A\n * - clear(profileId)\n * - clear(profileId, clearBloom)\n * - clear(profileId, env)\n * - clear(profileId, clearBloom, env)\n * - clear(profileId, env, clearBloom)\n */\n async clear(\n profileId: string,\n clearBloomOrEnv: boolean | Env = true,\n envOrClearBloom?: Env | boolean\n ): Promise<void> {\n let clearBloom = true;\n let targetEnv: Env | undefined;\n\n if (typeof clearBloomOrEnv === \"boolean\") {\n clearBloom = clearBloomOrEnv;\n if (envOrClearBloom && typeof envOrClearBloom === \"object\") {\n targetEnv = envOrClearBloom as Env;\n }\n } else if (clearBloomOrEnv && typeof clearBloomOrEnv === \"object\") {\n targetEnv = clearBloomOrEnv as Env;\n if (typeof envOrClearBloom === \"boolean\") {\n clearBloom = envOrClearBloom;\n }\n }\n\n // \u6E05\u7406 L1 (\u5185\u5B58)\n configCache.delete(profileId);\n if (clearBloom) {\n bloomMemoryMap.delete(profileId);\n }\n profileKeyMemoryMap.clear();\n for (const key of dnsCache.keys()) {\n if (key.startsWith(`${profileId}:`)) {\n dnsCache.delete(key);\n }\n }\n \n // \u6E05\u7406 L2 (\u672C\u5730\u6570\u636E\u4E2D\u5FC3 Cache API)\n try {\n if (typeof caches !== \"undefined\" && caches && (caches as any).default) {\n const cache = (caches as any).default;\n const tasks: Promise<any>[] = [\n cacheUtils.delete(cache, `profile_v6:${profileId}`)\n ];\n if (clearBloom) {\n tasks.push(cache.delete(`https://obex.local/bloom-bin/${profileId}`));\n }\n await Promise.all(tasks);\n }\n } catch (e) {\n console.error(\"Failed to clear cache API:\", e);\n }\n\n // \u6E05\u7406 L2 \u5168\u7403\u8FB9\u7F18\u8282\u70B9 (Cloudflare Zone Cache Purge by Tag)\n if (targetEnv && targetEnv.CF_ZONE_ID && targetEnv.CF_PURGE_TOKEN) {\n const tags = [`profile-${profileId}`];\n if (clearBloom) {\n tags.push(`bloom-${profileId}`);\n }\n await purgeTags(targetEnv, tags);\n }\n }\n};\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { Profile, ProfileSettings } from \"../types\";\nimport { generateId, generateZBase32Token } from \"../lib/auth\";\n\nexport interface ProfileWithBloom extends Profile {\n list_bloom?: string;\n list_updated_at?: number;\n}\n\nexport class ProfileModel {\n constructor(private db: D1Database) {}\n\n // --- Core Profile Methods ---\n async getById(id: string): Promise<ProfileWithBloom | null> {\n return await this.db.prepare(\"SELECT * FROM profiles WHERE id = ?\")\n .bind(id)\n .first<ProfileWithBloom>();\n }\n\n async findByKey(profileKey: string): Promise<ProfileWithBloom & { access_point_id?: string, access_point_name?: string } | null> {\n try {\n // 1. First attempt to match access point token (fast index seek via idx_access_points_token)\n const apMatch = await this.db.prepare(`\n SELECT p.*, ap.id as access_point_id, ap.name as access_point_name \n FROM access_points ap \n JOIN profiles p ON ap.profile_id = p.id \n WHERE ap.token = ?\n `)\n .bind(profileKey)\n .first<ProfileWithBloom & { access_point_id?: string, access_point_name?: string }>();\n\n if (apMatch) {\n return apMatch;\n }\n\n // 2. Fallback to profile_key or profile id direct match (index seek)\n const profileMatch = await this.db.prepare(`\n SELECT p.*, NULL as access_point_id, NULL as access_point_name \n FROM profiles p \n WHERE p.profile_key = ? OR p.id = ?\n `)\n .bind(profileKey, profileKey)\n .first<ProfileWithBloom & { access_point_id?: string, access_point_name?: string }>();\n\n return profileMatch || null;\n } catch (e: any) {\n console.warn(\"[ProfileModel] findByKey D1 query failed:\", e.message || e);\n return null;\n }\n }\n\n async list(filterSql: string, params: any[]): Promise<Profile[]> {\n const { results } = await this.db.prepare(`SELECT * FROM profiles ${filterSql}`)\n .bind(...params).all<Profile>();\n return results;\n }\n\n async listByOwner(ownerId: string): Promise<Profile[]> {\n const { results } = await this.db.prepare(\"SELECT * FROM profiles WHERE owner_id = ? ORDER BY created_at DESC\")\n .bind(ownerId).all<Profile>();\n return results;\n }\n\n async findByName(ownerId: string, name: string): Promise<Profile | null> {\n return await this.db.prepare(\"SELECT * FROM profiles WHERE owner_id = ? AND name = ?\")\n .bind(ownerId, name).first<Profile | null>();\n }\n\n async create(profile: { id: string, profile_key?: string, owner_id: string, name: string, settings: ProfileSettings }): Promise<boolean> {\n const now = Math.floor(Date.now() / 1000);\n const apId = generateId(12);\n const MAX_RETRIES = 5;\n\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n const apToken = generateZBase32Token(5);\n const profileKey = profile.profile_key || apToken;\n \n const statements = [\n this.db.prepare(\n \"INSERT INTO profiles (id, profile_key, owner_id, name, settings, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)\"\n ).bind(profile.id, profileKey, profile.owner_id, profile.name, JSON.stringify(profile.settings), now, now),\n this.db.prepare(\n \"INSERT INTO access_points (id, profile_id, name, token, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)\"\n ).bind(apId, profile.id, \"Device-1\", apToken, now, now)\n ];\n\n try {\n const results = await this.db.batch(statements);\n return results.every(r => r.success);\n } catch (err: any) {\n if (attempt < MAX_RETRIES - 1 && (String(err).includes(\"UNIQUE constraint failed\") || String(err).includes(\"SQLITE_CONSTRAINT\"))) {\n continue;\n }\n throw err;\n }\n }\n return false;\n }\n\n async delete(id: string): Promise<boolean> {\n const results = await this.db.batch([\n this.db.prepare(\"DELETE FROM logs WHERE profile_id = ?\").bind(id),\n this.db.prepare(\"DELETE FROM profiles WHERE id = ?\").bind(id)\n ]);\n return results.every(r => r.success);\n }\n\n async deleteByOwner(ownerId: string): Promise<boolean> {\n const results = await this.db.batch([\n this.db.prepare(\"DELETE FROM logs WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)\").bind(ownerId),\n this.db.prepare(\"DELETE FROM profiles WHERE owner_id = ?\").bind(ownerId)\n ]);\n return results.every(r => r.success);\n }\n\n async updateName(id: string, name: string): Promise<boolean> {\n const now = Math.floor(Date.now() / 1000);\n const result = await this.db.prepare(\"UPDATE profiles SET name = ?, updated_at = ? WHERE id = ?\")\n .bind(name, now, id).run();\n return result.success;\n }\n\n async updateSettings(id: string, settings: ProfileSettings): Promise<boolean> {\n const now = Math.floor(Date.now() / 1000);\n const result = await this.db.prepare(\n \"UPDATE profiles SET settings = ?, updated_at = ? WHERE id = ?\"\n )\n .bind(JSON.stringify(settings), now, id)\n .run();\n return result.success;\n }\n\n async rotateKey(id: string, newKey: string): Promise<boolean> {\n const now = Math.floor(Date.now() / 1000);\n const result = await this.db.prepare(\"UPDATE profiles SET profile_key = ?, updated_at = ? WHERE id = ?\")\n .bind(newKey, now, id).run();\n return result.success;\n }\n\n async updateLastActive(id: string, now: number): Promise<boolean> {\n const result = await this.db.prepare(\"UPDATE profiles SET last_active_at = ? WHERE id = ?\").bind(now, id).run();\n return result.success;\n }\n\n async getSyncTargets(threshold: number, limit: number): Promise<{id: string}[]> {\n const { results } = await this.db.prepare(\n \"SELECT id FROM profiles WHERE (list_updated_at IS NULL OR list_updated_at <= ?) AND EXISTS (SELECT 1 FROM lists WHERE lists.profile_id = profiles.id) ORDER BY list_updated_at ASC LIMIT ?\"\n ).bind(threshold, limit).all<{ id: string }>();\n return results;\n }\n\n async updateListUpdatedAt(profileId: string, now: number): Promise<boolean> {\n const result = await this.db.prepare(\"UPDATE profiles SET list_updated_at = ? WHERE id = ?\").bind(now, profileId).run();\n return result.success;\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { Rule } from \"../types\";\n\nexport class RuleModel {\n constructor(private db: D1Database) {}\n\n async getRules(profileId: string): Promise<Rule[]> {\n const { results } = await this.db.prepare(\"SELECT * FROM rules WHERE profile_id = ? ORDER BY id DESC\")\n .bind(profileId)\n .all<Rule>();\n return results;\n }\n\n async addRule(profileId: string, rule: Partial<Rule>): Promise<boolean> {\n const normalizedPattern = rule.pattern ? rule.pattern.trim().toLowerCase() : \"\";\n const createdAt = rule.created_at ?? Math.floor(Date.now() / 1000);\n const result = await this.db.prepare(\n \"INSERT INTO rules (profile_id, type, pattern, v_a, v_aaaa, v_txt, v_cname, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\"\n )\n .bind(profileId, rule.type, normalizedPattern, rule.v_a || null, rule.v_aaaa || null, rule.v_txt || null, rule.v_cname || null, createdAt)\n .run();\n return result.success;\n }\n\n /**\n * Batch inserts multiple rules for a profile using D1 batch operations.\n * Chunks execution into batches of up to 100 statements to respect D1 limits.\n *\n * @param profileId - Profile identifier.\n * @param rules - Array of partial rules to insert.\n * @returns Total number of rules successfully inserted.\n */\n async addRulesBulk(profileId: string, rules: Partial<Rule>[]): Promise<number> {\n if (!rules || rules.length === 0) return 0;\n const now = Math.floor(Date.now() / 1000);\n const statements = rules.map(rule => {\n const normalizedPattern = rule.pattern ? rule.pattern.trim().toLowerCase() : \"\";\n const createdAt = rule.created_at ?? now;\n return this.db.prepare(\n \"INSERT INTO rules (profile_id, type, pattern, v_a, v_aaaa, v_txt, v_cname, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\"\n ).bind(\n profileId,\n rule.type,\n normalizedPattern,\n rule.v_a || null,\n rule.v_aaaa || null,\n rule.v_txt || null,\n rule.v_cname || null,\n createdAt\n );\n });\n\n let inserted = 0;\n for (let i = 0; i < statements.length; i += 100) {\n const chunk = statements.slice(i, i + 100);\n const results = await this.db.batch(chunk);\n for (const res of results) {\n if (res.success) inserted += res.meta.changes || 1;\n }\n }\n return inserted;\n }\n\n async updateRule(id: number, profileId: string, rule: Partial<Rule>): Promise<boolean> {\n const normalizedPattern = rule.pattern ? rule.pattern.trim().toLowerCase() : \"\";\n const result = await this.db.prepare(\n \"UPDATE rules SET type = ?, pattern = ?, v_a = ?, v_aaaa = ?, v_txt = ?, v_cname = ? WHERE id = ? AND profile_id = ?\"\n )\n .bind(rule.type, normalizedPattern, rule.v_a || null, rule.v_aaaa || null, rule.v_txt || null, rule.v_cname || null, id, profileId)\n .run();\n return result.success;\n }\n\n async getRuleByPattern(profileId: string, pattern: string): Promise<Rule | null> {\n return await this.db.prepare(\"SELECT * FROM rules WHERE profile_id = ? AND LOWER(pattern) = ?\")\n .bind(profileId, pattern.trim().toLowerCase())\n .first<Rule | null>();\n }\n\n async getRuleByPatternExcludeId(profileId: string, pattern: string, id: number): Promise<Rule | null> {\n return await this.db.prepare(\"SELECT * FROM rules WHERE profile_id = ? AND LOWER(pattern) = ? AND id != ?\")\n .bind(profileId, pattern.trim().toLowerCase(), id)\n .first<Rule | null>();\n }\n\n async deleteRule(id: number, profileId: string): Promise<boolean> {\n const result = await this.db.prepare(\"DELETE FROM rules WHERE id = ? AND profile_id = ?\").bind(id, profileId).run();\n return result.success;\n }\n}\n", "/**\n * D1 \u5355\u884C / BLOB \u5B58\u50A8\u5207\u7247\u5927\u5C0F\uFF08\u5B57\u8282\uFF09\n * Cloudflare D1 \u5355\u884C/BLOB \u7684\u786C\u6027\u4E0A\u9650\u4E3A 2,000,000 \u5B57\u8282 (~1.907 MB)\u3002\n * \u6B64\u5904\u8BBE\u5B9A\u4E3A 1.5 MB (1,572,864 \u5B57\u8282)\uFF1A\n * - \u7559\u6709 ~417 KB \u7684\u5B89\u5168\u7F13\u51B2\uFF0C\u9632\u8303 SQLite \u884C\u5927\u5C0F\u8FB9\u754C\u6EA2\u51FA\uFF1B\n * - \u5C06\u9ED8\u8BA4 ~2.29 MB \u7684\u5E03\u9686\u8FC7\u6EE4\u5668\u5207\u7247\u6570\u7531 5 \u7247\u964D\u81F3 2 \u7247\uFF0C\u5927\u5E45\u7F29\u51CF D1 \u8BFB\u5199\u884C\u6570\u6210\u672C\u4E0E RPC \u5F80\u8FD4\u3002\n */\nexport const BLOOM_CHUNK_SIZE = 1.5 * 1024 * 1024; // 1,572,864 bytes\n\n/**\n * BloomFilter.ts - \u9AD8\u6027\u80FD\u5E03\u9686\u8FC7\u6EE4\u5668\u5B9E\u73B0\n * \n * \u9488\u5BF9 P = 10^-6 \u7CBE\u5EA6\u573A\u666F\u8FDB\u884C\u9AD8\u5EA6\u4F18\u5316\u3002\n * \u5728\u8BE5\u7CBE\u5EA6\u4E0B\uFF0C\u5BF9\u4E8E\u6BCF\u4E2A\u5143\u7D20\uFF0Ck \u2248 20 (\u54C8\u5E0C\u51FD\u6570\u6570\u91CF)\u3002\n * \u4F18\u5316\u91CD\u70B9\uFF1A\n * 1. \u4F7F\u7528 32\u4F4D\u4F4D\u6570\u7EC4 (Uint32Array) \u4EE3\u66FF 8\u4F4D (Uint8Array)\uFF0C\n * \u51CF\u5C11 4 \u500D\u7684\u5408\u5E76 (merge) \u5FAA\u73AF\u5F00\u9500\uFF0C\u4EE5\u53CA\u52A0\u901F\u4F4D\u504F\u79FB\u8BA1\u7B97\u3002\n * 2. \u5229\u7528 Kirsch-Mitzenmacher \u4F18\u5316\uFF0C\u4E0D\u518D\u72EC\u7ACB\u8BA1\u7B97\u4E24\u4E2A FNV-1a\uFF0C\n * \u800C\u662F\u8BA1\u7B97\u5355\u4E2A FNV-1a \u540E\uFF0C\u4F7F\u7528 MurmurHash3 \u7684 fmix32 finalizer\n * \u5728 O(1) \u590D\u6742\u5EA6\u5185\u6D3E\u751F h2\u3002\u4F7F\u57DF\u540D\u904D\u5386\u5B57\u7B26\u7684\u5FAA\u73AF\u5F00\u9500\u76F4\u63A5\u51CF\u534A\uFF01\n * 3. \u7F13\u5B58\u5C40\u90E8\u53D8\u91CF\uFF0C\u4F7F\u5F97 V8 \u7F16\u8BD1\u5668\u53EF\u4EE5\u6700\u5927\u5316\u505A\u5411\u91CF\u5316 (SIMD) \u4F18\u5316\u3002\n */\nexport class BloomFilter {\n private readonly size: number;\n private readonly hashes: number;\n private readonly bitArray: Uint32Array;\n\n private static readonly FNV_PRIME = 16777619;\n private static readonly FNV_SEED_0 = 2166136261;\n \n // \u9884\u5206\u914D TextEncoder \u4EE5\u907F\u514D\u91CD\u590D\u521B\u5EFA\u5F00\u9500 (Isolate \u5168\u5C40)\n private static readonly encoder = new TextEncoder();\n\n /**\n * \u6784\u9020\u51FD\u6570\n * @param size \u4F4D\u6570\u7EC4\u957F\u5EA6 (bits)\n * @param hashes \u54C8\u5E0C\u51FD\u6570\u4E2A\u6570 (k)\n * @param bitArray \u73B0\u6709\u7684\u4E8C\u8FDB\u5236\u4F4D\u6570\u636E (32\u4F4D\u6570\u7EC4)\n */\n constructor(size: number, hashes: number, bitArray?: Uint32Array) {\n this.size = size >>> 0;\n this.hashes = hashes >>> 0;\n // \u8BA1\u7B97\u9700\u8981\u7684 32\u4F4D\u5B57 (words) \u7684\u6570\u91CF\n const words = ((this.size + 31) >>> 5);\n this.bitArray = bitArray || new Uint32Array(words);\n }\n\n /**\n * \u521D\u59CB\u5316\u5E03\u9686\u8FC7\u6EE4\u5668\n * @param expectedItems \u9884\u671F\u5B58\u50A8\u7684\u6761\u76EE\u6570 (n)\n * @param errorRate \u5047\u9633\u6027\u7387 (p)\uFF0C\u9ED8\u8BA4 10^-4 (0.0001)\n */\n static create(expectedItems: number, errorRate: number = 0.0001): BloomFilter {\n const n = Math.max(expectedItems, 100);\n const p = errorRate;\n \n // \u516C\u5F0F: m = -(n * ln(p)) / (ln(2)^2)\n const m = Math.ceil(-(n * Math.log(p)) / (Math.log(2) ** 2));\n // \u516C\u5F0F: k = (m / n) * ln(2)\n const k = Math.round((m / n) * Math.log(2));\n \n return new BloomFilter(m, k);\n }\n\n /**\n * \u6DFB\u52A0\u5143\u7D20\n * @param element \u5B57\u7B26\u4E32\u5143\u7D20\n */\n add(element: string): void {\n let h1 = BloomFilter.FNV_SEED_0;\n const len = element.length;\n for (let i = 0; i < len; i++) {\n h1 ^= element.charCodeAt(i) & 0xff;\n h1 = Math.imul(h1, BloomFilter.FNV_PRIME);\n }\n h1 = h1 >>> 0;\n \n // \u7528 MurmurHash3 fmix32 \u6DF7\u6DC6\u7B97\u6CD5\u6839\u636E h1 \u5FEB\u901F\u6D3E\u751F h2\uFF0C\u51CF\u5C11\u4E00\u534A\u7684\u5B57\u7B26\u904D\u5386\u548C\u4E58\u6CD5\u5F00\u9500\n let h2 = h1 ^ (h1 >>> 16);\n h2 = Math.imul(h2, 0x85ebca6b);\n h2 = h2 ^ (h2 >>> 13);\n h2 = Math.imul(h2, 0xc2b2ae35);\n h2 = (h2 ^ (h2 >>> 16)) >>> 0;\n\n const arr = this.bitArray;\n const size = this.size;\n const k = this.hashes;\n\n for (let i = 0; i < k; i++) {\n // Double Hashing: (h1 + i * h2) % m\n const pos = (h1 + i * h2) % size;\n // 32\u4F4D\u6570\u7EC4\u4F18\u5316: index / 32 => pos >>> 5, index % 32 => pos & 31\n arr[pos >>> 5] |= (1 << (pos & 31));\n }\n }\n\n /**\n * \u68C0\u6D4B\u5143\u7D20\u662F\u5426\u5B58\u5728 (\u65E0\u5047\u9634\u6027\uFF0C\u6709\u6781\u4F4E\u5047\u9633\u6027)\n * @param element \u5F85\u68C0\u6D4B\u5B57\u7B26\u4E32\n */\n test(element: string): boolean {\n let h1 = BloomFilter.FNV_SEED_0;\n const len = element.length;\n for (let i = 0; i < len; i++) {\n h1 ^= element.charCodeAt(i) & 0xff;\n h1 = Math.imul(h1, BloomFilter.FNV_PRIME);\n }\n h1 = h1 >>> 0;\n \n // \u540C\u7406\uFF0C\u5728 O(1) \u590D\u6742\u5EA6\u5185\u6D3E\u751F h2\n let h2 = h1 ^ (h1 >>> 16);\n h2 = Math.imul(h2, 0x85ebca6b);\n h2 = h2 ^ (h2 >>> 13);\n h2 = Math.imul(h2, 0xc2b2ae35);\n h2 = (h2 ^ (h2 >>> 16)) >>> 0;\n\n const arr = this.bitArray;\n const size = this.size;\n const k = this.hashes;\n\n for (let i = 0; i < k; i++) {\n const pos = (h1 + i * h2) % size;\n if ((arr[pos >>> 5] & (1 << (pos & 31))) === 0) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * \u5BFC\u51FA\u4E3A\u539F\u59CB\u4E8C\u8FDB\u5236\u683C\u5F0F (\u7528\u4E8E R2 / D1 \u5B58\u50A8\uFF0C\u907F\u5F00 Base64 \u5F00\u9500)\n * \u7ED3\u6784: [4\u5B57\u8282 size][4\u5B57\u8282 hashes][\u4F4D\u6570\u7EC4]\n */\n toUint8Array(): Uint8Array {\n const res = new Uint8Array(8 + this.bitArray.byteLength);\n const view = new DataView(res.buffer);\n view.setUint32(0, this.size, true); // \u5C0F\u7AEF\u5E8F\u5B58\u50A8\n view.setUint32(4, this.hashes, true);\n \n // \u62F7\u8D1D underlying byte data \u5230\u76EE\u6807 Uint8Array\n res.set(new Uint8Array(this.bitArray.buffer, this.bitArray.byteOffset, this.bitArray.byteLength), 8);\n return res;\n }\n\n /**\n * \u4ECE\u539F\u59CB\u4E8C\u8FDB\u5236\u6D41\u6062\u590D (\u96F6\u62F7\u8D1D/\u5BF9\u9F50\u5B89\u5168\u53CD\u5E8F\u5217\u5316)\n */\n static fromUint8Array(buffer: Uint8Array): BloomFilter {\n if (!buffer || buffer.byteLength < 8) {\n throw new Error(\"Invalid bloom filter buffer: header too short\");\n }\n const view = new DataView(buffer.buffer, buffer.byteOffset, 8);\n const size = view.getUint32(0, true);\n const hashes = view.getUint32(4, true);\n\n if (size === 0 || hashes === 0) {\n throw new Error(\"Invalid bloom filter parameters: size and hashes must be positive integers\");\n }\n \n const offset = buffer.byteOffset + 8;\n const length = buffer.byteLength - 8;\n const expectedWords = (size + 31) >>> 5;\n const actualWords = length >> 2;\n\n if (actualWords < expectedWords) {\n throw new Error(\n `Invalid bloom filter buffer: bit array truncated (expected ${expectedWords} words, got ${actualWords})`\n );\n }\n \n let bitData: Uint32Array;\n // \u786E\u4FDD offset \u662F 4 \u7684\u500D\u6570\uFF0C\u5426\u5219 Uint32Array \u6784\u5EFA\u4F1A\u629B\u51FA\u5F02\u5E38\n if (offset % 4 === 0) {\n bitData = new Uint32Array(buffer.buffer, offset, expectedWords);\n } else {\n // \u975E 4 \u5B57\u8282\u5BF9\u9F50\u65F6\uFF0C\u6267\u884C\u62F7\u8D1D\u5230\u5BF9\u9F50\u7684 ArrayBuffer \u5185\u5B58\u4E2D\n const copy = new Uint8Array(expectedWords << 2);\n copy.set(buffer.subarray(8, 8 + (expectedWords << 2)));\n bitData = new Uint32Array(copy.buffer, 0, expectedWords);\n }\n return new BloomFilter(size, hashes, bitData);\n }\n\n /**\n * \u5408\u5E76\u53E6\u4E00\u4E2A\u5E03\u9686\u8FC7\u6EE4\u5668 (\u6309\u4F4D\u6216\u8FD0\u7B97)\n * \u5C40\u90E8\u53D8\u91CF\u7F13\u5B58\u4EE5\u5E2E\u52A9 V8 \u5F15\u64CE\u8FDB\u884C\u81EA\u52A8\u5FAA\u73AF\u5411\u91CF\u5316 (SIMD) \u4F18\u5316\n */\n merge(other: BloomFilter): void {\n if (this.size !== other.size || this.hashes !== other.hashes) {\n throw new Error(\"Cannot merge bloom filters with different sizes or hash counts.\");\n }\n const a = this.bitArray;\n const b = other.bitArray;\n const len = a.length;\n for (let i = 0; i < len; i++) {\n a[i] |= b[i];\n }\n }\n\n /**\n * \u57FA\u7840 FNV-1a \u54C8\u5E0C\u5B9E\u73B0\uFF0C\u64CD\u4F5C Uint8Array \u4EE5\u83B7\u5F97\u6700\u4F73\u6027\u80FD\n */\n private fnv1a(data: Uint8Array, seed: number): number {\n let hash = seed >>> 0;\n for (let i = 0; i < data.length; i++) {\n hash ^= data[i];\n hash = Math.imul(hash, BloomFilter.FNV_PRIME);\n }\n return hash >>> 0;\n }\n\n /**\n * \u57FA\u7840 FNV-1a \u54C8\u5E0C\u5B9E\u73B0\uFF0C\u76F4\u63A5\u64CD\u4F5C string \u5B57\u7B26\u4EE5\u907F\u514D\u5185\u5B58\u5206\u914D\u548C TextEncoder \u6027\u80FD\u5F00\u9500\n */\n private fnv1aString(str: string, seed: number): number {\n let hash = seed >>> 0;\n const len = str.length;\n for (let i = 0; i < len; i++) {\n hash ^= str.charCodeAt(i) & 0xff;\n hash = Math.imul(hash, BloomFilter.FNV_PRIME);\n }\n return hash >>> 0;\n }\n\n /**\n * \u4F20\u7EDF Base64 \u517C\u5BB9\u5BFC\u51FA\u903B\u8F91\n */\n dump(): { size: number; hashes: number; data: string } {\n const bytes = new Uint8Array(this.bitArray.buffer, this.bitArray.byteOffset, this.bitArray.byteLength);\n let binary = '';\n const len = bytes.byteLength;\n const chunk = 0x8000;\n for (let i = 0; i < len; i += chunk) {\n binary += String.fromCharCode.apply(null, bytes.subarray(i, Math.min(i + chunk, len)) as any);\n }\n return { size: this.size, hashes: this.hashes, data: btoa(binary) };\n }\n\n /**\n * \u4F20\u7EDF Base64 \u517C\u5BB9\u52A0\u8F7D\u903B\u8F91\n */\n static load(dump: { size: number; hashes: number; data: string }): BloomFilter {\n const binary = atob(dump.data);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n // \u62F7\u8D1D\u5E76\u586B\u5145\u5230\u5BF9\u9F50\u7684 Uint32Array \u7F13\u51B2\u533A\n const words = (bytes.length + 3) >> 2;\n const bitData = new Uint32Array(words);\n new Uint8Array(bitData.buffer).set(bytes);\n return new BloomFilter(dump.size, dump.hashes, bitData);\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { BloomFilter, BLOOM_CHUNK_SIZE } from \"../utils/bloom\";\n\nexport class ProfileBloomModel {\n constructor(private db: D1Database) {}\n\n // \u2500\u2500\u2500 Active Bloom (profile_blooms) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // This table holds the last fully-completed Bloom Filter for each profile.\n // It is the authoritative source for DNS query lookups and is never in a\n // partial state \u2014 it is only updated atomically when a sync cycle completes.\n\n async clearProfileBlooms(profileId: string): Promise<boolean> {\n const result = await this.db.prepare(\"DELETE FROM profile_blooms WHERE profile_id = ?\").bind(profileId).run();\n return result.success;\n }\n\n async upsertProfileBloom(profileId: string, bloomFilter: ArrayBuffer, now: number): Promise<boolean> {\n const uint8Array = new Uint8Array(bloomFilter);\n const statements = [];\n\n // Delete existing chunks to replace them entirely\n statements.push(\n this.db.prepare(\"DELETE FROM profile_blooms WHERE profile_id = ?\").bind(profileId)\n );\n\n // Insert new chunks (1.5MB per chunk, <= 2MB D1 limit)\n let chunkIndex = 0;\n for (let offset = 0; offset < uint8Array.length; offset += BLOOM_CHUNK_SIZE) {\n const chunkData = uint8Array.slice(offset, offset + BLOOM_CHUNK_SIZE).buffer as ArrayBuffer;\n statements.push(\n this.db.prepare(\n \"INSERT INTO profile_blooms (profile_id, chunk_index, bloom_filter_chunk, updated_at) VALUES (?, ?, ?, ?)\"\n ).bind(profileId, chunkIndex, chunkData, now)\n );\n chunkIndex++;\n }\n\n const results = await this.db.batch(statements);\n return results.every(r => r.success);\n }\n\n async getProfileBloom(profileId: string): Promise<ArrayBuffer | null> {\n const { results } = await this.db.prepare(\n \"SELECT bloom_filter_chunk FROM profile_blooms WHERE profile_id = ? ORDER BY chunk_index ASC\"\n ).bind(profileId).all<{ bloom_filter_chunk: ArrayBuffer }>();\n\n if (!results || results.length === 0) return null;\n\n const totalLength = results.reduce((acc, row) => {\n const len = row.bloom_filter_chunk.byteLength ?? (row.bloom_filter_chunk as any).length;\n return acc + len;\n }, 0);\n const combined = new Uint8Array(totalLength);\n\n let offset = 0;\n for (const row of results) {\n const chunk = new Uint8Array(row.bloom_filter_chunk);\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n\n return combined.buffer as ArrayBuffer;\n }\n\n // \u2500\u2500\u2500 Staging Bloom (profile_blooms_staging) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Used exclusively during an active sync cycle. Receives incremental domain\n // additions one list at a time. DNS queries never touch this table.\n // On cycle completion, staging is atomically promoted to the active table.\n\n /**\n * \u521D\u59CB\u5316 staging \u533A\u57DF\u4E2D\u7684\u7A7A\u767D\u9884\u5206\u914D Bloom Filter\uFF0C\u6807\u5FD7\u65B0\u540C\u6B65\u5468\u671F\u5F00\u59CB\u3002\n * \u6309 maxDomains \u9884\u5206\u914D\u5185\u5B58\uFF08\u5168\u96F6 bitArray\uFF09\uFF0C\u786E\u4FDD\u540E\u7EED\u589E\u91CF add \u53EF\u76F4\u63A5\u8BFB\u5199\uFF0C\n * \u4E0D\u9700\u8981\u6BCF\u6B21\u91CD\u65B0\u8BA1\u7B97 m \u548C k\u3002\n *\n * @param profileId - Profile ID\n * @param maxDomains - \u9884\u671F\u7684\u603B\u57DF\u540D\u6570\u91CF\u4E0A\u9650\uFF08\u7528\u4E8E\u786E\u5B9A m \u548C k\uFF09\n * @param falsePositiveRate - Bloom Filter \u5047\u9633\u6027\u7387\n * @param now - \u5F53\u524D Unix \u65F6\u95F4\u6233\uFF08\u79D2\uFF09\n */\n async initializeStagingBloom(\n profileId: string,\n maxDomains: number,\n falsePositiveRate: number,\n now: number\n ): Promise<boolean> {\n const emptyBloom = BloomFilter.create(maxDomains, falsePositiveRate);\n const binary = emptyBloom.toUint8Array();\n return this.upsertStagingBloom(profileId, binary.buffer as ArrayBuffer, now);\n }\n\n async clearStagingBloom(profileId: string): Promise<boolean> {\n const result = await this.db.prepare(\"DELETE FROM profile_blooms_staging WHERE profile_id = ?\").bind(profileId).run();\n return result.success;\n }\n\n async upsertStagingBloom(profileId: string, bloomFilter: ArrayBuffer, now: number): Promise<boolean> {\n const uint8Array = new Uint8Array(bloomFilter);\n const statements = [];\n\n statements.push(\n this.db.prepare(\"DELETE FROM profile_blooms_staging WHERE profile_id = ?\").bind(profileId)\n );\n\n // Insert new chunks (1.5MB per chunk, <= 2MB D1 limit)\n let chunkIndex = 0;\n for (let offset = 0; offset < uint8Array.length; offset += BLOOM_CHUNK_SIZE) {\n const chunkData = uint8Array.slice(offset, offset + BLOOM_CHUNK_SIZE).buffer as ArrayBuffer;\n statements.push(\n this.db.prepare(\n \"INSERT INTO profile_blooms_staging (profile_id, chunk_index, bloom_filter_chunk, updated_at) VALUES (?, ?, ?, ?)\"\n ).bind(profileId, chunkIndex, chunkData, now)\n );\n chunkIndex++;\n }\n\n const results = await this.db.batch(statements);\n return results.every(r => r.success);\n }\n\n async getStagingBloom(profileId: string): Promise<ArrayBuffer | null> {\n const { results } = await this.db.prepare(\n \"SELECT bloom_filter_chunk FROM profile_blooms_staging WHERE profile_id = ? ORDER BY chunk_index ASC\"\n ).bind(profileId).all<{ bloom_filter_chunk: ArrayBuffer }>();\n\n if (!results || results.length === 0) return null;\n\n const totalLength = results.reduce((acc, row) => {\n const len = row.bloom_filter_chunk.byteLength ?? (row.bloom_filter_chunk as any).length;\n return acc + len;\n }, 0);\n const combined = new Uint8Array(totalLength);\n\n let offset = 0;\n for (const row of results) {\n const chunk = new Uint8Array(row.bloom_filter_chunk);\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n\n return combined.buffer as ArrayBuffer;\n }\n\n /**\n * \u539F\u5B50\u6027\u5730\u5C06 staging Bloom \u5347\u7EA7\u4E3A active Bloom\uFF08A/B \u66FF\u6362\uFF09\u3002\n *\n * \u64CD\u4F5C\u6B65\u9AA4\uFF08\u901A\u8FC7 db.batch \u4FDD\u8BC1\u539F\u5B50\u6027\uFF09\uFF1A\n * 1. \u5220\u9664\u73B0\u6709 active bloom\uFF08profile_blooms\uFF09\n * 2. \u4ECE staging \u8BFB\u53D6\u6240\u6709 chunk \u5E76\u91CD\u65B0\u63D2\u5165\u5230 active \u8868\n * 3. \u6E05\u7A7A staging \u8868\n *\n * \u5347\u7EA7\u5B8C\u6210\u540E\uFF0CDNS \u67E5\u8BE2\u8DEF\u5F84\u7ACB\u5373\u8BFB\u53D6\u5230\u5B8C\u6574\u7684\u65B0 bloom\uFF0C\n * \u4E0D\u4F1A\u51FA\u73B0\u4E2D\u95F4\u72B6\u6001\uFF08partial bloom\uFF09\u3002\n *\n * @param profileId - Profile ID\n * @param now - \u5F53\u524D Unix \u65F6\u95F4\u6233\uFF08\u79D2\uFF09\n */\n async promoteStagingToActive(profileId: string, now: number): Promise<boolean> {\n // 1. \u8BFB\u53D6 staging \u6240\u6709 chunk\n const { results: stagingChunks } = await this.db.prepare(\n \"SELECT chunk_index, bloom_filter_chunk FROM profile_blooms_staging WHERE profile_id = ? ORDER BY chunk_index ASC\"\n ).bind(profileId).all<{ chunk_index: number; bloom_filter_chunk: ArrayBuffer }>();\n\n if (!stagingChunks || stagingChunks.length === 0) {\n // staging \u4E3A\u7A7A\uFF08\u5982\u5217\u8868\u5168\u90E8\u4E3A\u7A7A\uFF09\uFF0C\u76F4\u63A5\u6E05\u7A7A active\n await this.clearProfileBlooms(profileId);\n return true;\n }\n\n // 2. \u6784\u5EFA\u6279\u91CF\u5199\u5165\u8BED\u53E5\uFF1A\u5148\u5220\u9664 active\uFF0C\u518D\u9010 chunk \u5199\u5165\n const statements = [\n this.db.prepare(\"DELETE FROM profile_blooms WHERE profile_id = ?\").bind(profileId),\n ...stagingChunks.map(row =>\n this.db.prepare(\n \"INSERT INTO profile_blooms (profile_id, chunk_index, bloom_filter_chunk, updated_at) VALUES (?, ?, ?, ?)\"\n ).bind(profileId, row.chunk_index, row.bloom_filter_chunk, now)\n ),\n this.db.prepare(\"DELETE FROM profile_blooms_staging WHERE profile_id = ?\").bind(profileId),\n ];\n\n const batchResults = await this.db.batch(statements);\n return batchResults.every(r => r.success);\n }\n}\n", "import { Context, ProfileSettings, Rule } from \"../types\";\nimport { ProfileModel } from \"../models/profile\";\nimport { RuleModel } from \"../models/rule\";\nimport { ProfileBloomModel } from \"../models/profileBloom\";\nimport { BloomFilter } from \"../utils/bloom\";\nimport { cacheUtils } from \"../utils/cache\";\nimport { configCache, bloomMemoryMap } from \"./cache\";\n\nexport const pipelineConfig = {\n async load(context: Context, track: (name: string) => void): Promise<{ settings: ProfileSettings; rules: Rule[]; bloom?: BloomFilter } | null> {\n const { profileId, env, ctx } = context;\n\n // 1. \u68C0\u67E5 L1 Memory (Isolate Global)\n const inMem = bloomMemoryMap.get(profileId);\n const cachedConfig = configCache.get(profileId);\n \n const bloomMemTtl = Number(env.BLOOM_MEM_TTL) || 600000;\n const configMemTtl = 300000; // 5 minutes memory TTL for config\n if (cachedConfig && (Date.now() - (cachedConfig.timestamp || 0) < configMemTtl)) {\n track('load_config_l1_mem');\n const validBloom = (inMem && Date.now() - inMem.ts < bloomMemTtl) ? inMem.bloom : undefined;\n return { ...cachedConfig, bloom: validBloom };\n }\n\n // 2. \u68C0\u67E5 L2 Cache API\n const cache = (caches as any).default;\n const profileCacheKey = `profile_v6:${profileId}`;\n const bloomInternalUrl = `https://obex.local/bloom-bin/${profileId}`;\n \n let apiCached = await cacheUtils.get<any>(cache, profileCacheKey);\n let bloom: BloomFilter | undefined;\n\n if (apiCached) {\n track('load_config_l2_cache');\n if (inMem && Date.now() - inMem.ts < bloomMemTtl) {\n bloom = inMem.bloom;\n } else {\n try {\n const bloomRes = await cache.match(bloomInternalUrl);\n if (bloomRes) {\n track('load_bloom_l2_cache');\n const buffer = await bloomRes.arrayBuffer();\n bloom = BloomFilter.fromUint8Array(new Uint8Array(buffer));\n bloomMemoryMap.set(profileId, { bloom, ts: Date.now() });\n }\n } catch {\n // Non-critical bloom cache read error\n }\n }\n \n // \u5173\u952E\u4FEE\u590D\uFF1A\u65E0\u8BBA\u5E03\u9686\u8FC7\u6EE4\u5668\u662F\u5426\u5B58\u5728\uFF0CapiCached \u672C\u8EAB\u90FD\u662F\u6709\u6548\u7684\u914D\u7F6E\uFF0C\u7EDD\u5BF9\u4E0D\u80FD\u7A7F\u900F\u56DE D1\uFF01\n configCache.set(profileId, { ...apiCached, timestamp: Date.now() });\n return { ...apiCached, bloom };\n }\n\n // 3. \u56DE\u9000\u5230 D1 (\u5E26 Fail-Open \u964D\u7EA7\u4FDD\u62A4)\n try {\n const profileModel = new ProfileModel(env.DB);\n const ruleModel = new RuleModel(env.DB);\n const bloomModel = new ProfileBloomModel(env.DB);\n\n // \u5E76\u53D1\u4ECE D1 \u8BFB\u53D6\u914D\u7F6E\u3001\u89C4\u5219\u53CA\u5E03\u9686\u8FC7\u6EE4\u5668\uFF0C\u6D88\u9664\u4E32\u884C\u7F51\u7EDC\u5F80\u8FD4\u7B49\u5F85\n const [profile, rules, buffer] = await Promise.all([\n profileModel.getById(profileId),\n ruleModel.getRules(profileId),\n bloomModel.getProfileBloom(profileId).catch((e) => {\n console.error(\"[Config] D1 Bloom loading failed:\", e);\n return null;\n }),\n ]);\n\n if (!profile) {\n if (cachedConfig) {\n console.warn(`[Config] Profile ${profileId} not in D1, using stale memory config.`);\n return { ...cachedConfig, bloom: inMem?.bloom };\n }\n return null;\n }\n \n const settings = JSON.parse(profile.settings);\n \n // \u5904\u7406\u5E03\u9686\u8FC7\u6EE4\u5668\u5E76\u5199\u5165 L2 Cache API\n if (buffer) {\n track('load_bloom_l3_d1');\n const uint8 = new Uint8Array(buffer);\n bloom = BloomFilter.fromUint8Array(uint8);\n\n // \u5199\u5165 L2 Cache API \u4F9B\u4E0B\u6B21\u4F7F\u7528 (7 \u5929\u957F\u6548\u7F13\u5B58\uFF0C\u5217\u8868\u53D8\u66F4\u65F6\u6709\u4E3B\u52A8\u6DD8\u6C70)\n ctx.waitUntil(cache.put(bloomInternalUrl, new Response(uint8, {\n headers: { \n 'Content-Type': 'application/octet-stream',\n 'Cache-Control': 'public, max-age=604800',\n 'Cache-Tag': `bloom-${profileId}`\n }\n })));\n }\n\n const config = { settings, rules };\n if (bloom) bloomMemoryMap.set(profileId, { bloom, ts: Date.now() });\n \n configCache.set(profileId, { ...config, timestamp: Date.now() });\n // \u5199\u5165 L2 Cache API \u914D\u7F6E\u7F13\u5B58 (24 \u5C0F\u65F6\u957F\u6548\u7F13\u5B58\uFF0C\u914D\u7F6E\u53D8\u66F4\u65F6\u6709\u4E3B\u52A8\u6DD8\u6C70)\n ctx.waitUntil(cacheUtils.set(cache, profileCacheKey, config, 86400, [`profile-${profileId}`]));\n \n track('load_config_full_sync');\n return { ...config, bloom };\n } catch (e: any) {\n console.warn(`[Config] D1 profile fetch failed for ${profileId}:`, e?.message || e);\n \n // \u5BB9\u707E\u6A21\u5F0F 1: Stale-While-Error (\u4F7F\u7528\u8FC7\u671F\u7684 L1 \u7F13\u5B58)\n if (cachedConfig) {\n console.warn(`[Config] Serving stale config from memory for profile ${profileId}`);\n return { ...cachedConfig, bloom: inMem?.bloom };\n }\n\n // \u5BB9\u707E\u6A21\u5F0F 2: Emergency Safe Default Fallback\n // \u5F53 D1 \u8BFB\u989D\u5EA6\u8017\u5C3D\u4E14\u65E0\u7F13\u5B58\u65F6\uFF0C\u4F7F\u7528\u5185\u7F6E\u5B89\u5168\u4E0A\u6E38\u515C\u5E95\uFF0C\u4FDD\u8BC1 DNS \u7EDD\u4E0D\u62A5 500 \u65AD\u7F51\n console.warn(`[Config] Serving emergency fail-open safe fallback for profile ${profileId}`);\n const failOpenUpstream = env.FAIL_OPEN_UPSTREAM || \"https://freedns.controld.com/no-ads-malware-typo\";\n const fallbackSettings: ProfileSettings = {\n upstream: [failOpenUpstream],\n default_policy: \"ALLOW\",\n log_retention_days: 0,\n ecs: { enabled: true, use_client_ip: true }\n };\n return { settings: fallbackSettings, rules: [], bloom: undefined };\n }\n }\n};\n", "/**\n * @file dnsStamp.ts\n * @description RFC draft DNS Stamp (sdns://) parser for DNS Worker.\n * Decodes base64url DNS stamps into structured resolver configurations (DoH, DoT, Plain DNS, DNSCrypt).\n */\n\nexport interface DnsStamp {\n /** Resolved protocol type */\n protocol: 'dns' | 'dnscrypt' | 'doh' | 'dot' | 'doq' | 'odoh' | 'unknown';\n /** Raw protocol identifier byte */\n protocolId: number;\n /** Properties flags (DNSSEC, No-Log, No-Filter) */\n props: bigint;\n /** Server IP address or IP:port (optional in stamps) */\n serverAddress?: string;\n /** Hostname (SNI / vhost) */\n hostname?: string;\n /** URL path (for DoH/ODoH) */\n path?: string;\n /** SHA-256 certificate hashes (if present) */\n hashes?: string[];\n /** Provider name (for DNSCrypt) */\n providerName?: string;\n /** Equivalent standard URL (https://..., tls://..., tcp://...) */\n resolvedUrl: string;\n}\n\n/**\n * Decodes a DNS Stamp (sdns://...) into its protocol components and resolved URL.\n *\n * @param stampStr - The DNS Stamp string starting with sdns://\n * @returns Parsed DnsStamp object\n * @throws Error if stamp format or base64 decoding is invalid\n */\nexport function parseDnsStamp(stampStr: string): DnsStamp {\n const clean = stampStr.trim();\n if (!clean.startsWith('sdns://')) {\n throw new Error('Invalid DNS Stamp: must start with sdns://');\n }\n\n const rawB64 = clean.slice(7);\n let end = rawB64.length;\n while (end > 0 && rawB64.charCodeAt(end - 1) === 61) { // 61 is '='\n end--;\n }\n const b64 = rawB64.slice(0, end);\n let binaryStr: string;\n\n if (typeof Buffer !== 'undefined') {\n binaryStr = Buffer.from(b64, 'base64url').toString('latin1');\n } else {\n // Cloudflare Workers Web runtime: URL-safe base64 conversion\n const standardB64 = b64.replace(/-/g, '+').replace(/_/g, '/');\n const padLen = standardB64.length % 4;\n const padded = padLen ? standardB64 + '='.repeat(4 - padLen) : standardB64;\n binaryStr = atob(padded);\n }\n\n const bytes = new Uint8Array(binaryStr.length);\n for (let i = 0; i < binaryStr.length; i++) {\n bytes[i] = binaryStr.charCodeAt(i);\n }\n\n if (bytes.length < 9) {\n throw new Error('DNS Stamp payload too short (minimum 9 bytes)');\n }\n\n let offset = 0;\n const protocolId = bytes[offset++];\n\n // 8 bytes props (little-endian uint64)\n let props = 0n;\n for (let i = 0; i < 8; i++) {\n props |= BigInt(bytes[offset++]) << BigInt(i * 8);\n }\n\n const readLpString = (): string => {\n if (offset >= bytes.length) return '';\n const len = bytes[offset++];\n if (offset + len > bytes.length) return '';\n const strBytes = bytes.slice(offset, offset + len);\n offset += len;\n return new TextDecoder().decode(strBytes);\n };\n\n const readHashes = (): string[] => {\n const hashes: string[] = [];\n while (offset < bytes.length) {\n const hLen = bytes[offset++];\n if (hLen === 0) break;\n const actualLen = hLen & 0x7f;\n if (offset + actualLen > bytes.length) break;\n const hashBytes = bytes.slice(offset, offset + actualLen);\n offset += actualLen;\n hashes.push(Array.from(hashBytes).map(b => b.toString(16).padStart(2, '0')).join(''));\n if ((hLen & 0x80) === 0) break;\n }\n return hashes;\n };\n\n let protocol: DnsStamp['protocol'] = 'unknown';\n let serverAddress: string | undefined;\n let hostname: string | undefined;\n let path: string | undefined;\n let hashes: string[] | undefined;\n let providerName: string | undefined;\n let resolvedUrl = '';\n\n switch (protocolId) {\n case 0x00: { // Plain DNS\n protocol = 'dns';\n serverAddress = readLpString();\n if (serverAddress) {\n resolvedUrl = serverAddress.includes(':') && !serverAddress.startsWith('[')\n ? `tcp://${serverAddress}`\n : `tcp://${serverAddress}:53`;\n }\n break;\n }\n case 0x01: { // DNSCrypt\n protocol = 'dnscrypt';\n serverAddress = readLpString();\n offset += 32; // Skip 32-byte provider public key\n providerName = readLpString();\n resolvedUrl = `dnscrypt://${providerName || serverAddress}`;\n break;\n }\n case 0x02: { // DoH (DNS over HTTPS)\n protocol = 'doh';\n serverAddress = readLpString();\n hashes = readHashes();\n hostname = readLpString();\n path = readLpString() || '/dns-query';\n const host = hostname || serverAddress;\n if (!path.startsWith('/')) path = `/${path}`;\n resolvedUrl = `https://${host}${path}`;\n break;\n }\n case 0x03: { // DoT (DNS over TLS)\n protocol = 'dot';\n serverAddress = readLpString();\n hashes = readHashes();\n hostname = readLpString();\n const host = hostname || serverAddress;\n const port = serverAddress && serverAddress.includes(':') ? serverAddress.split(':').pop() : '853';\n resolvedUrl = host?.includes(':') ? `tls://${host}` : `tls://${host}:${port}`;\n break;\n }\n case 0x04: { // DoQ (DNS over QUIC)\n protocol = 'doq';\n serverAddress = readLpString();\n hashes = readHashes();\n hostname = readLpString();\n resolvedUrl = `quic://${hostname || serverAddress}:853`;\n break;\n }\n case 0x05: { // Oblivious DoH Target\n protocol = 'odoh';\n serverAddress = readLpString();\n hashes = readHashes();\n hostname = readLpString();\n path = readLpString() || '/dns-query';\n resolvedUrl = `https://${hostname || serverAddress}${path}`;\n break;\n }\n default:\n resolvedUrl = clean;\n break;\n }\n\n return {\n protocol,\n protocolId,\n props,\n serverAddress: serverAddress || undefined,\n hostname: hostname || undefined,\n path: path || undefined,\n hashes: hashes && hashes.length > 0 ? hashes : undefined,\n providerName: providerName || undefined,\n resolvedUrl\n };\n}\n", "/**\n * @file validator.ts\n * @description Utility functions for validating inputs and ensuring security.\n * This includes regex patterns for usernames, passwords, access keys, and TOTP tokens,\n * as well as functions to validate URLs against SSRF attacks and check IP address types.\n * \n * The URL validation function checks for:\n * - Allowed protocols (http, https, tcp)\n * - Forbidden hostnames (localhost, metadata services)\n * - IP addresses in private, loopback, or link-local ranges\n * - Common URL parsing bypass techniques (user info in URLs)\n */\n\nimport { isIPv4, isIPv6, ipv6ToBigInt, createCidrMatcher } from \"./cidr\";\nimport { parseDnsStamp } from \"./dnsStamp\";\n\nexport { isIPv4, isIPv6 } from \"./cidr\";\n\n/**\n * Password validation regular expression.\n * Requirements: 12-100 characters containing letters, numbers, and special characters.\n * Allowed special characters: ~`!@#$%^&*()_-+={[}]|\\:;\"'<,>.?/\n */\nexport const PASSWORD_REGEX = /^[a-zA-Z\\d~`!@#$%^&*()_\\-+={[}\\]|\\\\:;\"'<,>.?\\/]{12,100}$/;\n\n/**\n * Username validation regular expression.\n * Requirements: 5-32 characters, starting with a letter or underscore, followed by letters, numbers, underscores, or hyphens.\n */\nexport const USERNAME_REGEX = /^[a-z_][a-z0-9_-]{4,31}$/;\n\n/**\n * Access point name validation regular expression.\n * Requirements: 1-30 characters, containing letters, numbers, underscores, or hyphens.\n */\nexport const AP_NAME_REGEX = /^[a-zA-Z0-9_-]{1,30}$/;\n\n/**\n * Passkey name validation regular expression.\n * Requirements: 1-30 characters, containing letters, numbers, underscores, or hyphens (same as AP_NAME_REGEX).\n */\nexport const PASSKEY_NAME_REGEX = /^[a-zA-Z0-9_-]{1,30}$/;\n\n/**\n * Validates whether a passkey name conforms to the required format.\n *\n * @param name - The passkey name to validate.\n * @returns True if valid.\n */\nexport function validatePasskeyName(name: string): boolean {\n return PASSKEY_NAME_REGEX.test(name);\n}\n\n/**\n * Profile name validation regular expression.\n * Requirements: 1-30 characters, containing letters, numbers, underscores, hyphens, or parentheses.\n */\nexport const PROFILE_NAME_REGEX = /^[\\p{L}\\p{N}_ ()-]{1,30}$/u;\n\n/**\n * Access key validation regular expression.\n * Requirements: 5-12 characters, containing only letters and numbers (supporting 5-char z-base-32 tokens).\n */\nexport const ACCESS_KEY_REGEX = /^[a-zA-Z0-9]{5,12}$/;\n\n/**\n * TOTP token validation regular expression.\n * Requirements: exactly 6 digits.\n */\nexport const TOTP_TOKEN_REGEX = /^\\d{6}$/;\n\nconst FORBIDDEN_HOSTNAMES: readonly string[] = [\n 'localhost',\n 'metadata.google.internal', // GCP\n 'metadata.goog',\n '169.254.169.254', // AWS/GCP/Azure IMDS\n 'instance-data',\n 'localtest.me',\n];\n\nconst FORBIDDEN_HOSTNAME_SUFFIXES: readonly string[] = [\n '.localhost',\n '.local',\n '.internal',\n '.lan',\n '.home.arpa',\n '.corp',\n '.home',\n '.localtest.me',\n];\n\n/**\n * Extracts embedded IPv4 addresses from wildcard/dynamic DNS services (e.g. nip.io, sslip.io).\n *\n * @param hostname - Normalized lowercase hostname.\n * @returns Extracted IPv4 string or null if not an embedded IP service.\n */\nfunction extractEmbeddedDnsIp(hostname: string): string | null {\n // Dot-separated IPv4: e.g. 127.0.0.1.nip.io, foo.192.168.1.1.sslip.io\n const dotMatch = hostname.match(/(?:^|\\.)(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\.(?:nip\\.io|sslip\\.io)$/i);\n if (dotMatch) return dotMatch[1];\n\n // Dash-separated IPv4: e.g. 127-0-0-1.sslip.io, 10-0-0-1.nip.io\n const dashMatch = hostname.match(/(?:^|\\.)(\\d{1,3})-(\\d{1,3})-(\\d{1,3})-(\\d{1,3})\\.(?:nip\\.io|sslip\\.io)$/i);\n if (dashMatch) return `${dashMatch[1]}.${dashMatch[2]}.${dashMatch[3]}.${dashMatch[4]}`;\n\n return null;\n}\n\n/**\n * Hardcoded standard private, loopback, link-local, multicast, and reserved CIDR ranges\n * for IPv4 and IPv6 according to RFC 6890, RFC 1918, RFC 6598, RFC 4291, RFC 4193, etc.\n */\nexport const PRIVATE_AND_RESERVED_CIDRS: readonly string[] = [\n // IPv4 Special/Private Ranges\n \"0.0.0.0/8\", // \"This host on this network\" (RFC 1122)\n \"10.0.0.0/8\", // Private-Use (RFC 1918)\n \"100.64.0.0/10\", // Shared Address Space / CGNAT (RFC 6598)\n \"127.0.0.0/8\", // Loopback (RFC 1122)\n \"169.254.0.0/16\", // Link-Local (RFC 3927)\n \"172.16.0.0/12\", // Private-Use (RFC 1918)\n \"192.0.0.0/24\", // IETF Protocol Assignments (RFC 6890)\n \"192.0.2.0/24\", // TEST-NET-1 (RFC 5737)\n \"192.88.99.0/24\", // 6to4 Relay Anycast (RFC 3068)\n \"192.168.0.0/16\", // Private-Use (RFC 1918)\n \"198.18.0.0/15\", // Benchmarking (RFC 2544)\n \"198.51.100.0/24\", // TEST-NET-2 (RFC 5737)\n \"203.0.113.0/24\", // TEST-NET-3 (RFC 5737)\n \"224.0.0.0/4\", // Multicast (RFC 5771)\n \"240.0.0.0/4\", // Reserved (RFC 1112)\n \"255.255.255.255/32\",// Limited Broadcast (RFC 919)\n\n // IPv6 Special/Private Ranges\n \"::/128\", // Unspecified (RFC 4291)\n \"::1/128\", // Loopback (RFC 4291)\n \"64:ff9b::/96\", // IPv4-IPv6 Translation (RFC 6052)\n \"100::/64\", // Discard Prefix (RFC 6666)\n \"2001:db8::/32\", // Documentation (RFC 3849)\n \"fc00::/7\", // Unique Local (RFC 4193)\n \"fe80::/10\", // Link-Local (RFC 4291)\n \"ff00::/8\" // Multicast (RFC 4291)\n];\n\nconst isPrivateOrReservedIp: (ip: string) => boolean = createCidrMatcher(PRIVATE_AND_RESERVED_CIDRS);\n\n/**\n * Checks whether an IP address is a publicly routable Internet IP.\n * Uses CIDR matching against standard private and reserved IP blocks.\n * Handles IPv4, IPv6, and IPv4-mapped IPv6 (::ffff:x.x.x.x).\n *\n * @param ip - The IP string to validate.\n * @returns True if public Internet IP, false if private, reserved, or invalid.\n */\nexport function isPublicInternetIP(ip: string): boolean {\n if (!ip || typeof ip !== \"string\") return false;\n\n let cleanIp = ip.trim();\n if (cleanIp.startsWith(\"[\") && cleanIp.endsWith(\"]\")) {\n cleanIp = cleanIp.slice(1, -1);\n }\n\n // If IPv4-mapped IPv6 (e.g. ::ffff:192.168.1.1)\n if (cleanIp.includes(\":\") && cleanIp.includes(\".\")) {\n const lastColon = cleanIp.lastIndexOf(\":\");\n const ipv4Part = cleanIp.substring(lastColon + 1);\n if (!isIPv4(ipv4Part)) return false;\n return !isPrivateOrReservedIp(ipv4Part);\n }\n\n // Validate that it's a valid IPv4 or IPv6\n if (!isIPv4(cleanIp) && !isIPv6(cleanIp)) {\n return false;\n }\n\n // Check IPv4-mapped IPv6 in hex format (e.g. ::ffff:7f00:1 canonicalized from ::ffff:127.0.0.1)\n const ipv6Val = ipv6ToBigInt(cleanIp);\n if (ipv6Val !== null && (ipv6Val >> 32n) === 0xffffn) {\n const ipv4Num = Number(ipv6Val & 0xffffffffn);\n const ipv4Str = `${(ipv4Num >>> 24) & 0xff}.${(ipv4Num >>> 16) & 0xff}.${(ipv4Num >>> 8) & 0xff}.${ipv4Num & 0xff}`;\n return !isPrivateOrReservedIp(ipv4Str);\n }\n\n return !isPrivateOrReservedIp(cleanIp);\n}\n\n/**\n * Checks whether the given URL is safe to fetch (prevents SSRF).\n * - Restricts to HTTP/HTTPS/TCP/TLS/DNS Stamp protocols.\n * - Blocks local, loopback, and private IP ranges.\n * - Blocks common metadata hostnames.\n * @param urlString The URL to validate.\n * @returns boolean True if safe, false otherwise.\n */\nexport function isSafeUrl(urlString: string): boolean {\n try {\n if (urlString.startsWith('sdns://')) {\n const stamp = parseDnsStamp(urlString);\n return isSafeUrl(stamp.resolvedUrl);\n }\n\n let parseableUrl: string;\n if (urlString.startsWith('tcp://') || urlString.startsWith('udp://')) {\n parseableUrl = urlString.replace(/^(tcp|udp):\\/\\//, 'http://');\n } else if (urlString.startsWith('tls://') || urlString.startsWith('dot://')) {\n parseableUrl = urlString.replace(/^(tls|dot):\\/\\//, 'http://');\n } else if (urlString.startsWith('http://') || urlString.startsWith('https://')) {\n parseableUrl = urlString;\n } else {\n parseableUrl = `http://${urlString}`;\n }\n\n const url = new URL(parseableUrl);\n const rawHostname = url.hostname.replace(/^\\[|\\]$/g, '').toLowerCase();\n\n if (FORBIDDEN_HOSTNAMES.includes(rawHostname) || FORBIDDEN_HOSTNAMES.includes(url.hostname.toLowerCase())) {\n return false;\n }\n\n // Check forbidden internal / mDNS / local suffixes\n for (const suffix of FORBIDDEN_HOSTNAME_SUFFIXES) {\n if (rawHostname.endsWith(suffix) || rawHostname === suffix.slice(1)) {\n return false;\n }\n }\n\n // Check cloud metadata endpoints\n if (rawHostname.includes('metadata.google')) {\n return false;\n }\n\n // Check wildcard dynamic DNS embedded IPs (nip.io, sslip.io)\n const embeddedIp = extractEmbeddedDnsIp(rawHostname);\n if (embeddedIp) {\n if (!isPublicInternetIP(embeddedIp)) {\n return false;\n }\n }\n\n // Check if hostname is an IP and matches forbidden ranges\n if ((isIPv4(rawHostname) || isIPv6(rawHostname)) && !isPublicInternetIP(rawHostname)) {\n return false;\n }\n\n // Additional safeguard: If it contains special characters often used to bypass parsers\n if (url.hostname.includes('@') || url.username || url.password) {\n return false;\n }\n\n return true;\n } catch (e) {\n return false; // Invalid URL\n }\n}\n", "/**\n * @file sockets.ts\n * @description Universal socket connector supporting both Cloudflare Workers (cloudflare:sockets)\n * and Node.js (node:net) runtimes.\n */\n\nimport { Buffer } from 'node:buffer';\n\nexport interface UniversalSocket {\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n close(): Promise<void>;\n}\n\nexport interface ConnectOptions {\n hostname: string;\n port: number;\n secureTransport?: 'off' | 'on' | 'starttls';\n}\n\n/**\n * Connects to a remote TCP or TLS server across Cloudflare Workers and Node.js environments.\n *\n * @param options - Target hostname, port, and optional secureTransport flag.\n * @returns An object containing readable and writable Web Streams and a close method.\n */\nexport async function connectUniversal(options: ConnectOptions): Promise<UniversalSocket> {\n // Check if running under Cloudflare Workers / Pages runtime vs. native Node.js\n const isCloudflare = (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') || typeof WebSocketPair !== 'undefined';\n const isNode = !isCloudflare && typeof process !== 'undefined' && !!process.versions?.node;\n\n if (isNode) {\n if (options.secureTransport === 'on') {\n const tls = await import('node:tls');\n const net = await import('node:net');\n return new Promise<UniversalSocket>((resolve, reject) => {\n const client = tls.connect({\n host: options.hostname,\n port: options.port,\n servername: net.isIP(options.hostname) ? undefined : options.hostname\n }, () => {\n let isClosed = false;\n\n const readable = new ReadableStream<Uint8Array>({\n start(controller) {\n client.on('data', (chunk: Buffer | Uint8Array) => {\n controller.enqueue(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));\n });\n client.on('end', () => {\n try { controller.close(); } catch { /* ignore */ }\n });\n client.on('error', (err: Error) => {\n try { controller.error(err); } catch { /* ignore */ }\n });\n },\n cancel() {\n if (!isClosed) {\n isClosed = true;\n client.destroy();\n }\n }\n });\n\n const writable = new WritableStream<Uint8Array>({\n write(chunk) {\n return new Promise((res, rej) => {\n client.write(chunk, (err: Error | null | undefined) => (err ? rej(err) : res()));\n });\n },\n close() {\n return new Promise(res => {\n client.end(() => res());\n });\n },\n abort(err) {\n client.destroy(err as any);\n }\n });\n\n resolve({\n readable,\n writable,\n close: async () => {\n if (!isClosed) {\n isClosed = true;\n client.destroy();\n }\n }\n });\n });\n\n client.on('error', reject);\n });\n }\n\n const net = await import('node:net');\n return new Promise<UniversalSocket>((resolve, reject) => {\n const client = net.createConnection({ host: options.hostname, port: options.port }, () => {\n let isClosed = false;\n\n const readable = new ReadableStream<Uint8Array>({\n start(controller) {\n client.on('data', (chunk: Buffer | Uint8Array) => {\n controller.enqueue(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));\n });\n client.on('end', () => {\n try { controller.close(); } catch { /* ignore */ }\n });\n client.on('error', (err: Error) => {\n try { controller.error(err); } catch { /* ignore */ }\n });\n },\n cancel() {\n if (!isClosed) {\n isClosed = true;\n client.destroy();\n }\n }\n });\n\n const writable = new WritableStream<Uint8Array>({\n write(chunk) {\n return new Promise((res, rej) => {\n client.write(chunk, (err: Error | null | undefined) => (err ? rej(err) : res()));\n });\n },\n close() {\n return new Promise(res => {\n client.end(() => res());\n });\n },\n abort(err) {\n client.destroy(err as any);\n }\n });\n\n resolve({\n readable,\n writable,\n close: async () => {\n if (!isClosed) {\n isClosed = true;\n client.destroy();\n }\n }\n });\n });\n\n client.on('error', reject);\n });\n }\n\n // Cloudflare Workers runtime\n try {\n // @ts-ignore\n const cf = await import('cloudflare:sockets');\n return cf.connect(\n { hostname: options.hostname, port: options.port },\n { secureTransport: options.secureTransport || 'off', allowHalfOpen: false }\n );\n } catch (err) {\n throw new Error(`Socket connection failed in current environment: ${err}`);\n }\n}\n", "/**\n * Custom error representing an upstream HTTP error response during DoH resolution.\n */\nexport class UpstreamHttpError extends Error {\n constructor(\n public readonly status: number,\n public readonly statusText: string,\n public readonly responseBodySnippet: string,\n public readonly cfRay?: string\n ) {\n super(`Upstream HTTP ${status}${statusText ? ` ${statusText}` : \"\"}`);\n this.name = \"UpstreamHttpError\";\n }\n}\n\n/**\n * Transport execution outcome returned by fetchFromUpstream.\n */\nexport interface UpstreamTransportResult {\n /** Raw wire-format DNS answer packet. */\n answer: Uint8Array;\n /** Latency in milliseconds spent communicating with the upstream. */\n latency: number;\n /** Diagnostic string of the transport protocol used (e.g., 'POST', 'DoT', 'TCP'). */\n diagMethod: string;\n /** Diagnostic target string (URL or socket address). */\n diagTarget: string;\n}\n\n/**\n * Structured representation of a decoded DNS answer resource record.\n */\nexport interface ParsedDNSAnswerRecord {\n name: string;\n type: string;\n data: string;\n ttl: number;\n}\n\n/**\n * Result of applying EDNS Client Subnet to a query.\n */\nexport interface EcsApplicationResult {\n /** Wire-format query bytes (with ECS injected if configured). */\n queryRaw: Uint8Array;\n /** Normalized ECS subnet string for logging, if applied. */\n ecs?: string;\n}\n\n/**\n * Result of evaluating best-effort Encrypted Client Hello rewrite.\n */\nexport interface EchProcessResult {\n /** Effective wire-format answer packet (rewritten if ECH injected). */\n answer: Uint8Array;\n /** Parsed answer records reflecting any rewritten records. */\n parsedAnswers: ParsedDNSAnswerRecord[];\n /** Effective resolution reason (e.g. 'ECH Rewritten' or original reason). */\n effectiveReason?: string;\n}\n", "import { connectUniversal } from \"../../utils/sockets\";\nimport { parseDnsStamp } from \"../../utils/dnsStamp\";\nimport { UpstreamHttpError, UpstreamTransportResult } from \"./types\";\n\n/**\n * Parsed upstream endpoint and diagnostic information.\n */\nexport interface UpstreamEndpointInfo {\n /** The effective target URL after resolving stamps or protocols. */\n effectiveUrl: string;\n /** Diagnostic string of the transport protocol (e.g., 'POST', 'DoT', 'TCP'). */\n diagMethod: string;\n /** Diagnostic target string (URL or socket address). */\n diagTarget: string;\n}\n\n/**\n * Reads a 2-byte framed DNS message from a ReadableStream reader (RFC 1035 / RFC 7858).\n * Handles TCP / TLS packet fragmentation and enforces a timeout.\n *\n * @param reader - ReadableStream reader for the upstream socket.\n * @param timeoutMs - Timeout duration in milliseconds.\n * @returns Complete wire-format DNS answer packet without 2-byte length header.\n */\nexport async function readFramedDnsResponse(\n reader: ReadableStreamDefaultReader<Uint8Array>,\n timeoutMs = 5000\n): Promise<Uint8Array> {\n const chunks: Uint8Array[] = [];\n let totalBytes = 0;\n let expectedBodyLength: number | null = null;\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(\"Upstream Socket Timeout\")), timeoutMs);\n });\n\n try {\n while (true) {\n const { value, done } = await Promise.race([reader.read(), timeoutPromise]);\n if (done || !value) {\n throw new Error(\"Socket closed before complete DNS response received\");\n }\n\n chunks.push(value);\n totalBytes += value.length;\n\n // RFC 1035 Section 4.2.2: Max DNS TCP message is 65535 bytes + 2 bytes prefix\n if (totalBytes > 65537) {\n throw new Error(\"DNS upstream message exceeded maximum RFC frame length (65535 bytes)\");\n }\n\n if (expectedBodyLength === null && totalBytes >= 2) {\n const b0 = chunks[0][0];\n const b1 = chunks[0].length > 1 ? chunks[0][1] : chunks[1][0];\n expectedBodyLength = (b0 << 8) | b1;\n }\n\n if (expectedBodyLength !== null && totalBytes >= 2 + expectedBodyLength) {\n const combined = new Uint8Array(totalBytes);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n return combined.slice(2, 2 + expectedBodyLength);\n }\n }\n } finally {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n }\n}\n\n/**\n * Parses a host and port from a target URL with support for IPv6 brackets.\n *\n * @param address - Host string potentially prefixed with protocol and containing port.\n * @param defaultPort - Default port to use if omitted.\n * @returns Object containing parsed host and port.\n */\nexport function parseHostAndPort(\n address: string,\n defaultPort: number\n): { host: string; port: number } {\n let host = address;\n let port = defaultPort;\n\n if (host.startsWith(\"[\")) {\n const closeIdx = host.indexOf(\"]\");\n if (closeIdx !== -1) {\n const ip = host.slice(1, closeIdx);\n const rest = host.slice(closeIdx + 1);\n host = ip;\n if (rest.startsWith(\":\")) {\n port = parseInt(rest.slice(1), 10) || defaultPort;\n }\n }\n } else if (host.includes(\":\")) {\n const parts = host.split(\":\");\n host = parts[0];\n port = parseInt(parts[1], 10) || defaultPort;\n }\n\n return { host, port };\n}\n\n/**\n * Resolves raw upstream URL or DNS stamp into effective endpoint info and diagnostic targets.\n *\n * @param rawUpstreamUrl - Configured upstream URL or connection string.\n * @returns Endpoint details for network dispatch and diagnostics.\n */\nexport function resolveUpstreamEndpoint(rawUpstreamUrl: string): UpstreamEndpointInfo {\n let effectiveUrl = rawUpstreamUrl;\n\n if (rawUpstreamUrl.startsWith(\"sdns://\")) {\n const stamp = parseDnsStamp(rawUpstreamUrl);\n if (stamp.protocol === \"dnscrypt\") {\n throw new Error(\n \"DNSCrypt (0x01) protocol in DNS Stamp is not supported; please use DoH (0x02) or DoT (0x03) DNS Stamps\"\n );\n }\n if (stamp.protocol === \"doq\") {\n throw new Error(\n \"DNS over QUIC (0x04) in DNS Stamp is not supported; please use DoH (0x02) or DoT (0x03) DNS Stamps\"\n );\n }\n if (!stamp.resolvedUrl) {\n throw new Error(`Unsupported DNS Stamp protocol: 0x${stamp.protocolId.toString(16)}`);\n }\n effectiveUrl = stamp.resolvedUrl;\n }\n\n if (effectiveUrl.startsWith(\"dot://\")) {\n effectiveUrl = effectiveUrl.replace(/^dot:\\/\\//, \"tls://\");\n }\n\n if (effectiveUrl.startsWith(\"tls://\")) {\n const rawHost = effectiveUrl.replace(/^tls:\\/\\//, \"\");\n const { host, port } = parseHostAndPort(rawHost, 853);\n return {\n effectiveUrl,\n diagMethod: \"DoT\",\n diagTarget: `tls://${host}:${port}`\n };\n }\n\n if (!effectiveUrl.startsWith(\"http://\") && !effectiveUrl.startsWith(\"https://\")) {\n const rawHost = effectiveUrl.replace(/^(tcp|udp):\\/\\//, \"\");\n const { host, port } = parseHostAndPort(rawHost, 53);\n return {\n effectiveUrl,\n diagMethod: \"TCP\",\n diagTarget: `tcp://${host}:${port}`\n };\n }\n\n return {\n effectiveUrl,\n diagMethod: \"POST\",\n diagTarget: effectiveUrl\n };\n}\n\n/**\n * Communicates with the upstream DNS server over DoH, DoT, TCP, or DNS Stamps.\n *\n * @param endpoint - Resolved upstream endpoint information.\n * @param queryRaw - Wire-format DNS query packet.\n * @returns Upstream resolution result containing raw answer, latency, and diagnostics.\n */\nexport async function fetchFromUpstream(\n endpoint: UpstreamEndpointInfo,\n queryRaw: Uint8Array\n): Promise<UpstreamTransportResult> {\n const { effectiveUrl, diagMethod, diagTarget } = endpoint;\n const startFetch = Date.now();\n let answer: Uint8Array;\n\n if (effectiveUrl.startsWith(\"tls://\") || effectiveUrl.startsWith(\"dot://\")) {\n // \u2500\u2500 DNS over TLS (DoT - RFC 7858) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const rawHost = effectiveUrl.replace(/^(tls|dot):\\/\\//, \"\");\n const { host: dotHost, port: dotPort } = parseHostAndPort(rawHost, 853);\n\n const socket = await connectUniversal({\n hostname: dotHost,\n port: dotPort,\n secureTransport: \"on\"\n });\n\n try {\n const writer = socket.writable.getWriter();\n const reader = socket.readable.getReader();\n\n // RFC 7858 Section 3.3: 2-byte length prefix + DNS message\n const framedQuery = new Uint8Array(queryRaw.length + 2);\n framedQuery[0] = (queryRaw.length >> 8) & 0xff;\n framedQuery[1] = queryRaw.length & 0xff;\n framedQuery.set(queryRaw, 2);\n\n await writer.write(framedQuery);\n writer.releaseLock();\n\n answer = await readFramedDnsResponse(reader, 5000);\n } finally {\n await socket.close().catch(() => {});\n }\n } else if (\n !effectiveUrl.startsWith(\"http://\") &&\n !effectiveUrl.startsWith(\"https://\")\n ) {\n // \u2500\u2500 Classic DNS (TCP Socket) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const rawHost = effectiveUrl.replace(/^(tcp|udp):\\/\\//, \"\");\n const { host: tcpHost, port: tcpPort } = parseHostAndPort(rawHost, 53);\n\n const socket = await connectUniversal({\n hostname: tcpHost,\n port: tcpPort,\n secureTransport: \"off\"\n });\n\n try {\n const writer = socket.writable.getWriter();\n const reader = socket.readable.getReader();\n\n const framedQuery = new Uint8Array(queryRaw.length + 2);\n framedQuery[0] = (queryRaw.length >> 8) & 0xff;\n framedQuery[1] = queryRaw.length & 0xff;\n framedQuery.set(queryRaw, 2);\n\n await writer.write(framedQuery);\n writer.releaseLock();\n\n answer = await readFramedDnsResponse(reader, 5000);\n } finally {\n await socket.close().catch(() => {});\n }\n } else {\n // \u2500\u2500 DoH (DNS over HTTPS) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const response = await fetch(effectiveUrl, {\n method: \"POST\",\n headers: {\n Accept: \"application/dns-message\",\n \"Content-Type\": \"application/dns-message\",\n \"User-Agent\": \"Obex-DNS/1.0\"\n },\n body: queryRaw,\n signal: AbortSignal.timeout(5000)\n });\n\n if (!response.ok) {\n let snippet = \"\";\n try {\n const rawBody = await response.text();\n // Extract plain text snippet (strip HTML tags and extra whitespace), retain max 300 chars\n snippet = rawBody.replace(/<[^>]*>?/gm, \" \").replace(/\\s+/g, \" \").trim().slice(0, 300);\n } catch {}\n const cfRay = response.headers.get(\"cf-ray\") || undefined;\n throw new UpstreamHttpError(response.status, response.statusText, snippet, cfRay);\n }\n\n const answerBuffer = await response.arrayBuffer();\n answer = new Uint8Array(answerBuffer);\n }\n\n const latency = Date.now() - startFetch;\n return { answer, latency, diagMethod, diagTarget };\n}\n", "import { DNSQuery, ProfileSettings } from \"../../types\";\nimport { injectEcsIntoQuery } from \"../../utils/dns\";\nimport { EcsApplicationResult } from \"./types\";\n\n/**\n * Evaluates and applies EDNS Client Subnet (RFC 7871) into the DNS wire-format query.\n *\n * ECS is injected directly into the wire format via OPT RR rather than as a URL query\n * parameter, ensuring broad compatibility with mainstream upstreams (ControlD, NextDNS, etc.).\n *\n * @param request - Inbound HTTP Request.\n * @param query - Parsed incoming DNS query.\n * @param settings - Current profile settings.\n * @returns Object containing the resulting wire query and the applied subnet string (if any).\n */\nexport function applyEcs(\n request: Request,\n query: DNSQuery,\n settings: ProfileSettings\n): EcsApplicationResult {\n let ecs: string | undefined;\n let queryRaw = query.raw;\n\n if (settings.ecs?.enabled) {\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n ecs = settings.ecs.use_client_ip\n ? `${clientIp}/${clientIp.includes(\":\") ? 48 : 24}`\n : query.type === \"AAAA\"\n ? settings.ecs.ipv6_cidr || settings.ecs.ipv4_cidr\n : settings.ecs.ipv4_cidr || settings.ecs.ipv6_cidr;\n\n if (ecs) {\n queryRaw = injectEcsIntoQuery(query.raw, ecs);\n }\n }\n\n return { queryRaw, ecs };\n}\n", "import { Context, DNSQuery, ProfileSettings, ResolutionResult } from \"../../types\";\nimport { buildDNSQuery, buildResponse, parseDNSAnswer } from \"../../utils/dns\";\nimport {\n isCloudflareIp,\n buildCloudflareEchConfig,\n DEFAULT_ECH_FRONTING_DOMAIN,\n ensureCloudflareIpRangesLoaded\n} from \"../../utils/ech\";\nimport { EchProcessResult, ParsedDNSAnswerRecord } from \"./types\";\n\n/**\n * Resolver callback interface for probing A records during ECH checking.\n */\nexport type ProbeResolverFn = (\n request: Request,\n query: DNSQuery,\n context: Context,\n settings: ProfileSettings,\n action: \"PASS\"\n) => Promise<ResolutionResult>;\n\n/**\n * Evaluates and injects Best-effort Encrypted Client Hello (ECH) for Cloudflare-fronted domains.\n *\n * Applicable to Type 65 (HTTPS) or Type 64 (SVCB) queries when best_effort_ech is enabled.\n * If the target domain resolves to Cloudflare IP ranges, synthesizes an HTTPS/SVCB answer\n * containing a valid ECHConfig parameter.\n *\n * @param query - The incoming DNS query.\n * @param initialParsedAnswers - Answers parsed from the upstream response.\n * @param initialAnswer - Raw wire answer from upstream.\n * @param context - Request context containing DB and execution context.\n * @param settings - Current profile settings.\n * @param request - Inbound HTTP Request.\n * @param probeResolver - Function to probe A records if IP hints are missing.\n * @param initialReason - Initial resolution reason string.\n * @returns Object with effective wire answer, parsed answers, and effective reason.\n */\nexport async function processBestEffortEch(\n query: DNSQuery,\n initialParsedAnswers: ParsedDNSAnswerRecord[],\n initialAnswer: Uint8Array,\n context: Context,\n settings: ProfileSettings,\n request: Request,\n probeResolver: ProbeResolverFn,\n initialReason?: string\n): Promise<EchProcessResult> {\n const isHttpsOrSvcb =\n query.type === \"HTTPS\" ||\n query.type === \"SVCB\" ||\n query.type === \"TYPE65\" ||\n query.type === \"TYPE64\";\n\n const echEnabled =\n typeof settings.best_effort_ech === \"boolean\"\n ? settings.best_effort_ech\n : Boolean(settings.best_effort_ech?.enabled);\n\n let answer = initialAnswer;\n let parsedAnswers = initialParsedAnswers;\n let effectiveReason = initialReason;\n\n if (isHttpsOrSvcb && echEnabled) {\n await ensureCloudflareIpRangesLoaded(context.env.DB);\n const existingIpv4s: string[] = [];\n const existingIpv6s: string[] = [];\n let existingEch: string | undefined;\n\n for (const a of parsedAnswers) {\n const v4Match = a.data.match(/ipv4hint=([^\\s]+)/);\n if (v4Match) existingIpv4s.push(...v4Match[1].split(\",\"));\n\n const v6Match = a.data.match(/ipv6hint=([^\\s]+)/);\n if (v6Match) existingIpv6s.push(...v6Match[1].split(\",\"));\n\n const echMatch = a.data.match(/ech=([A-Za-z0-9+/=]+)/);\n if (echMatch) {\n existingEch = echMatch[1];\n }\n }\n\n let isCf = existingIpv4s.some(isCloudflareIp) || existingIpv6s.some(isCloudflareIp);\n\n // If existing hints do not confirm Cloudflare proxying, probe A records\n if (!isCf) {\n try {\n const aQueryRaw = buildDNSQuery(query.name, \"A\");\n const aRes = await probeResolver(\n request,\n { name: query.name, type: \"A\", raw: aQueryRaw },\n context,\n settings,\n \"PASS\"\n );\n if (aRes.answer && aRes.answer.length > 0) {\n const aAnswers = parseDNSAnswer(aRes.answer);\n for (const ans of aAnswers) {\n if (ans.type === \"A\") {\n existingIpv4s.push(ans.data);\n if (isCloudflareIp(ans.data)) isCf = true;\n }\n }\n }\n } catch (e) {\n console.warn(\"Failed to probe A records for ECH check:\", e);\n }\n }\n\n if (isCf) {\n const frontingDomain =\n typeof settings.best_effort_ech === \"object\" && settings.best_effort_ech?.fronting_domain\n ? settings.best_effort_ech.fronting_domain\n : DEFAULT_ECH_FRONTING_DOMAIN;\n\n const echConfigBase64 = buildCloudflareEchConfig(frontingDomain, existingEch);\n const targetType =\n query.type === \"SVCB\" || query.type === \"TYPE64\" ? \"SVCB\" : \"HTTPS\";\n\n const params: string[] = [\"alpn=h3,h2\"];\n if (existingIpv4s.length > 0) {\n params.push(`ipv4hint=${Array.from(new Set(existingIpv4s)).join(\",\")}`);\n }\n params.push(`ech=${echConfigBase64}`);\n if (existingIpv6s.length > 0) {\n params.push(`ipv6hint=${Array.from(new Set(existingIpv6s)).join(\",\")}`);\n }\n\n const rdataValue = `1 . ${params.join(\" \")}`;\n answer = buildResponse(query.raw, targetType, rdataValue, 300, 0);\n parsedAnswers = parseDNSAnswer(answer);\n effectiveReason = \"ECH Rewritten\";\n }\n }\n\n return { answer, parsedAnswers, effectiveReason };\n}\n", "import { D1Database, D1PreparedStatement } from \"@cloudflare/workers-types\";\nimport { ResolutionLog } from \"../../types\";\n\nlet logSeqCounter = 0;\n\n/**\n * Generates a unique, monotonically increasing 64-bit safe integer ID for log records.\n * Fits within JavaScript Number.MAX_SAFE_INTEGER (9,007,199,254,740,991) and is valid until year 2255.\n *\n * Combines milliseconds timestamp with local sequence counter to guarantee uniqueness\n * across micro-batches and concurrent resolutions.\n *\n * @returns Monotonically increasing 64-bit safe integer ID.\n */\nexport function generateLogId(): number {\n const seq = (logSeqCounter++) % 1000;\n return Date.now() * 1000 + seq;\n}\n\n/**\n * Filter and pagination options for retrieving raw resolution logs.\n */\nexport interface GetLogsOptions {\n since: number;\n until: number;\n status?: string;\n search?: string;\n before?: number;\n limit?: number;\n access_point_id?: string;\n dest_country?: string;\n isp?: string;\n export?: boolean;\n domain?: string;\n geo_country?: string;\n reason?: string;\n record_type?: string;\n}\n\n/**\n * Core model handling raw resolution log persistence, point lookups,\n * filtered pagination, and profile-scoped deletions.\n */\nexport class LogCoreModel {\n constructor(private readonly db: D1Database) {}\n\n /**\n * Generates a prepared D1 INSERT statement for a single resolution log entry.\n *\n * @param log - Resolution log entry to insert.\n * @returns D1PreparedStatement ready for single or batch execution.\n */\n createInsertStatement(log: ResolutionLog): D1PreparedStatement {\n const logId = log.id ?? generateLogId();\n return this.db.prepare(\n \"INSERT INTO logs (profile_id, timestamp, id, access_point_id, client_ip, geo_country, domain, record_type, action, reason, answer, dest_geoip, ecs, upstream, latency, dest_country_code, dest_country, dest_isp, encrypt_version, kem_key_id, encrypted_payload) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\"\n ).bind(\n log.profile_id,\n log.timestamp,\n logId,\n log.access_point_id || null,\n log.client_ip,\n log.geo_country || null,\n log.domain,\n log.record_type,\n log.action,\n log.reason || null,\n log.answer || null,\n log.dest_geoip || null,\n log.ecs || null,\n log.upstream || null,\n log.latency || null,\n log.dest_country_code || null,\n log.dest_country || null,\n log.dest_isp || null,\n log.encrypt_version ?? 0,\n log.kem_key_id || null,\n log.encrypted_payload || null\n );\n }\n\n /**\n * Inserts a single resolution log synchronously into D1.\n *\n * @param log - Resolution log to insert.\n * @returns Promise resolving to true if inserted successfully.\n */\n async insert(log: ResolutionLog): Promise<boolean> {\n const result = await this.createInsertStatement(log).run();\n return result.success;\n }\n\n /**\n * Retrieves a paginated list of resolution logs based on provided query filters.\n *\n * @param profileId - Profile identifier.\n * @param options - Filtering and pagination options.\n * @returns Array of resolution log records.\n */\n async getLogs(profileId: string, options: GetLogsOptions): Promise<ResolutionLog[]> {\n let baseSelect = \"\";\n if (options.export) {\n baseSelect = `\n SELECT l.profile_id, l.access_point_id, l.timestamp, l.client_ip, l.geo_country, l.domain, l.record_type, l.action, l.reason, l.answer, l.dest_geoip, l.ecs, l.upstream, l.latency, l.dest_country_code, l.dest_country, l.dest_isp, l.encrypt_version, l.kem_key_id, l.encrypted_payload, k.kem_ct\n FROM logs l\n LEFT JOIN kem_keys k ON l.kem_key_id = k.id\n `;\n } else {\n baseSelect = `\n SELECT l.id, l.timestamp, l.client_ip, l.domain, l.action, l.record_type, l.latency, l.answer, l.geo_country, l.reason, l.access_point_id, l.dest_country_code, l.dest_country, l.dest_isp, l.encrypt_version, l.kem_key_id, l.encrypted_payload, k.kem_ct, ap.name as access_point_name \n FROM logs l\n LEFT JOIN access_points ap ON l.access_point_id = ap.id\n LEFT JOIN kem_keys k ON l.kem_key_id = k.id\n `;\n }\n\n const whereClauses: string[] = [\"l.profile_id = ?\"];\n const params: (string | number)[] = [profileId];\n\n // Place equality filters first so SQLite query planner matches composite indexes prefix\n if (options.access_point_id) {\n whereClauses.push(\"l.access_point_id = ?\");\n params.push(options.access_point_id);\n }\n if (options.status) {\n whereClauses.push(\"l.action = ?\");\n params.push(options.status);\n }\n if (options.geo_country) {\n whereClauses.push(\"l.geo_country = ?\");\n params.push(options.geo_country.toUpperCase());\n }\n if (options.reason) {\n whereClauses.push(\"l.reason = ?\");\n params.push(options.reason);\n }\n if (options.domain) {\n whereClauses.push(\"l.domain = ?\");\n params.push(options.domain);\n }\n if (options.record_type) {\n whereClauses.push(\"l.record_type = ?\");\n params.push(options.record_type);\n }\n if (options.search) {\n whereClauses.push(\"l.domain LIKE ?\");\n params.push(`%${options.search}%`);\n }\n if (options.before) {\n whereClauses.push(\"l.timestamp < ?\");\n params.push(options.before);\n }\n\n whereClauses.push(\"l.timestamp >= ?\");\n params.push(options.since);\n whereClauses.push(\"l.timestamp <= ?\");\n params.push(options.until);\n\n if (options.dest_country) {\n whereClauses.push(\"COALESCE(l.dest_country_code, json_extract(l.dest_geoip, '$.country_code')) = ?\");\n params.push(options.dest_country.toUpperCase());\n }\n if (options.isp) {\n whereClauses.push(\"COALESCE(l.dest_isp, json_extract(l.dest_geoip, '$.isp')) = ?\");\n params.push(options.isp);\n }\n\n let queryStr = baseSelect + \" WHERE \" + whereClauses.join(\" AND \");\n\n if (options.export) {\n queryStr += \" ORDER BY l.timestamp DESC LIMIT 5000\";\n } else {\n let limit = options.limit !== undefined && !isNaN(options.limit) && options.limit > 0 ? options.limit : 50;\n if (limit > 100) {\n limit = 100;\n }\n queryStr += ` ORDER BY l.timestamp DESC LIMIT ${limit}`;\n }\n\n const { results } = await this.db.prepare(queryStr).bind(...params).all<ResolutionLog>();\n return results;\n }\n\n /**\n * Retrieves a single resolution log by ID and optional timestamp for fast indexed lookup.\n *\n * @param profileId - Profile identifier.\n * @param logId - Unique log integer ID.\n * @param timestamp - Optional timestamp to hit primary key index prefix.\n * @returns Detailed log entry or null if not found.\n */\n async getLog(profileId: string, logId: number, timestamp?: number): Promise<ResolutionLog | null> {\n if (timestamp !== undefined && !isNaN(timestamp)) {\n return await this.db.prepare(`\n SELECT l.*, p.name as profile_name, ap.name as access_point_name, k.kem_ct\n FROM logs l \n JOIN profiles p ON l.profile_id = p.id \n LEFT JOIN access_points ap ON l.access_point_id = ap.id\n LEFT JOIN kem_keys k ON l.kem_key_id = k.id\n WHERE l.profile_id = ? AND l.timestamp = ? AND l.id = ?\n `)\n .bind(profileId, timestamp, logId)\n .first<ResolutionLog | null>();\n }\n\n return await this.db.prepare(`\n SELECT l.*, p.name as profile_name, ap.name as access_point_name, k.kem_ct\n FROM logs l \n JOIN profiles p ON l.profile_id = p.id \n LEFT JOIN access_points ap ON l.access_point_id = ap.id\n LEFT JOIN kem_keys k ON l.kem_key_id = k.id\n WHERE l.profile_id = ? AND l.id = ?\n LIMIT 1\n `)\n .bind(profileId, logId)\n .first<ResolutionLog | null>();\n }\n\n /**\n * Deletes all logs belonging to all profiles of an owner.\n *\n * @param ownerId - User identifier.\n * @returns Promise resolving to true if delete succeeded.\n */\n async deleteByOwner(ownerId: string): Promise<boolean> {\n const result = await this.db\n .prepare(\"DELETE FROM logs WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)\")\n .bind(ownerId)\n .run();\n return result.success;\n }\n\n /**\n * Manually cleans up logs older than a given timestamp for a specific profile in bounded batches.\n *\n * @param profileId - Profile identifier.\n * @param olderThanTimestamp - Unix epoch timestamp threshold in seconds.\n * @param maxRows - Maximum number of raw log rows to delete (default 1000).\n * @returns Total number of rows deleted.\n */\n async cleanup(profileId: string, olderThanTimestamp: number, maxRows = 1000): Promise<number> {\n let totalDeleted = 0;\n const batchSize = Math.min(1000, maxRows);\n while (totalDeleted < maxRows) {\n const currentBatch = Math.min(batchSize, maxRows - totalDeleted);\n const result = await this.db.prepare(`\n DELETE FROM logs WHERE (profile_id, timestamp, id) IN (\n SELECT profile_id, timestamp, id FROM logs WHERE profile_id = ? AND timestamp < ? LIMIT ?\n )\n `)\n .bind(profileId, olderThanTimestamp, currentBatch)\n .run();\n const count = result.meta.changes || 0;\n totalDeleted += count;\n if (count < currentBatch) {\n break;\n }\n }\n return totalDeleted;\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\n\n/**\n * @deprecated Hourly rollups have been superseded by client-side Local-First analytics (SQLite WASM)\n * and direct clustered-index queries on raw logs for server-side fallback.\n *\n * This class is maintained for facade backward compatibility.\n */\nexport class LogAggregationModel {\n constructor(private readonly db: D1Database) {}\n\n /**\n * @deprecated Rollup tables are dropped. Always returns null.\n */\n async getLatestRollupHour(_profileId: string): Promise<number | null> {\n return null;\n }\n\n /**\n * @deprecated Rollup tables are dropped. Always returns null.\n */\n async getLatestClientRollupHour(_profileId: string): Promise<number | null> {\n return null;\n }\n\n /**\n * @deprecated Rollup tables are dropped. Always returns null.\n */\n async getLatestDestinationRollupHour(_profileId: string): Promise<number | null> {\n return null;\n }\n\n /**\n * @deprecated Rollup tables are dropped. Background aggregation is no longer needed.\n * Always returns 0.\n */\n async aggregateHourlyRollups(\n _sinceSec?: number,\n _untilSec?: number,\n _minDomainCount = 2\n ): Promise<number> {\n return 0;\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { KemKeyRecord } from \"../types\";\n\n/**\n * Model responsible for lifecycle management and orphan cleanup of kem_keys.\n *\n * kem_keys store recipient-encapsulated post-quantum hybrid ciphertext (kem_ct)\n * required by the client to derive the DEK and decrypt query logs.\n *\n * CLEANUP RULE:\n * A kem_key must NEVER be deleted based solely on its expiration time (expires_at),\n * because doing so would make historical logs referencing this key permanently undecryptable.\n * Instead, a kem_key is eligible for automatic cleanup ONLY when no logs in the logs table\n * reference it (zero dependents), with a safety grace period to protect active in-memory\n * keys currently being rotated by running Worker isolates.\n */\nexport class KemKeyModel {\n constructor(private readonly db: D1Database) {}\n\n /**\n * Retrieves a KEM key record by ID.\n *\n * @param id The kem_key unique identifier.\n * @returns KemKeyRecord or null if not found.\n */\n async getById(id: string): Promise<KemKeyRecord | null> {\n try {\n return await this.db\n .prepare(\"SELECT id, profile_id, kem_ct, created_at, expires_at FROM kem_keys WHERE id = ?\")\n .bind(id)\n .first<KemKeyRecord | null>();\n } catch (err) {\n console.error(`[KemKeyModel] Failed to fetch kem_key ${id}:`, err);\n return null;\n }\n }\n\n /**\n * Checks whether a specific kem_key is referenced by any logs.\n *\n * @param id The kem_key unique identifier.\n * @returns True if at least one log references this key.\n */\n async hasDependents(id: string): Promise<boolean> {\n try {\n const row = await this.db\n .prepare(\"SELECT 1 FROM logs WHERE kem_key_id = ? LIMIT 1\")\n .bind(id)\n .first<{ 1: number } | null>();\n return row !== null;\n } catch (err) {\n console.error(`[KemKeyModel] Failed to check dependents for kem_key ${id}:`, err);\n return true; // Fail safe: assume it has dependents on error to prevent accidental deletion\n }\n }\n\n /**\n * Automatically purges orphaned kem_keys that have ZERO dependent rows in logs.\n *\n * Criterion: NOT EXISTS (SELECT 1 FROM logs WHERE logs.kem_key_id = kem_keys.id)\n * Notice: Time (expires_at) is NOT the deletion criterion. Even very old keys\n * are kept as long as logs reference them. A safety window (default 3600s = 1h)\n * protects newly minted keys in active isolate memory from being deleted before\n * their first log batch lands in D1.\n *\n * @param safetyWindowSec Minimum age in seconds before an unreferenced key can be purged (default 3600).\n * @param batchLimit Maximum rows to delete in a single batch (default 1000).\n * @returns Total number of orphaned keys deleted.\n */\n async cleanupOrphans(safetyWindowSec = 3600, batchLimit = 1000): Promise<number> {\n const cutoffTimestamp = Math.floor(Date.now() / 1000) - safetyWindowSec;\n try {\n const result = await this.db\n .prepare(`\n DELETE FROM kem_keys\n WHERE id IN (\n SELECT k.id FROM kem_keys k\n WHERE k.created_at <= ?\n AND NOT EXISTS (\n SELECT 1 FROM logs l WHERE l.kem_key_id = k.id\n )\n LIMIT ?\n )\n `)\n .bind(cutoffTimestamp, batchLimit)\n .run();\n\n const deletedCount = result.meta?.changes ?? result.meta?.rows_written ?? 0;\n return deletedCount;\n } catch (err) {\n console.error(\"[KemKeyModel] Failed to cleanup orphaned kem_keys:\", err);\n return 0;\n }\n }\n\n /**\n * Automatically purges orphaned kem_keys specifically belonging to a single profile.\n * Useful when a profile's logs have been explicitly cleared or disabled.\n *\n * @param profileId Profile identifier.\n * @param safetyWindowSec Minimum age in seconds before an unreferenced key can be purged (default 3600).\n * @returns Total number of orphaned keys deleted for this profile.\n */\n async cleanupProfileOrphans(profileId: string, safetyWindowSec = 3600): Promise<number> {\n const cutoffTimestamp = Math.floor(Date.now() / 1000) - safetyWindowSec;\n try {\n const result = await this.db\n .prepare(`\n DELETE FROM kem_keys\n WHERE profile_id = ?\n AND created_at <= ?\n AND NOT EXISTS (\n SELECT 1 FROM logs l WHERE l.kem_key_id = kem_keys.id\n )\n `)\n .bind(profileId, cutoffTimestamp)\n .run();\n\n const deletedCount = result.meta?.changes ?? result.meta?.rows_written ?? 0;\n return deletedCount;\n } catch (err) {\n console.error(`[KemKeyModel] Failed to cleanup profile orphan kem_keys for ${profileId}:`, err);\n return 0;\n }\n }\n}\n", "import { D1Database, D1PreparedStatement } from \"@cloudflare/workers-types\";\nimport { SystemSettingsModel } from \"../systemSettings\";\nimport { KemKeyModel } from \"../kemKey\";\n\n/**\n * Model responsible for enforcing log retention policies and purging\n * expired raw resolution logs and rollup records.\n */\nexport class LogRetentionModel {\n private readonly settingsModel: SystemSettingsModel;\n\n constructor(\n private readonly db: D1Database,\n settingsModel?: SystemSettingsModel\n ) {\n this.settingsModel = settingsModel ?? new SystemSettingsModel(db);\n }\n\n /**\n * Global log cleanup: runs on every cron trigger.\n *\n * Applies three independent safety caps:\n * 1. Time-based: deletes logs older than min(user_setting, MAX_LOG_RETENTION_DAYS).\n * The global cap prevents users from setting arbitrarily long retention periods\n * (e.g. 360 days) that would cause D1 to overflow.\n * 2. Batch limiting: deletes bounded rows per profile per run based on batchLimit\n * to prevent write spikes.\n * 3. Daily quota budget: tracks deleted rows today via SystemSettingsModel,\n * capping total deletions at dailyBudget per UTC day to prevent exhausting\n * Cloudflare D1 daily write quota (100k writes/day).\n *\n * @param maxRetentionDays - Hard cap on log retention days (default 30).\n * @param batchLimit - Maximum total log rows deleted across all profiles in this run (default 1000).\n * @param dailyBudget - Maximum total log rows deleted across all profiles per day (default 20000).\n */\n async cleanupGlobal(\n maxRetentionDays = 30,\n batchLimit = 1000,\n dailyBudget = 20000\n ): Promise<void> {\n try {\n const { results: profiles } = await this.db.prepare(\n \"SELECT id, settings FROM profiles\"\n ).all<{ id: string; settings: string }>();\n\n if (!profiles || profiles.length === 0) {\n return;\n }\n\n // \u2500\u2500 Daily Cleanup Budget Check \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const todayDate = new Date().toISOString().slice(0, 10);\n let deletedToday = 0;\n\n if (dailyBudget > 0) {\n try {\n const savedDate = await this.settingsModel.get(\"log_cleanup_daily_date\");\n const savedCount = await this.settingsModel.get(\"log_cleanup_daily_count\");\n if (savedDate === todayDate && savedCount) {\n deletedToday = parseInt(savedCount, 10) || 0;\n }\n } catch (err) {\n console.warn(\"[LogRetentionModel] Failed to read daily cleanup settings:\", err);\n }\n }\n\n const remainingBudget = dailyBudget > 0 ? Math.max(0, dailyBudget - deletedToday) : batchLimit;\n const canDeleteLogs = dailyBudget <= 0 || remainingBudget > 0;\n\n if (dailyBudget > 0 && remainingBudget <= 0) {\n console.log(\n `[LogRetentionModel] cleanupGlobal: daily budget reached (${deletedToday}/${dailyBudget}), skipping raw log deletion for today (${todayDate})`\n );\n }\n\n // Distribute batchLimit evenly across profiles, bounded by remaining daily budget\n const effectiveBatchLimit = dailyBudget > 0 ? Math.min(batchLimit, remainingBudget) : batchLimit;\n const perProfileLimit = Math.max(1, Math.floor(effectiveBatchLimit / profiles.length));\n\n const logDeleteStatements: D1PreparedStatement[] = [];\n\n for (const profile of profiles) {\n // \u2500\u2500 1. Time-based retention calculation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let days = 30;\n try {\n const settings = JSON.parse(profile.settings);\n if (settings?.log_retention_days != null) {\n days = Number(settings.log_retention_days);\n }\n } catch {\n // Use default on parse error\n }\n\n // Enforce global hard cap: user setting cannot exceed maxRetentionDays\n const effectiveDays = Math.min(days, maxRetentionDays);\n const threshold = Math.floor(Date.now() / 1000 - effectiveDays * 24 * 3600);\n\n // Delete bounded number of rows per profile per hourly cron run if budget remains\n if (canDeleteLogs && effectiveBatchLimit > 0) {\n logDeleteStatements.push(\n this.db.prepare(`\n DELETE FROM logs WHERE (profile_id, timestamp, id) IN (\n SELECT profile_id, timestamp, id FROM logs WHERE profile_id = ? AND timestamp < ? LIMIT ?\n )\n `).bind(profile.id, threshold, perProfileLimit)\n );\n }\n }\n\n // Execute log deletions in chunks and track affected rows\n const CHUNK_SIZE = 50;\n let newlyDeletedLogs = 0;\n if (logDeleteStatements.length > 0) {\n for (let i = 0; i < logDeleteStatements.length; i += CHUNK_SIZE) {\n const chunk = logDeleteStatements.slice(i, i + CHUNK_SIZE);\n const results = await this.db.batch(chunk);\n if (Array.isArray(results)) {\n for (const res of results) {\n if (res?.meta?.changes !== undefined) {\n newlyDeletedLogs += res.meta.changes;\n } else if (res?.meta?.rows_written !== undefined) {\n newlyDeletedLogs += res.meta.rows_written;\n }\n }\n }\n }\n\n // Persist daily cleanup counter to system_settings\n if (dailyBudget > 0) {\n const updatedTotal = deletedToday + newlyDeletedLogs;\n try {\n await this.settingsModel.setMany({\n log_cleanup_daily_date: todayDate,\n log_cleanup_daily_count: String(updatedTotal)\n });\n } catch (err) {\n console.warn(\"[LogRetentionModel] Failed to persist daily cleanup count:\", err);\n }\n }\n }\n\n // \u2500\u2500 Orphan kem_keys cleanup (Zero Dependents) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Automatically purges kem_keys that have zero referencing logs in D1,\n // strictly ensuring keys with active dependent logs are preserved regardless of age.\n try {\n const kemKeyModel = new KemKeyModel(this.db);\n const deletedOrphanKeys = await kemKeyModel.cleanupOrphans();\n if (deletedOrphanKeys > 0) {\n console.log(\n `[LogRetentionModel] Cleaned up ${deletedOrphanKeys} orphaned kem_keys with zero referencing logs.`\n );\n }\n } catch (kemErr) {\n console.warn(\"[LogRetentionModel] Failed to cleanup orphaned kem_keys:\", kemErr);\n }\n\n console.log(\n `[LogRetentionModel] cleanupGlobal: processed ${profiles.length} profile(s), maxRetentionDays=${maxRetentionDays}d, batchLimit=${batchLimit} (effective=${effectiveBatchLimit}, perProfile=${perProfileLimit}), dailyBudget=${dailyBudget} (today: ${deletedToday + newlyDeletedLogs}/${dailyBudget})`\n );\n } catch (e: any) {\n console.error(\"[LogRetentionModel] cleanupGlobal failed:\", e.message || e);\n }\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { ActionCountResult, TimeSeriesTrendPoint } from \"./types\";\n\n/**\n * Handles action volume summaries and time-series trend analytics.\n * Queries raw resolution logs directly using the (profile_id, timestamp) clustered index\n * as a server-side fallback for client-side Local-First SQLite analytics.\n */\nexport class LogTrafficAnalytics {\n constructor(private readonly db: D1Database) {}\n\n /**\n * Retrieves action counts (PASS, BLOCK, REDIRECT, FAIL) for a given time range.\n *\n * @param profileId - Profile identifier.\n * @param since - Start timestamp in seconds.\n * @param until - End timestamp in seconds.\n * @param search - Optional domain search substring.\n * @param accessPointId - Optional device/access point ID.\n * @returns Array of action counts.\n */\n async getSummary(\n profileId: string,\n since: number,\n until: number,\n search?: string,\n accessPointId?: string\n ): Promise<ActionCountResult[]> {\n let queryStr =\n \"SELECT action, COUNT(*) as count FROM logs WHERE profile_id = ? AND timestamp >= ? AND timestamp <= ?\";\n const params: (string | number)[] = [profileId, since, until];\n if (search) {\n queryStr += \" AND domain LIKE ?\";\n params.push(`%${search}%`);\n }\n if (accessPointId) {\n queryStr += \" AND access_point_id = ?\";\n params.push(accessPointId);\n }\n queryStr += \" GROUP BY action\";\n const { results } = await this.db\n .prepare(queryStr)\n .bind(...params)\n .all<ActionCountResult>();\n return results;\n }\n\n /**\n * Retrieves timeseries trend data aggregated by interval.\n *\n * @param profileId - Profile identifier.\n * @param since - Start timestamp in seconds.\n * @param until - End timestamp in seconds.\n * @param interval - SQL group by expression (e.g. `(timestamp/3600)*3600` or `(timestamp/86400)*86400`).\n * @param accessPointId - Optional device filter.\n * @returns Array of timeseries points ordered by timestamp ascending.\n */\n async getTrend(\n profileId: string,\n since: number,\n until: number,\n interval: string,\n accessPointId?: string\n ): Promise<TimeSeriesTrendPoint[]> {\n // Defensively sanitize SQL interval expression against an explicit allowlist\n const SAFE_INTERVALS = new Set([\n \"(timestamp/60)*60\",\n \"(timestamp/300)*300\",\n \"(timestamp/900)*900\",\n \"(timestamp/3600)*3600\",\n \"(timestamp/86400)*86400\"\n ]);\n const safeInterval = SAFE_INTERVALS.has(interval.trim()) ? interval.trim() : \"(timestamp/3600)*3600\";\n\n let queryStr = `SELECT ${safeInterval} as timestamp, action, COUNT(*) as count FROM logs WHERE profile_id = ? AND timestamp >= ? AND timestamp <= ?`;\n const params: (string | number)[] = [profileId, since, until];\n if (accessPointId) {\n queryStr += \" AND access_point_id = ?\";\n params.push(accessPointId);\n }\n queryStr += ` GROUP BY ${safeInterval}, action ORDER BY timestamp ASC`;\n const { results } = await this.db\n .prepare(queryStr)\n .bind(...params)\n .all<TimeSeriesTrendPoint>();\n return results;\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { DomainCountResult } from \"./types\";\n\n/**\n * Handles top allowed and top blocked domain analytics.\n * Queries raw resolution logs directly using the (profile_id, timestamp) clustered index\n * as a server-side fallback for client-side Local-First SQLite analytics.\n */\nexport class LogDomainAnalytics {\n constructor(private readonly db: D1Database) {}\n\n /**\n * Retrieves top allowed domains for a given time range.\n *\n * @param profileId - Profile identifier.\n * @param since - Start timestamp in seconds.\n * @param until - End timestamp in seconds.\n * @param accessPointId - Optional device filter.\n * @returns Array of top allowed domain records ordered by count descending.\n */\n async getTopAllowed(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string\n ): Promise<DomainCountResult[]> {\n let queryStr =\n \"SELECT domain, COUNT(*) as count FROM logs WHERE profile_id = ? AND timestamp >= ? AND timestamp <= ? AND action = 'PASS'\";\n const params: (string | number)[] = [profileId, since, until];\n if (accessPointId) {\n queryStr += \" AND access_point_id = ?\";\n params.push(accessPointId);\n }\n queryStr += \" GROUP BY domain ORDER BY count DESC LIMIT 10\";\n const { results } = await this.db\n .prepare(queryStr)\n .bind(...params)\n .all<DomainCountResult>();\n return results;\n }\n\n /**\n * Retrieves top blocked/redirected domains for a given time range.\n *\n * @param profileId - Profile identifier.\n * @param since - Start timestamp in seconds.\n * @param until - End timestamp in seconds.\n * @param accessPointId - Optional device filter.\n * @returns Array of top blocked domain records ordered by count descending.\n */\n async getTopBlocked(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string\n ): Promise<DomainCountResult[]> {\n let queryStr =\n \"SELECT domain, COUNT(*) as count FROM logs WHERE profile_id = ? AND timestamp >= ? AND timestamp <= ? AND action IN ('BLOCK', 'REDIRECT')\";\n const params: (string | number)[] = [profileId, since, until];\n if (accessPointId) {\n queryStr += \" AND access_point_id = ?\";\n params.push(accessPointId);\n }\n queryStr += \" GROUP BY domain ORDER BY count DESC LIMIT 10\";\n const { results } = await this.db\n .prepare(queryStr)\n .bind(...params)\n .all<DomainCountResult>();\n return results;\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { ClientCountResult } from \"./types\";\n\n/**\n * Handles client device and IP-level analytics.\n * Queries raw resolution logs directly using the (profile_id, timestamp) clustered index\n * as a server-side fallback for client-side Local-First SQLite analytics.\n */\nexport class LogClientAnalytics {\n constructor(private readonly db: D1Database) {}\n\n /**\n * Retrieves top client IPs and locations for a given time range.\n *\n * @param profileId - Profile identifier.\n * @param since - Start timestamp in seconds.\n * @param until - End timestamp in seconds.\n * @param accessPointId - Optional device/access point ID.\n * @returns Array of top client records ordered by query count descending.\n */\n async getClients(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string\n ): Promise<ClientCountResult[]> {\n let queryStr =\n \"SELECT client_ip, geo_country, COUNT(*) as count FROM logs WHERE profile_id = ? AND timestamp >= ? AND timestamp <= ?\";\n const params: (string | number)[] = [profileId, since, until];\n if (accessPointId) {\n queryStr += \" AND access_point_id = ?\";\n params.push(accessPointId);\n }\n queryStr += \" GROUP BY client_ip, geo_country ORDER BY count DESC LIMIT 20\";\n const { results } = await this.db\n .prepare(queryStr)\n .bind(...params)\n .all<ClientCountResult>();\n return results;\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { DestinationCountResult, ISPCountResult } from \"./types\";\n\n/**\n * Handles geographic destination country and ISP distribution analytics.\n * Queries raw resolution logs directly using the (profile_id, timestamp) clustered index\n * as a server-side fallback for client-side Local-First SQLite analytics.\n */\nexport class LogDestinationAnalytics {\n constructor(private readonly db: D1Database) {}\n\n /**\n * Retrieves top destination countries for a given time range.\n *\n * @param profileId - Profile identifier.\n * @param since - Start timestamp in seconds.\n * @param until - End timestamp in seconds.\n * @param accessPointId - Optional device/access point ID.\n * @param limit - Maximum number of destinations to return (default 250).\n * @returns Array of destination country records ordered by query count descending.\n */\n async getDestinations(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string,\n limit = 250\n ): Promise<DestinationCountResult[]> {\n let queryStr = `\n SELECT \n COALESCE(dest_country_code, json_extract(dest_geoip, '$.country_code')) as country_code,\n COALESCE(dest_country, json_extract(dest_geoip, '$.country')) as country,\n COUNT(*) as count\n FROM logs \n WHERE profile_id = ? AND timestamp >= ? AND timestamp <= ? AND (dest_country_code IS NOT NULL OR dest_geoip IS NOT NULL)\n `;\n const params: (string | number)[] = [profileId, since, until];\n if (accessPointId) {\n queryStr += \" AND access_point_id = ?\";\n params.push(accessPointId);\n }\n queryStr += ` GROUP BY country_code ORDER BY count DESC LIMIT ${limit}`;\n const { results } = await this.db\n .prepare(queryStr)\n .bind(...params)\n .all<DestinationCountResult>();\n return results;\n }\n\n /**\n * Retrieves ISP distribution for queries matching an optional country code.\n *\n * @param profileId - Profile identifier.\n * @param countryCode - Optional ISO 2-letter country code.\n * @param since - Start timestamp in seconds.\n * @param until - End timestamp in seconds.\n * @param accessPointId - Optional device filter.\n * @param limit - Maximum number of ISPs to return (default 250).\n * @returns Array of ISP count records.\n */\n async getISPByCountry(\n profileId: string,\n countryCode: string | undefined,\n since: number,\n until: number,\n accessPointId?: string,\n limit = 250\n ): Promise<ISPCountResult[]> {\n let queryStr = `\n SELECT \n COALESCE(dest_isp, json_extract(dest_geoip, '$.isp')) as name, \n COUNT(*) as count \n FROM logs \n WHERE profile_id = ? \n AND timestamp >= ? \n AND timestamp <= ? \n AND (dest_country_code IS NOT NULL OR dest_geoip IS NOT NULL)\n AND COALESCE(dest_isp, json_extract(dest_geoip, '$.isp')) IS NOT NULL\n AND COALESCE(dest_isp, json_extract(dest_geoip, '$.isp')) != ''\n `;\n const params: (string | number)[] = [profileId, since, until];\n if (countryCode) {\n queryStr += \" AND UPPER(COALESCE(dest_country_code, json_extract(dest_geoip, '$.country_code'))) = ?\";\n params.push(countryCode.toUpperCase());\n }\n if (accessPointId) {\n queryStr += \" AND access_point_id = ?\";\n params.push(accessPointId);\n }\n queryStr += ` GROUP BY name ORDER BY count DESC LIMIT ${limit}`;\n const { results } = await this.db\n .prepare(queryStr)\n .bind(...params)\n .all<{ name: string | null; count: number }>();\n\n return results.map((r) => ({\n name: r.name || \"Unknown\",\n count: r.count\n }));\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { LogTrafficAnalytics } from \"./traffic\";\nimport { LogDomainAnalytics } from \"./domains\";\nimport { LogClientAnalytics } from \"./clients\";\nimport { LogDestinationAnalytics } from \"./destinations\";\nimport {\n ActionCountResult,\n TimeSeriesTrendPoint,\n DomainCountResult,\n ClientCountResult,\n DestinationCountResult,\n ISPCountResult,\n DashboardAnalyticsResult\n} from \"./types\";\n\nexport * from \"./types\";\nexport * from \"./traffic\";\nexport * from \"./domains\";\nexport * from \"./clients\";\nexport * from \"./destinations\";\n\n/**\n * Unified analytics model facade coordinating specialized sub-analytics:\n * - traffic (action summaries and time-series trends)\n * - domains (top allowed and top blocked/redirected domains)\n * - clients (top client IPs and geo-locations)\n * - destinations (destination countries and ISP distributions)\n */\nexport class LogAnalyticsModel {\n public readonly traffic: LogTrafficAnalytics;\n public readonly domains: LogDomainAnalytics;\n public readonly clients: LogClientAnalytics;\n public readonly destinations: LogDestinationAnalytics;\n\n constructor(\n private readonly db: D1Database,\n _aggregation?: unknown\n ) {\n this.traffic = new LogTrafficAnalytics(db);\n this.domains = new LogDomainAnalytics(db);\n this.clients = new LogClientAnalytics(db);\n this.destinations = new LogDestinationAnalytics(db);\n }\n\n /**\n * Retrieves action counts (PASS, BLOCK, REDIRECT, FAIL) for a given time range.\n */\n async getSummary(\n profileId: string,\n since: number,\n until: number,\n search?: string,\n accessPointId?: string\n ): Promise<ActionCountResult[]> {\n return this.traffic.getSummary(profileId, since, until, search, accessPointId);\n }\n\n /**\n * Retrieves timeseries trend data aggregated by interval.\n */\n async getTrend(\n profileId: string,\n since: number,\n until: number,\n interval: string,\n accessPointId?: string\n ): Promise<TimeSeriesTrendPoint[]> {\n return this.traffic.getTrend(profileId, since, until, interval, accessPointId);\n }\n\n /**\n * Retrieves top allowed domains for a given time range.\n */\n async getTopAllowed(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string\n ): Promise<DomainCountResult[]> {\n return this.domains.getTopAllowed(profileId, since, until, accessPointId);\n }\n\n /**\n * Retrieves top blocked/redirected domains for a given time range.\n */\n async getTopBlocked(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string\n ): Promise<DomainCountResult[]> {\n return this.domains.getTopBlocked(profileId, since, until, accessPointId);\n }\n\n /**\n * Retrieves top client IPs and locations for a given time range.\n */\n async getClients(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string\n ): Promise<ClientCountResult[]> {\n return this.clients.getClients(profileId, since, until, accessPointId);\n }\n\n /**\n * Retrieves top destination countries for a given time range.\n */\n async getDestinations(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string,\n limit = 250\n ): Promise<DestinationCountResult[]> {\n return this.destinations.getDestinations(profileId, since, until, accessPointId, limit);\n }\n\n /**\n * Retrieves ISP distribution for queries matching an optional country code.\n */\n async getISPByCountry(\n profileId: string,\n countryCode: string | undefined,\n since: number,\n until: number,\n accessPointId?: string,\n limit = 250\n ): Promise<ISPCountResult[]> {\n return this.destinations.getISPByCountry(\n profileId,\n countryCode,\n since,\n until,\n accessPointId,\n limit\n );\n }\n\n /**\n * Concurrently aggregates all primary analytics components for the dashboard.\n */\n async getAnalytics(\n profileId: string,\n since: number,\n until: number,\n interval: string,\n accessPointId?: string\n ): Promise<DashboardAnalyticsResult> {\n const [summary, trend, topAllowed, topBlocked, clients, destinations] =\n await Promise.all([\n this.getSummary(profileId, since, until, undefined, accessPointId),\n this.getTrend(profileId, since, until, interval, accessPointId),\n this.getTopAllowed(profileId, since, until, accessPointId),\n this.getTopBlocked(profileId, since, until, accessPointId),\n this.getClients(profileId, since, until, accessPointId),\n this.getDestinations(profileId, since, until, accessPointId)\n ]);\n return {\n summary,\n trend,\n top_allowed: topAllowed,\n top_blocked: topBlocked,\n clients,\n destinations\n };\n }\n}\n", "import { D1Database, D1PreparedStatement } from \"@cloudflare/workers-types\";\nimport { ResolutionLog } from \"../../types\";\nimport {\n LogCoreModel,\n GetLogsOptions\n} from \"./core\";\nimport { LogAggregationModel } from \"./aggregation\";\nimport { LogRetentionModel } from \"./retention\";\nimport { KemKeyModel } from \"../kemKey\";\nimport {\n LogAnalyticsModel,\n ActionCountResult,\n TimeSeriesTrendPoint,\n DomainCountResult,\n ClientCountResult,\n DestinationCountResult,\n ISPCountResult,\n DashboardAnalyticsResult\n} from \"./analytics\";\n\n// Re-export sub-models, types, and utility functions\nexport * from \"./core\";\nexport * from \"./aggregation\";\nexport * from \"./retention\";\nexport * from \"./analytics\";\nexport * from \"../kemKey\";\n\n/**\n * Unified LogModel Facade providing backward-compatible access to\n * all log-related subsystems (core CRUD, aggregation, retention, analytics, kemKeys).\n */\nexport class LogModel {\n public readonly core: LogCoreModel;\n public readonly aggregation: LogAggregationModel;\n public readonly retention: LogRetentionModel;\n public readonly analytics: LogAnalyticsModel;\n public readonly kemKeys: KemKeyModel;\n\n constructor(private readonly db: D1Database) {\n this.core = new LogCoreModel(db);\n this.aggregation = new LogAggregationModel(db);\n this.retention = new LogRetentionModel(db);\n this.analytics = new LogAnalyticsModel(db, this.aggregation);\n this.kemKeys = new KemKeyModel(db);\n }\n\n createInsertStatement(log: ResolutionLog): D1PreparedStatement {\n return this.core.createInsertStatement(log);\n }\n\n async insert(log: ResolutionLog): Promise<boolean> {\n return this.core.insert(log);\n }\n\n async getLogs(profileId: string, options: GetLogsOptions): Promise<ResolutionLog[]> {\n return this.core.getLogs(profileId, options);\n }\n\n async getLog(profileId: string, logId: number, timestamp?: number): Promise<ResolutionLog | null> {\n return this.core.getLog(profileId, logId, timestamp);\n }\n\n async deleteByOwner(ownerId: string): Promise<boolean> {\n return this.core.deleteByOwner(ownerId);\n }\n\n async cleanup(profileId: string, olderThanTimestamp: number, maxRows = 1000): Promise<number> {\n return this.core.cleanup(profileId, olderThanTimestamp, maxRows);\n }\n\n async cleanupGlobal(maxRetentionDays = 30, batchLimit = 1000, dailyBudget = 20000): Promise<void> {\n return this.retention.cleanupGlobal(maxRetentionDays, batchLimit, dailyBudget);\n }\n\n async getLatestRollupHour(profileId: string): Promise<number | null> {\n return this.aggregation.getLatestRollupHour(profileId);\n }\n\n async getLatestClientRollupHour(profileId: string): Promise<number | null> {\n return this.aggregation.getLatestClientRollupHour(profileId);\n }\n\n async getLatestDestinationRollupHour(profileId: string): Promise<number | null> {\n return this.aggregation.getLatestDestinationRollupHour(profileId);\n }\n\n async aggregateHourlyRollups(sinceSec?: number, untilSec?: number, minDomainCount = 2): Promise<number> {\n return this.aggregation.aggregateHourlyRollups(sinceSec, untilSec, minDomainCount);\n }\n\n async getSummary(\n profileId: string,\n since: number,\n until: number,\n search?: string,\n accessPointId?: string\n ): Promise<ActionCountResult[]> {\n return this.analytics.getSummary(profileId, since, until, search, accessPointId);\n }\n\n async getTrend(\n profileId: string,\n since: number,\n until: number,\n interval: string,\n accessPointId?: string\n ): Promise<TimeSeriesTrendPoint[]> {\n return this.analytics.getTrend(profileId, since, until, interval, accessPointId);\n }\n\n async getTopAllowed(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string\n ): Promise<DomainCountResult[]> {\n return this.analytics.getTopAllowed(profileId, since, until, accessPointId);\n }\n\n async getTopBlocked(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string\n ): Promise<DomainCountResult[]> {\n return this.analytics.getTopBlocked(profileId, since, until, accessPointId);\n }\n\n async getClients(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string\n ): Promise<ClientCountResult[]> {\n return this.analytics.getClients(profileId, since, until, accessPointId);\n }\n\n async getDestinations(\n profileId: string,\n since: number,\n until: number,\n accessPointId?: string,\n limit = 250\n ): Promise<DestinationCountResult[]> {\n return this.analytics.getDestinations(profileId, since, until, accessPointId, limit);\n }\n\n async getISPByCountry(\n profileId: string,\n countryCode: string | undefined,\n since: number,\n until: number,\n accessPointId?: string,\n limit = 250\n ): Promise<ISPCountResult[]> {\n return this.analytics.getISPByCountry(profileId, countryCode, since, until, accessPointId, limit);\n }\n\n async getAnalytics(\n profileId: string,\n since: number,\n until: number,\n interval: string,\n accessPointId?: string\n ): Promise<DashboardAnalyticsResult> {\n return this.analytics.getAnalytics(profileId, since, until, interval, accessPointId);\n }\n}\n\n", "import { D1Database, D1PreparedStatement } from \"@cloudflare/workers-types\";\nimport { Env, ExecutionContext, ProfileSettings, ResolutionLog } from \"../types\";\nimport { LogModel, generateLogId } from \"../models/log\";\nimport { cacheUtils } from \"../utils/cache\";\nimport {\n encryptSensitiveLogData,\n encryptSensitiveLogDataWithDek,\n PqcProfilePublicKey,\n} from \"../lib/crypto/e2ee\";\nimport {\n getCurrentDek,\n ensureActiveDek,\n invalidateActiveDek,\n ActiveDek,\n} from \"../lib/crypto/dekManager\";\n\nexport type ProfileLogPublicKey = JsonWebKey | PqcProfilePublicKey;\n\n/** In-memory cache for profile E2EE public keys with 5-minute TTL */\nconst profileKeyCache = new Map<string, { key: ProfileLogPublicKey | null; expiresAt: number }>();\n\n/**\n * Retrieves the E2EE public key for a profile, using an in-memory cache to avoid repeated D1 reads.\n */\nexport async function getProfileLogPublicKey(\n db: D1Database,\n profileId: string\n): Promise<ProfileLogPublicKey | null> {\n const now = Date.now();\n const cached = profileKeyCache.get(profileId);\n if (cached && cached.expiresAt > now) {\n return cached.key;\n }\n\n try {\n let row: { public_key: string } | null = null;\n try {\n row = await db\n .prepare(\"SELECT public_key FROM user_log_keys WHERE profile_id = ? AND (is_active IS NULL OR is_active = 1)\")\n .bind(profileId)\n .first<{ public_key: string }>();\n } catch (columnErr: any) {\n if (String(columnErr?.message || columnErr).includes(\"no such column: is_active\")) {\n row = await db\n .prepare(\"SELECT public_key FROM user_log_keys WHERE profile_id = ?\")\n .bind(profileId)\n .first<{ public_key: string }>();\n } else {\n throw columnErr;\n }\n }\n\n const key = row?.public_key ? (JSON.parse(row.public_key) as ProfileLogPublicKey) : null;\n // Cache active key for 5 minutes; if null (E2EE not enabled), cache for only 15 seconds for rapid activation\n const ttlMs = key ? 5 * 60 * 1000 : 15 * 1000;\n profileKeyCache.set(profileId, { key, expiresAt: now + ttlMs });\n return key;\n } catch (err) {\n console.error(`[E2EE] Failed to fetch log public key for profile ${profileId}:`, err);\n return null;\n }\n}\n\n/**\n * Invalidates the in-memory E2EE public key and active DEK cache for a profile.\n */\nexport function invalidateProfileLogKeyCache(profileId: string): void {\n profileKeyCache.delete(profileId);\n invalidateActiveDek(profileId);\n}\n\n/** In-memory batch queue of logs waiting to be flushed to D1 */\nconst logBatchQueue: ResolutionLog[] = [];\n\n/** Maximum number of statements in a single db.batch() transaction */\nconst MAX_BATCH_SIZE = 50;\n\n/** Debounce time window for micro-batch flushing (15 seconds) */\nconst FLUSH_INTERVAL_MS = 15_000;\n\n/** Circuit breaker cooldown duration when D1 write quota is exceeded (1 hour) */\nconst CIRCUIT_BREAKER_COOLDOWN_SEC = 3600;\n\n/** In-memory timestamp until which write operations are silenced */\nlet memoryCircuitBreakerUntil = 0;\n\n/** Whether a deferred flush timer has already been scheduled */\nlet isFlushScheduled = false;\n\n/** Timestamp of the last successful or attempted flush */\nlet lastFlushTime = Date.now();\n\n/**\n * Checks if the D1 write quota circuit breaker is currently active.\n *\n * @param cache Cloudflare Cache API instance\n * @returns boolean True if write operations are currently silenced\n */\nexport async function isWriteQuotaTripped(cache?: any): Promise<boolean> {\n const now = Date.now();\n if (memoryCircuitBreakerUntil > now) {\n return true;\n }\n\n if (cache) {\n try {\n const trippedUntil = await cacheUtils.get<number>(cache, \"d1:write_quota_tripped\");\n if (trippedUntil && trippedUntil > Math.floor(now / 1000)) {\n memoryCircuitBreakerUntil = trippedUntil * 1000;\n return true;\n }\n } catch {\n // Non-critical cache read error\n }\n }\n\n return false;\n}\n\n/**\n * Activates the write circuit breaker when a D1 write quota limit error occurs.\n *\n * @param cache Cloudflare Cache API instance\n */\nexport async function tripWriteCircuitBreaker(cache?: any): Promise<void> {\n const nowSec = Math.floor(Date.now() / 1000);\n const trippedUntilSec = nowSec + CIRCUIT_BREAKER_COOLDOWN_SEC;\n memoryCircuitBreakerUntil = trippedUntilSec * 1000;\n\n // Clear pending queue to prevent memory leaks while writes are blocked\n logBatchQueue.length = 0;\n isFlushScheduled = false;\n\n console.warn(\n `[LogBatcher] D1 write quota limit reached! Silencing log writes for ${CIRCUIT_BREAKER_COOLDOWN_SEC}s.`\n );\n\n if (cache) {\n try {\n await cacheUtils.set(cache, \"d1:write_quota_tripped\", trippedUntilSec, CIRCUIT_BREAKER_COOLDOWN_SEC);\n } catch {\n // Non-critical cache write error\n }\n }\n}\n\n/**\n * Flushes a batch of logs to D1 via db.batch().\n *\n * @param env Cloudflare Worker environment bindings\n */\nexport async function flushLogBatch(env: Env): Promise<void> {\n isFlushScheduled = false;\n lastFlushTime = Date.now();\n\n if (logBatchQueue.length === 0) {\n return;\n }\n\n const cache = (caches as any).default;\n if (await isWriteQuotaTripped(cache)) {\n logBatchQueue.length = 0;\n return;\n }\n\n // Atomically extract up to MAX_BATCH_SIZE raw logs from the front of the queue\n const logsToFlush = logBatchQueue.splice(0, MAX_BATCH_SIZE);\n\n if (logsToFlush.length === 0) {\n return;\n }\n\n const logModel = new LogModel(env.DB);\n const statements: D1PreparedStatement[] = [];\n\n // Group logs by profile to batch E2EE public key lookup\n const profileIds = Array.from(new Set(logsToFlush.map((l) => l.profile_id)));\n const keyMap = new Map<string, ProfileLogPublicKey | null>();\n const dekMap = new Map<string, ActiveDek | null>();\n\n await Promise.all(\n profileIds.map(async (pid) => {\n const pubKey = await getProfileLogPublicKey(env.DB, pid);\n if (pubKey) {\n keyMap.set(pid, pubKey);\n if (\"pqc_pk\" in pubKey) {\n // Retrieve current DEK, or ensure initial provisioning if isolate is fresh\n let activeDek = getCurrentDek(pid);\n if (!activeDek) {\n activeDek = await ensureActiveDek(env.DB, pid, pubKey.pqc_pk);\n }\n if (activeDek) {\n dekMap.set(pid, activeDek);\n }\n }\n }\n })\n );\n\n for (const log of logsToFlush) {\n const pubKey = keyMap.get(log.profile_id);\n if (pubKey) {\n try {\n const sensitiveData = {\n domain: log.domain,\n client_ip: log.client_ip,\n geo_country: log.geo_country,\n answer: log.answer,\n dest_geoip: log.dest_geoip,\n dest_country_code: log.dest_country_code,\n dest_country: log.dest_country,\n dest_isp: log.dest_isp,\n ecs: log.ecs,\n upstream: log.upstream,\n reason: log.reason,\n };\n\n if (\"pqc_pk\" in pubKey) {\n // Post-Quantum Periodic Envelope Encryption\n // ZERO expiration checks here: directly use the active DEK\n const activeDek = dekMap.get(log.profile_id);\n if (activeDek) {\n const encrypted = await encryptSensitiveLogDataWithDek(activeDek.dek, sensitiveData);\n log.encrypt_version = 2;\n log.kem_key_id = activeDek.kemKeyId;\n log.encrypted_payload = encrypted;\n }\n } else if (\"kty\" in pubKey) {\n // Legacy P-256 E2EE (ECDH JWK)\n const encrypted = await encryptSensitiveLogData(pubKey, sensitiveData);\n log.encrypt_version = 1;\n log.kem_key_id = null;\n log.encrypted_payload = encrypted;\n }\n\n if (log.encrypt_version && log.encrypt_version > 0) {\n log.domain = \"\";\n log.client_ip = \"\";\n log.geo_country = \"\";\n log.answer = undefined;\n log.dest_geoip = undefined;\n log.dest_country_code = null;\n log.dest_country = null;\n log.dest_isp = null;\n log.ecs = undefined;\n log.upstream = undefined;\n log.reason = undefined;\n }\n } catch (encErr) {\n console.error(`[LogBatcher] Encryption failed for profile ${log.profile_id}:`, encErr);\n }\n } else {\n log.encrypt_version = 0;\n log.kem_key_id = null;\n }\n statements.push(logModel.createInsertStatement(log));\n }\n\n try {\n await env.DB.batch(statements);\n } catch (err: any) {\n const errorMsg = String(err?.message || err);\n\n // Detect Cloudflare D1 row write quota exhaustion\n if (\n errorMsg.includes(\"exceeded D1's free tier daily row write limit\") ||\n errorMsg.includes(\"row write limit\") ||\n errorMsg.includes(\"daily write limit\")\n ) {\n await tripWriteCircuitBreaker(cache);\n } else {\n console.warn(`[LogBatcher] Batch write failed (${statements.length} stmts):`, errorMsg);\n }\n }\n\n // If there are still items remaining in queue, schedule the next batch\n if (logBatchQueue.length > 0) {\n scheduleDeferredFlush(env);\n }\n}\n\n/**\n * Schedules a background deferred flush after FLUSH_INTERVAL_MS if one is not already pending.\n *\n * @param env Cloudflare Worker environment bindings\n */\nfunction scheduleDeferredFlush(env: Env): void {\n if (isFlushScheduled) {\n return;\n }\n isFlushScheduled = true;\n\n // Use a promise timer resolved in background execution context\n new Promise((resolve) => setTimeout(resolve, FLUSH_INTERVAL_MS))\n .then(() => flushLogBatch(env))\n .catch((e) => {\n isFlushScheduled = false;\n console.error(\"[LogBatcher] Deferred flush error:\", e);\n });\n}\n\n/**\n * Enqueues a DNS resolution log for 15-second micro-batching and quota protection.\n *\n * @param log ResolutionLog object to insert\n * @param settings Current profile settings\n * @param env Cloudflare Worker environment bindings\n * @param ctx ExecutionContext to extend background lifetime\n */\nexport function enqueueLog(\n log: ResolutionLog,\n settings: ProfileSettings | undefined,\n env: Env,\n ctx: ExecutionContext\n): void {\n // 1. Zero-retention policy check: if log_retention_days is 0, completely skip logging\n if (settings && Number(settings.log_retention_days) === 0) {\n return;\n }\n\n // 2. Skip logging for PASS queries when skip_log_on_pass is enabled\n const action = (log.action || \"PASS\").toUpperCase();\n if (settings?.skip_log_on_pass && action === \"PASS\") {\n return;\n }\n\n // 3. Fast memory circuit-breaker check\n if (memoryCircuitBreakerUntil > Date.now()) {\n return;\n }\n\n // 4. Ensure unique id is assigned before queueing raw log\n if (!log.id) {\n log.id = generateLogId();\n }\n\n // 5. Enqueue the raw log entry\n logBatchQueue.push(log);\n\n // 6. Determine if an immediate flush is required or a deferred flush should be scheduled\n const now = Date.now();\n if (\n logBatchQueue.length >= MAX_BATCH_SIZE ||\n now - lastFlushTime >= FLUSH_INTERVAL_MS\n ) {\n ctx.waitUntil(flushLogBatch(env));\n } else {\n scheduleDeferredFlush(env);\n }\n}\n", "/**\n * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.\n * @param a - value to test\n * @returns `true` when the value is a Uint8Array-compatible view.\n * @example\n * Check whether a value is a Uint8Array-compatible view.\n * ```ts\n * isBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function isBytes(a) {\n // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.\n // The fallback still requires a real ArrayBuffer view, so plain\n // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and\n // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.\n return (a instanceof Uint8Array ||\n (ArrayBuffer.isView(a) &&\n a.constructor.name === 'Uint8Array' &&\n 'BYTES_PER_ELEMENT' in a &&\n a.BYTES_PER_ELEMENT === 1));\n}\n// Shared error-message prefix builder. Only called on throw paths, so assert\n// success paths never pay for the string concatenation.\nconst atitle = (title) => (title ? `\"${title}\" ` : '');\n/**\n * Asserts something is a non-negative integer.\n * @param n - number to validate\n * @param title - label included in thrown errors\n * @returns The validated number.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a non-negative integer option.\n * ```ts\n * anumber(32, 'length');\n * ```\n */\nexport function anumber(n, title = '') {\n if (typeof n !== 'number')\n throw new TypeError(atitle(title) + 'expected number, got ' + typeof n);\n if (!Number.isSafeInteger(n) || n < 0)\n throw new RangeError(atitle(title) + 'expected integer >= 0, got ' + n);\n return n;\n}\n/**\n * Asserts something is a boolean.\n * @param value - value to validate\n * @param title - label included in thrown errors\n * @returns The validated boolean.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Validate a boolean option.\n * ```ts\n * abool(true, 'enableXOF');\n * ```\n */\nexport function abool(value, title = '') {\n if (typeof value !== 'boolean')\n throw new TypeError(atitle(title) + 'expected boolean, got type=' + typeof value);\n return value;\n}\n/**\n * Asserts something is Uint8Array.\n * @param value - value to validate\n * @param length - optional exact length constraint\n * @param title - label included in thrown errors\n * @returns The validated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate that a value is a byte array.\n * ```ts\n * abytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function abytes(value, length, title = '') {\n // Success path first: this runs at the start of every update() / digestInto(), and the\n // common `abytes(data)` form must not pay for length handling it does not use.\n if (isBytes(value) && (length === undefined || value.length === length))\n return value;\n // Error path: recompute freely to build the exact message.\n if (length !== undefined)\n anumber(length, 'length');\n const bytes = isBytes(value);\n const ofLen = length !== undefined ? ` of length ${length}` : '';\n const got = bytes ? `length=${value.length}` : `type=${typeof value}`;\n const message = atitle(title) + 'expected Uint8Array' + ofLen + ', got ' + got;\n if (!bytes)\n throw new TypeError(message);\n throw new RangeError(message);\n}\n/**\n * Copies bytes into a fresh Uint8Array.\n * Buffer-style slices can alias the same backing store, so callers that need ownership should copy.\n * @param bytes - source bytes to clone\n * @returns Freshly allocated copy of `bytes`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Clone a byte array before mutating it.\n * ```ts\n * const copy = copyBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function copyBytes(bytes) {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes));\n}\n/**\n * Asserts something is a wrapped hash constructor.\n * @param h - hash constructor to validate\n * @throws On wrong argument types or invalid hash wrapper shape. {@link TypeError}\n * @throws On invalid hash metadata ranges or values. {@link RangeError}\n * @throws If the hash metadata allows empty outputs or block sizes. {@link Error}\n * @example\n * Validate a callable hash wrapper.\n * ```ts\n * import { ahash } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * ahash(sha256);\n * ```\n */\nexport function ahash(h) {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new TypeError('expected hash wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n // HMAC and KDF callers treat these as real byte lengths; allowing zero lets fake wrappers pass\n // validation and can produce empty outputs instead of failing fast.\n if (h.outputLen < 1 || h.blockLen < 1)\n throw new Error('hash blockLen / outputLen must be >= 1');\n}\nconst aobject = (value, label) => {\n if (value === null || typeof value !== 'object' || Array.isArray(value))\n throw new TypeError((label === 'object' ? '' : `\"${label}\" `) + 'expected object, got type=' + typeof value);\n};\nconst aopts = (value, label) => {\n aobject(value, label);\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null)\n throw new TypeError(`\"${label}\" expected plain object`);\n // Object.assign() treats an own \"__proto__\" source key as a write to the target's legacy\n // prototype setter. Reject it before merging so inherited option values cannot be injected.\n if (Object.hasOwn(value, '__proto__'))\n throw new TypeError(`\"${label}.__proto__\" is not allowed`);\n};\n/**\n * Asserts a hash instance has not been destroyed or finished.\n * @param instance - hash instance to validate\n * @param checkFinished - whether to reject finalized instances\n * @throws If the hash instance has already been destroyed or finalized. {@link Error}\n * @example\n * Validate that a hash instance is still usable.\n * ```ts\n * import { aexists } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aexists(hash);\n * ```\n */\nexport function aexists(instance, checkFinished = true) {\n // Runs on every update()/digestInto(); the flags are library-owned booleans, so only their\n // truthiness is checked - re-validating their type per call was pure hot-path overhead.\n if (instance.destroyed)\n throw new Error('hash was destroyed');\n if (checkFinished && instance.finished)\n throw new Error('digest() was already called');\n}\n/**\n * Asserts output is a sufficiently-sized byte array.\n * @param out - destination buffer\n * @param instance - hash instance providing output length\n * Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a caller-provided digest buffer.\n * ```ts\n * import { aoutput } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aoutput(new Uint8Array(hash.outputLen), hash);\n * ```\n */\nexport function aoutput(out, instance) {\n abytes(out, undefined, 'output');\n // `outputLen` is a library-owned readonly number; the negated comparison keeps failing fast\n // when it is missing/NaN (comparisons with undefined/NaN are false) without an anumber() call.\n const min = instance.outputLen;\n if (!(out.length >= min)) {\n throw new RangeError('\"output\" expected length >= ' + min);\n }\n}\n/**\n * Casts a typed array view to Uint8Array.\n * @param arr - source typed array\n * @returns Uint8Array view over the same buffer.\n * @example\n * Reinterpret a typed array as bytes.\n * ```ts\n * u8(new Uint32Array([1, 2]));\n * ```\n */\nexport function u8(arr) {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n/**\n * Casts a typed array view to Uint32Array.\n * `arr.byteOffset` must already be 4-byte aligned or the platform\n * Uint32Array constructor will throw.\n * @param arr - source typed array\n * @returns Uint32Array view over the same buffer.\n * @example\n * Reinterpret a byte array as 32-bit words.\n * ```ts\n * u32(new Uint8Array(8));\n * ```\n */\nexport function u32(arr) {\n return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));\n}\n/**\n * Zeroizes typed arrays in place. Warning: JS provides no guarantees.\n * @param arrays - arrays to overwrite with zeros\n * @example\n * Zeroize sensitive buffers in place.\n * ```ts\n * clean(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function clean(...arrays) {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n/**\n * Creates a DataView for byte-level manipulation.\n * @param arr - source typed array\n * @returns DataView over the same buffer region.\n * @example\n * Create a DataView over an existing buffer.\n * ```ts\n * createView(new Uint8Array(4));\n * ```\n */\nexport function createView(arr) {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n/**\n * Rotate-right operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the right.\n * ```ts\n * rotr(0x12345678, 8);\n * ```\n */\nexport function rotr(word, shift) {\n return (word << (32 - shift)) | (word >>> shift);\n}\n/**\n * Rotate-left operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the left.\n * ```ts\n * rotl(0x12345678, 8);\n * ```\n */\nexport function rotl(word, shift) {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n/** Whether the current platform is little-endian. */\nexport const isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n/**\n * Byte-swap operation for uint32 values.\n * @param word - source word\n * @returns Word with reversed byte order.\n * @example\n * Reverse the byte order of a 32-bit word.\n * ```ts\n * byteSwap(0x11223344);\n * ```\n */\nexport function byteSwap(word) {\n return (((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff));\n}\n/**\n * Conditionally byte-swaps one 32-bit word on big-endian platforms.\n * @param n - source word\n * @returns Original or byte-swapped word depending on platform endianness.\n * @example\n * Normalize a 32-bit word for host endianness.\n * ```ts\n * swap8IfBE(0x11223344);\n * ```\n */\nexport const swap8IfBE = isLE\n ? (n) => n\n : (n) => byteSwap(n) >>> 0;\n/**\n * Byte-swaps every word of a Uint32Array in place.\n * @param arr - array to mutate\n * @returns The same array after mutation; callers pass live state arrays here.\n * @example\n * Reverse the byte order of every word in place.\n * ```ts\n * byteSwap32(new Uint32Array([0x11223344]));\n * ```\n */\nexport function byteSwap32(arr) {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr;\n}\n/**\n * Conditionally byte-swaps a Uint32Array on big-endian platforms.\n * @param u - array to normalize for host endianness\n * @returns Original or byte-swapped array depending on platform endianness.\n * On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`.\n * @example\n * Normalize a word array for host endianness.\n * ```ts\n * swap32IfBE(new Uint32Array([0x11223344]));\n * ```\n */\nexport const swap32IfBE = isLE\n ? (u) => u\n : byteSwap32;\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin = /* @__PURE__ */ (() => \n// @ts-ignore\ntypeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));\n/**\n * Convert byte array to hex string.\n * Uses the built-in function when available and assumes it matches the tested\n * fallback semantics.\n * @param bytes - bytes to encode\n * @returns Lowercase hexadecimal string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Convert bytes to lowercase hexadecimal.\n * ```ts\n * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'\n * ```\n */\nexport function bytesToHex(bytes) {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin)\n return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n// Strict ASCII nibble parser: non-ASCII hex lookalikes are rejected as undefined.\n// ASCII codes: '0'..'9' = 48..57, 'A'..'F' = 65..70, 'a'..'f' = 97..102.\n// prettier-ignore\nfunction asciiToBase16(ch) {\n return ch >= 48 && ch <= 57 ? ch - 48 // '2' => 50-48\n : ch >= 65 && ch <= 70 ? ch - (65 - 10) // 'B' => 66-(65-10)\n : ch >= 97 && ch <= 102 ? ch - (97 - 10) // 'b' => 98-(97-10)\n : undefined;\n}\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @param hex - hexadecimal string to decode\n * @returns Decoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Decode lowercase hexadecimal into bytes.\n * ```ts\n * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n * ```\n */\nexport function hexToBytes(hex) {\n if (typeof hex !== 'string')\n throw new TypeError('hex string expected, got ' + typeof hex);\n if (hasHexBuiltin) {\n try {\n return Uint8Array.fromHex(hex);\n }\n catch (error) {\n if (error instanceof SyntaxError)\n throw new RangeError(error.message);\n throw error;\n }\n }\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2)\n throw new RangeError('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi)); // parse first char, multiply it by 16\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1)); // parse second char\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new RangeError('hex string expected, got non-hex character \"' + char + '\" at index ' + hi);\n }\n array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9\n }\n return array;\n}\n/**\n * Yields to the host task scheduler so timers, I/O, and rendering can make progress.\n * Uses the Web Scheduling API when available or `setTimeout` as a cross-platform fallback.\n * Host-task yields are much slower than microtasks (roughly 1ms with the timer fallback), so\n * async loops should use `asyncTick >= 10` to amortize scheduling overhead to about 10% while\n * still allowing other event-loop work to proceed.\n * @param onReject - optional cleanup invoked only if the host yield fails\n * @example\n * Yield to the next scheduler tick.\n * ```ts\n * await nextTick();\n * ```\n */\nexport function nextTick(onReject) {\n const host = globalThis;\n if (typeof host.scheduler?.yield === 'function') {\n const promise = host.scheduler.yield();\n // Keep the original scheduler rejection; this handler exists only for cleanup.\n if (onReject)\n promise.catch(onReject);\n return promise;\n }\n return new Promise((resolve) => host.setTimeout(resolve, 0));\n}\n/**\n * Returns control to the host event loop every `tick` milliseconds to avoid blocking long loops.\n * @param iters - number of loop iterations to run\n * @param tick - maximum time slice in milliseconds\n * @param cb - callback executed on each iteration\n * @param onReject - optional cleanup invoked only if a host yield fails\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Run a loop that periodically yields back to the event loop.\n * ```ts\n * await asyncLoop(2, 0, () => {});\n * ```\n */\nexport async function asyncLoop(iters, tick, cb, onReject) {\n anumber(iters, 'iters');\n anumber(tick, 'tick');\n if (typeof cb !== 'function')\n throw new TypeError('callback must be a function');\n // Callback is synchronous by contract; asyncLoop only yields between sync work windows.\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick)\n continue;\n await nextTick(onReject);\n // Track only synchronous work time; scheduler delay after yielding is outside our budget.\n ts = Date.now();\n }\n}\n/**\n * Converts string to bytes using UTF8 encoding.\n * Built-in doesn't validate input to be string: we do the check.\n * Non-ASCII details are delegated to the platform `TextEncoder`.\n * @param str - string to encode\n * @returns UTF-8 encoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encode a string as UTF-8 bytes.\n * ```ts\n * utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99])\n * ```\n */\nexport function utf8ToBytes(str) {\n if (typeof str !== 'string')\n throw new TypeError('string expected');\n const encoded = new TextEncoder().encode(str);\n try {\n // Copy into the current realm for Firefox extension contexts. Callers that own the returned\n // buffer can then wipe it independently of TextEncoder's temporary result.\n return new Uint8Array(encoded); // https://bugzil.la/1681809\n }\n finally {\n clean(encoded);\n }\n}\n/**\n * Helper for KDFs: consumes Uint8Array or string.\n * String inputs are UTF-8 encoded; byte-array inputs stay aliased to the caller buffer.\n * @param data - user-provided KDF input\n * @param errorTitle - label included in thrown errors\n * @returns Byte representation of the input.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Normalize KDF input to bytes.\n * ```ts\n * kdfInputToBytes('password');\n * ```\n */\nexport function kdfInputToBytes(data, errorTitle = '') {\n if (typeof data === 'string')\n return utf8ToBytes(data);\n return abytes(data, undefined, errorTitle);\n}\n/**\n * Copies several Uint8Arrays into one.\n * @param arrays - arrays to concatenate\n * @returns Concatenated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Concatenate multiple byte arrays.\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nexport function concatBytes(...arrays) {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n/**\n * Validates declared required and optional field types on a plain object.\n * This walks field schemas and formats detailed errors, so avoid it on hot paths; use direct\n * one-line guards such as `abytes()`, `abool()`, or `anumber()` instead.\n * @param object - object to validate\n * @param fields - map of required field names to expected types\n * @param optFields - map of optional field names to expected types\n * @param title - label included in thrown errors\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Validate required and optional option fields.\n * ```ts\n * validateObject({ N: 1024, dkLen: 32 }, { N: 'number' }, { dkLen: 'number' });\n * ```\n */\nexport const validateObject = (object, fields = {}, optFields = {}, title = 'object') => {\n aobject(object, title);\n aobject(fields, 'fields');\n aobject(optFields, 'optFields');\n function checkField(fieldName, expectedType, isOpt) {\n const label = title === 'object' ? `param \"${String(fieldName)}\"` : `\"${title}.${String(fieldName)}\"`;\n // Config fields must be explicit own properties. Optional inherited values are rejected too\n // because callers keep reading the same options object after validation.\n const val = object[fieldName];\n // Runtime objects such as Field instances intentionally satisfy required method slots\n // via their shared prototype.\n if (!Object.hasOwn(object, fieldName) &&\n (isOpt ? val !== undefined : expectedType !== 'function')) {\n throw new TypeError(`${label} is invalid: expected own property`);\n }\n if (isOpt && val === undefined)\n return;\n const current = typeof val;\n if (current !== expectedType || val === null)\n throw new TypeError(`${label} is invalid: expected ${expectedType}, got ${current}`);\n }\n const iter = (f, isOpt) => Object.entries(f).forEach(([k, v]) => checkField(k, v, isOpt));\n iter(fields, false);\n iter(optFields, true);\n};\n/**\n * Merges default options and passed options.\n * @param defaults - base option object\n * @param opts - user overrides\n * @param title - label included in thrown override errors\n * @returns Fresh merged option object with a null prototype.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Merge user overrides onto default options.\n * ```ts\n * checkOpts({ dkLen: 32 }, { asyncTick: 10 });\n * ```\n */\nexport function checkOpts(defaults, opts, title = 'opts') {\n aopts(defaults, 'defaults');\n if (opts !== undefined)\n aopts(opts, title);\n // Callers read optional fields directly, so omitted values must not fall through to ambient\n // Object.prototype pollution (for example a forged `dkLen` changing SHAKE's default output).\n const merged = Object.assign(Object.create(null), defaults, opts);\n return merged;\n}\n/**\n * Creates a callable hash function from a stateful class constructor.\n * @param hashCons - hash constructor or factory\n * @param info - optional metadata such as DER OID\n * @returns Frozen callable hash wrapper with `.create()`.\n * Wrapper construction eagerly calls `hashCons(undefined)` once to read\n * `outputLen` / `blockLen`, so constructor side effects happen at module\n * init time.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Wrap a stateful hash constructor into a callable helper.\n * ```ts\n * import { createHasher } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const wrapped = createHasher(sha256.create, { oid: sha256.oid });\n * wrapped(new Uint8Array([1]));\n * ```\n */\nexport function createHasher(hashCons, info = {}) {\n if (typeof hashCons !== 'function')\n throw new TypeError('\"hashCons\" expected function, got type=' + typeof hashCons);\n info = checkOpts({}, info, 'info');\n const hashC = (msg, opts) => hashCons(opts)\n .update(msg)\n .digest();\n const tmp = hashCons(undefined);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.canXOF = tmp.canXOF;\n hashC.create = (opts) => hashCons(opts);\n Object.assign(hashC, info);\n return Object.freeze(hashC);\n}\n/**\n * Cryptographically secure PRNG backed by `crypto.getRandomValues`.\n * @param bytesLength - number of random bytes to generate\n * @returns Random bytes.\n * The platform `getRandomValues()` implementation still defines any\n * single-call length cap, and this helper rejects oversize requests\n * with a stable library `RangeError` instead of host-specific errors.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If the current runtime does not provide `crypto.getRandomValues`. {@link Error}\n * @example\n * Generate a fresh random key or nonce.\n * ```ts\n * const key = randomBytes(16);\n * ```\n */\nexport function randomBytes(bytesLength = 32) {\n // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion.\n anumber(bytesLength, 'bytesLength');\n const cr = typeof globalThis === 'object' ? globalThis.crypto : null;\n if (typeof cr?.getRandomValues !== 'function')\n throw new Error('crypto.getRandomValues must be defined');\n // Web Cryptography API Level 2 \u00A710.1.1:\n // if `byteLength > 65536`, throw `QuotaExceededError`.\n // Keep the guard explicit so callers can see the quota in code\n // instead of discovering it by reading the spec or host errors.\n // This wrapper surfaces the same quota as a stable library RangeError.\n if (bytesLength > 65536)\n throw new RangeError(`\"bytesLength\" expected <= 65536, got ${bytesLength}`);\n return cr.getRandomValues(new Uint8Array(bytesLength));\n}\n/**\n * Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.\n * @param suffix - final OID byte for the selected hash.\n * The helper accepts any byte even though only the documented NIST hash\n * suffixes are meaningful downstream.\n * @returns Object containing the DER-encoded OID.\n * @example\n * Build OID metadata for a NIST hash.\n * ```ts\n * oidNist(0x01);\n * ```\n */\nexport const oidNist = (suffix) => ({\n // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.\n // Larger suffix values would need base-128 OID encoding and a different length byte.\n oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),\n});\n", "/**\n * Hex, bytes and number utilities.\n * @module\n */\n/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */\nimport { abytes as abytes_, anumber as anumber_, bytesToHex as bytesToHex_, concatBytes as concatBytes_, hexToBytes as hexToBytes_, isBytes as isBytes_, randomBytes as randomBytes_, } from '@noble/hashes/utils.js';\n/**\n * Validates that a value is an array, optionally validating each element.\n * @param item - Value to validate.\n * @param title - Label included in thrown errors.\n * @param inner - Optional per-element validator, called with the element and its label.\n * @returns The validated array.\n * @example\n * Validate an array of points before batch processing.\n *\n * ```ts\n * aarray([1n, 2n], 'scalars');\n * ```\n */\nexport function aarray(item, title, inner = () => { }) {\n if (!Array.isArray(item))\n throw new TypeError(`\"${title}\" expected array, got type=${typeof item}`);\n for (let i = 0; i < item.length; i++)\n inner(item[i], `${title}[${i}]`);\n return item;\n}\n/**\n * Validates that a value is a byte array.\n * @param value - Value to validate.\n * @param length - Optional exact byte length.\n * @param title - Optional field name.\n * @returns Original byte array.\n * @example\n * Reject non-byte input before passing data into curve code.\n *\n * ```ts\n * abytes(new Uint8Array(1));\n * ```\n */\nexport const abytes = (value, length, title) => abytes_(value, length, title);\n/**\n * Validates that a value is a non-negative safe integer.\n * @param n - Value to validate.\n * @param title - Optional field name.\n * @returns The validated number.\n * @example\n * Validate a numeric length before allocating buffers.\n *\n * ```ts\n * anumber(1);\n * ```\n */\nexport const anumber = anumber_;\n/**\n * Asserts something is a string.\n * @param value - Value to validate.\n * @param title - Label included in thrown errors.\n * @returns The validated string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Validate a label string.\n *\n * ```ts\n * astring('example', 'label');\n * ```\n */\nexport function astring(value, title = '') {\n if (typeof value !== 'string') {\n const prefix = title && `\"${title}\" `;\n throw new TypeError(prefix + 'expected string, got type=' + typeof value);\n }\n return value;\n}\n/**\n * Asserts something is a plain object-ish value, not null or array.\n * @param value - Value to validate.\n * @param title - Label included in thrown errors.\n * @returns The validated object.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Validate an options object before checking fields.\n *\n * ```ts\n * aobject({ flag: true });\n * ```\n */\nexport function aobject(value, title = 'object') {\n if (value === null || typeof value !== 'object' || Array.isArray(value))\n throw new TypeError(title === 'object'\n ? 'expected valid options object'\n : `\"${title}\" expected object, got type=${typeof value}`);\n return value;\n}\n/**\n * Asserts something is a function.\n * @param value - Value to validate.\n * @param title - Label included in thrown errors.\n * @returns The validated function.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Validate a required method before calling it.\n *\n * ```ts\n * afunction(() => true, 'predicate');\n * ```\n */\nexport function afunction(value, title) {\n if (typeof value !== 'function')\n throw new TypeError(`\"${title}\" is invalid: expected function, got ${typeof value}`);\n return value;\n}\n/**\n * Encodes bytes as lowercase hex.\n * @param bytes - Bytes to encode.\n * @returns Lowercase hex string.\n * @example\n * Serialize bytes as hex for logging or fixtures.\n *\n * ```ts\n * bytesToHex(Uint8Array.of(1, 2, 3));\n * ```\n */\nexport const bytesToHex = bytesToHex_;\n/**\n * Concatenates byte arrays.\n * @param arrays - Byte arrays to join.\n * @returns Concatenated bytes.\n * @example\n * Join domain-separated chunks into one buffer.\n *\n * ```ts\n * concatBytes(Uint8Array.of(1), Uint8Array.of(2));\n * ```\n */\nexport const concatBytes = (...arrays) => concatBytes_(...arrays);\n/**\n * Decodes lowercase or uppercase hex into bytes.\n * @param hex - Hex string to decode.\n * @returns Decoded bytes.\n * @example\n * Parse fixture hex into bytes before hashing.\n *\n * ```ts\n * hexToBytes('0102');\n * ```\n */\nexport const hexToBytes = (hex) => hexToBytes_(hex);\n/**\n * Checks whether a value is a Uint8Array.\n * @param a - Value to inspect.\n * @returns `true` when `a` is a Uint8Array.\n * @example\n * Branch on byte input before decoding it.\n *\n * ```ts\n * isBytes(new Uint8Array(1));\n * ```\n */\nexport const isBytes = isBytes_;\n/**\n * Reads random bytes from the platform CSPRNG.\n * @param bytesLength - Number of random bytes to read.\n * @returns Fresh random bytes.\n * @example\n * Generate a random seed for a keypair.\n *\n * ```ts\n * randomBytes(2);\n * ```\n */\nexport const randomBytes = (bytesLength) => randomBytes_(bytesLength);\nconst _0n = /* @__PURE__ */ BigInt(0);\nconst _1n = /* @__PURE__ */ BigInt(1);\n// Shared error-message prefix builder. Only called on throw paths, so assert\n// success paths never pay for the string concatenation.\nconst atitle = (title) => (title ? `\"${title}\" ` : '');\n/**\n * Validates that a flag is boolean.\n * @param value - Value to validate.\n * @param title - Optional field name.\n * @returns Original value.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Reject non-boolean option flags early.\n *\n * ```ts\n * abool(true);\n * ```\n */\nexport function abool(value, title = '') {\n if (typeof value !== 'boolean')\n throw new TypeError(atitle(title) + 'expected boolean, got type=' + typeof value);\n return value;\n}\n/**\n * Validates that a value is a non-negative bigint or safe integer.\n * @param n - Value to validate.\n * @returns The same validated value.\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate one integer-like value before serializing it.\n *\n * ```ts\n * abignumber(1n);\n * ```\n */\nexport function abignumber(n) {\n if (typeof n === 'bigint') {\n if (!isPosBig(n))\n throw new RangeError('positive bigint expected, got ' + n);\n }\n else\n anumber(n);\n return n;\n}\n/**\n * Validates that a value is a safe integer.\n * @param value - Integer to validate.\n * @param title - Optional field name.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a window size before scalar arithmetic uses it.\n *\n * ```ts\n * asafenumber(1);\n * ```\n */\nexport function asafenumber(value, title = '') {\n if (typeof value !== 'number') {\n const prefix = title && `\"${title}\" `;\n throw new TypeError(prefix + 'expected number, got type=' + typeof value);\n }\n if (!Number.isSafeInteger(value)) {\n const prefix = title && `\"${title}\" `;\n throw new RangeError(prefix + 'expected safe integer, got ' + value);\n }\n}\n/**\n * Encodes a bigint into even-length big-endian hex.\n * The historical \"unpadded\" name only means \"no fixed-width field padding\"; odd-length hex still\n * gets one leading zero nibble so the result always represents whole bytes.\n * @param num - Number to encode.\n * @returns Big-endian hex string.\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Encode a scalar into hex without a `0x` prefix.\n *\n * ```ts\n * numberToHexUnpadded(255n);\n * ```\n */\nexport function numberToHexUnpadded(num) {\n const hex = abignumber(num).toString(16);\n return hex.length & 1 ? '0' + hex : hex;\n}\n/**\n * Parses a big-endian hex string into bigint.\n * Accepts odd-length hex through the native `BigInt('0x' + hex)` parser and currently surfaces the\n * same native `SyntaxError` for malformed hex instead of wrapping it in a library-specific error.\n * @param hex - Hex string without `0x`.\n * @returns Parsed bigint value.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Parse a scalar from fixture hex.\n *\n * ```ts\n * hexToNumber('ff');\n * ```\n */\nexport function hexToNumber(hex) {\n if (typeof hex !== 'string')\n throw new TypeError('hex string expected, got ' + typeof hex);\n return hex === '' ? _0n : BigInt('0x' + hex); // Big Endian\n}\n// BE: Big Endian, LE: Little Endian\n/**\n * Parses big-endian bytes into bigint.\n * @param bytes - Bytes in big-endian order.\n * @returns Parsed bigint value.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Read a scalar encoded in network byte order.\n *\n * ```ts\n * bytesToNumberBE(Uint8Array.of(1, 0));\n * ```\n */\nexport function bytesToNumberBE(bytes) {\n return hexToNumber(bytesToHex_(bytes));\n}\n/**\n * Parses little-endian bytes into bigint.\n * @param bytes - Bytes in little-endian order.\n * @returns Parsed bigint value.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Read a scalar encoded in little-endian form.\n *\n * ```ts\n * bytesToNumberLE(Uint8Array.of(1, 0));\n * ```\n */\nexport function bytesToNumberLE(bytes) {\n return hexToNumber(bytesToHex_(copyBytes(abytes_(bytes)).reverse()));\n}\n/**\n * Encodes a bigint into fixed-length big-endian bytes.\n * @param n - Number to encode.\n * @param len - Output length in bytes. Must be greater than zero.\n * @returns Big-endian byte array.\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If a documented runtime validation or state check fails. {@link Error}\n * @example\n * Serialize a scalar into a 32-byte field element.\n *\n * ```ts\n * numberToBytesBE(255n, 2);\n * ```\n */\nexport function numberToBytesBE(n, len) {\n anumber_(len);\n if (len === 0)\n throw new Error('zero output length is invalid');\n n = abignumber(n);\n const expectedLen = len * 2;\n const hex = n.toString(16);\n // Detect overflow before hex parsing so oversized values don't leak the shared odd-hex error.\n if (hex.length > expectedLen)\n throw new RangeError('number is too large');\n return hexToBytes_(hex.padStart(expectedLen, '0'));\n}\n/**\n * Encodes a bigint into fixed-length little-endian bytes.\n * @param n - Number to encode.\n * @param len - Output length in bytes.\n * @returns Little-endian byte array.\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If a documented runtime validation or state check fails. {@link Error}\n * @example\n * Serialize a scalar for little-endian protocols.\n *\n * ```ts\n * numberToBytesLE(255n, 2);\n * ```\n */\nexport function numberToBytesLE(n, len) {\n return numberToBytesBE(n, len).reverse();\n}\n// Unpadded, rarely used\n/**\n * Encodes a bigint into variable-length big-endian bytes.\n * @param n - Number to encode.\n * @returns Variable-length big-endian bytes.\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Serialize a bigint without fixed-width padding.\n *\n * ```ts\n * numberToVarBytesBE(255n);\n * ```\n */\nexport function numberToVarBytesBE(n) {\n return hexToBytes_(numberToHexUnpadded(abignumber(n)));\n}\n// Compares 2 u8a-s in kinda constant time\n/**\n * Compares two byte arrays in constant-ish time.\n * @param a - Left byte array.\n * @param b - Right byte array.\n * @returns `true` when bytes match.\n * @example\n * Compare two encoded points without early exit.\n *\n * ```ts\n * equalBytes(Uint8Array.of(1), Uint8Array.of(1));\n * ```\n */\nexport function equalBytes(a, b) {\n a = abytes(a);\n b = abytes(b);\n if (a.length !== b.length)\n return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++)\n diff |= a[i] ^ b[i];\n return diff === 0;\n}\n/**\n * Copies Uint8Array. We can't use u8a.slice(), because u8a can be Buffer,\n * and Buffer#slice creates mutable copy. Never use Buffers!\n * @param bytes - Bytes to copy.\n * @returns Detached copy.\n * @example\n * Make an isolated copy before mutating serialized bytes.\n *\n * ```ts\n * copyBytes(Uint8Array.of(1, 2, 3));\n * ```\n */\nexport function copyBytes(bytes) {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes));\n}\n/**\n * Decodes 7-bit ASCII string to Uint8Array, throws on non-ascii symbols\n * Should be safe to use for things expected to be ASCII.\n * Returns exact same result as `TextEncoder` for ASCII or throws.\n * @param ascii - ASCII input text.\n * @returns Encoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encode an ASCII domain-separation tag.\n *\n * ```ts\n * asciiToBytes('ABC');\n * ```\n */\nexport function asciiToBytes(ascii) {\n if (typeof ascii !== 'string')\n throw new TypeError('ascii string expected, got ' + typeof ascii);\n return Uint8Array.from(ascii, (c, i) => {\n const charCode = c.charCodeAt(0);\n if (c.length !== 1 || charCode > 127) {\n throw new RangeError(`string contains non-ASCII character \"${ascii[i]}\" with code ${charCode} at position ${i}`);\n }\n return charCode;\n });\n}\n/**\n * Checks whether n is non-negative bigint. Historical name.\n * @param n - candidate value\n * @returns `true` when the value is bigint and 0 or larger\n * @example\n * Check a candidate scalar before range validation.\n *\n * ```ts\n * isPosBig(2n);\n * ```\n */\nexport function isPosBig(n) {\n return typeof n === 'bigint' && _0n <= n;\n}\n/**\n * Checks whether a bigint lies inside a half-open range.\n * @param n - Candidate value.\n * @param min - Inclusive lower bound.\n * @param max - Exclusive upper bound.\n * @returns `true` when the value is inside the range.\n * @example\n * Check whether a candidate scalar fits the field order.\n *\n * ```ts\n * inRange(2n, 1n, 3n);\n * ```\n */\nexport function inRange(n, min, max) {\n return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;\n}\n/**\n * Asserts `min <= n < max`. NOTE: upper bound is exclusive.\n * @param title - Value label for error messages.\n * @param n - Candidate value.\n * @param min - Inclusive lower bound.\n * @param max - Exclusive upper bound.\n * Wrong-type inputs are not separated from out-of-range values here: they still flow through the\n * shared `RangeError` path because this is only a throwing wrapper around `inRange(...)`.\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Assert that a bigint stays within one half-open range.\n *\n * ```ts\n * aInRange('x', 2n, 1n, 256n);\n * ```\n */\nexport function aInRange(title, n, min, max) {\n // Why min <= n < max and not a (min < n < max) OR b (min <= n <= max)?\n // consider P=256n, min=0n, max=P\n // - a for min=0 would require -1: `inRange('x', x, -1n, P)`\n // - b would commonly require subtraction: `inRange('x', x, 0n, P - 1n)`\n // - our way is the cleanest: `inRange('x', x, 0n, P)\n if (!inRange(n, min, max))\n throw new RangeError('expected valid ' + title + ': ' + min + ' <= n < ' + max + ', got ' + n);\n}\n// Bit operations\n/**\n * Calculates amount of bits in a bigint.\n * Same as `n.toString(2).length`\n * TODO: merge with nLength in modular\n * @param n - Value to inspect.\n * @returns Bit length.\n * @throws If the value is negative. {@link Error}\n * @example\n * Measure the bit length of a scalar before serialization.\n *\n * ```ts\n * bitLen(8n);\n * ```\n */\nexport function bitLen(n) {\n // Size callers in this repo only use non-negative orders / scalars, so negative inputs are a\n // contract bug and must not silently collapse to zero bits.\n if (n < _0n)\n throw new Error('expected non-negative bigint, got ' + n);\n // Native radix conversion beats a shift loop at every size, and the loop is quadratic in bits.\n return n === _0n ? 0 : n.toString(2).length;\n}\n/**\n * Gets single bit at position.\n * NOTE: first bit position is 0 (same as arrays)\n * Same as `!!+Array.from(n.toString(2)).reverse()[pos]`\n * @param n - Source value.\n * @param pos - Bit position. Negative positions are passed through to raw\n * bigint shift semantics; because the mask is built as `1n << pos`,\n * they currently collapse to `0n` and make the helper a no-op.\n * @returns Bit as bigint.\n * @example\n * Gets single bit at position.\n *\n * ```ts\n * bitGet(5n, 0);\n * ```\n */\nexport function bitGet(n, pos) {\n if (typeof n !== 'bigint')\n throw new TypeError('\"n\" expected bigint, got type=' + typeof n);\n asafenumber(pos, 'pos');\n return (n >> BigInt(pos)) & _1n;\n}\n/**\n * Sets single bit at position.\n * @param n - Source value.\n * @param pos - Bit position. Negative positions are passed through to raw bigint shift semantics,\n * so they currently behave like left shifts.\n * @param value - Whether the bit should be set.\n * @returns Updated bigint.\n * @example\n * Sets single bit at position.\n *\n * ```ts\n * bitSet(0n, 1, true);\n * ```\n */\nexport function bitSet(n, pos, value) {\n if (typeof n !== 'bigint')\n throw new TypeError('\"n\" expected bigint, got type=' + typeof n);\n asafenumber(pos, 'pos');\n abool(value, 'value');\n const mask = _1n << BigInt(pos);\n // Clearing needs AND-not here; OR with zero leaves an already-set bit untouched.\n return value ? n | mask : n & ~mask;\n}\n/**\n * Calculate mask for N bits. Not using ** operator with bigints because of old engines.\n * Same as BigInt(`0b${Array(i).fill('1').join('')}`)\n * @param n - Number of bits. Negative widths are currently passed through to raw bigint shift\n * semantics and therefore produce `-1n`.\n * @returns Bitmask value.\n * @example\n * Calculate mask for N bits.\n *\n * ```ts\n * bitMask(4);\n * ```\n */\nexport const bitMask = (n) => {\n asafenumber(n, 'n');\n return (_1n << BigInt(n)) - _1n;\n};\n/**\n * Minimal HMAC-DRBG from NIST 800-90 for RFC6979 sigs.\n * @param hashLen - Hash output size in bytes. Callers are expected to pass a positive length; `0`\n * is not rejected here and would make the internal generate loop non-progressing.\n * @param qByteLen - Requested output size in bytes. Callers are expected to pass a positive length.\n * @param hmacFn - HMAC implementation.\n * @returns Function that will call DRBG until the predicate returns anything\n * other than `undefined`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Build a deterministic nonce generator for RFC6979-style signing.\n *\n * ```ts\n * import { createHmacDrbg } from '@noble/curves/utils.js';\n * import { hmac } from '@noble/hashes/hmac.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hmacFn = (key: Uint8Array, msg: Uint8Array) => hmac(sha256, key, msg);\n * const drbg = createHmacDrbg(32, 32, hmacFn);\n * const seed = new Uint8Array(32);\n * drbg(seed, (bytes) => bytes);\n * ```\n */\nexport function createHmacDrbg(hashLen, qByteLen, hmacFn) {\n anumber_(hashLen, 'hashLen');\n anumber_(qByteLen, 'qByteLen');\n if (typeof hmacFn !== 'function')\n throw new TypeError('hmacFn must be a function');\n // creates Uint8Array\n const u8n = (len) => new Uint8Array(len);\n const NULL = Uint8Array.of();\n const byte0 = Uint8Array.of(0x00);\n const byte1 = Uint8Array.of(0x01);\n const _maxDrbgIters = 1000;\n // Step B, Step C: set hashLen to 8*ceil(hlen/8).\n // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 signatures.\n let v = u8n(hashLen);\n // Steps B and C of RFC6979 3.2.\n let k = u8n(hashLen);\n let i = 0; // Iterations counter, will throw when over 1000\n const reset = () => {\n v.fill(1);\n k.fill(0);\n i = 0;\n };\n // hmac(k)(v, ...values)\n const h = (...msgs) => hmacFn(k, concatBytes(v, ...msgs));\n const reseed = (seed = NULL) => {\n // HMAC-DRBG reseed() function. Steps D-G\n k = h(byte0, seed); // k = hmac(k || v || 0x00 || seed)\n v = h(); // v = hmac(k || v)\n if (seed.length === 0)\n return;\n k = h(byte1, seed); // k = hmac(k || v || 0x01 || seed)\n v = h(); // v = hmac(k || v)\n };\n const gen = () => {\n // HMAC-DRBG generate() function\n if (i++ >= _maxDrbgIters)\n throw new Error('drbg: tried max amount of iterations');\n let len = 0;\n const out = [];\n while (len < qByteLen) {\n v = h();\n const sl = v.slice();\n out.push(sl);\n len += v.length;\n }\n return concatBytes(...out);\n };\n const genUntil = (seed, pred) => {\n reset();\n reseed(seed); // Steps D-G\n let res = undefined; // Step H: grind until the predicate accepts a candidate.\n // Falsy values like 0 are valid outputs.\n while ((res = pred(gen())) === undefined)\n reseed();\n reset();\n return res;\n };\n return genUntil;\n}\n/**\n * Validates declared required and optional field types on a plain object.\n * Extra keys are intentionally ignored because many callers validate only the subset they use from\n * richer option bags or runtime objects.\n * This walks field schemas and formats detailed errors, so avoid it on hot paths; use direct\n * one-line guards such as `aobject()`, `afunction()`, `abool()`, or `asafenumber()` instead.\n * @param object - Object to validate.\n * @param fields - Required field types.\n * @param optFields - Optional field types.\n * @param title - Object label included in thrown errors.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Check user options before building a curve helper.\n *\n * ```ts\n * validateObject({ flag: true }, { flag: 'boolean' });\n * ```\n */\nexport function validateObject(object, fields = {}, optFields = {}, title = 'object') {\n aobject(object, title);\n aobject(fields, 'fields');\n aobject(optFields, 'optFields');\n function checkField(fieldName, expectedType, isOpt) {\n const label = title === 'object' ? `param \"${String(fieldName)}\"` : `\"${title}.${String(fieldName)}\"`;\n // Config fields must be explicit own properties. Optional inherited values are rejected too\n // because callers keep reading the same options object after validation.\n const val = object[fieldName];\n // Runtime objects such as Field instances intentionally satisfy required method slots\n // via their shared prototype.\n if (!Object.hasOwn(object, fieldName) &&\n (isOpt ? val !== undefined : expectedType !== 'function')) {\n throw new TypeError(`${label} is invalid: expected own property`);\n }\n if (isOpt && val === undefined)\n return;\n const current = typeof val;\n if (current !== expectedType || val === null)\n throw new TypeError(`${label} is invalid: expected ${expectedType}, got ${current}`);\n }\n const iter = (f, isOpt) => Object.entries(f).forEach(([k, v]) => checkField(k, v, isOpt));\n iter(fields, false);\n iter(optFields, true);\n}\n/**\n * Throws not implemented error.\n * @returns Never returns.\n * @throws If the unfinished code path is reached. {@link Error}\n * @example\n * Surface the placeholder error from an unfinished code path.\n *\n * ```ts\n * try {\n * notImplemented();\n * } catch {}\n * ```\n */\nexport const notImplemented = () => {\n throw new Error('not implemented');\n};\n", "/**\n * Utils for modular division and fields.\n * Field over 11 is a finite (Galois) field is integer number operations `mod 11`.\n * There is no division: it is replaced by modular multiplicative inverse.\n * @module\n */\n/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */\nimport { aarray, abool, abytes, afunction, anumber, aobject, asafenumber, bitLen, bytesToNumberBE, bytesToNumberLE, numberToBytesBE, numberToBytesLE, } from \"../utils.js\";\n// Numbers aren't used in x25519 / x448 builds\n// prettier-ignore\nconst _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1), _2n = /* @__PURE__ */ BigInt(2);\n// prettier-ignore\nconst _3n = /* @__PURE__ */ BigInt(3), _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5);\n// prettier-ignore\nconst _7n = /* @__PURE__ */ BigInt(7), _8n = /* @__PURE__ */ BigInt(8), _9n = /* @__PURE__ */ BigInt(9);\nconst _15n = /* @__PURE__ */ BigInt(15), _16n = /* @__PURE__ */ BigInt(16);\n// 2^64: exponents below this use plain square-and-multiply in pow()/FpPow(); the windowed path's\n// table build (14 multiplications) only pays off for longer exponents (break-even ~50 bits).\nconst POW_WINDOWED_MIN = /* @__PURE__ */ BigInt('0x10000000000000000');\n/**\n * @param a - Dividend value.\n * @param b - Positive modulus.\n * @returns Reduced value in `[0, b)` only when `b` is positive.\n * @throws If the modulus is not positive. {@link Error}\n * @example\n * Normalize a bigint into one field residue.\n *\n * ```ts\n * mod(-1n, 5n);\n * ```\n */\nexport function mod(a, b) {\n if (b <= _0n)\n throw new Error('mod: expected positive modulus, got ' + b);\n const result = a % b;\n return result >= _0n ? result : b + result;\n}\n/**\n * Efficiently raise num to a power with modular reduction.\n * Unsafe in some contexts: uses ladder, so can expose bigint bits.\n * Low-level helper: callers that need canonical residues must pass a valid `num` for the chosen\n * modulus instead of relying on the `power===0/1` fast paths to normalize it.\n * @param num - Base value.\n * @param power - Exponent value.\n * @param modulo - Reduction modulus.\n * @returns Modular exponentiation result.\n * @throws If the modulus or exponent is invalid. {@link Error}\n * @example\n * Raise one bigint to a modular power.\n *\n * ```ts\n * pow(2n, 6n, 11n) // 64n % 11n == 9n\n * ```\n */\nexport function pow(num, power, modulo) {\n if (modulo <= _1n)\n throw new Error('pow: expected modulus > 1, got ' + modulo);\n // Non-bigint exponents coerce every comparison below to false and would silently return 1.\n if (typeof power !== 'bigint')\n throw new TypeError('invalid exponent: expected bigint, got ' + typeof power);\n if (power < _0n)\n throw new Error('invalid exponent, negatives unsupported');\n if (power === _0n)\n return _1n;\n if (power === _1n)\n return num;\n let d = num % modulo;\n if (d < _0n)\n d += modulo;\n // Control flow in both branches below depends only on the exponent, never on `num` \u2014 invertCt()\n // relies on that for its (public-exponent) secret-independence guarantee.\n if (power < POW_WINDOWED_MIN) {\n // Square-and-multiply: cheaper than the windowed path for short exponents.\n let p = _1n;\n while (power > _0n) {\n if (power & _1n)\n p = (p * d) % modulo;\n d = (d * d) % modulo;\n power >>= _1n;\n }\n return p;\n }\n // Fixed 4-bit windows, MSB-first: a 14-multiplication table drops per-window cost to <1\n // multiplication (vs ~2 per window for square-and-multiply), ~25-30% faster for the dense\n // 256-bit exponents of sqrt / Legendre / invertCt.\n const digits = [];\n while (power > _0n) {\n digits.push(Number(power & _15n));\n power >>= _4n;\n }\n const table = new Array(16);\n table[0] = _1n;\n table[1] = d;\n for (let i = 2; i < 16; i++)\n table[i] = (table[i - 1] * d) % modulo;\n let p = table[digits[digits.length - 1]]; // top digit is nonzero: the loop above stops on 0\n for (let w = digits.length - 2; w >= 0; w--) {\n p = (p * p) % modulo;\n p = (p * p) % modulo;\n p = (p * p) % modulo;\n p = (p * p) % modulo;\n const digit = digits[w];\n if (digit !== 0)\n p = (p * table[digit]) % modulo;\n }\n return p;\n}\n/**\n * Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)`.\n * Low-level helper: callers that need canonical residues must pass a valid `x` for the chosen\n * modulus; the `power===0` fast path intentionally returns the input unchanged.\n * @param x - Base value.\n * @param power - Number of squarings.\n * @param modulo - Reduction modulus.\n * @returns Repeated-squaring result.\n * @throws If the exponent is negative. {@link Error}\n * @example\n * Apply repeated squaring inside one field.\n *\n * ```ts\n * pow2(3n, 2n, 11n);\n * ```\n */\nexport function pow2(x, power, modulo) {\n if (modulo <= _1n)\n throw new Error('pow2: expected modulus > 1, got ' + modulo);\n if (power < _0n)\n throw new Error('pow2: expected non-negative exponent, got ' + power);\n let res = x;\n while (power-- > _0n) {\n res *= res;\n res %= modulo;\n }\n return res;\n}\n/**\n * Inverses number over modulo.\n * Implemented using the {@link https://brilliant.org/wiki/extended-euclidean-algorithm/ | extended Euclidean algorithm}.\n * @param number - Value to invert.\n * @param modulo - Modulus greater than 1.\n * @returns Multiplicative inverse.\n * @throws If the modulus is invalid or the inverse does not exist. {@link Error}\n * @example\n * Compute one modular inverse with the extended Euclidean algorithm.\n *\n * ```ts\n * invert(3n, 11n);\n * ```\n */\nexport function invert(number, modulo) {\n if (number === _0n)\n throw new Error('invert: expected non-zero number');\n // modulo = 1 is the zero ring: gcd(x, 1) = 1 makes the loop below \"succeed\" and return the\n // useless inverse 0. Reject it like pow() and invertCt() do.\n if (modulo <= _1n)\n throw new Error('invert: expected modulus > 1, got ' + modulo);\n // This is variable-time: the loop count depends on `number`. For a secret-independent\n // (Fermat) alternative over a prime modulus, see {@link invertCt} (~4x slower).\n let a = mod(number, modulo);\n let b = modulo;\n // Only the B\u00E9zout coefficient of `number` (x/u chain) is tracked; the coefficient of `modulo`\n // never affects the output, so it is not computed.\n // prettier-ignore\n let x = _0n, u = _1n;\n while (a !== _0n) {\n const q = b / a;\n const r = b - a * q;\n const m = x - u * q;\n // prettier-ignore\n b = a, a = r, x = u, u = m;\n }\n const gcd = b;\n if (gcd !== _1n)\n throw new Error('invert: does not exist');\n return mod(x, modulo);\n}\n/**\n * Inverses number over modulo using Fermat's little theorem: `a^(p-2) \u2261 a\u207B\u00B9 (mod p)`.\n *\n * Unlike {@link invert} (extended Euclidean), the exponent `p-2` is a public constant, so the\n * underlying square-and-multiply has the same control flow for every secret `a`: there is no\n * data-dependent branching or loop count that could leak `a` through timing (e.g. Minerva-style\n * ECDSA nonce-inversion attacks). This is only \"algorithmically\" constant-time \u2014 JS bigint\n * multiplication/reduction is still value-dependent \u2014 and it is roughly 4x slower than\n * {@link invert}.\n *\n * REQUIRES a prime modulus; Fermat's theorem does not hold otherwise. The result is verified to be\n * a real inverse, so a non-prime modulus (or a non-invertible input) fails closed with an error\n * instead of returning a wrong value.\n * @param a - Value to invert.\n * @param prime - Prime modulus.\n * @returns Multiplicative inverse in `[1, prime)`.\n * @throws If the modulus is below 2, the input reduces to zero, or the inverse does not exist.\n * {@link Error}\n * @example\n * Compute one modular inverse without secret-dependent branching.\n *\n * ```ts\n * invertCt(3n, 11n); // 4n, since 3 * 4 = 12 \u2261 1 (mod 11)\n * ```\n */\nexport function invertCt(a, prime) {\n if (prime <= _1n)\n throw new Error('invertCt: expected prime modulus > 1, got ' + prime);\n const an = mod(a, prime);\n if (an === _0n)\n throw new Error('invertCt: expected non-zero number');\n // Exponent (prime - 2) is public, so FpPow's square-and-multiply is secret-independent.\n const inverse = pow(an, prime - _2n, prime);\n // O(1) safety net: verifies the inverse and rejects composite moduli where a^(p-2) is not one.\n if (mod(an * inverse, prime) !== _1n)\n throw new Error('invertCt: does not exist');\n return inverse;\n}\nfunction assertIsSquare(Fp, root, n) {\n const F = Fp;\n if (!F.eql(F.sqr(root), n))\n throw new Error('Cannot find square root');\n}\n// The Legendre symbol and every sqrt variant here are only defined over an odd (prime) modulus.\n// An even ORDER makes their integer divisions \u2014 (p-1)/2, (p+1)/4, (p-5)/8, (p+7)/16 \u2014 truncate and\n// silently return a wrong result, so reject it explicitly at the entry points instead. This is a\n// cheap necessary-condition check, not a primality test (composite odd moduli are caught later by\n// the Legendre-result / assertIsSquare checks).\nfunction aoddModulus(order, fnName) {\n if ((order & _1n) === _0n)\n throw new Error(fnName + ': expected odd modulus, got ' + order);\n}\n// Not all roots are possible! Example which will throw:\n// const NUM =\n// n = 72057594037927816n;\n// Fp = Field(BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'));\nfunction sqrt3mod4(Fp, n) {\n const F = Fp;\n const p1div4 = (F.ORDER + _1n) / _4n;\n const root = F.pow(n, p1div4);\n assertIsSquare(F, root, n);\n return root;\n}\n// Equivalent `q = 5 (mod 8)` square-root formula (Atkin-style), not the RFC Appendix I.2 CMOV\n// pseudocode verbatim.\nfunction sqrt5mod8(Fp, n) {\n const F = Fp;\n const p5div8 = (F.ORDER - _5n) / _8n;\n const n2 = F.mul(n, _2n);\n const v = F.pow(n2, p5div8);\n const nv = F.mul(n, v);\n const i = F.mul(F.mul(nv, _2n), v);\n const root = F.mul(nv, F.sub(i, F.ONE));\n assertIsSquare(F, root, n);\n return root;\n}\n// Based on RFC9380, Kong algorithm\n// prettier-ignore\nfunction sqrt9mod16(P) {\n const Fp_ = Field(P);\n const tn = tonelliShanks(P);\n const c1 = tn(Fp_, Fp_.neg(Fp_.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F\n const c2 = tn(Fp_, c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F\n const c3 = tn(Fp_, Fp_.neg(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F\n const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic\n return ((Fp, n) => {\n const F = Fp;\n let tv1 = F.pow(n, c4); // 1. tv1 = x^c4\n let tv2 = F.mul(tv1, c1); // 2. tv2 = c1 * tv1\n const tv3 = F.mul(tv1, c2); // 3. tv3 = c2 * tv1\n const tv4 = F.mul(tv1, c3); // 4. tv4 = c3 * tv1\n const e1 = F.eql(F.sqr(tv2), n); // 5. e1 = (tv2^2) == x\n const e2 = F.eql(F.sqr(tv3), n); // 6. e2 = (tv3^2) == x\n tv1 = F.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x\n tv2 = F.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x\n const e3 = F.eql(F.sqr(tv2), n); // 9. e3 = (tv2^2) == x\n const root = F.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select sqrt from tv1 & tv2\n assertIsSquare(F, root, n);\n return root;\n });\n}\n/**\n * Tonelli-Shanks square root search algorithm.\n * This implementation is variable-time: it searches data-dependently for the first non-residue `Z`\n * and for the smallest `i` in the main loop, unlike RFC 9380 Appendix I.4's constant-time shape.\n * 1. {@link https://eprint.iacr.org/2012/685.pdf | eprint 2012/685}, page 12\n * 2. Square Roots from 1; 24, 51, 10 to Dan Shanks\n * @param P - field order\n * @returns function that takes field Fp (created from P) and number n\n * @throws If the field is too small, non-prime, or the square root does not exist. {@link Error}\n * @example\n * Construct a square-root helper for primes that need Tonelli-Shanks.\n *\n * ```ts\n * import { Field, tonelliShanks } from '@noble/curves/abstract/modular.js';\n * const Fp = Field(17n);\n * const sqrt = tonelliShanks(17n)(Fp, 4n);\n * ```\n */\nexport function tonelliShanks(P) {\n // Initialization (precomputation).\n // Caching initialization could boost perf by 7%.\n if (P < _3n)\n throw new Error('sqrt is not defined for small field');\n aoddModulus(P, 'tonelliShanks');\n // Factor P - 1 = Q * 2^S, where Q is odd\n let Q = P - _1n;\n let S = 0;\n while (Q % _2n === _0n) {\n Q /= _2n;\n S++;\n }\n // Find the first quadratic non-residue Z >= 2\n let Z = _2n;\n const _Fp = Field(P);\n while (FpLegendre(_Fp, Z) === 1) {\n // Basic primality test for P. After x iterations, chance of\n // not finding quadratic non-residue is 2^x, so 2^1000.\n if (Z++ > 1000)\n throw new Error('Cannot find square root: probably non-prime P');\n }\n // Fast-path; usually done before Z, but we do \"primality test\".\n if (S === 1)\n return sqrt3mod4;\n // Slow-path\n // TODO: test on Fp2 and others\n let cc = _Fp.pow(Z, Q); // c = z^Q\n const Q1div2 = (Q + _1n) / _2n;\n return function tonelliSlow(Fp, n) {\n const F = Fp;\n if (F.is0(n))\n return n;\n // Check if n is a quadratic residue using Legendre symbol\n if (FpLegendre(F, n) !== 1)\n throw new Error('Cannot find square root');\n // Initialize variables for the main loop\n let M = S;\n let c = F.mul(F.ONE, cc); // c = z^Q, move cc from field _Fp into field Fp\n let t = F.pow(n, Q); // t = n^Q, first guess at the fudge factor\n let R = F.pow(n, Q1div2); // R = n^((Q+1)/2), first guess at the square root\n // Main loop\n // while t != 1\n while (!F.eql(t, F.ONE)) {\n // Unreachable over a genuine field (no zero divisors; n=0 already returned above). A zero t\n // means composite ORDER, where a fabricated root would be wrong: fail closed instead.\n if (F.is0(t))\n throw new Error('Cannot find square root: probably non-prime P');\n let i = 1;\n // Find the smallest i >= 1 such that t^(2^i) \u2261 1 (mod P)\n let t_tmp = F.sqr(t); // t^(2^1)\n while (!F.eql(t_tmp, F.ONE)) {\n i++;\n t_tmp = F.sqr(t_tmp); // t^(2^2)...\n if (i === M)\n throw new Error('Cannot find square root');\n }\n // Calculate the exponent for b: 2^(M - i - 1)\n const exponent = _1n << BigInt(M - i - 1); // bigint is important\n const b = F.pow(c, exponent); // b = 2^(M - i - 1)\n // Update variables\n M = i;\n c = F.sqr(b); // c = b^2\n t = F.mul(t, c); // t = (t * b^2)\n R = F.mul(R, b); // R = R*b\n }\n return R;\n };\n}\n/**\n * Square root for a finite field. Will try optimized versions first:\n *\n * 1. P \u2261 3 (mod 4)\n * 2. P \u2261 5 (mod 8)\n * 3. P \u2261 9 (mod 16)\n * 4. Tonelli-Shanks algorithm\n *\n * Different algorithms can give different roots, it is up to user to decide which one they want.\n * For example there is FpSqrtOdd/FpSqrtEven to choose a root by oddness\n * (used for hash-to-curve).\n * @param P - Field order.\n * @returns Square-root helper. The generic fallback inherits Tonelli-Shanks' variable-time\n * behavior and this selector assumes prime-field-style integer moduli.\n * @throws If the field is unsupported or the square root does not exist. {@link Error}\n * @example\n * Choose the square-root helper appropriate for one field modulus.\n *\n * ```ts\n * import { Field, FpSqrt } from '@noble/curves/abstract/modular.js';\n * const Fp = Field(17n);\n * const sqrt = FpSqrt(17n)(Fp, 4n);\n * ```\n */\nexport function FpSqrt(P) {\n aoddModulus(P, 'Fp.sqrt');\n // P \u2261 3 (mod 4) => \u221An = n^((P+1)/4)\n if (P % _4n === _3n)\n return sqrt3mod4;\n // P \u2261 5 (mod 8) => Atkin algorithm, page 10 of https://eprint.iacr.org/2012/685.pdf\n if (P % _8n === _5n)\n return sqrt5mod8;\n // P \u2261 9 (mod 16) => Kong algorithm, page 11 of https://eprint.iacr.org/2012/685.pdf (algorithm 4)\n if (P % _16n === _9n)\n return sqrt9mod16(P);\n // Tonelli-Shanks algorithm\n return tonelliShanks(P);\n}\n/**\n * @param num - Value to inspect.\n * @param modulo - Field modulus.\n * @returns `true` when the least-significant little-endian bit is set.\n * @throws If the modulus is invalid for `mod(...)`. {@link Error}\n * @example\n * Inspect the low bit used by little-endian sign conventions.\n *\n * ```ts\n * isNegativeLE(3n, 11n);\n * ```\n */\nexport const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;\n// prettier-ignore\n// Arithmetic-only subset checked by validateField(). This is intentionally not the full runtime\n// IField contract: helpers like `isValidNot0`, `invertBatch`, `toBytes`, `fromBytes`, `cmov`, and\n// field-specific extras like `isOdd` are left to the callers that actually need them.\nconst FIELD_FIELDS = [\n 'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',\n 'eql', 'add', 'sub', 'mul', 'pow', 'div',\n 'addN', 'subN', 'mulN', 'sqrN'\n];\n/**\n * @param field - Field implementation.\n * @returns Validated field. This only checks the arithmetic subset needed by generic helpers; it\n * does not guarantee full runtime-method coverage for serialization, batching, `cmov`, or\n * field-specific extras beyond positive `BYTES` / `BITS`.\n * @throws If the field shape or numeric metadata are invalid. {@link Error}\n * @example\n * Check that a field implementation exposes the operations curve code expects.\n *\n * ```ts\n * import { Field, validateField } from '@noble/curves/abstract/modular.js';\n * const Fp = validateField(Field(17n));\n * ```\n */\nexport function validateField(field) {\n aobject(field, 'field');\n if (typeof field.ORDER !== 'bigint')\n throw new TypeError('param \"ORDER\" is invalid: expected bigint, got ' + typeof field.ORDER);\n // Runtime field implementations must expose real integer byte/bit sizes; fractional / NaN /\n // infinite metadata breaks encoders and caches.\n asafenumber(field.BYTES, 'BYTES');\n asafenumber(field.BITS, 'BITS');\n for (const name of FIELD_FIELDS)\n afunction(field[name], 'field.' + name);\n // Runtime field implementations must expose positive byte/bit sizes; zero leaks through the\n // numeric shape checks above but still breaks encoding helpers and cached-length assumptions.\n if (field.BYTES < 1 || field.BITS < 1)\n throw new Error('invalid field: expected BYTES/BITS > 0');\n if (field.ORDER <= _1n)\n throw new Error('invalid field: expected ORDER > 1, got ' + field.ORDER);\n return field;\n}\n// Generic field functions\n/**\n * Same as `pow` but for Fp: non-constant-time.\n * Unsafe in some contexts: uses ladder, so can expose bigint bits.\n * @param Fp - Field implementation.\n * @param num - Base value.\n * @param power - Exponent value.\n * @returns Powered field element.\n * @throws If the exponent is negative. {@link Error}\n * @example\n * Raise one field element to a public exponent.\n *\n * ```ts\n * import { Field, FpPow } from '@noble/curves/abstract/modular.js';\n * const Fp = Field(17n);\n * const x = FpPow(Fp, 3n, 5n);\n * ```\n */\nexport function FpPow(Fp, num, power) {\n validateField(Fp);\n const F = Fp;\n // Non-bigint exponents (e.g. an accidental field element) coerce every comparison below to\n // false and would silently return ONE.\n if (typeof power !== 'bigint')\n throw new TypeError('invalid exponent: expected bigint, got ' + typeof power);\n if (power < _0n)\n throw new Error('invalid exponent, negatives unsupported');\n if (power === _0n)\n return F.ONE;\n if (power === _1n)\n return num;\n if (power < POW_WINDOWED_MIN) {\n // Square-and-multiply: cheaper than the windowed path for short exponents (e.g. poseidon\n // sbox x^5), which would waste the 14-multiplication table build.\n let p = F.ONE;\n let d = num;\n while (power > _0n) {\n if (power & _1n)\n p = F.mul(p, d);\n d = F.sqr(d);\n power >>= _1n;\n }\n return p;\n }\n // Fixed 4-bit windows, MSB-first \u2014 same shape as pow() above, over generic field ops.\n // Speeds up dense long exponents (extension-field sqrt / Legendre, e.g. Fp2 decompression).\n const digits = [];\n while (power > _0n) {\n digits.push(Number(power & _15n));\n power >>= _4n;\n }\n const table = new Array(16);\n table[0] = F.ONE;\n table[1] = num;\n for (let i = 2; i < 16; i++)\n table[i] = F.mul(table[i - 1], num);\n let p = table[digits[digits.length - 1]]; // top digit is nonzero: the loop above stops on 0\n for (let w = digits.length - 2; w >= 0; w--) {\n p = F.sqr(F.sqr(F.sqr(F.sqr(p))));\n const digit = digits[w];\n if (digit !== 0)\n p = F.mul(p, table[digit]);\n }\n return p;\n}\nexport function FpInvertBatch(Fp, nums, passZero = false) {\n validateField(Fp);\n aarray(nums, 'nums');\n abool(passZero, 'passZero');\n const F = Fp;\n const inverted = new Array(nums.length).fill(passZero ? F.ZERO : undefined);\n // Walk from first to last, multiply them by each other MOD p\n const multipliedAcc = nums.reduce((acc, num, i) => {\n if (F.is0(num))\n return acc;\n inverted[i] = acc;\n return F.mul(acc, num);\n }, F.ONE);\n // Invert last element\n const invertedAcc = F.inv(multipliedAcc);\n // Walk from last to first, multiply them by inverted each other MOD p\n nums.reduceRight((acc, num, i) => {\n if (F.is0(num))\n return acc;\n // Non-zero `num` means the forward pass already stored a defined prefix product at index i.\n inverted[i] = F.mul(acc, inverted[i]);\n return F.mul(acc, num);\n }, invertedAcc);\n return inverted;\n}\n/**\n * @param Fp - Field implementation.\n * @param lhs - Dividend value.\n * @param rhs - Divisor value.\n * @returns Division result.\n * @throws If the divisor is non-invertible. {@link Error}\n * @example\n * Divide one field element by another.\n *\n * ```ts\n * import { Field, FpDiv } from '@noble/curves/abstract/modular.js';\n * const Fp = Field(17n);\n * const x = FpDiv(Fp, 6n, 3n);\n * ```\n */\nexport function FpDiv(Fp, lhs, rhs) {\n validateField(Fp);\n const F = Fp;\n return F.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, F.ORDER) : F.inv(rhs));\n}\n/**\n * Legendre symbol.\n * Legendre constant is used to calculate Legendre symbol (a | p)\n * which denotes the value of a^((p-1)/2) (mod p).\n *\n * * (a | p) \u2261 1 if a is a square (mod p), quadratic residue\n * * (a | p) \u2261 -1 if a is not a square (mod p), quadratic non residue\n * * (a | p) \u2261 0 if a \u2261 0 (mod p)\n * @param Fp - Field implementation.\n * @param n - Value to inspect.\n * @returns Legendre symbol.\n * @throws If the powered value does not match a valid Legendre symbol. {@link Error}\n * @example\n * Compute the Legendre symbol of one field element.\n *\n * ```ts\n * import { Field, FpLegendre } from '@noble/curves/abstract/modular.js';\n * const Fp = Field(17n);\n * const symbol = FpLegendre(Fp, 4n);\n * ```\n */\nexport function FpLegendre(Fp, n) {\n validateField(Fp);\n const F = Fp;\n aoddModulus(F.ORDER, 'FpLegendre');\n // We can use 3rd argument as optional cache of this value\n // but seems unneeded for now. The operation is very fast.\n const p1mod2 = (F.ORDER - _1n) / _2n;\n const powered = F.pow(n, p1mod2);\n const yes = F.eql(powered, F.ONE);\n const zero = F.eql(powered, F.ZERO);\n const no = F.eql(powered, F.neg(F.ONE));\n if (!yes && !zero && !no)\n throw new Error('invalid Legendre symbol result');\n return yes ? 1 : zero ? 0 : -1;\n}\n/**\n * @param Fp - Field implementation.\n * @param n - Value to inspect.\n * @returns `true` when `Fp.sqrt(n)` exists. This includes `0`, even though strict \"quadratic\n * residue\" terminology often reserves that name for the non-zero square class.\n * @throws If the field returns an invalid Legendre symbol value. {@link Error}\n * @example\n * Check whether one field element has a square root in the field.\n *\n * ```ts\n * import { Field, FpIsSquare } from '@noble/curves/abstract/modular.js';\n * const Fp = Field(17n);\n * const isSquare = FpIsSquare(Fp, 4n);\n * ```\n */\nexport function FpIsSquare(Fp, n) {\n const l = FpLegendre(Fp, n);\n // Zero is a square too: 0 = 0^2, and Fp.sqrt(0) already returns 0.\n return l !== -1;\n}\n/**\n * @param n - Curve order. Callers are expected to pass a positive order.\n * @param nBitLength - Optional cached bit length. Callers are expected to pass a positive cached\n * value when overriding the derived bit length.\n * @returns Byte and bit lengths.\n * @throws If the order or cached bit length is invalid. {@link Error}\n * @example\n * Measure the encoding sizes needed for one modulus.\n *\n * ```ts\n * nLength(255n);\n * ```\n */\nexport function nLength(n, nBitLength) {\n // Bit size, byte size of CURVE.n\n if (nBitLength !== undefined)\n anumber(nBitLength);\n if (n <= _0n)\n throw new Error('invalid n length: expected positive n, got ' + n);\n if (nBitLength !== undefined && nBitLength < 1)\n throw new Error('invalid n length: expected positive bit length, got ' + nBitLength);\n const bits = bitLen(n);\n // Cached bit lengths smaller than ORDER would truncate serialized scalars/elements and poison\n // any math that relies on the derived field metadata.\n if (nBitLength !== undefined && nBitLength < bits)\n throw new Error(`invalid n length: expected nBitLength (${nBitLength}) >= bitLen(n) (${bits})`);\n const _nBitLength = nBitLength !== undefined ? nBitLength : bits;\n const nByteLength = Math.ceil(_nBitLength / 8);\n return { nBitLength: _nBitLength, nByteLength };\n}\n// Keep the lazy sqrt cache off-instance so Field(...) can return a frozen object. Otherwise the\n// cached helper write would keep the field surface externally mutable.\nconst FIELD_SQRT = new WeakMap();\nclass _Field {\n ORDER;\n BITS;\n BYTES;\n isLE;\n ZERO = _0n;\n ONE = _1n;\n _lengths;\n _mod;\n constructor(ORDER, opts = {}) {\n // ORDER <= 1 is degenerate: ONE would not be a valid field element and helpers like pow/inv\n // would stop modeling field arithmetic.\n if (ORDER <= _1n)\n throw new Error('invalid field: expected ORDER > 1, got ' + ORDER);\n let _nbitLength = undefined;\n this.isLE = false;\n if (opts != null && typeof opts === 'object') {\n // Cached bit lengths are trusted here and should already be positive / consistent with ORDER.\n if (typeof opts.BITS === 'number')\n _nbitLength = opts.BITS;\n if (typeof opts.sqrt === 'function')\n // `_Field.prototype` is frozen below, so custom sqrt hooks must become own properties\n // explicitly instead of relying on writable prototype shadowing via assignment.\n Object.defineProperty(this, 'sqrt', { value: opts.sqrt, enumerable: true });\n if (typeof opts.isLE === 'boolean')\n this.isLE = opts.isLE;\n if (opts.allowedLengths)\n this._lengths = Object.freeze(opts.allowedLengths.slice());\n if (typeof opts.modFromBytes === 'boolean')\n this._mod = opts.modFromBytes;\n }\n const { nBitLength, nByteLength } = nLength(ORDER, _nbitLength);\n if (nByteLength > 2048)\n throw new Error('invalid field: expected ORDER of <= 2048 bytes');\n this.ORDER = ORDER;\n this.BITS = nBitLength;\n this.BYTES = nByteLength;\n Object.freeze(this);\n }\n create(num) {\n return mod(num, this.ORDER);\n }\n isValid(num) {\n if (typeof num !== 'bigint')\n throw new TypeError('invalid field element: expected bigint, got ' + typeof num);\n return _0n <= num && num < this.ORDER; // 0 is valid element, but it's not invertible\n }\n is0(num) {\n return num === _0n;\n }\n // is valid and invertible\n isValidNot0(num) {\n return !this.is0(num) && this.isValid(num);\n }\n isOdd(num) {\n return (num & _1n) === _1n;\n }\n neg(num) {\n return mod(-num, this.ORDER);\n }\n eql(lhs, rhs) {\n return lhs === rhs;\n }\n sqr(num) {\n return mod(num * num, this.ORDER);\n }\n add(lhs, rhs) {\n return mod(lhs + rhs, this.ORDER);\n }\n sub(lhs, rhs) {\n return mod(lhs - rhs, this.ORDER);\n }\n mul(lhs, rhs) {\n return mod(lhs * rhs, this.ORDER);\n }\n pow(num, power) {\n return pow(num, power, this.ORDER);\n }\n div(lhs, rhs) {\n return mod(lhs * invert(rhs, this.ORDER), this.ORDER);\n }\n // Same as above, but doesn't normalize\n sqrN(num) {\n return num * num;\n }\n addN(lhs, rhs) {\n return lhs + rhs;\n }\n subN(lhs, rhs) {\n return lhs - rhs;\n }\n mulN(lhs, rhs) {\n return lhs * rhs;\n }\n inv(num) {\n return invert(num, this.ORDER);\n }\n sqrt(num) {\n // Caching sqrt helpers speeds up sqrt9mod16 by 5x and Tonelli-Shanks by about 10% without keeping\n // the field instance itself mutable.\n let sqrt = FIELD_SQRT.get(this);\n if (!sqrt)\n FIELD_SQRT.set(this, (sqrt = FpSqrt(this.ORDER)));\n return sqrt(this, num);\n }\n toBytes(num) {\n // Serialize fixed-width limbs without re-validating the field range. Callers that need a\n // canonical encoding must pass a valid element; some protocols intentionally serialize raw\n // residues here and reduce or validate them elsewhere.\n return this.isLE ? numberToBytesLE(num, this.BYTES) : numberToBytesBE(num, this.BYTES);\n }\n fromBytes(bytes, skipValidation = false) {\n abytes(bytes);\n const { _lengths: allowedLengths, BYTES, isLE, ORDER, _mod: modFromBytes } = this;\n if (allowedLengths) {\n // `allowedLengths` must list real positive byte lengths; otherwise empty input would get\n // padded into zero and silently decode as a field element.\n if (bytes.length < 1 || !allowedLengths.includes(bytes.length) || bytes.length > BYTES) {\n throw new Error('Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length);\n }\n const padded = new Uint8Array(BYTES);\n // isLE add 0 to right, !isLE to the left.\n padded.set(bytes, isLE ? 0 : padded.length - bytes.length);\n bytes = padded;\n }\n if (bytes.length !== BYTES)\n throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);\n let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);\n if (modFromBytes)\n scalar = mod(scalar, ORDER);\n if (!skipValidation)\n if (!this.isValid(scalar))\n throw new Error('invalid field element: outside of range 0..ORDER');\n // Range validation is optional here because some protocols intentionally decode raw residues\n // and reduce or validate them elsewhere.\n return scalar;\n }\n // TODO: we don't need it here, move out to separate fn\n invertBatch(lst) {\n // `passZero` keeps the `bigint[]` contract honest: zero inputs map to `0` instead of leaking\n // `undefined` into a `bigint[]`. Callers that must distinguish non-invertible inputs should use\n // `FpInvertBatch` directly, whose default omits `passZero` and returns `(bigint | undefined)[]`.\n return FpInvertBatch(this, lst, true);\n }\n // We can't move this out because Fp6, Fp12 implement it\n // and it's unclear what to return in there.\n cmov(a, b, condition) {\n // Field elements have `isValid(...)`; the CMOV branch bit is a direct runtime input, so reject\n // non-boolean selectors here instead of letting JS truthiness silently change arithmetic.\n abool(condition, 'condition');\n return condition ? b : a;\n }\n}\n/**\n * Creates a finite field. Major performance optimizations:\n * * 1. Denormalized operations like mulN instead of mul.\n * * 2. Identical object shape: never add or remove keys.\n * * 3. Frozen stable object shape; the lazy sqrt cache lives in a module-level `WeakMap`.\n * Fragile: always run a benchmark on a change.\n * Security note: operations and low-level serializers like `toBytes` don't check `isValid` for\n * all elements for performance and protocol-flexibility reasons; callers are responsible for\n * supplying valid elements when they need canonical field behavior.\n * This is low-level code, please make sure you know what you're doing.\n *\n * Note about field properties:\n * * CHARACTERISTIC p = prime number, number of elements in main subgroup.\n * * ORDER q = similar to cofactor in curves, may be composite `q = p^m`.\n *\n * @param ORDER - field order, probably prime, or could be composite\n * @param opts - Field options such as bit length or endianness. See {@link FieldOpts}.\n * @returns Frozen field instance with a stable object shape. This wrapper forwards `opts` straight\n * into `_Field`, so it inherits `_Field`'s assumptions about cached sizes and `allowedLengths`.\n * @example\n * Construct one prime field with optional overrides.\n *\n * ```ts\n * Field(11n);\n * ```\n */\nexport function Field(ORDER, opts = {}) {\n // Freeze the shared method surface before any instance is reachable; otherwise callers can\n // poison every Field instance by monkey-patching `_Field.prototype` even if each instance is\n // frozen. Freezing here instead of module scope keeps `_Field` tree-shakeable for importers\n // that never construct a field; the call is idempotent and cheap.\n Object.freeze(_Field.prototype);\n return new _Field(ORDER, opts);\n}\n/**\n * @param Fp - Field implementation.\n * @param elm - Value to square-root.\n * @returns Odd square root when two roots exist. The special case `elm = 0` still returns `0`,\n * which is the only square root but is not odd.\n * @throws If the field lacks oddness checks or the square root does not exist. {@link Error}\n * @example\n * Select the odd square root when two roots exist.\n *\n * ```ts\n * import { Field, FpSqrtOdd } from '@noble/curves/abstract/modular.js';\n * const Fp = Field(17n);\n * const root = FpSqrtOdd(Fp, 4n);\n * ```\n */\nexport function FpSqrtOdd(Fp, elm) {\n validateField(Fp);\n const F = Fp;\n if (!F.isOdd)\n throw new Error(\"Field doesn't have isOdd\");\n const root = F.sqrt(elm);\n return F.isOdd(root) ? root : F.neg(root);\n}\n/**\n * @param Fp - Field implementation.\n * @param elm - Value to square-root.\n * @returns Even square root.\n * @throws If the field lacks oddness checks or the square root does not exist. {@link Error}\n * @example\n * Select the even square root when two roots exist.\n *\n * ```ts\n * import { Field, FpSqrtEven } from '@noble/curves/abstract/modular.js';\n * const Fp = Field(17n);\n * const root = FpSqrtEven(Fp, 4n);\n * ```\n */\nexport function FpSqrtEven(Fp, elm) {\n validateField(Fp);\n const F = Fp;\n if (!F.isOdd)\n throw new Error(\"Field doesn't have isOdd\");\n const root = F.sqrt(elm);\n return F.isOdd(root) ? F.neg(root) : root;\n}\n/**\n * Returns total number of bytes consumed by the field element.\n * For example, 32 bytes for usual 256-bit weierstrass curve.\n * @param fieldOrder - number of field elements, usually CURVE.n. Callers are expected to pass an\n * order greater than 1.\n * @returns byte length of field\n * @throws If the field order is not a bigint. {@link Error}\n * @example\n * Read the fixed-width byte length of one field.\n *\n * ```ts\n * getFieldBytesLength(255n);\n * ```\n */\nexport function getFieldBytesLength(fieldOrder) {\n if (typeof fieldOrder !== 'bigint')\n throw new Error('field order must be bigint');\n // Valid field elements are in 0..ORDER-1, so ORDER <= 1 would make the encoded range degenerate.\n if (fieldOrder <= _1n)\n throw new Error('field order must be greater than 1');\n // Valid field elements are < ORDER, so the maximal encoded element is ORDER - 1.\n const bitLength = bitLen(fieldOrder - _1n);\n return Math.ceil(bitLength / 8);\n}\n/**\n * Returns minimal amount of bytes that can be safely reduced\n * by field order.\n * Should be 2^-128 for 128-bit curve such as P256.\n * This is the reduction / modulo-bias lower bound; higher-level helpers may still impose a larger\n * absolute floor for policy reasons.\n * @param fieldOrder - number of field elements greater than 1, usually CURVE.n.\n * @returns byte length of target hash\n * @throws If the field order is invalid. {@link Error}\n * @example\n * Compute the minimum hash length needed for field reduction.\n *\n * ```ts\n * getMinHashLength(255n);\n * ```\n */\nexport function getMinHashLength(fieldOrder) {\n const length = getFieldBytesLength(fieldOrder);\n return length + Math.ceil(length / 2);\n}\n/**\n * \"Constant-time\" private key generation utility.\n * Can take (n + n/2) or more bytes of uniform input e.g. from CSPRNG or KDF\n * and convert them into private scalar, with the modulo bias being negligible.\n * Needs at least 48 bytes of input for 32-byte private key. The implementation also keeps a hard\n * 16-byte minimum even when `getMinHashLength(...)` is smaller, so toy-small inputs do not look\n * accidentally acceptable for real scalar derivation.\n * See {@link https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ | Kudelski's modulo-bias guide},\n * {@link https://csrc.nist.gov/publications/detail/fips/186/5/final | FIPS 186-5 appendix A.2}, and\n * {@link https://www.rfc-editor.org/rfc/rfc9380#section-5 | RFC 9380 section 5}. Unlike RFC 9380\n * `hash_to_field`, this helper intentionally maps into the non-zero private-scalar range `1..n-1`.\n * @param key - Uniform input bytes.\n * @param fieldOrder - Size of subgroup.\n * @param isLE - interpret hash bytes as LE num\n * @returns valid private scalar\n * @throws If the hash length or field order is invalid for scalar reduction. {@link Error}\n * @example\n * Map hash output into a private scalar range.\n *\n * ```ts\n * mapHashToField(new Uint8Array(48).fill(1), 255n);\n * ```\n */\nexport function mapHashToField(key, fieldOrder, isLE = false) {\n abytes(key);\n const len = key.length;\n const fieldLen = getFieldBytesLength(fieldOrder);\n const minLen = Math.max(getMinHashLength(fieldOrder), 16);\n // No toy-small inputs: the helper is for real scalar derivation, not tiny test curves. No huge\n // inputs: easier to reason about JS timing / allocation behavior.\n if (len < minLen || len > 1024)\n throw new Error('expected ' + minLen + '-1024 bytes of input, got ' + len);\n const num = isLE ? bytesToNumberLE(key) : bytesToNumberBE(key);\n // Map into the non-zero scalar range [1, fieldOrder-1]: reduce mod (fieldOrder-1) to land in\n // [0, fieldOrder-2], then add 1. This shifts the range off zero; it is NOT equal to\n // `mod(num, fieldOrder)` (which spans [0, fieldOrder-1] and can be 0). A residual modulo bias\n // remains but is negligible (~2^-(nBits/2), e.g. ~2^-128 for a 256-bit order) because `key` is\n // required to be at least `getMinHashLength(fieldOrder)` (~1.5x field size) bytes of input.\n const reduced = mod(num, fieldOrder - _1n) + _1n;\n return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);\n}\n", "/**\n * Methods for elliptic curve multiplication by scalars.\n * Contains wNAF-based ScalarMultiplier, pippenger.\n * @module\n */\n/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */\nimport { aarray, abool, afunction, aobject, bitLen, bitMask, bytesToNumberBE, inRange, isBytes, isPosBig, validateObject, } from \"../utils.js\";\nimport { Field, FpInvertBatch, validateField } from \"./modular.js\";\nconst _0n = /* @__PURE__ */ BigInt(0);\nconst _1n = /* @__PURE__ */ BigInt(1);\nconst _4n = /* @__PURE__ */ BigInt(4);\nconst BLIND_BYTES = 16;\nconst BLIND_BITS = 128;\n// Fixed-window width for the constant-time multiply of un-precomputed points (W===1).\n// A flat 2^FW_WINDOW table has a small, scalar-independent build cost that amortizes over a single\n// multiply, unlike the larger per-point wNAF tables that only pay off when cached.\nconst FW_WINDOW = 5;\n// Precompute tables are capped at ~2 GiB of estimated heap. Rejecting larger windows up front\n// turns a typo'd window size into an immediate error instead of a multi-GB allocation (or an\n// effective hang) when the lazy table is built on first multiply.\nconst TABLE_BYTES_MAX = /* @__PURE__ */ (() => 2 ** 31)();\n/**\n * Validates the static surface of a point constructor.\n * This is only a cheap sanity check for the constructor hooks and fields consumed by generic\n * factories; it does not certify `BASE`/`ZERO` semantics or prove the curve implementation itself.\n * @param Point - Runtime point constructor.\n * @throws On missing constructor hooks or malformed field metadata. {@link TypeError}\n * @example\n * Check that one point constructor exposes the static hooks generic helpers need.\n *\n * ```ts\n * import { ed25519 } from '@noble/curves/ed25519.js';\n * import { validatePointCons } from '@noble/curves/abstract/curve.js';\n * validatePointCons(ed25519.Point);\n * ```\n */\nexport function validatePointCons(Point) {\n const pc = Point;\n if (typeof pc !== 'function')\n throw new TypeError('\"Point\" expected constructor, got type=' + typeof Point);\n afunction(pc.fromAffine, 'Point.fromAffine');\n afunction(pc.fromBytes, 'Point.fromBytes');\n afunction(pc.fromHex, 'Point.fromHex');\n // Generic helpers (ScalarMultiplier, normalizeZ, MSM) dereference BASE / ZERO:\n // fail here with a typed error instead of an `undefined` access later.\n aobject(pc.BASE, 'Point.BASE');\n aobject(pc.ZERO, 'Point.ZERO');\n validateField(pc.Fp);\n validateField(pc.Fn);\n}\n/**\n * Takes a bunch of Projective Points but executes only one\n * inversion on all of them. Inversion is very slow operation,\n * so this improves performance massively.\n * Optimization: converts a list of projective points to a list of identical points with Z=1.\n * Input points are left unchanged; the normalized points are returned as fresh instances.\n * @param c - Point constructor.\n * @param points - Projective points.\n * @returns Fresh projective points reconstructed from normalized affine coordinates.\n * @example\n * Batch-normalize projective points with a single shared inversion.\n *\n * ```ts\n * import { normalizeZ } from '@noble/curves/abstract/curve.js';\n * import { p256 } from '@noble/curves/nist.js';\n * const points = normalizeZ(p256.Point, [p256.Point.BASE, p256.Point.BASE.double()]);\n * ```\n */\nexport function normalizeZ(c, points) {\n // Match MSM helpers: reject malformed public inputs before reading projective internals.\n validatePointCons(c);\n validateMSMPoints(points, c);\n // Identity points (Z=0) rely on an implicit contract: FpInvertBatch without `passZero`\n // yields `undefined` for zero inputs, and `toAffine(undefined)` falls back to its internal\n // is0 handling instead of using the batch inverse.\n const invertedZs = FpInvertBatch(c.Fp, points.map((p) => p.Z));\n return points.map((p, i) => c.fromAffine(p.toAffine(invertedZs[i])));\n}\nfunction validateW(W, bits, min = 1) {\n if (!Number.isSafeInteger(W) || W < min || W > bits)\n throw new Error('invalid window size, expected [' + min + '..' + bits + '], got W=' + W);\n}\n// Rough per-point heap estimate for the {@link TABLE_BYTES_MAX} cap: up to 4 projective/extended\n// coordinates of Fp.BYTES each, plus bigint/object overhead. Callers pass the point count of the\n// largest table the checked parameters can produce.\nfunction validateTableBytes(numPoints, fpBytes) {\n const bytes = numPoints * (4 * fpBytes + 128);\n if (bytes > TABLE_BYTES_MAX)\n throw new Error('invalid window size: table would need ~' +\n Math.ceil(bytes / 2 ** 20) +\n ' MiB, max ' +\n TABLE_BYTES_MAX / 2 ** 20 +\n ' MiB');\n}\n/**\n * Probes an RNG once, at construction time: returns `undefined` when it is unavailable \u2014\n * throws or returns malformed bytes \u2014 so callers can downgrade to their unblinded /\n * deterministic constant-time fallback. Blinding is defense-in-depth (DPA/template\n * hardening), not a correctness or key-secrecy requirement, so availability-based\n * downgrade is acceptable.\n *\n * The downgrade decision is deliberately static. After a successful probe the RNG becomes\n * part of the trusted contract: later misbehavior must fail closed in per-call validation\n * (throw), never downgrade \u2014 a dynamic fallback would let a tampered RNG silently strip\n * blinding on demand. A probe can only ever classify broken environments, not adversarial\n * RNGs: a stateful RNG can always behave while probed and misbehave later.\n * @param randomBytes - RNG to probe, or `undefined` when the environment provides none.\n * @param length - Byte length requested from the probe call.\n * @returns The RNG when the probe produced `length` valid bytes; `undefined` otherwise.\n * @example\n * Probe an RNG once before enabling scalar blinding.\n *\n * ```ts\n * import { probeRandomBytes } from '@noble/curves/abstract/curve.js';\n * import { randomBytes } from '@noble/hashes/utils.js';\n * const rng = probeRandomBytes(randomBytes, 16);\n * ```\n */\nexport function probeRandomBytes(randomBytes, length) {\n if (randomBytes === undefined)\n return undefined;\n afunction(randomBytes, 'randomBytes');\n try {\n const probe = randomBytes(length);\n if (!isBytes(probe) || probe.length !== length)\n return undefined;\n }\n catch {\n return undefined;\n }\n return randomBytes;\n}\nfunction validateMSMPoints(points, c) {\n aarray(points, 'points');\n points.forEach((p, i) => {\n if (!(p instanceof c))\n throw new Error('invalid point at index ' + i);\n });\n}\n// Default bound is field membership (0 <= s < field.ORDER); a `maxScalar` override widens it\n// to 0 <= s < maxScalar for callers that accept oversized scalars.\nfunction validateMSMScalars(scalars, field, maxScalar) {\n if (!Array.isArray(scalars))\n throw new Error('array of scalars expected');\n scalars.forEach((s, i) => {\n const ok = maxScalar === undefined ? field.isValid(s) : isPosBig(s) && s < maxScalar;\n if (!ok)\n throw new Error('invalid scalar at index ' + i);\n });\n}\nconst pointWindowSizes = new WeakMap();\nfunction getWindowSize(P) {\n // `1` is the uncached sentinel: use the non-precomputed (wNAF / fixed-window) path.\n return pointWindowSizes.get(P) || 1;\n}\n/** Table of odd multiples [1P, 3P, ..., (2\u22C5size\u22121)P]; width-W wNAF uses size = 2^(W\u22122). */\nfunction oddMultiples(p, size) {\n const dbl = p.double();\n const t = [p];\n for (let j = 1; j < size; j++)\n t.push(t[j - 1].add(dbl));\n return t;\n}\n/**\n * Width-W wNAF signed-digit recoding (W >= 2), LSB-first: digits are 0 or odd with\n * |digit| < 2^(W\u22121); nonzero density ~1/(W+1) (a nonzero digit is followed by W\u22121 zeros).\n */\nfunction wnafDigits(n, W) {\n const size = 2 ** W;\n const half = size / 2;\n const mask = BigInt(size - 1);\n const d = [];\n while (n > _0n) {\n let w = 0;\n if (n & _1n) {\n w = Number(n & mask); // n mod 2^W, odd\n if (w >= half)\n w -= size; // signed residue\n n -= BigInt(w); // n - w \u2261 0 mod 2^W: next W\u22121 digits are zero\n }\n d.push(w);\n n >>= _1n;\n }\n return d;\n}\n/**\n * Fixed-position signed-window recoding for precomputed wNAF: `n = \u03A3 digits[w]\u22C52^(w\u22C5W)` with\n * digits in `[\u22122^(W\u22121)+1, 2^(W\u22121)]`. Digit count is fixed by `windows` (callers reserve one\n * extra window for the final carry), so recoding length does not depend on the scalar.\n */\nfunction signedWindowDigits(n, W, windows) {\n const size = 2 ** W;\n const half = size / 2;\n const mask = BigInt(size - 1);\n const shiftBy = BigInt(W);\n const d = [];\n for (let w = 0; w < windows; w++) {\n let v = Number(n & mask);\n n >>= shiftBy;\n if (v > half) {\n v -= size; // negative digit, carry into the next window\n n += _1n;\n }\n d.push(v);\n }\n // Internal invariant: leftover bits mean the window count did not cover the scalar.\n if (n !== _0n)\n throw new Error('invalid wnaf');\n return d;\n}\n/**\n * Shared vartime walk over per-scalar wNAF digit streams: one doubling of a single shared\n * accumulator per bit position of the longest recoding, one signed table addition per\n * nonzero digit. `tables[i]` must hold the odd multiples of the i-th point.\n */\nfunction wnafWalk(zero, tables, digits) {\n let max = 0;\n for (const d of digits)\n max = Math.max(max, d.length);\n let acc = zero;\n for (let bit = max - 1; bit >= 0; bit--) {\n if (bit !== max - 1)\n acc = acc.double();\n for (let i = 0; i < digits.length; i++) {\n const w = digits[i][bit]; // reads past shorter recodings yield undefined, skipped below\n if (w) {\n const item = tables[i][(Math.abs(w) - 1) >> 1];\n acc = acc.add(w < 0 ? item.negate() : item);\n }\n }\n }\n return acc;\n}\n/**\n * Elliptic curve multiplication of Point by scalar.\n * Routes between cached-table, fixed-window, and one-shot wNAF paths; entry points validate\n * their own scalars (`mulCT`/`mulCTBlinded`: `1 <= s < Fn.ORDER`; `mulUnsafe`: up to the\n * `Fn.ORDER^4` DoS cap via {@link mulAddUnsafe}).\n * Table generation is expensive and happens on first call of `multiply()`\n * (or eagerly via `precompute(W, false)`). By default, `BASE` point is precomputed.\n *\n * Cached algorithm is signed fixed-window wNAF:\n * - table stores, for every window w, the multiples `[1..2^(W\u22121)]\u22C52^(w\u22C5W)\u22C5P` \u2014 all doublings\n * are baked in, so a multiplication is exactly one table addition per window\n * - window count is fixed (`ceil(bits/W) + 1`), so the point-operation count is scalar-independent\n * (basis of the constant-time path)\n * - for a 256-bit curve and W=6: 44\u22C532 = 1408 table points, 44 additions per multiply\n * - secret scalars are additionally blinded (see {@link ScalarMultiplier.mulCTBlinded}), which\n * widens tables by 128 bits\n * @param Point - Point constructor.\n * @param randomBytes - RNG used for scalar blinding; required by the blinded secret path.\n * @example\n * Elliptic curve multiplication of Point by scalar.\n *\n * ```ts\n * import { ScalarMultiplier } from '@noble/curves/abstract/curve.js';\n * import { p256 } from '@noble/curves/nist.js';\n * const mul = new ScalarMultiplier(p256.Point);\n * ```\n */\nexport class ScalarMultiplier {\n Point;\n BASE;\n ZERO;\n randomBytes;\n wnafPrecomputes = new WeakMap();\n baseCanBeBlinded;\n bits;\n // Parametrized with a given Point class (not individual point)\n constructor(Point, randomBytes) {\n validatePointCons(Point);\n // Probe the RNG once (see {@link probeRandomBytes}): in environments without working\n // randomness (e.g. no WebCrypto), shouldBlind() then routes secret multiplication to the\n // unblinded constant-time path instead of throwing on every multiply(). The shape of\n // returned bytes is still validated on every blinded call, where breakage fails closed.\n this.randomBytes = probeRandomBytes(randomBytes, BLIND_BYTES);\n this.Point = Point;\n this.BASE = Point.BASE;\n this.ZERO = Point.ZERO;\n this.bits = Point.Fn.BITS;\n }\n /**\n * Creates a signed fixed-window wNAF precomputation table: for every window w, the\n * multiples `[1..2^(W\u22121)]\u22C52^(w\u22C5W)\u22C5P`, flattened. All doublings are baked into the table,\n * so cached multiplication is additions-only. `windows = ceil(bits/W) + 1`: the extra\n * window absorbs the final carry of signed-digit recoding.\n * For a 256-bit curve and W=6, the table is 44\u22C532 = 1408 points.\n * @param point - Point instance\n * @param W - window size\n * @param bits - scalar bitlength the table must cover\n */\n buildWnafTable(point, W, bits) {\n // W needs no re-validation: its only source is setWindowSize(), which enforces\n // 1 <= W <= Fn.BITS <= bits (the blinded path only ever widens bits) and caps the\n // resulting table at ~2 GiB (sized against the wider blinded layout).\n const windows = Math.ceil(bits / W) + 1;\n const half = 2 ** (W - 1);\n const comp = [];\n let base = point;\n for (let w = 0; w < windows; w++) {\n let acc = base;\n for (let i = 0; i < half; i++) {\n comp.push(acc);\n acc = acc.add(base);\n }\n base = comp[comp.length - 1].double(); // 2\u22C5(2^(W\u22121)\u22C5base) = next window's base\n }\n return { W, bits, windows, comp };\n }\n /**\n * Implements ec multiplication using precomputed signed fixed-window wNAF tables.\n * Constant-time: fixed window count with one table addition per window \u2014 zero digits feed\n * the fake accumulator \u2014 and no doublings; the lookup scans the whole window slice.\n * Scalar bounds are validated by the public entry points ({@link ScalarMultiplier.mulCT},\n * {@link ScalarMultiplier.mulCTBlinded}, {@link ScalarMultiplier.mulUnsafe});\n * signedWindowDigits throws if `n` exceeds the table.\n * @returns real and fake (for const-time) points\n */\n wnafCachedCT(precomputes, n) {\n const { W, windows, comp } = precomputes;\n const half = 2 ** (W - 1);\n const digits = signedWindowDigits(n, W, windows);\n let p = this.ZERO;\n let f = this.BASE;\n for (let w = 0; w < windows; w++) {\n const digit = digits[w];\n const start = w * half;\n // Data-oblivious select: touch every entry of the window before the digit branch.\n const idx = Math.abs(digit) - 1; // -1 for zero digits: matches nothing, `sel` unused\n let sel = comp[start];\n for (let i = 1; i < half; i++)\n sel = i === idx ? comp[start + i] : sel;\n const neg = sel.negate(); // compute both signs; the digit only picks one\n if (digit === 0)\n f = f.add(comp[start]);\n else\n p = p.add(digit < 0 ? neg : sel);\n }\n return { p, f };\n }\n // Cache key is point identity plus (W, bits); at most two entries exist per point (public-width\n // `Fn.BITS` and blinded `Fn.BITS + BLIND_BITS`). Callers must not reuse the same point with\n // incompatible `transform(...)` layouts and expect a separate cache entry.\n getWnafPrecomputes(W, point, bits, transform) {\n let entries = this.wnafPrecomputes.get(point);\n let comp = entries?.find((entry) => entry.W === W && entry.bits === bits);\n if (!comp) {\n comp = this.buildWnafTable(point, W, bits);\n if (typeof transform === 'function')\n comp = { ...comp, comp: transform(comp.comp) };\n if (!entries) {\n entries = [];\n this.wnafPrecomputes.set(point, entries);\n }\n entries.push(comp);\n }\n return comp;\n }\n assertPoint(point) {\n if (!(point instanceof this.Point))\n throw new TypeError('\"point\" expected Point instance, got type=' + typeof point);\n }\n // Shared prologue of the constant-time entry points. Rejects scalar 0: in key/signature-style\n // callers a zero scalar means broken upstream plumbing, and concrete Points already reject it.\n // Uses inRange instead of Fn.isValidNot0: validateField() only certifies the arithmetic subset.\n validateMulInput(point, scalar) {\n this.assertPoint(point);\n if (!inRange(scalar, _1n, this.Point.Fn.ORDER))\n throw new Error('invalid scalar');\n }\n // Constant-time dispatch shared by mulCT / mulCTBlinded. Un-precomputed points (W===1, e.g.\n // ECDH peer keys) skip building a throwaway cached table in favor of a small fixed-window\n // multiply. `n` must be < 2^bits.\n runCT(point, n, bits, transform) {\n const W = getWindowSize(point);\n if (W === 1)\n return this.fixedWindowCT(point, n, bits);\n return this.wnafCachedCT(this.getWnafPrecomputes(W, point, bits, transform), n);\n }\n mulCT(point, scalar, transform) {\n this.validateMulInput(point, scalar);\n return this.runCT(point, scalar, this.bits, transform);\n }\n mulCTBlinded(point, scalar, transform) {\n this.validateMulInput(point, scalar);\n // Blinding computes n = scalar + blind*Fn.ORDER, then n*P via a constant-time multiply. This\n // equals scalar*P only when Fn.ORDER*P == O; callers guarantee that via shouldBlind() (always\n // for cofactor-1 curves; for cofactored curves only BASE, and only after checking BASE*n == O).\n // Fail before building the (large) precompute table if randomness is unavailable.\n if (this.randomBytes === undefined)\n throw new Error('randomBytes is required for scalar blinding');\n const bits = this.Point.Fn.BITS + BLIND_BITS;\n const blind = this.randomBytes(BLIND_BYTES);\n if (!isBytes(blind) || blind.length !== BLIND_BYTES)\n throw new Error('randomBytes returned invalid byte array');\n // Force the top two bits of the 128-bit blind to 10xxxxxx, so blind is in [2^127, 1.5*2^127):\n // * `| 0x80` (bit 127 = 1) is the load-bearing part: it guarantees blind >= 2^127, so the blind\n // is always a full-width, nonzero factor and the scalar is masked even with a degenerate RNG.\n // * `& 0x3f` (bit 126 = 0) is a safety margin: it caps blind < 1.5*2^127, keeping\n // blind*Fn.ORDER + scalar < 0.75*2^(nBits+128), i.e. ~half a window below the 2^(nBits+128)\n // ceiling. Not strictly required for the bound (see below), but it reserves headroom so the\n // guarantee does not rest on the tight `Fn.ORDER < 2^Fn.BITS` fact and the final carry window\n // only ever holds a small carry, never a full digit.\n blind[0] = (blind[0] & 0x3f) | 0x80;\n // Even at the extreme (blind < 2^128, scalar < Fn.ORDER < 2^nBits): n <= 2^128*Fn.ORDER - 1 <\n // 2^(nBits+128), so n stays below 2^bits and within the blinded table's\n // window count. Both cached CT kernels run a fixed number of windows/rows with one point-add\n // each, so the add count is independent of scalar (constant-time).\n const n = scalar + bytesToNumberBE(blind) * this.Point.Fn.ORDER;\n return this.runCT(point, n, bits, transform);\n }\n /**\n * Constant-time multiplication `n*point` for an un-precomputed point, via a small fixed window.\n * A cached wNAF table only pays off when reused; a flat 2^FW_WINDOW table (`size-1` adds) is\n * far cheaper to build for a single use. The point-operation sequence is independent of `n`:\n * build the table, then per window exactly FW_WINDOW doublings, a data-oblivious scan over\n * every table entry, and one addition (adds the identity when the window digit is 0 \u2014 never\n * skipped).\n *\n * `n` must be `< 2^bits`. Assumes complete addition (adding the identity costs the same as any\n * add), which holds for the Weierstrass/Edwards point types used here. The table is left in\n * projective form (no normalizeZ): normalizing this small a table costs more than the\n * mixed-add savings it would buy for a single multiply.\n * @returns real point `p`; `f` duplicates it only to match {@link wnafCachedCT}'s return shape\n * (this path needs no fake accumulator \u2014 its op-count is already scalar-independent).\n */\n fixedWindowCT(point, n, bits) {\n const W = FW_WINDOW;\n const size = 1 << W;\n const mask = bitMask(W);\n // Flat table [O, point, 2*point, ..., (size-1)*point].\n const table = new Array(size);\n table[0] = this.ZERO;\n for (let i = 1; i < size; i++)\n table[i] = table[i - 1].add(point);\n // Horner MSB->LSB. windows*W >= bits and n < 2^bits, so every bit of n is consumed.\n const windows = Math.ceil(bits / W);\n let acc = this.ZERO;\n for (let window = windows - 1; window >= 0; window--) {\n // W doublings per window; skipped for the first (topmost) window, where acc is still the\n // identity. The skip is scalar-independent: it depends only on the loop index.\n if (window !== windows - 1)\n for (let d = 0; d < W; d++)\n acc = acc.double();\n const digit = Number((n >> BigInt(window * W)) & mask);\n // Data-oblivious select: touch every entry, same as wnafCachedCT.\n let sel = table[0];\n for (let i = 1; i < size; i++)\n sel = i === digit ? table[i] : sel;\n acc = acc.add(sel); // one add per window, even for digit 0\n }\n return { p: acc, f: acc };\n }\n shouldBlind(point, cofactor) {\n // No usable RNG (probed in the constructor): blinding is impossible, use the plain CT path.\n if (this.randomBytes === undefined)\n return false;\n if (cofactor === _1n)\n return true;\n if (point !== this.BASE)\n return false;\n if (this.baseCanBeBlinded === undefined)\n this.baseCanBeBlinded = this.mulUnsafe(this.BASE, this.Point.Fn.ORDER).is0();\n return this.baseCanBeBlinded;\n }\n mulSecret(point, scalar, cofactor, transform) {\n return this.shouldBlind(point, cofactor)\n ? this.mulCTBlinded(point, scalar, transform)\n : this.mulCT(point, scalar, transform);\n }\n mulUnsafe(point, scalar, transform) {\n this.assertPoint(point);\n if (!isPosBig(scalar))\n throw new Error('invalid scalar');\n const W = getWindowSize(point);\n // W === 1 (un-precomputed): one-shot width-4 wNAF via {@link mulAddUnsafe} with L=1 \u2014\n // a cached table would be thrown away after one use. `allowOversized` swaps the\n // `s < Fn.ORDER` check for mulAddUnsafe's `Fn.ORDER^4` DoS cap.\n //\n // Oversized scalar could happen when:\n // a) user passes large scalar on their own (rare)\n // b) `assertValidity()` calls `isTorsionFree()`, which multiplies point by `Fn.ORDER`\n if (W === 1 || scalar >= this.Point.Fn.ORDER)\n return mulAddUnsafe(this.Point, [point], [scalar], true);\n // Precomputed points reuse the CT kernel (fake accumulator discarded): with W=6 only\n // ~1/64 of window-adds are skippable, so a dedicated vartime kernel saved just ~6% on\n // this path while doubling the cached-table code surface.\n const precomputes = this.getWnafPrecomputes(W, point, this.bits, transform);\n return this.wnafCachedCT(precomputes, scalar).p;\n }\n // Remembers the window size used for precomputed wNAF multiplication of the given point\n // and drops any previously built tables. Usually only the base point is precomputed.\n // W=1 resets the point to the un-precomputed (table-less) paths.\n // W is additionally capped so tables stay under ~2 GiB ({@link TABLE_BYTES_MAX}).\n setWindowSize(point, W) {\n this.assertPoint(point);\n validateW(W, this.bits);\n // Size against the widest table this W can produce: the blinded path adds BLIND_BITS.\n const windows = Math.ceil((this.bits + BLIND_BITS) / W) + 1;\n validateTableBytes(windows * 2 ** (W - 1), this.Point.Fp.BYTES);\n pointWindowSizes.set(point, W);\n this.wnafPrecomputes.delete(point);\n }\n // True when a window size is set: tables themselves are built lazily on first multiply.\n hasWindowSize(point) {\n return getWindowSize(point) !== 1;\n }\n}\n/**\n * Combined multi-scalar multiplication `\u03A3 scalars[i]\u22C5points[i]` via interleaved width-4 wNAF\n * (Strauss\u2013Shamir). Every input gets its own table of odd multiples `[1P, 3P, 5P, 7P]` and\n * signed-digit recoding, but all walks share one doubling chain, so total cost is\n * `~bits` doublings + `L\u22C5bits/5` additions instead of `L\u22C5bits` doublings for separate\n * multiplications. Intended for the 2-4 point shapes of signature verification\n * (`R = u1\u22C5G + u2\u22C5P`); use {@link pippenger} for larger batches.\n *\n * Not constant-time: only for public inputs. Scalars must satisfy `0 <= s < Fn.ORDER`;\n * fold negative signs into the points before calling.\n * @param c - Point constructor.\n * @param points - Array of curve points.\n * @param scalars - Array of non-negative scalars, same length as points.\n * @param allowOversized - Replace the `s < Fn.ORDER` scalar check with a `Fn.ORDER^4` DoS cap.\n * Off by default. For scalars that must NOT be reduced mod ORDER: torsion checks\n * (`Fn.ORDER\u22C5P \u225F O`) and cofactor-clearing multiples. Walk length grows with `bitLen(s)`.\n * @returns Combined multiplication result; identity for empty input.\n * @throws If the point set or scalar set is invalid. {@link Error}\n * @example\n * Combined multi-scalar multiplication via Strauss\u2013Shamir.\n *\n * ```ts\n * import { mulAddUnsafe } from '@noble/curves/abstract/curve.js';\n * import { p256 } from '@noble/curves/nist.js';\n * const G = p256.Point.BASE;\n * const R = mulAddUnsafe(p256.Point, [G, G.double()], [2n, 3n]); // 2\u22C5G + 3\u22C5(2\u22C5G)\n * ```\n */\nexport function mulAddUnsafe(c, points, scalars, allowOversized = false) {\n validatePointCons(c);\n validateMSMPoints(points, c);\n abool(allowOversized, 'allowOversized');\n // Oversized cap is ORDER^4: hard bound to mitigate DoS, walk length grows with bitLen(s).\n validateMSMScalars(scalars, c.Fn, allowOversized ? c.Fn.ORDER ** _4n : undefined);\n if (points.length !== scalars.length)\n throw new Error('arrays of points and scalars must have equal length');\n const tables = points.map((p) => oddMultiples(p, 4));\n const digits = scalars.map((n) => wnafDigits(n, 4));\n return wnafWalk(c.ZERO, tables, digits);\n}\n/**\n * Pippenger algorithm for multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).\n * 30x faster vs naive addition on L=4096, 10x faster than precomputes.\n * For N=254bit, L=1, it does: 1024 ADD + 254 DBL. For L=5: 1536 ADD + 254 DBL.\n * Point-operation count is scalar-independent (for same L), even when 1 point + scalar, or when\n * scalar = 0 \u2014 but bucket indices are scalar windows, so the memory-access pattern is\n * scalar-dependent: do not rely on this for secret scalars.\n *\n * A repaired LFG bucket-set variant from ePrint 2024/750 was benchmarked on BLS12-381 G1\n * against this implementation: ~1.4x faster at 2048 points and ~1.1-1.25x faster at\n * 4096-32768 points, at the cost of extra recoding and multiplier-table complexity.\n * @param c - Curve Point constructor\n * @param points - array of L curve points\n * @param scalars - array of L scalars (aka secret keys / bigints)\n * @returns MSM result point. Empty input is accepted and returns the identity.\n * @throws If the point set, scalar set, or MSM sizing is invalid. {@link Error}\n * @example\n * Pippenger algorithm for multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).\n *\n * ```ts\n * import { pippenger } from '@noble/curves/abstract/curve.js';\n * import { p256 } from '@noble/curves/nist.js';\n * const point = pippenger(p256.Point, [p256.Point.BASE, p256.Point.BASE.double()], [2n, 3n]);\n * ```\n */\nexport function pippenger(c, points, scalars) {\n // 0 is accepted in scalars\n validatePointCons(c);\n const fieldN = c.Fn;\n validateMSMPoints(points, c);\n validateMSMScalars(scalars, fieldN);\n const plength = points.length;\n const slength = scalars.length;\n if (plength !== slength)\n throw new Error('arrays of points and scalars must have equal length');\n const zero = c.ZERO;\n // Without this, the window loop below would still run ~Fn.BITS doublings of ZERO.\n if (plength === 0)\n return zero;\n const wbits = bitLen(BigInt(plength));\n let windowSize = 1; // bits\n if (wbits > 12)\n windowSize = wbits - 3;\n else if (wbits > 4)\n windowSize = wbits - 2;\n else if (wbits > 0)\n windowSize = 2;\n const MASK = bitMask(windowSize);\n const buckets = new Array(Number(MASK) + 1).fill(zero); // +1 for zero array\n const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize;\n let sum = zero;\n for (let i = lastBits; i >= 0; i -= windowSize) {\n buckets.fill(zero);\n for (let j = 0; j < slength; j++) {\n const scalar = scalars[j];\n const wbits = Number((scalar >> BigInt(i)) & MASK);\n buckets[wbits] = buckets[wbits].add(points[j]);\n }\n let resI = zero; // not using this will do small speed-up, but will lose ct\n // Skip first bucket, because it is zero\n for (let j = buckets.length - 1, sumI = zero; j > 0; j--) {\n sumI = sumI.add(buckets[j]);\n resI = resI.add(sumI);\n }\n sum = sum.add(resI);\n if (i !== 0)\n for (let j = 0; j < windowSize; j++)\n sum = sum.double();\n }\n return sum;\n}\n/**\n * Interleaved wNAF multi-scalar multiplication (MSM, Pa + Qb + Rc + ...) over a FIXED set\n * of points: each point gets a one-time table of odd multiples\n * `[1P, 3P, ..., (2^(W\u22121)\u22121)P]`, and the returned closure evaluates MSMs against those\n * tables. All scalars share one doubling chain (Straus 1964) \u2014 one doubling per scalar bit\n * plus one signed table addition per nonzero width-W wNAF digit (density ~1/(W+1)) \u2014 the\n * \"interleaving\" method of M\u00F6ller, \"Algorithms for multi-exponentiation\" (SAC 2001).\n *\n * Table memory is `L\u22C52^(W\u22122)` points, capped at ~2 GiB. Prefer this over {@link pippenger}\n * when the same points are reused across many MSMs (fixed-base commitments etc.) and up to a\n * few hundred points; prefer pippenger for one-shot MSMs or thousands of points, where\n * bucketing beats per-point tables.\n *\n * Not constant-time (zero digits are skipped): public inputs only.\n * @param c - Curve Point constructor\n * @param points - array of L curve points, captured by the returned closure\n * @param windowSize - window width W in bits, from 2 to Fn.BITS; also capped so the\n * per-closure tables stay under ~2 GiB\n * @returns Function which multiplies points with scalars. The closure accepts at most\n * `points.length` scalars, and omitted trailing scalars are treated as zero.\n * @throws If the point set or precompute window is invalid. {@link Error}\n * @example\n * Interleaved wNAF multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).\n *\n * ```ts\n * import { interleavedMSMUnsafe } from '@noble/curves/abstract/curve.js';\n * import { p256 } from '@noble/curves/nist.js';\n * const msm = interleavedMSMUnsafe(p256.Point, [p256.Point.BASE], 4);\n * const point = msm([3n]);\n * ```\n */\nexport function interleavedMSMUnsafe(c, points, windowSize) {\n validatePointCons(c);\n const fieldN = c.Fn;\n // Signed odd digits need at least width 2 (W=2 is plain NAF with a single-entry table).\n validateW(windowSize, fieldN.BITS, 2);\n validateMSMPoints(points, c);\n validateTableBytes(points.length * 2 ** (windowSize - 2), c.Fp.BYTES);\n const tables = points.map((p) => oddMultiples(p, 2 ** (windowSize - 2)));\n return (scalars) => {\n validateMSMScalars(scalars, fieldN);\n if (scalars.length > points.length)\n throw new Error('array of scalars must not be larger than array of points');\n return wnafWalk(c.ZERO, tables, scalars.map((n) => wnafDigits(n, windowSize)));\n };\n}\nfunction createField(order, field, isLE) {\n if (field) {\n // Reuse supplied field overrides as-is; `isLE` only affects freshly constructed fallback\n // fields, and validateField() below only checks the arithmetic subset, not full byte/cmov\n // behavior.\n if (field.ORDER !== order)\n throw new Error('Field.ORDER must match order: Fp == p, Fn == n');\n validateField(field);\n return field;\n }\n else {\n return Field(order, { isLE });\n }\n}\n/**\n * Validates basic CURVE shape and field membership, then creates fields.\n * This does not prove that the generator is on-curve, that subgroup/order data are consistent, or\n * that the curve equation itself is otherwise sane.\n * @param type - Curve family.\n * @param CURVE - Curve parameters.\n * @param curveOpts - Optional field overrides. See {@link FpFn}:\n * - `Fp` (optional): Optional base-field override.\n * - `Fn` (optional): Optional scalar-field override.\n * @param FpFnLE - Whether field encoding is little-endian.\n * @returns Frozen curve parameters and fields.\n * @throws If the curve parameters or field overrides are invalid. {@link Error}\n * @example\n * Build curve fields from raw constants before constructing a curve instance.\n *\n * ```ts\n * const curve = createCurveFields('weierstrass', {\n * p: 17n,\n * n: 19n,\n * h: 1n,\n * a: 2n,\n * b: 2n,\n * Gx: 5n,\n * Gy: 1n,\n * });\n * ```\n */\nexport function createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) {\n if (type !== 'weierstrass' && type !== 'edwards')\n throw new Error('expected curve type \"weierstrass\" or \"edwards\"');\n if (FpFnLE === undefined)\n FpFnLE = type === 'edwards';\n if (!CURVE || typeof CURVE !== 'object')\n throw new Error(`expected valid ${type} CURVE object`);\n // Validate before reading Fp/Fn so explicit null fails with an options-object error.\n validateObject(curveOpts);\n for (const p of ['p', 'n', 'h']) {\n const val = CURVE[p];\n if (!(isPosBig(val) && val !== _0n))\n throw new Error(`CURVE.${p} must be positive bigint`);\n }\n const Fp = createField(CURVE.p, curveOpts.Fp, FpFnLE);\n const Fn = createField(CURVE.n, curveOpts.Fn, FpFnLE);\n const _b = type === 'weierstrass' ? 'b' : 'd';\n const params = ['Gx', 'Gy', 'a', _b];\n for (const p of params) {\n // @ts-ignore\n if (!Fp.isValid(CURVE[p]))\n throw new Error(`CURVE.${p} must be valid field element of CURVE.Fp`);\n }\n CURVE = Object.freeze(Object.assign({}, CURVE));\n return { CURVE, Fp, Fn };\n}\n/**\n * @param randomSecretKey - Secret-key generator.\n * @param getPublicKey - Public-key derivation helper.\n * @returns Keypair generator.\n * @example\n * Build a `keygen()` helper from existing secret-key and public-key primitives.\n *\n * ```ts\n * import { createKeygen } from '@noble/curves/abstract/curve.js';\n * import { p256 } from '@noble/curves/nist.js';\n * const keygen = createKeygen(p256.utils.randomSecretKey, p256.getPublicKey);\n * const pair = keygen();\n * ```\n */\nexport function createKeygen(randomSecretKey, getPublicKey) {\n return function keygen(seed) {\n const secretKey = randomSecretKey(seed);\n return { secretKey, publicKey: getPublicKey(secretKey) };\n };\n}\n", "/**\n * HMAC: RFC2104 message authentication code.\n * @module\n */\nimport { abytes, aexists, ahash, aoutput, clean, } from \"./utils.js\";\n/**\n * Internal class for HMAC.\n * Accepts any byte key, although RFC 2104 \u00A73 recommends keys at least\n * `HashLen` bytes long.\n */\nexport class _HMAC {\n oHash;\n iHash;\n blockLen;\n outputLen;\n canXOF = false;\n finished = false;\n destroyed = false;\n constructor(hash, key) {\n ahash(hash);\n abytes(key, undefined, 'key');\n this.iHash = hash.create();\n if (typeof this.iHash.update !== 'function')\n throw new Error('expected Hash instance');\n this.blockLen = this.iHash.blockLen;\n this.outputLen = this.iHash.outputLen;\n const blockLen = this.blockLen;\n const pad = new Uint8Array(blockLen);\n // blockLen can be bigger than outputLen\n pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);\n for (let i = 0; i < pad.length; i++)\n pad[i] ^= 0x36;\n this.iHash.update(pad);\n // By doing update (processing of the first block) of the outer hash here,\n // we can re-use it between multiple calls via clone.\n this.oHash = hash.create();\n // Undo internal XOR && apply outer XOR\n for (let i = 0; i < pad.length; i++)\n pad[i] ^= 0x36 ^ 0x5c;\n this.oHash.update(pad);\n clean(pad);\n }\n update(buf) {\n aexists(this);\n this.iHash.update(buf);\n return this;\n }\n digestInto(out) {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n const buf = out.subarray(0, this.outputLen);\n // Reuse the first outputLen bytes for the inner digest; the outer hash consumes them before\n // overwriting that same prefix with the final tag, leaving any oversized tail untouched.\n this.iHash.digestInto(buf);\n this.oHash.update(buf);\n this.oHash.digestInto(buf);\n this.destroy();\n }\n digest() {\n const out = new Uint8Array(this.oHash.outputLen);\n this.digestInto(out);\n return out;\n }\n _cloneInto(to) {\n // Create new instance without calling constructor since the key\n // is already in state and we don't know it.\n to ||= Object.create(Object.getPrototypeOf(this), {});\n const { oHash, iHash, finished, destroyed, blockLen, outputLen, canXOF } = this;\n to = to;\n to.finished = finished;\n to.destroyed = destroyed;\n to.blockLen = blockLen;\n to.outputLen = outputLen;\n to.canXOF = canXOF;\n to.oHash = oHash._cloneInto(to.oHash);\n to.iHash = iHash._cloneInto(to.iHash);\n return to;\n }\n clone() {\n return this._cloneInto();\n }\n destroy() {\n this.destroyed = true;\n this.oHash.destroy();\n this.iHash.destroy();\n }\n}\nexport const hmac = /* @__PURE__ */ (() => {\n const hmac_ = ((hash, key, message) => new _HMAC(hash, key).update(message).digest());\n hmac_.create = (hash, key) => new _HMAC(hash, key);\n return hmac_;\n})();\n", "/**\n * ASN.1 DER (Distinguished Encoding Rules) helpers for ECDSA signatures.\n * Only implements the tiny subset needed for `SEQUENCE(INTEGER r, INTEGER s)`.\n * @module\n */\n/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */\nimport { abignumber, abytes, asafenumber, astring, bytesToNumberBE, numberToHexUnpadded, validateObject, } from \"../utils.js\";\nconst _0n = /* @__PURE__ */ BigInt(0);\n/**\n * @param m - Error message.\n * @example\n * Throw a DER-specific error when signature parsing encounters invalid bytes.\n *\n * ```ts\n * new DERErr('bad der');\n * ```\n */\nexport class DERErr extends Error {\n constructor(m = '') {\n super(m);\n }\n}\n// Plain const so the freezes can live inside the pure initializer of the `DER` export below:\n// bare top-level `Object.freeze(...)` calls would defeat tree-shaking for every importer.\nconst _DER = {\n // asn.1 DER encoding utils\n Err: DERErr,\n // Basic building block is TLV (Tag-Length-Value)\n _tlv: {\n encode: (tag, data) => {\n const { Err: E } = _DER;\n asafenumber(tag, 'tag');\n if (tag < 0 || tag > 255)\n throw new E('tlv.encode: wrong tag');\n astring(data, 'data');\n // Internal helper: callers hand this already-validated hex payload, so we only enforce\n // byte alignment here instead of re-validating every nibble.\n if (data.length & 1)\n throw new E('tlv.encode: unpadded data');\n const dataLen = data.length / 2;\n const len = numberToHexUnpadded(dataLen);\n if ((len.length / 2) & 0b1000_0000)\n throw new E('tlv.encode: long form length too big');\n // length of length with long form flag\n const lenLen = dataLen > 127 ? numberToHexUnpadded((len.length / 2) | 0b1000_0000) : '';\n const t = numberToHexUnpadded(tag);\n return t + lenLen + len + data;\n },\n // v - value, l - left bytes (unparsed)\n decode(tag, data) {\n const { Err: E } = _DER;\n data = abytes(data, undefined, 'DER data');\n let pos = 0;\n if (tag < 0 || tag > 255)\n throw new E('tlv.decode: wrong tag');\n if (data.length < 2 || data[pos++] !== tag)\n throw new E('tlv.decode: wrong tlv');\n const first = data[pos++];\n // First bit of first length byte is the short/long form flag.\n const isLong = !!(first & 0b1000_0000);\n let length = 0;\n if (!isLong)\n length = first;\n else {\n // Long form: [longFlag(1bit), lengthLength(7bit), length (BE)]\n const lenLen = first & 0b0111_1111;\n if (!lenLen)\n throw new E('tlv.decode(long): indefinite length not supported');\n // This would overflow u32 in JS.\n if (lenLen > 4)\n throw new E('tlv.decode(long): byte length is too big');\n const lengthBytes = data.subarray(pos, pos + lenLen);\n if (lengthBytes.length !== lenLen)\n throw new E('tlv.decode: length bytes not complete');\n if (lengthBytes[0] === 0)\n throw new E('tlv.decode(long): zero leftmost byte');\n for (const b of lengthBytes)\n length = (length << 8) | b;\n pos += lenLen;\n if (length < 128)\n throw new E('tlv.decode(long): not minimal encoding');\n }\n const v = data.subarray(pos, pos + length);\n if (v.length !== length)\n throw new E('tlv.decode: wrong value length');\n return { v, l: data.subarray(pos + length) };\n },\n },\n // https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,\n // since we always use positive integers here. It must always be empty:\n // - add zero byte if exists\n // - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)\n _int: {\n encode(num) {\n const { Err: E } = _DER;\n abignumber(num);\n if (num < _0n)\n throw new E('integer: negative integers are not allowed');\n let hex = numberToHexUnpadded(num);\n // Pad with zero byte if negative flag is present\n if (Number.parseInt(hex[0], 16) & 0b1000)\n hex = '00' + hex;\n if (hex.length & 1)\n throw new E('unexpected DER parsing assertion: unpadded hex');\n return hex;\n },\n decode(data) {\n const { Err: E } = _DER;\n if (data.length < 1)\n throw new E('invalid signature integer: empty');\n if (data[0] & 0b1000_0000)\n throw new E('invalid signature integer: negative');\n // Single-byte zero `00` is the canonical DER INTEGER encoding for zero.\n if (data.length > 1 && data[0] === 0x00 && !(data[1] & 0b1000_0000))\n throw new E('invalid signature integer: unnecessary leading zero');\n return bytesToNumberBE(data);\n },\n },\n toSig(bytes, maxScalarBytes) {\n // parse DER signature\n const { Err: E, _int: int, _tlv: tlv } = _DER;\n if (maxScalarBytes !== undefined) {\n asafenumber(maxScalarBytes, 'maxScalarBytes');\n if (maxScalarBytes < 1)\n throw new E('invalid signature: maxScalarBytes must be positive');\n }\n const data = abytes(bytes, undefined, 'signature');\n const { v: seqBytes, l: seqLeftBytes } = tlv.decode(0x30, data);\n if (seqLeftBytes.length)\n throw new E('invalid signature: left bytes after parsing');\n const { v: rBytes, l: rLeftBytes } = tlv.decode(0x02, seqBytes);\n const { v: sBytes, l: sLeftBytes } = tlv.decode(0x02, rLeftBytes);\n if (sLeftBytes.length)\n throw new E('invalid signature: left bytes after parsing');\n // Enforce curve-provided bounds before bytes-to-hex-to-BigInt conversion can amplify memory.\n if (maxScalarBytes !== undefined &&\n (rBytes.length > maxScalarBytes || sBytes.length > maxScalarBytes))\n throw new E('invalid signature: integer too large');\n return { r: int.decode(rBytes), s: int.decode(sBytes) };\n },\n hexFromSig(sig) {\n const { _tlv: tlv, _int: int } = _DER;\n validateObject(sig, { r: 'bigint', s: 'bigint' }, {}, 'sig');\n const rs = tlv.encode(0x02, int.encode(sig.r));\n const ss = tlv.encode(0x02, int.encode(sig.s));\n const seq = rs + ss;\n return tlv.encode(0x30, seq);\n },\n};\n/**\n * ASN.1 DER encoding utilities. ASN is very complex & fragile. Format:\n *\n * [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]\n *\n * Docs: {@link https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/ | Let's Encrypt ASN.1 guide} and\n * {@link https://luca.ntop.org/Teaching/Appunti/asn1.html | Luca Deri's ASN.1 notes}.\n * @example\n * ASN.1 DER encoding utilities.\n *\n * ```ts\n * const der = DER.hexFromSig({ r: 1n, s: 2n });\n * ```\n */\nexport const DER = /* @__PURE__ */ (() => {\n Object.freeze(_DER._tlv);\n Object.freeze(_DER._int);\n return Object.freeze(_DER);\n})();\n", "/**\n * Short Weierstrass curve methods. The formula is: y\u00B2 = x\u00B3 + ax + b.\n *\n * ### Design rationale for types\n *\n * * Interaction between classes from different curves should fail:\n * `k256.Point.BASE.add(p256.Point.BASE)`\n * * For this purpose we want to use `instanceof` operator, which is fast and works during runtime\n * * Different calls of `curve()` would return different classes -\n * `curve(params) !== curve(params)`: if somebody decided to monkey-patch their curve,\n * it won't affect others\n *\n * TypeScript can't infer types for classes created inside a function. Classes is one instance\n * of nominative types in TypeScript and interfaces only check for shape, so it's hard to create\n * unique type for every function call.\n *\n * We can use generic types via some param, like curve opts, but that would:\n * 1. Enable interaction between `curve(params)` and `curve(params)` (curves of same params)\n * which is hard to debug.\n * 2. Params can be generic and we can't enforce them to be constant value:\n * if somebody creates curve from non-constant params,\n * it would be allowed to interact with other curves with non-constant params\n *\n * @todo https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#unique-symbol\n * @module\n */\n/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */\nimport { hmac as nobleHmac } from '@noble/hashes/hmac.js';\nimport { ahash } from '@noble/hashes/utils.js';\nimport { abool, abytes, aInRange, bitLen, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, hexToBytes, isBytes, validateObject, randomBytes as wcRandomBytes, } from \"../utils.js\";\nimport { createCurveFields, createKeygen, mulAddUnsafe, normalizeZ, probeRandomBytes, ScalarMultiplier, validatePointCons, } from \"./curve.js\";\nimport { DER } from \"./der.js\";\nimport { getMinHashLength, invertCt, mapHashToField } from \"./modular.js\";\n// DER codec lives in der.ts; re-exported here because ECDSA signatures are its main consumer.\nexport { DER, DERErr } from \"./der.js\";\n// We construct the basis so `den` is always positive and equals `n`,\n// but the `num` sign depends on the basis, not on the secret value.\n// Exact half-way cases round away from zero, which keeps the split symmetric\n// around the reduced-basis boundaries used by endomorphism decomposition.\nconst divNearest = (num, den) => (num + (num >= 0 ? den : -den) / _2n) / den;\n/** Splits scalar for GLV endomorphism. */\nexport function _splitEndoScalar(k, basis, n) {\n // Split scalar into two such that part is ~half bits: `abs(part) < sqrt(N)`\n // Since part can be negative, we need to do this on point.\n // Callers must provide a reduced GLV basis whose vectors satisfy\n // `a + b * lambda \u2261 0 (mod n)`; this helper only sees the basis and `n`.\n // Reject unreduced scalars instead of silently treating them mod n.\n aInRange('scalar', k, _0n, n);\n // TODO: verifyScalar function which consumes lambda\n const [[a1, b1], [a2, b2]] = basis;\n const c1 = divNearest(b2 * k, n);\n const c2 = divNearest(-b1 * k, n);\n // |k1|/|k2| is < sqrt(N), but can be negative.\n // If we do `k1 mod N`, we'll get big scalar (`> sqrt(N)`): so, we do cheaper negation instead.\n let k1 = k - c1 * a1 - c2 * a2;\n let k2 = -c1 * b1 - c2 * b2;\n const k1neg = k1 < _0n;\n const k2neg = k2 < _0n;\n if (k1neg)\n k1 = -k1;\n if (k2neg)\n k2 = -k2;\n // Double check that resulting scalar is less than half bits of N: the wNAF pair walk\n // relies on the halves being short. This should only happen on wrong bases.\n // Also, the math inside is complex enough that this guard is worth keeping.\n const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n; // Half bits of N\n if (k1 < _0n || k1 >= MAX_NUM || k2 < _0n || k2 >= MAX_NUM) {\n throw new Error('splitScalar (endomorphism): failed for k');\n }\n return { k1neg, k1, k2neg, k2 };\n}\nfunction validateSigFormat(format) {\n if (!['compact', 'recovered', 'der'].includes(format))\n throw new Error('Signature format must be \"compact\", \"recovered\", or \"der\"');\n return format;\n}\nfunction validateSigOpts(opts, def) {\n validateObject(opts);\n const optsn = {};\n // Normalize only the declared option subset from `def`; unknown keys are\n // intentionally ignored so shared / superset option bags stay valid here too.\n // `extraEntropy` stays an opaque payload until the signing path consumes it.\n for (let optName of Object.keys(def)) {\n // @ts-ignore\n optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];\n }\n abool(optsn.lowS, 'lowS');\n abool(optsn.prehash, 'prehash');\n if (optsn.format !== undefined)\n validateSigFormat(optsn.format);\n return optsn;\n}\n// Be friendly to bad ECMAScript parsers by not using bigint literals\n// prettier-ignore\nconst _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3), _4n = /* @__PURE__ */ BigInt(4);\n/**\n * Creates weierstrass Point constructor, based on specified curve options.\n *\n * See {@link WeierstrassOpts}.\n * @param params - Curve parameters. See {@link WeierstrassOpts}.\n * @param extraOpts - Optional helpers and overrides. See {@link WeierstrassExtraOpts}.\n * @returns Weierstrass point constructor.\n * @throws If the curve parameters, overrides, or point codecs are invalid. {@link Error}\n *\n * @example\n * Construct a point type from explicit Weierstrass curve parameters.\n *\n * ```js\n * const opts = {\n * p: 0xfffffffffffffffffffffffffffffffeffffac73n,\n * n: 0x100000000000000000001b8fa16dfab9aca16b6b3n,\n * h: 1n,\n * a: 0n,\n * b: 7n,\n * Gx: 0x3b4c382ce37aa192a4019e763036f4f5dd4d7ebbn,\n * Gy: 0x938cf935318fdced6bc28286531733c3f03c4feen,\n * };\n * const secp160k1_Point = weierstrass(opts);\n * ```\n */\nexport function weierstrass(params, extraOpts = {}) {\n const validated = createCurveFields('weierstrass', params, extraOpts);\n const Fp = validated.Fp;\n const Fn = validated.Fn;\n let CURVE = validated.CURVE;\n const { h: cofactor, n: CURVE_ORDER } = CURVE;\n validateObject(extraOpts, {}, {\n allowInfinityPoint: 'boolean',\n clearCofactor: 'function',\n isTorsionFree: 'function',\n fromBytes: 'function',\n toBytes: 'function',\n endo: 'object',\n randomBytes: 'function',\n });\n // Snapshot every hook and take an owned copy of nested GLV configuration. Later mutations of\n // the caller's options must not change arithmetic, subgroup, or codec policy.\n const { endo: endoOpts, allowInfinityPoint, clearCofactor, isTorsionFree, fromBytes, toBytes, } = extraOpts;\n const randomBytes = extraOpts.randomBytes === undefined ? wcRandomBytes : extraOpts.randomBytes;\n if (endoOpts) {\n if (!Fp.is0(CURVE.a) || typeof endoOpts.beta !== 'bigint' || !Array.isArray(endoOpts.basises)) {\n throw new Error('invalid endo: expected \"beta\": bigint and \"basises\": array');\n }\n }\n const endo = endoOpts\n ? {\n beta: endoOpts.beta,\n basises: endoOpts.basises.map((basis) => [...basis]),\n }\n : undefined;\n const lengths = getWLengths(Fp, Fn);\n function assertCompressionIsSupported() {\n if (!Fp.isOdd)\n throw new Error('compression is not supported: Field does not have .isOdd()');\n }\n // Implements IEEE P1363 point encoding\n function pointToBytes(_c, point, isCompressed) {\n // Handle infinity before toAffine(), which maps ZERO to (0, 0) silently: without this the\n // encoder would emit an all-zero byte string that decodes back as a different point. Only\n // curves that opt into infinity as a public point value get the SEC 1 byte form.\n if (point.is0()) {\n if (!allowInfinityPoint)\n throw new Error('bad point: ZERO');\n return Uint8Array.of(0); // SEC 1 v2.0 \u00A72.3.3\n }\n const { x, y } = point.toAffine();\n const bx = Fp.toBytes(x);\n abool(isCompressed, 'isCompressed');\n if (isCompressed) {\n assertCompressionIsSupported();\n const hasEvenY = !Fp.isOdd(y);\n return concatBytes(pprefix(hasEvenY), bx);\n }\n else {\n return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y));\n }\n }\n function pointFromBytes(bytes) {\n abytes(bytes, undefined, 'Point');\n const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65\n const length = bytes.length;\n const head = bytes[0];\n const tail = bytes.subarray(1);\n // SEC 1 v2.0 \u00A72.3.4 decodes 0x00 as infinity, but \u00A73.2.2 rejects infinity as a public key.\n // Leaving 0x00 undecodable by default makes that rejection structural rather than a check:\n // callers reuse this parser as the strict key boundary, and infinity is simply not\n // expressible. Curves where infinity is a real wire value opt into the codec instead.\n // secp256k1 crosstests show OpenSSL raw point codecs accept 0x00 too.\n if (allowInfinityPoint && length === 1 && head === 0x00)\n return { x: Fp.ZERO, y: Fp.ZERO };\n // No actual validation is done here: use .assertValidity()\n if (length === comp && (head === 0x02 || head === 0x03)) {\n const x = Fp.fromBytes(tail);\n if (!Fp.isValid(x))\n throw new Error('bad point: is not on curve, wrong x');\n const y2 = weierstrassEquation(x); // y\u00B2 = x\u00B3 + ax + b\n let y;\n try {\n y = Fp.sqrt(y2); // y = y\u00B2 ^ (p+1)/4\n }\n catch (sqrtError) {\n const err = sqrtError instanceof Error ? ': ' + sqrtError.message : '';\n throw new Error('bad point: is not on curve, sqrt error' + err);\n }\n assertCompressionIsSupported();\n const evenY = Fp.isOdd(y);\n const evenH = (head & 1) === 1; // ECDSA-specific\n if (evenH !== evenY)\n y = Fp.neg(y);\n return { x, y };\n }\n else if (length === uncomp && head === 0x04) {\n // TODO: more checks\n const L = Fp.BYTES;\n const x = Fp.fromBytes(tail.subarray(0, L));\n const y = Fp.fromBytes(tail.subarray(L, L * 2));\n if (!isValidXY(x, y))\n throw new Error('bad point: is not on curve');\n return { x, y };\n }\n else {\n throw new Error(`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`);\n }\n }\n const encodePoint = toBytes === undefined ? pointToBytes : toBytes;\n const decodePoint = fromBytes === undefined ? pointFromBytes : fromBytes;\n // Hoisted from double() / add(): curve params never change after construction.\n // Koblitz curves (a=0, e.g. secp256k1) skip the three a-multiplications per operation;\n // the selection depends only on public curve constants.\n const b3 = Fp.mul(CURVE.b, _3n);\n const mulA = Fp.is0(CURVE.a) ? (_) => Fp.ZERO : (x) => Fp.mul(CURVE.a, x);\n function weierstrassEquation(x) {\n const x2 = Fp.sqr(x);\n const x3 = Fp.mul(x2, x);\n return Fp.add(Fp.add(x3, Fp.mul(x, CURVE.a)), CURVE.b); // x\u00B3 + a * x + b\n }\n // TODO: move top-level\n /** Checks whether equation holds for given x, y: y\u00B2 == x\u00B3 + ax + b */\n function isValidXY(x, y) {\n const left = Fp.sqr(y);\n const right = weierstrassEquation(x); // x\u00B3 + ax + b\n return Fp.eql(left, right);\n }\n // Keep constructor-time generator validation cheap: callers are responsible for supplying the\n // correct prime-order base point, while eager subgroup checks here would slow heavy module imports.\n // Test 1: equation y\u00B2 = x\u00B3 + ax + b should work for generator point.\n if (!isValidXY(CURVE.Gx, CURVE.Gy))\n throw new Error('bad curve params: generator point');\n // Test 2: discriminant \u0394 part should be non-zero: 4a\u00B3 + 27b\u00B2 != 0.\n // Guarantees curve is genus-1, smooth (non-singular).\n const _4a3 = Fp.mul(Fp.pow(CURVE.a, _3n), _4n);\n const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));\n if (Fp.is0(Fp.add(_4a3, _27b2)))\n throw new Error('bad curve params: a or b');\n /** Asserts coordinate is valid: 0 <= n < Fp.ORDER. */\n function acoord(title, n, banZero = false) {\n if (!Fp.isValid(n) || (banZero && Fp.is0(n)))\n throw new Error(`bad point coordinate ${title}`);\n // Extension-field elements are objects. Keep point coordinates detached from caller-owned\n // objects so their mutation cannot invalidate cached on-curve / subgroup checks. Primitive\n // field elements (bigints in shipped prime fields) are already immutable values.\n return typeof n === 'object' && n !== null ? Fp.create(n) : n;\n }\n function aprjpoint(other) {\n if (!(other instanceof Point))\n throw new Error('Weierstrass Point expected');\n }\n function splitEndoScalarN(k) {\n if (!endo || !endo.basises)\n throw new Error('no endo');\n return _splitEndoScalar(k, endo.basises, Fn.ORDER);\n }\n /**\n * Appends a (point, scalar) pair to the inputs of a vartime wNAF walk\n * ({@link mulAddUnsafe}). With GLV endomorphism the scalar is split into two half-width\n * pairs against P and \u03C8(P) = (\u03B2\u22C5x, y), halving the walk's shared doubling chain;\n * split signs fold into the points.\n */\n function pushWnafPair(points, scalars, p, k) {\n if (!Fn.isValid(k))\n throw new RangeError('invalid scalar: out of range'); // 0 is valid\n if (endo) {\n const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(k);\n const psi = new Point(Fp.mul(p.X, endo.beta), p.Y, p.Z);\n points.push(k1neg ? p.negate() : p, k2neg ? psi.negate() : psi);\n scalars.push(k1, k2);\n }\n else {\n points.push(p);\n scalars.push(k);\n }\n }\n // Successful assertValidity() results are cached: Point instances are frozen at construction,\n // so on-curve + subgroup facts cannot change afterwards. Only success is cached \u2014 invalid\n // points re-throw on every call. This matters most for pairing curves, where subgroup checks\n // cost a scalar multiplication and the same instance is re-validated across layers\n // (signature fromBytes, pairingBatch) or across repeated verifies with a cached public key.\n const validityCache = new WeakSet();\n /**\n * Projective Point works in 3d / projective (homogeneous) coordinates:(X, Y, Z) \u220B (x=X/Z, y=Y/Z).\n * Default Point works in 2d / affine coordinates: (x, y).\n * We're doing calculations in projective, because its operations don't require costly inversion.\n */\n class Point {\n static BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);\n static ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO);\n static Fp = Fp;\n static Fn = Fn;\n X;\n Y;\n Z;\n /** Does NOT validate if the point is valid. Use `.assertValidity()`. */\n constructor(X, Y, Z) {\n this.X = acoord('x', X);\n // This is not just about ZERO / infinity: ambient curves can have real\n // finite points with y=0. Those points are 2-torsion, so they cannot lie\n // in the odd prime-order subgroups this point type is meant to represent.\n this.Y = acoord('y', Y, true);\n this.Z = acoord('z', Z);\n Object.freeze(this);\n }\n static CURVE() {\n return CURVE;\n }\n /** Does NOT validate if the point is valid. Use `.assertValidity()`. */\n static fromAffine(p) {\n const { x, y } = p || {};\n if (!p || !Fp.isValid(x) || !Fp.isValid(y))\n throw new Error('invalid affine point');\n if (p instanceof Point)\n throw new Error('projective point not allowed');\n // (0, 0) would've produced (0, 0, 1) - instead, we need (0, 1, 0)\n if (Fp.is0(x) && Fp.is0(y))\n return Point.ZERO;\n return new Point(x, y, Fp.ONE);\n }\n static fromBytes(bytes) {\n const P = Point.fromAffine(decodePoint(abytes(bytes, undefined, 'point')));\n P.assertValidity();\n return P;\n }\n static fromHex(hex) {\n return Point.fromBytes(hexToBytes(hex));\n }\n get x() {\n return this.toAffine().x;\n }\n get y() {\n return this.toAffine().y;\n }\n /**\n * @param isLazy - true will defer table computation until the first multiplication\n */\n precompute(windowSize = 6, isLazy = true) {\n wnaf.setWindowSize(this, windowSize);\n if (!isLazy)\n this.multiply(_3n); // random number\n return this;\n }\n // TODO: return `this`\n /** A point on curve is valid if it conforms to equation. */\n assertValidity() {\n const p = this;\n if (p.is0()) {\n // (0, 1, 0) aka ZERO is invalid in most contexts.\n // In BLS, ZERO can be serialized, so we allow it.\n // Keep the accepted infinity encoding canonical: projective-equivalent (X, Y, 0) points\n // like (1, 1, 0) compare equal to ZERO, but only (0, 1, 0) should pass this guard.\n if (allowInfinityPoint && Fp.is0(p.X) && Fp.eql(p.Y, Fp.ONE) && Fp.is0(p.Z))\n return;\n throw new Error('bad point: ZERO');\n }\n if (validityCache.has(p))\n return;\n // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`\n const { x, y } = p.toAffine();\n if (!Fp.isValid(x) || !Fp.isValid(y))\n throw new Error('bad point: x or y not field elements');\n if (!isValidXY(x, y))\n throw new Error('bad point: equation left != right');\n if (!p.isTorsionFree())\n throw new Error('bad point: not in prime-order subgroup');\n validityCache.add(p);\n }\n hasEvenY() {\n const { y } = this.toAffine();\n if (!Fp.isOdd)\n throw new Error(\"Field doesn't support isOdd\");\n return !Fp.isOdd(y);\n }\n /** Compare one point to another. */\n equals(other) {\n aprjpoint(other);\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const { X: X2, Y: Y2, Z: Z2 } = other;\n const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));\n const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));\n return U1 && U2;\n }\n /** Flips point to one corresponding to (x, -y) in Affine coordinates. */\n negate() {\n return new Point(this.X, Fp.neg(this.Y), this.Z);\n }\n // Renes-Costello-Batina exception-free doubling formula.\n // There is 30% faster Jacobian formula, but it is not complete.\n // https://eprint.iacr.org/2015/1060, algorithm 3\n // Cost: 8M + 3S + 3*a + 2*b3 + 15add.\n double() {\n const { X: X1, Y: Y1, Z: Z1 } = this;\n let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore\n let t0 = Fp.mul(X1, X1); // step 1\n let t1 = Fp.mul(Y1, Y1);\n let t2 = Fp.mul(Z1, Z1);\n let t3 = Fp.mul(X1, Y1);\n t3 = Fp.add(t3, t3); // step 5\n Z3 = Fp.mul(X1, Z1);\n Z3 = Fp.add(Z3, Z3);\n X3 = mulA(Z3);\n Y3 = Fp.mul(b3, t2);\n Y3 = Fp.add(X3, Y3); // step 10\n X3 = Fp.sub(t1, Y3);\n Y3 = Fp.add(t1, Y3);\n Y3 = Fp.mul(X3, Y3);\n X3 = Fp.mul(t3, X3);\n Z3 = Fp.mul(b3, Z3); // step 15\n t2 = mulA(t2);\n t3 = Fp.sub(t0, t2);\n t3 = mulA(t3);\n t3 = Fp.add(t3, Z3);\n Z3 = Fp.add(t0, t0); // step 20\n t0 = Fp.add(Z3, t0);\n t0 = Fp.add(t0, t2);\n t0 = Fp.mul(t0, t3);\n Y3 = Fp.add(Y3, t0);\n t2 = Fp.mul(Y1, Z1); // step 25\n t2 = Fp.add(t2, t2);\n t0 = Fp.mul(t2, t3);\n X3 = Fp.sub(X3, t0);\n Z3 = Fp.mul(t2, t1);\n Z3 = Fp.add(Z3, Z3); // step 30\n Z3 = Fp.add(Z3, Z3);\n return new Point(X3, Y3, Z3);\n }\n // Renes-Costello-Batina exception-free addition formula.\n // There is 30% faster Jacobian formula, but it is not complete.\n // https://eprint.iacr.org/2015/1060, algorithm 1\n // Cost: 12M + 0S + 3*a + 3*b3 + 23add.\n add(other) {\n aprjpoint(other);\n const { X: X1, Y: Y1, Z: Z1 } = this;\n const { X: X2, Y: Y2, Z: Z2 } = other;\n let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore\n let t0 = Fp.mul(X1, X2); // step 1\n let t1 = Fp.mul(Y1, Y2);\n let t2 = Fp.mul(Z1, Z2);\n let t3 = Fp.add(X1, Y1);\n let t4 = Fp.add(X2, Y2); // step 5\n t3 = Fp.mul(t3, t4);\n t4 = Fp.add(t0, t1);\n t3 = Fp.sub(t3, t4);\n t4 = Fp.add(X1, Z1);\n let t5 = Fp.add(X2, Z2); // step 10\n t4 = Fp.mul(t4, t5);\n t5 = Fp.add(t0, t2);\n t4 = Fp.sub(t4, t5);\n t5 = Fp.add(Y1, Z1);\n X3 = Fp.add(Y2, Z2); // step 15\n t5 = Fp.mul(t5, X3);\n X3 = Fp.add(t1, t2);\n t5 = Fp.sub(t5, X3);\n Z3 = mulA(t4);\n X3 = Fp.mul(b3, t2); // step 20\n Z3 = Fp.add(X3, Z3);\n X3 = Fp.sub(t1, Z3);\n Z3 = Fp.add(t1, Z3);\n Y3 = Fp.mul(X3, Z3);\n t1 = Fp.add(t0, t0); // step 25\n t1 = Fp.add(t1, t0);\n t2 = mulA(t2);\n t4 = Fp.mul(b3, t4);\n t1 = Fp.add(t1, t2);\n t2 = Fp.sub(t0, t2); // step 30\n t2 = mulA(t2);\n t4 = Fp.add(t4, t2);\n t0 = Fp.mul(t1, t4);\n Y3 = Fp.add(Y3, t0);\n t0 = Fp.mul(t5, t4); // step 35\n X3 = Fp.mul(t3, X3);\n X3 = Fp.sub(X3, t0);\n t0 = Fp.mul(t3, t1);\n Z3 = Fp.mul(t5, Z3);\n Z3 = Fp.add(Z3, t0); // step 40\n return new Point(X3, Y3, Z3);\n }\n subtract(other) {\n // Validate before calling `negate()` so wrong inputs fail with the point guard\n // instead of leaking a foreign `negate()` error.\n aprjpoint(other);\n return this.add(other.negate());\n }\n is0() {\n return this.equals(Point.ZERO);\n }\n /**\n * Constant time multiplication.\n * Uses precomputed tables (signed fixed-window wNAF) when available.\n * Uses scalar blinding and avoids endomorphism splitting in the secret-scalar path.\n * @param scalar - by which the point would be multiplied\n * @returns New point\n */\n multiply(scalar) {\n // Keep the subgroup-scalar contract strict instead of reducing 0 / n to ZERO.\n // In key/signature-style callers, those values usually mean broken hash/scalar plumbing,\n // and failing closed is safer than silently producing the identity point.\n if (!Fn.isValidNot0(scalar))\n throw new RangeError('invalid scalar: out of range'); // 0 is invalid\n const { p, f } = wnaf.mulSecret(this, scalar, cofactor, normalize);\n return normalize([p, f])[0];\n }\n /**\n * Non-constant-time multiplication. Uses width-4 wNAF with GLV endomorphism splitting\n * when available (two half-width scalars sharing one halved doubling chain).\n * It's faster, but should only be used when you don't care about\n * an exposed secret key e.g. sig verification, which works over *public* keys.\n */\n multiplyUnsafe(scalar) {\n const p = this;\n const sc = scalar;\n // Public-scalar callers may need 0, but n and larger values stay rejected here too.\n // Reducing them mod n would turn bad caller input into an accidental identity point.\n if (!Fn.isValid(sc))\n throw new RangeError('invalid scalar: out of range'); // 0 is valid\n if (sc === _0n || p.is0())\n return Point.ZERO;\n if (sc === _1n)\n return p;\n if (wnaf.hasWindowSize(this))\n return wnaf.mulUnsafe(p, sc, normalize); // precomputes\n const points = [];\n const scalars = [];\n pushWnafPair(points, scalars, p, sc);\n return mulAddUnsafe(Point, points, scalars);\n }\n /**\n * Non-constant-time double-scalar multiplication `a\u22C5this + b\u22C5other` (Strauss\u2013Shamir).\n * Both walks share one doubling chain via {@link mulAddUnsafe}, and GLV endomorphism\n * (when available) halves the chain again by splitting each scalar into two half-width\n * parts. Used by ECDSA verification and public-key recovery for `R = u1\u22C5G + u2\u22C5P`.\n * Only for public scalars.\n */\n mulAddUnsafe(a, other, b) {\n aprjpoint(other);\n const points = [];\n const scalars = [];\n pushWnafPair(points, scalars, this, a);\n pushWnafPair(points, scalars, other, b);\n return mulAddUnsafe(Point, points, scalars);\n }\n /**\n * Converts Projective point to affine (x, y) coordinates.\n * (X, Y, Z) \u220B (x=X/Z, y=Y/Z).\n * @param invertedZ - Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch\n */\n toAffine(invertedZ) {\n const p = this;\n let iz = invertedZ;\n if (iz != null && !Fp.isValid(iz))\n throw new RangeError('\"invertedZ\" expected valid field element');\n const { X, Y, Z } = p;\n // Fast-path for normalized points\n if (Fp.eql(Z, Fp.ONE))\n return { x: X, y: Y };\n const is0 = p.is0();\n // If invZ was 0, we return zero point. However we still want to execute\n // all operations, so we replace invZ with a random number, 1.\n if (iz == null)\n iz = is0 ? Fp.ONE : Fp.inv(Z);\n const x = Fp.mul(X, iz);\n const y = Fp.mul(Y, iz);\n const zz = Fp.mul(Z, iz);\n if (is0)\n return { x: Fp.ZERO, y: Fp.ZERO };\n if (!Fp.eql(zz, Fp.ONE))\n throw new Error('invZ was invalid');\n return { x, y };\n }\n /**\n * Checks whether Point is free of torsion elements (is in prime subgroup).\n * Always torsion-free for cofactor=1 curves.\n */\n isTorsionFree() {\n if (cofactor === _1n)\n return true;\n if (isTorsionFree)\n return isTorsionFree(Point, this);\n // unsafe() will use the uncached wNAF path internally, since CURVE_ORDER >= Fn.ORDER\n return wnaf.mulUnsafe(this, CURVE_ORDER).is0();\n }\n clearCofactor() {\n if (cofactor === _1n)\n return this; // Fast-path\n if (clearCofactor)\n return clearCofactor(Point, this);\n // Default fallback assumes the cofactor fits the usual subgroup-scalar\n // multiplyUnsafe() contract. Curves with larger / structured cofactors\n // should define a clearCofactor override anyway (e.g. psi/Frobenius maps).\n return this.multiplyUnsafe(cofactor);\n }\n isSmallOrder() {\n if (cofactor === _1n)\n return this.is0(); // Fast-path\n return this.clearCofactor().is0();\n }\n toBytes(isCompressed = true) {\n abool(isCompressed, 'isCompressed');\n // assertValidity() covers on-curve and subgroup membership. The encoder below repeats the\n // infinity check rather than relying on this call, so it stays correct for any caller.\n this.assertValidity();\n return encodePoint(Point, this, isCompressed);\n }\n toHex(isCompressed = true) {\n return bytesToHex(this.toBytes(isCompressed));\n }\n toString() {\n return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;\n }\n }\n const normalize = (points) => normalizeZ(Point, points);\n const wnaf = new ScalarMultiplier(Point, randomBytes);\n // Enable W=6 wNAF precomputes. Slows down first publicKey computation.\n // Disable for tiny toy curves, with scalar fields < 6 bits.\n if (wnaf.bits >= 6)\n Point.BASE.precompute(6);\n Object.freeze(Point.prototype);\n Object.freeze(Point);\n return Point;\n}\n// Points start with byte 0x02 when y is even; otherwise 0x03\nfunction pprefix(hasEvenY) {\n return Uint8Array.of(hasEvenY ? 0x02 : 0x03);\n}\nfunction getWLengths(Fp, Fn) {\n return {\n secretKey: Fn.BYTES,\n publicKey: 1 + Fp.BYTES,\n publicKeyUncompressed: 1 + 2 * Fp.BYTES,\n publicKeyHasPrefix: true,\n // Raw compact `(r || s)` signature width; DER and recovered signatures use\n // different lengths outside this helper.\n signature: 2 * Fn.BYTES,\n };\n}\n/**\n * Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.\n * This helper ensures no signature functionality is present. Less code, smaller bundle size.\n * @param Point - Weierstrass point constructor.\n * @param ecdhOpts - Optional randomness helpers:\n * - `randomBytes` (optional): Optional RNG override.\n * @returns ECDH helper namespace.\n * @example\n * Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.\n *\n * ```ts\n * import { ecdh } from '@noble/curves/abstract/weierstrass.js';\n * import { p256 } from '@noble/curves/nist.js';\n * const dh = ecdh(p256.Point);\n * const alice = dh.keygen();\n * const shared = dh.getSharedSecret(alice.secretKey, alice.publicKey);\n * ```\n */\nexport function ecdh(Point, ecdhOpts = {}) {\n validatePointCons(Point);\n const { Fn } = Point;\n const randomBytes_ = ecdhOpts.randomBytes === undefined ? wcRandomBytes : ecdhOpts.randomBytes;\n // Keep the advertised seed length aligned with mapHashToField(), which keeps a hard 16-byte\n // minimum even on toy curves.\n const lengths = Object.assign(getWLengths(Point.Fp, Fn), {\n seed: Math.max(getMinHashLength(Fn.ORDER), 16),\n });\n function isValidSecretKey(secretKey) {\n try {\n const num = Fn.fromBytes(secretKey);\n return Fn.isValidNot0(num);\n }\n catch (error) {\n return false;\n }\n }\n function isValidPublicKey(publicKey, isCompressed) {\n const { publicKey: comp, publicKeyUncompressed } = lengths;\n try {\n const l = publicKey.length;\n if (isCompressed === true && l !== comp)\n return false;\n if (isCompressed === false && l !== publicKeyUncompressed)\n return false;\n // SEC 1 \u00A73.2.2: the identity is never a valid public key, even on curves whose codec\n // can decode it.\n return !Point.fromBytes(publicKey).is0();\n }\n catch (error) {\n return false;\n }\n }\n /**\n * Produces cryptographically secure secret key from random of size\n * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.\n */\n function randomSecretKey(seed) {\n seed = seed === undefined ? randomBytes_(lengths.seed) : seed;\n return mapHashToField(abytes(seed, lengths.seed, 'seed'), Fn.ORDER);\n }\n /**\n * Computes public key for a secret key. Checks for validity of the secret key.\n * @param isCompressed - whether to return compact (default), or full key\n * @returns Public key, full when isCompressed=false; short when isCompressed=true\n */\n function getPublicKey(secretKey, isCompressed = true) {\n return Point.BASE.multiply(Fn.fromBytes(secretKey)).toBytes(isCompressed);\n }\n /**\n * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.\n */\n function isProbPub(item) {\n const { secretKey, publicKey, publicKeyUncompressed } = lengths;\n const allowedLengths = Fn._lengths;\n if (!isBytes(item))\n return undefined;\n const l = abytes(item, undefined, 'key').length;\n const isPub = l === publicKey || l === publicKeyUncompressed;\n const isSec = l === secretKey || !!allowedLengths?.includes(l);\n // P-521 accepts both 65- and 66-byte secret keys, so overlapping lengths stay ambiguous.\n if (isPub && isSec)\n return undefined;\n return isPub;\n }\n /**\n * ECDH (Elliptic Curve Diffie Hellman).\n * Computes encoded shared point from secret key A and public key B.\n * Checks: 1) secret key validity 2) shared key is on-curve.\n * Does NOT hash the result or expose the SEC 1 x-coordinate-only `z`.\n * Returns the encoded shared point on purpose: callers that need `x_P`\n * can derive it from the encoded point, but `x_P` alone cannot recover the\n * point/parity back.\n * This helper only exposes the fully validated public-key path, not cofactor DH.\n * @param isCompressed - whether to return compact (default), or full key\n * @returns shared point encoding\n */\n function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {\n if (isProbPub(secretKeyA) === true)\n throw new Error('first arg must be private key');\n if (isProbPub(publicKeyB) === false)\n throw new Error('second arg must be public key');\n const s = Fn.fromBytes(secretKeyA);\n const b = Point.fromBytes(publicKeyB); // checks for being on-curve\n if (b.is0())\n throw new Error('invalid public key: point at infinity');\n return b.multiply(s).toBytes(isCompressed);\n }\n const utils = {\n isValidSecretKey,\n isValidPublicKey,\n randomSecretKey,\n };\n const keygen = createKeygen(randomSecretKey, getPublicKey);\n Object.freeze(utils);\n Object.freeze(lengths);\n return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });\n}\n/**\n * Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.\n *\n * @param Point - created using {@link weierstrass} function\n * @param hash - used for 1) message prehash-ing 2) k generation in `sign`, using hmac_drbg(hash)\n * @param ecdsaOpts - rarely needed, see {@link ECDSAOpts}:\n * - `lowS`: Default low-S policy.\n * - `hmac`: HMAC implementation used by RFC6979 DRBG.\n * - `randomBytes`: Optional RNG override.\n * - `bits2int`: Optional hash-to-int conversion override.\n * - `bits2int_modN`: Optional hash-to-int-mod-n conversion override.\n *\n * @returns ECDSA helper namespace.\n * @example\n * Create an ECDSA signer/verifier bundle for one curve implementation.\n *\n * ```ts\n * import { ecdsa } from '@noble/curves/abstract/weierstrass.js';\n * import { p256 } from '@noble/curves/nist.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const p256ecdsa = ecdsa(p256.Point, sha256);\n * const { secretKey, publicKey } = p256ecdsa.keygen();\n * const msg = new TextEncoder().encode('hello noble');\n * const sig = p256ecdsa.sign(msg, secretKey);\n * const isValid = p256ecdsa.verify(sig, msg, publicKey);\n * ```\n */\nexport function ecdsa(Point, hash, ecdsaOpts = {}) {\n validatePointCons(Point);\n // Custom hash / bits2int hooks are treated as pure functions over validated caller-owned bytes.\n const hash_ = hash;\n ahash(hash_);\n validateObject(ecdsaOpts, {}, {\n hmac: 'function',\n lowS: 'boolean',\n randomBytes: 'function',\n bits2int: 'function',\n bits2int_modN: 'function',\n });\n const opts = Object.assign({}, ecdsaOpts);\n const randomBytes = opts.randomBytes === undefined ? wcRandomBytes : opts.randomBytes;\n const hmac = opts.hmac === undefined\n ? (key, msg) => nobleHmac(hash_, key, msg)\n : opts.hmac;\n const { Fp, Fn } = Point;\n const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;\n // Nonce-inversion blinding in k2sig draws `getMinHashLength(n)` bytes per sign. Probe the RNG\n // once (see {@link probeRandomBytes}, shared with ScalarMultiplier): in environments without\n // working randomness, signing downgrades to Fermat inversion (invertCt) instead of throwing on\n // every sign(). The shape of returned bytes is still validated (by mapHashToField) on every\n // blinded call, where breakage fails closed.\n const blindLength = getMinHashLength(CURVE_ORDER);\n const csprng = probeRandomBytes(randomBytes, blindLength);\n const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, opts);\n const defaultSigOpts = {\n prehash: true,\n lowS: typeof opts.lowS === 'boolean' ? opts.lowS : true,\n format: 'compact',\n extraEntropy: false,\n };\n // SEC 1 4.1.6 public-key recovery tries x = r + jn for j = 0..h. Our recovered-signature\n // format only stores one overflow bit, so it can only distinguish q.x = r from q.x = r + n.\n // A third lift would have the form q.x = r + 2n. Since valid ECDSA r is in 1..n-1, the\n // smallest such lift is 1 + 2n, not 2n.\n const hasLargeRecoveryLifts = CURVE_ORDER * _2n + _1n < Fp.ORDER;\n function isBiggerThanHalfOrder(number) {\n const HALF = CURVE_ORDER >> _1n;\n return number > HALF;\n }\n function validateRS(title, num) {\n if (!Fn.isValidNot0(num))\n throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);\n return num;\n }\n function assertFieldSignIsSupported() {\n if (!Fp.isOdd)\n throw new Error(\"Field doesn't support isOdd\");\n }\n // Recovery id of an affine point (x, y) whose x reduces to signature `r` mod n:\n // bit 0 = y parity, bit 1 = x overflowed the group order (x = r + n).\n function getRecoveryBit(x, y, r) {\n assertFieldSignIsSupported();\n return (x === r ? 0 : 2) | Number(Fp.isOdd(y));\n }\n function assertRecoverableCurve() {\n // ECDSA recovery only supports curves where the current recovery id can distinguish\n // q.x = r and q.x = r + n; larger lifts may need additional `r + n*i` branches.\n // SEC 1 4.1.6 recovers candidates via x = r + jn, but this format only encodes j = 0 or 1.\n // The next possible candidate is q.x = r + 2n, and its smallest valid value is 1 + 2n.\n // To easily get i, we either need to:\n // a. increase amount of valid recid values (4, 5...); OR\n // b. prohibit recovered signatures for those curves.\n if (hasLargeRecoveryLifts)\n throw new Error('\"recovered\" sig type is not supported for cofactor >2 curves');\n }\n function validateSigLength(bytes, format) {\n validateSigFormat(format);\n const size = lengths.signature;\n const sizer = format === 'compact' ? size : format === 'recovered' ? size + 1 : undefined;\n return abytes(bytes, sizer);\n }\n /**\n * ECDSA signature with its (r, s) properties. Supports compact, recovered & DER representations.\n */\n class Signature {\n r;\n s;\n recovery;\n constructor(r, s, recovery) {\n this.r = validateRS('r', r); // r in [1..N-1];\n this.s = validateRS('s', s); // s in [1..N-1];\n if (recovery != null) {\n assertRecoverableCurve();\n if (![0, 1, 2, 3].includes(recovery))\n throw new Error('invalid recovery id');\n this.recovery = recovery;\n }\n Object.freeze(this);\n }\n static fromBytes(bytes, format = defaultSigOpts.format) {\n validateSigLength(bytes, format);\n let recid;\n if (format === 'der') {\n // Valid scalar INTEGERs use at most Fn.BYTES plus one DER sign-padding byte. Keep the\n // total bound conservative so malformed inputs are rejected before parsing any INTEGER.\n if (bytes.length > 2 * Fn.BYTES + 16)\n throw new DER.Err('invalid signature: DER signature too long');\n const { r, s } = DER.toSig(abytes(bytes), Fn.BYTES + 1);\n return new Signature(r, s);\n }\n if (format === 'recovered') {\n recid = bytes[0];\n format = 'compact';\n bytes = bytes.subarray(1);\n }\n const L = lengths.signature / 2;\n const r = bytes.subarray(0, L);\n const s = bytes.subarray(L, L * 2);\n return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);\n }\n static fromHex(hex, format) {\n return this.fromBytes(hexToBytes(hex), format);\n }\n assertRecovery() {\n const { recovery } = this;\n if (recovery == null)\n throw new Error('invalid recovery id: must be present');\n return recovery;\n }\n addRecoveryBit(recovery) {\n return new Signature(this.r, this.s, recovery);\n }\n // Unlike the top-level helper below, this method expects a digest that has\n // already been hashed to the curve's message representative.\n recoverPublicKey(messageHash) {\n const { r, s } = this;\n const recovery = this.assertRecovery();\n const radj = recovery === 2 || recovery === 3 ? r + CURVE_ORDER : r;\n if (!Fp.isValid(radj))\n throw new Error('invalid recovery id: sig.r+curve.n != R.x');\n const x = Fp.toBytes(radj);\n const R = Point.fromBytes(concatBytes(pprefix((recovery & 1) === 0), x));\n const ir = Fn.inv(radj); // r^-1\n const h = bits2int_modN(abytes(messageHash, undefined, 'msgHash')); // Truncate hash\n const u1 = Fn.create(-h * ir); // -hr^-1\n const u2 = Fn.create(s * ir); // sr^-1\n // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.\n const Q = Point.BASE.mulAddUnsafe(u1, R, u2);\n if (Q.is0())\n throw new Error('invalid recovery: point at infinify');\n Q.assertValidity();\n return Q;\n }\n // Signatures should be low-s, to prevent malleability.\n hasHighS() {\n return isBiggerThanHalfOrder(this.s);\n }\n toBytes(format = defaultSigOpts.format) {\n validateSigFormat(format);\n if (format === 'der')\n return hexToBytes(DER.hexFromSig(this));\n const { r, s } = this;\n const rb = Fn.toBytes(r);\n const sb = Fn.toBytes(s);\n if (format === 'recovered') {\n assertRecoverableCurve();\n return concatBytes(Uint8Array.of(this.assertRecovery()), rb, sb);\n }\n return concatBytes(rb, sb);\n }\n toHex(format) {\n return bytesToHex(this.toBytes(format));\n }\n }\n Object.freeze(Signature.prototype);\n Object.freeze(Signature);\n // RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.\n // FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.\n // bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.\n // int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors\n const bits2int = opts.bits2int === undefined\n ? function bits2int_def(bytes) {\n // Our custom check \"just in case\", for protection against DoS\n if (bytes.length > 8192)\n throw new Error('input is too large');\n // For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)\n // for some cases, since bytes.length * 8 is not actual bitLength.\n const num = bytesToNumberBE(bytes); // check for == u8 done here\n const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits\n return delta > 0 ? num >> BigInt(delta) : num;\n }\n : opts.bits2int;\n const bits2int_modN = opts.bits2int_modN === undefined\n ? function bits2int_modN_def(bytes) {\n return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here\n }\n : opts.bits2int_modN;\n const ORDER_MASK = bitMask(fnBits);\n // Pads output with zero as per spec.\n /** Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`. */\n function int2octets(num) {\n aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);\n return Fn.toBytes(num);\n }\n function validateMsgAndHash(message, prehash) {\n abytes(message, undefined, 'message');\n return (prehash ? abytes(hash_(message), undefined, 'prehashed message') : message);\n }\n /**\n * Steps A, D of RFC6979 3.2.\n * Creates RFC6979 seed; converts msg/privKey to numbers.\n * Used only in sign, not in verify.\n *\n * Warning: we cannot assume here that message has same amount of bytes as curve order,\n * this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.\n */\n function prepSig(message, secretKey, opts) {\n const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);\n message = validateMsgAndHash(message, prehash); // RFC6979 3.2 A: h1 = H(m)\n // We can't later call bits2octets, since nested bits2int is broken for curves\n // with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.\n // const bits2octets = (bits) => int2octets(bits2int_modN(bits))\n const h1int = bits2int_modN(message);\n const d = Fn.fromBytes(secretKey); // validate secret key, convert to bigint\n if (!Fn.isValidNot0(d))\n throw new Error('invalid private key');\n const seedArgs = [int2octets(d), int2octets(h1int)];\n // extraEntropy. RFC6979 3.6: additional k' (optional).\n if (extraEntropy != null && extraEntropy !== false) {\n // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')\n // gen random bytes OR pass as-is\n const e = extraEntropy === true ? randomBytes(lengths.secretKey) : extraEntropy;\n seedArgs.push(abytes(e, undefined, 'extraEntropy')); // check for being bytes\n }\n const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2\n const m = h1int; // no need to call bits2int second time here, it is inside truncateHash!\n // Converts signature params into point w r/s, checks result for validity.\n // To transform k => Signature:\n // q = k\u22C5G\n // r = q.x mod n\n // s = k^-1(m + rd) mod n\n // The nonce inversion is blinded: with random b \u2208 [1,n\u22121], s = (bk)^-1(bm + bdr) per\n // https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. Fn.inv()'s extended-Euclidean\n // loop count depends on its input (cf. Minerva), but here it only ever sees b\u00B7k \u2014 uniformly\n // random, independent of k \u2014 so its timing reveals nothing about the nonce; b also masks d in\n // the products. Without a CSPRNG (probed in ecdsa()) we fall back to Fermat inversion\n // (invertCt), whose control flow is data-independent, at ~4x the inversion cost.\n function k2sig(kBytes) {\n // RFC 6979 Section 3.2, step 3: k = bits2int(T)\n // Important: all mod() calls here must be done over N\n const k = bits2int(kBytes); // Cannot use fields methods, since it is group element\n if (!Fn.isValidNot0(k))\n return; // Valid scalars (including k) must be in 1..N-1\n const q = Point.BASE.multiply(k).toAffine(); // q = k\u22C5G\n const r = Fn.create(q.x); // r = q.x mod n\n if (r === _0n)\n return;\n let s;\n if (csprng !== undefined) {\n // mapHashToField maps 1.5x-order-length uniform bytes into [1, n-1], negligible bias.\n const b = bytesToNumberBE(mapHashToField(csprng(blindLength), CURVE_ORDER));\n const ibk = Fn.inv(Fn.mul(b, k)); // (bk)^-1: inversion input is decorrelated from k\n const bm = Fn.mul(b, m);\n const bd = Fn.mul(b, d);\n s = Fn.create(ibk * Fn.create(bm + bd * r)); // s = (bk)^-1(bm + bdr) = k^-1(m + rd) mod n\n }\n else {\n const ik = invertCt(k, CURVE_ORDER); // k^-1 mod n with data-independent control flow\n s = Fn.create(ik * Fn.create(m + r * d)); // s = k^-1(m + rd) mod n\n }\n if (s === _0n)\n return;\n let recovery = getRecoveryBit(q.x, q.y, r); // recovery bit (2 or 3 when q.x>n)\n let normS = s;\n if (lowS && isBiggerThanHalfOrder(s)) {\n normS = Fn.neg(s); // if lowS was passed, ensure s is always in the bottom half of N\n recovery ^= 1;\n }\n return new Signature(r, normS, hasLargeRecoveryLifts ? undefined : recovery);\n }\n return { seed, k2sig };\n }\n /**\n * Signs a message or message hash with a secret key.\n * With the default `prehash: true`, raw message bytes are hashed internally;\n * only `{ prehash: false }` expects a caller-supplied digest.\n *\n * ```\n * sign(m, d) where\n * k = rfc6979_hmac_drbg(m, d)\n * (x, y) = G \u00D7 k\n * r = x mod n\n * s = (m + dr) / k mod n\n * ```\n */\n function sign(message, secretKey, opts = {}) {\n const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.\n const drbg = createHmacDrbg(hash_.outputLen, Fn.BYTES, hmac);\n const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G\n return sig.toBytes(opts.format);\n }\n /**\n * Verifies a signature against message and public key.\n * Rejects lowS signatures by default: see {@link ECDSAVerifyOpts}.\n * Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:\n *\n * ```\n * verify(r, s, h, P) where\n * u1 = hs^-1 mod n\n * u2 = rs^-1 mod n\n * R = u1\u22C5G + u2\u22C5P\n * mod(R.x, n) == r\n * ```\n */\n function verify(signature, message, publicKey, opts = {}) {\n const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);\n publicKey = abytes(publicKey, undefined, 'publicKey');\n message = validateMsgAndHash(message, prehash);\n if (!isBytes(signature)) {\n const end = signature instanceof Signature ? ', use sig.toBytes()' : '';\n throw new Error('verify expects Uint8Array signature' + end);\n }\n validateSigLength(signature, format); // execute this twice because we want loud error\n try {\n const sig = Signature.fromBytes(signature, format);\n const P = Point.fromBytes(publicKey);\n // SEC 1 verification keys must not be the identity, even when the generic point decoder\n // permits infinity for another protocol (for example, a pairing curve).\n if (P.is0())\n return false;\n if (lowS && sig.hasHighS())\n return false;\n const { r, s } = sig;\n const h = bits2int_modN(message); // mod n, not mod p\n const is = Fn.inv(s); // s^-1 mod n\n const u1 = Fn.create(h * is); // u1 = hs^-1 mod n\n const u2 = Fn.create(r * is); // u2 = rs^-1 mod n\n const R = Point.BASE.mulAddUnsafe(u1, P, u2); // u1\u22C5G + u2\u22C5P, joint Strauss\u2013Shamir\n if (R.is0())\n return false;\n const q = R.toAffine();\n const v = Fn.create(q.x); // v = R.x mod n\n if (v !== r)\n return false;\n // R is the exact point `recoverPublicKey(r, recid)` reconstructs (sR = hG + rP),\n // so binding the signature to its recovery id only needs R's parity/overflow bits.\n if (format === 'recovered' && sig.recovery !== getRecoveryBit(q.x, q.y, r))\n return false;\n return true;\n }\n catch (e) {\n return false;\n }\n }\n function recoverPublicKey(signature, message, opts = {}) {\n // Top-level recovery mirrors `sign()` / `verify()`: it hashes raw message\n // bytes first unless the caller passes `{ prehash: false }`.\n const { prehash } = validateSigOpts(opts, defaultSigOpts);\n message = validateMsgAndHash(message, prehash);\n return Signature.fromBytes(signature, 'recovered').recoverPublicKey(message).toBytes();\n }\n // utils.isValidPublicKey() already rejects the identity, so ECDSA needs no shadow copy.\n return Object.freeze({\n keygen,\n getPublicKey,\n getSharedSecret,\n utils,\n lengths,\n Point,\n sign,\n verify,\n recoverPublicKey,\n Signature,\n hash: hash_,\n });\n}\n", "const U32_MASK64 = /* @__PURE__ */ (() => BigInt(2 ** 32 - 1))();\nconst _32n = /* @__PURE__ */ BigInt(32);\n// Split bigint into two 32-bit halves. With `le=true`, returned fields become `{ h: low, l: high\n// }` to match little-endian word order rather than the property names.\nfunction fromBig(n, le = false) {\n if (le)\n return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };\n return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };\n}\n// Split bigint list into `[highWords, lowWords]` when `le=false`; with `le=true`, the first array\n// holds the low halves because `fromBig(...)` swaps the semantic meaning of `h` and `l`.\nfunction split(lst, le = false) {\n const len = lst.length;\n let Ah = new Uint32Array(len);\n let Al = new Uint32Array(len);\n for (let i = 0; i < len; i++) {\n const { h, l } = fromBig(lst[i], le);\n [Ah[i], Al[i]] = [h, l];\n }\n return [Ah, Al];\n}\n// Combine explicit `(high, low)` 32-bit halves into a bigint; `>>> 0` normalizes signed JS\n// bitwise results back to uint32 first, and little-endian callers must swap.\nconst toBig = (h, l) => (BigInt(h >>> 0) << _32n) | BigInt(l >>> 0);\n// Split a JS number into u32 halves without a BigInt allocation. Exact only for integers\n// `0 <= n < 2**53`; callers use it on byte / bit counters, which JS length math caps far below\n// that (an ArrayBuffer cannot exceed 2**53 - 1 bytes).\nconst fromNumH = (n) => (n / 2 ** 32) | 0;\nconst fromNumL = (n) => n >>> 0;\n// Drop-in replacement for `view.setBigUint64(byteOffset, BigInt(n), isLE)` without the per-call\n// BigInt allocation. Same `n < 2**53` precondition as `fromNumH`/`fromNumL`.\nfunction setU64FromNum(view, byteOffset, n, isLE) {\n const h = fromNumH(n);\n const l = fromNumL(n);\n view.setUint32(byteOffset, isLE ? l : h, isLE);\n view.setUint32(byteOffset + 4, isLE ? h : l, isLE);\n}\n// High 32-bit half of a 64-bit logical right shift for `s` in `0..31`.\nconst shrSH = (h, _l, s) => h >>> s;\n// Low 32-bit half of a 64-bit logical right shift, valid for `s` in `1..31`.\nconst shrSL = (h, l, s) => (h << (32 - s)) | (l >>> s);\n// High 32-bit half of a 64-bit right rotate, valid for `s` in `1..31`.\nconst rotrSH = (h, l, s) => (h >>> s) | (l << (32 - s));\n// Low 32-bit half of a 64-bit right rotate, valid for `s` in `1..31`.\nconst rotrSL = (h, l, s) => (h << (32 - s)) | (l >>> s);\n// High 32-bit half of a 64-bit right rotate, valid for `s` in `33..63`; `32` uses `rotr32*`.\nconst rotrBH = (h, l, s) => (h << (64 - s)) | (l >>> (s - 32));\n// Low 32-bit half of a 64-bit right rotate, valid for `s` in `33..63`; `32` uses `rotr32*`.\nconst rotrBL = (h, l, s) => (h >>> (s - 32)) | (l << (64 - s));\n// High 32-bit half of a 64-bit right rotate for `s === 32`; this is just the swapped low half.\nconst rotr32H = (_h, l) => l;\n// Low 32-bit half of a 64-bit right rotate for `s === 32`; this is just the swapped high half.\nconst rotr32L = (h, _l) => h;\n// 64-bit left rotates (rotl*) are not defined here: sha3.ts, their only consumer, keeps\n// local copies so V8 inlines them into keccakP.\n// Add two split 64-bit words and return the split `{ h, l }` sum.\n// JS uses 32-bit signed integers for bitwise operations, so we cannot simply shift the carry out\n// of the low sum and instead use division.\nfunction add(Ah, Al, Bh, Bl) {\n const l = (Al >>> 0) + (Bl >>> 0);\n return { h: (Ah + Bh + ((l / 2 ** 32) | 0)) | 0, l: l | 0 };\n}\n// Addition with more than 2 elements\n// Unmasked low-word accumulator for 3-way addition; pass the raw result into `add3H(...)`.\nconst add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);\n// High-word finalize step for 3-way addition; `low` must be the untruncated output of `add3L(...)`.\nconst add3H = (low, Ah, Bh, Ch) => (Ah + Bh + Ch + ((low / 2 ** 32) | 0)) | 0;\n// Unmasked low-word accumulator for 4-way addition; pass the raw result into `add4H(...)`.\nconst add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);\n// High-word finalize step for 4-way addition; `low` must be the untruncated output of `add4L(...)`.\nconst add4H = (low, Ah, Bh, Ch, Dh) => (Ah + Bh + Ch + Dh + ((low / 2 ** 32) | 0)) | 0;\n// Unmasked low-word accumulator for 5-way addition; pass the raw result into `add5H(...)`.\nconst add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);\n// High-word finalize step for 5-way addition; `low` must be the untruncated output of `add5L(...)`.\nconst add5H = (low, Ah, Bh, Ch, Dh, Eh) => (Ah + Bh + Ch + Dh + Eh + ((low / 2 ** 32) | 0)) | 0;\n// prettier-ignore\nexport { add, add3H, add3L, add4H, add4L, add5H, add5L, fromBig, fromNumH, fromNumL, rotr32H, rotr32L, rotrBH, rotrBL, rotrSH, rotrSL, setU64FromNum, shrSH, shrSL, split, toBig };\n", "/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport { setU64FromNum } from \"./_u64.js\";\nimport { abytes, aexists, aoutput, createView } from \"./utils.js\";\n/**\n * Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`.\n * Returns bits from `b` when `a` is set, otherwise from `c`.\n * The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never\n * set the same bit.\n * @param a - selector word\n * @param b - word chosen when selector bit is set\n * @param c - word chosen when selector bit is clear\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit choice primitive.\n * ```ts\n * Chi(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Chi(a, b, c) {\n return (a & b) ^ (~a & c);\n}\n/**\n * Shared 32-bit majority primitive reused by SHA-256 and SHA-1.\n * Returns bits shared by at least two inputs.\n * @param a - first input word\n * @param b - second input word\n * @param c - third input word\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit majority primitive.\n * ```ts\n * Maj(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Maj(a, b, c) {\n return (a & b) ^ (a & c) ^ (b & c);\n}\n/**\n * Merkle-Damgard hash construction base class.\n * Could be used to create MD5, RIPEMD, SHA1, SHA2.\n * Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit\n * strings with partial-byte tails.\n * @param blockLen - internal block size in bytes\n * @param outputLen - digest size in bytes\n * @param padOffset - trailing length field size in bytes\n * @param isLE - whether length and state words are encoded in little-endian\n * @example\n * Use a concrete subclass to get the shared Merkle-Damgard update/digest flow.\n * ```ts\n * import { _SHA1 } from '@noble/hashes/legacy.js';\n * const hash = new _SHA1();\n * hash.update(new Uint8Array([97, 98, 99]));\n * hash.digest();\n * ```\n */\nexport class HashMD {\n blockLen;\n outputLen;\n canXOF = false;\n padOffset;\n isLE;\n // For partial updates less than block size\n buffer;\n view;\n finished = false;\n length = 0;\n pos = 0;\n destroyed = false;\n constructor(blockLen, outputLen, padOffset, isLE) {\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data) {\n aexists(this);\n abytes(data);\n const { view, buffer, blockLen } = this;\n const len = data.length;\n let processed = false;\n for (let pos = 0; pos < len;) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path only when there is no buffered partial block: `take === blockLen` implies\n // `this.pos === 0`, so we can process full blocks directly from the input view.\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen)\n this.process(dataView, pos);\n processed = true;\n continue;\n }\n // When the whole input is buffered in one go (common for short messages), passing `data`\n // directly avoids allocating a subarray view.\n buffer.set(pos === 0 && take === len ? data : data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n processed = true;\n }\n }\n this.length += data.length;\n // Shared schedule buffers only pick up input-derived words inside process(); if everything\n // was buffered without processing, there is nothing to zero.\n if (processed)\n this.roundClean();\n return this;\n }\n digestInto(out) {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message, then zero-pad the rest of the block\n buffer[pos++] = 0b10000000;\n buffer.fill(0, pos);\n // we have less than padOffset left in buffer, so we cannot put length in\n // current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n buffer.fill(0);\n }\n // `padOffset` reserves the whole length field. For SHA-384/512 the high 64 bits stay zero from\n // the padding fill above, and JS will overflow before user input can make that half non-zero.\n // So we only need to write the low 64 bits here (`length * 8` only scales the exponent of an\n // integer below 2**53, so the split inside the helper stays exact).\n setU64FromNum(view, blockLen - 8, this.length * 8, isLE);\n this.process(view, 0);\n // The final block above is processed outside update(), so the shared message-schedule\n // buffers (e.g. SHA256_W) would otherwise retain input-derived words after digest().\n this.roundClean();\n // digest() passes our own `buffer` as `out`; reuse its cached view instead of allocating one.\n const oview = out === buffer ? view : createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT\n const outLen = len / 4;\n const state = this.get();\n // Subclass-misconfiguration invariant: outputLen must be 32-bit aligned and fit the state.\n if (len % 4 || outLen > state.length)\n throw new Error('invalid outputLen');\n for (let i = 0; i < outLen; i++)\n oview.setUint32(4 * i, state[i], isLE);\n }\n digest() {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n // Copy before destroy(): subclasses wipe `buffer` during cleanup, but `digest()` must return\n // fresh bytes to the caller.\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res;\n }\n _cloneIntoMeta(to) {\n const { buffer, length, finished, destroyed, pos } = this;\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n // Only partial-block bytes need copying: when `length % blockLen === 0`, `pos === 0` and\n // later `update()` / `digestInto()` overwrite `to.buffer` from the start before reading it.\n if (pos)\n to.buffer.set(buffer); // Avoid a hot modulo guard.\n return to;\n }\n clone() {\n return this._cloneInto();\n }\n}\n/**\n * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.\n * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.\n */\n/** Initial SHA256 state from RFC 6234 \u00A76.1: the first 32 bits of the fractional parts of the\n * square roots of the first eight prime numbers. Exported as a shared table; callers must treat\n * it as read-only because constructors copy words from it by index. */\nexport const SHA256_IV = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n/** Initial SHA224 state `H(0)` from RFC 6234 \u00A76.1. Exported as a shared table; callers must\n * treat it as read-only because constructors copy words from it by index. */\nexport const SHA224_IV = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n/** Initial SHA384 state from RFC 6234 \u00A76.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the ninth\n * through sixteenth prime numbers. Exported as a shared table; callers must treat it as read-only\n * because constructors copy halves from it by index. */\nexport const SHA384_IV = /* @__PURE__ */ Uint32Array.from([\n 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939,\n 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4,\n]);\n/** Initial SHA512 state from RFC 6234 \u00A76.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the first\n * eight prime numbers. Exported as a shared table; callers must treat it as read-only because\n * constructors copy halves from it by index. */\nexport const SHA512_IV = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,\n]);\n", "/**\n * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.\n * SHA256 is the fastest hash implementable in JS, even faster than Blake3.\n * Check out {@link https://www.rfc-editor.org/rfc/rfc4634 | RFC 4634} and\n * {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf | FIPS 180-4}.\n * @module\n */\nimport { Chi, HashMD, Maj, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from \"./_md.js\";\nimport * as u64 from \"./_u64.js\";\nimport { clean, createHasher, oidNist, rotr } from \"./utils.js\";\n/**\n * SHA-224 / SHA-256 round constants from RFC 6234 \u00A75.1: the first 32 bits\n * of the cube roots of the first 64 primes (2..311).\n */\n// prettier-ignore\nconst SHA256_K = /* @__PURE__ */ Uint32Array.from([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n/** Reusable SHA-224 / SHA-256 message schedule buffer `W_t` from RFC 6234 \u00A76.2 step 1. */\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\n/** Internal SHA-224 / SHA-256 compression engine from RFC 6234 \u00A76.2. */\nclass SHA2_32B extends HashMD {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n // Numeric initializers matter: starting the fields as `undefined` changes\n // V8's field representation and makes sha256 3x slower (measured).\n A = 0;\n B = 0;\n C = 0;\n D = 0;\n E = 0;\n F = 0;\n G = 0;\n H = 0;\n constructor(outputLen, IV) {\n super(64, outputLen, 8, false);\n this.A = IV[0] | 0;\n this.B = IV[1] | 0;\n this.C = IV[2] | 0;\n this.D = IV[3] | 0;\n this.E = IV[4] | 0;\n this.F = IV[5] | 0;\n this.G = IV[6] | 0;\n this.H = IV[7] | 0;\n }\n get() {\n const { A, B, C, D, E, F, G, H } = this;\n return [A, B, C, D, E, F, G, H];\n }\n // prettier-ignore\n set(A, B, C, D, E, F, G, H) {\n this.A = A | 0;\n this.B = B | 0;\n this.C = C | 0;\n this.D = D | 0;\n this.E = E | 0;\n this.F = F | 0;\n this.G = G | 0;\n this.H = H | 0;\n }\n _cloneInto(to) {\n (to ||= new this.constructor()).set(...this.get());\n return this._cloneIntoMeta(to);\n }\n process(view, offset) {\n // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4)\n SHA256_W[i] = view.getUint32(offset, false);\n for (let i = 16; i < 64; i++) {\n const W15 = SHA256_W[i - 15];\n const W2 = SHA256_W[i - 2];\n const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);\n const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);\n SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;\n }\n // Compression function main loop, 64 rounds\n let { A, B, C, D, E, F, G, H } = this;\n for (let i = 0; i < 64; i++) {\n const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);\n const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);\n const T2 = (sigma0 + Maj(A, B, C)) | 0;\n H = G;\n G = F;\n F = E;\n E = (D + T1) | 0;\n D = C;\n C = B;\n B = A;\n A = (T1 + T2) | 0;\n }\n // Add the compressed chunk to the current hash value\n A = (A + this.A) | 0;\n B = (B + this.B) | 0;\n C = (C + this.C) | 0;\n D = (D + this.D) | 0;\n E = (E + this.E) | 0;\n F = (F + this.F) | 0;\n G = (G + this.G) | 0;\n H = (H + this.H) | 0;\n this.set(A, B, C, D, E, F, G, H);\n }\n roundClean() {\n clean(SHA256_W);\n }\n destroy() {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n clean(this.buffer);\n }\n}\n/** Internal SHA-256 hash class grounded in RFC 6234 \u00A76.2. */\nexport class _SHA256 extends SHA2_32B {\n constructor() {\n super(32, SHA256_IV);\n }\n}\n/** Internal SHA-224 hash class grounded in RFC 6234 \u00A76.2 and \u00A78.5. */\nexport class _SHA224 extends SHA2_32B {\n constructor() {\n super(28, SHA224_IV);\n }\n}\n// SHA2-512 is slower than sha256 in js because u64 operations are slow.\n// SHA-384 / SHA-512 round constants from RFC 6234 \u00A75.2:\n// 80 full 64-bit words split into high/low halves.\n// prettier-ignore\nconst K512 = /* @__PURE__ */ (() => u64.split([\n '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc',\n '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118',\n '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2',\n '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694',\n '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65',\n '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5',\n '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4',\n '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70',\n '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df',\n '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b',\n '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30',\n '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8',\n '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8',\n '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3',\n '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec',\n '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b',\n '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178',\n '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b',\n '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c',\n '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817'\n].map(n => BigInt(n))))();\nconst SHA512_Kh = /* @__PURE__ */ (() => K512[0])();\nconst SHA512_Kl = /* @__PURE__ */ (() => K512[1])();\n// Reusable high-half schedule buffer for the RFC 6234 \u00A76.4 64-bit `W_t` words.\nconst SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);\n// Reusable low-half schedule buffer for the RFC 6234 \u00A76.4 64-bit `W_t` words.\nconst SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);\n/** Internal SHA-384 / SHA-512 compression engine from RFC 6234 \u00A76.4. */\nclass SHA2_64B extends HashMD {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n // h -- high 32 bits, l -- low 32 bits\n // Numeric initializers matter: starting the fields as `undefined` changes\n // V8's field representation and slows hashing down (measured on sha256).\n Ah = 0;\n Al = 0;\n Bh = 0;\n Bl = 0;\n Ch = 0;\n Cl = 0;\n Dh = 0;\n Dl = 0;\n Eh = 0;\n El = 0;\n Fh = 0;\n Fl = 0;\n Gh = 0;\n Gl = 0;\n Hh = 0;\n Hl = 0;\n constructor(outputLen, IV) {\n super(128, outputLen, 16, false);\n this.Ah = IV[0] | 0;\n this.Al = IV[1] | 0;\n this.Bh = IV[2] | 0;\n this.Bl = IV[3] | 0;\n this.Ch = IV[4] | 0;\n this.Cl = IV[5] | 0;\n this.Dh = IV[6] | 0;\n this.Dl = IV[7] | 0;\n this.Eh = IV[8] | 0;\n this.El = IV[9] | 0;\n this.Fh = IV[10] | 0;\n this.Fl = IV[11] | 0;\n this.Gh = IV[12] | 0;\n this.Gl = IV[13] | 0;\n this.Hh = IV[14] | 0;\n this.Hl = IV[15] | 0;\n }\n // prettier-ignore\n get() {\n const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];\n }\n // prettier-ignore\n set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) {\n this.Ah = Ah | 0;\n this.Al = Al | 0;\n this.Bh = Bh | 0;\n this.Bl = Bl | 0;\n this.Ch = Ch | 0;\n this.Cl = Cl | 0;\n this.Dh = Dh | 0;\n this.Dl = Dl | 0;\n this.Eh = Eh | 0;\n this.El = El | 0;\n this.Fh = Fh | 0;\n this.Fl = Fl | 0;\n this.Gh = Gh | 0;\n this.Gl = Gl | 0;\n this.Hh = Hh | 0;\n this.Hl = Hl | 0;\n }\n _cloneInto(to) {\n (to ||= new this.constructor()).set(...this.get());\n return this._cloneIntoMeta(to);\n }\n process(view, offset) {\n // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) {\n SHA512_W_H[i] = view.getUint32(offset);\n SHA512_W_L[i] = view.getUint32((offset += 4));\n }\n for (let i = 16; i < 80; i++) {\n // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)\n const W15h = SHA512_W_H[i - 15] | 0;\n const W15l = SHA512_W_L[i - 15] | 0;\n const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);\n const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);\n // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)\n const W2h = SHA512_W_H[i - 2] | 0;\n const W2l = SHA512_W_L[i - 2] | 0;\n const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);\n const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);\n // SHA512_W[i] = s0 + s1 + SHA512_W[i - 7] + SHA512_W[i - 16];\n const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);\n const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);\n SHA512_W_H[i] = SUMh | 0;\n SHA512_W_L[i] = SUMl | 0;\n }\n let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n // Compression function main loop, 80 rounds\n for (let i = 0; i < 80; i++) {\n // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)\n const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);\n const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);\n //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const CHIh = (Eh & Fh) ^ (~Eh & Gh);\n const CHIl = (El & Fl) ^ (~El & Gl);\n // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]\n // prettier-ignore\n const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);\n const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);\n const T1l = T1ll | 0;\n // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)\n const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);\n const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);\n const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);\n const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);\n Hh = Gh | 0;\n Hl = Gl | 0;\n Gh = Fh | 0;\n Gl = Fl | 0;\n Fh = Eh | 0;\n Fl = El | 0;\n ({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));\n Dh = Ch | 0;\n Dl = Cl | 0;\n Ch = Bh | 0;\n Cl = Bl | 0;\n Bh = Ah | 0;\n Bl = Al | 0;\n const All = u64.add3L(T1l, sigma0l, MAJl);\n Ah = u64.add3H(All, T1h, sigma0h, MAJh);\n Al = All | 0;\n }\n // Add the compressed chunk to the current hash value\n ({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));\n ({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));\n ({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));\n ({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));\n ({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));\n ({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));\n ({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));\n ({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));\n this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);\n }\n roundClean() {\n clean(SHA512_W_H, SHA512_W_L);\n }\n destroy() {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n clean(this.buffer);\n this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n/** Internal SHA-512 hash class grounded in RFC 6234 \u00A76.3 and \u00A76.4. */\nexport class _SHA512 extends SHA2_64B {\n constructor() {\n super(64, SHA512_IV);\n }\n}\n/** Internal SHA-384 hash class grounded in RFC 6234 \u00A76.3 and \u00A76.4. */\nexport class _SHA384 extends SHA2_64B {\n constructor() {\n super(48, SHA384_IV);\n }\n}\n/**\n * Truncated SHA512/256 and SHA512/224.\n * SHA512_IV is XORed with 0xa5a5a5a5a5a5a5a5, then used as \"intermediary\" IV of SHA512/t.\n * Then t hashes string to produce result IV.\n * See the repo-side derivation recipe in `test/misc/sha2-gen-iv.js`.\n * These IV literals are checked against that script rather than a dedicated\n * local RFC section.\n */\n/** SHA-512/224 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T224_IV = /* @__PURE__ */ Uint32Array.from([\n 0x8c3d37c8, 0x19544da2, 0x73e19966, 0x89dcd4d6, 0x1dfab7ae, 0x32ff9c82, 0x679dd514, 0x582f9fcf,\n 0x0f6d2b69, 0x7bd44da8, 0x77e36f73, 0x04c48942, 0x3f9d85a8, 0x6a1d36c8, 0x1112e6ad, 0x91d692a1,\n]);\n/** SHA-512/256 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T256_IV = /* @__PURE__ */ Uint32Array.from([\n 0x22312194, 0xfc2bf72c, 0x9f555fa3, 0xc84c64c2, 0x2393b86b, 0x6f53b151, 0x96387719, 0x5940eabd,\n 0x96283ee2, 0xa88effe3, 0xbe5e1e25, 0x53863992, 0x2b0199fc, 0x2c85b8aa, 0x0eb72ddc, 0x81c52ca2,\n]);\n/** Internal SHA-512/224 hash class using the derived `T224_IV` and the shared\n * RFC 6234 \u00A76.4 compression engine. */\nexport class _SHA512_224 extends SHA2_64B {\n constructor() {\n super(28, T224_IV);\n }\n}\n/** Internal SHA-512/256 hash class using the derived `T256_IV` and the shared\n * RFC 6234 \u00A76.4 compression engine. */\nexport class _SHA512_256 extends SHA2_64B {\n constructor() {\n super(32, T256_IV);\n }\n}\n/**\n * SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:\n *\n * - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.\n * - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.\n * - Each sha256 hash is executing 2^18 bit operations.\n * - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-256.\n * ```ts\n * sha256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha256 = /* @__PURE__ */ createHasher(() => new _SHA256(), \n/* @__PURE__ */ oidNist(0x01));\n/**\n * SHA2-224 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-224.\n * ```ts\n * sha224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha224 = /* @__PURE__ */ createHasher(() => new _SHA224(), \n/* @__PURE__ */ oidNist(0x04));\n/**\n * SHA2-512 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512.\n * ```ts\n * sha512(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512 = /* @__PURE__ */ createHasher(() => new _SHA512(), \n/* @__PURE__ */ oidNist(0x03));\n/**\n * SHA2-384 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-384.\n * ```ts\n * sha384(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha384 = /* @__PURE__ */ createHasher(() => new _SHA384(), \n/* @__PURE__ */ oidNist(0x02));\n/**\n * SHA2-512/256 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/256.\n * ```ts\n * sha512_256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_256 = /* @__PURE__ */ createHasher(() => new _SHA512_256(), \n/* @__PURE__ */ oidNist(0x06));\n/**\n * SHA2-512/224 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/224.\n * ```ts\n * sha512_224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_224 = /* @__PURE__ */ createHasher(() => new _SHA512_224(), \n/* @__PURE__ */ oidNist(0x05));\n", "/**\n * Experimental implementation of NTT / FFT (Fast Fourier Transform) over finite fields.\n * API may change at any time. The code has not been audited. Feature requests are welcome.\n * @module\n */\nimport { aarray, validateObject } from \"../utils.js\";\nimport { validateField } from \"./modular.js\";\nfunction checkU32(n, title = 'n') {\n // 0xff_ff_ff_ff\n if (typeof n !== 'number')\n throw new TypeError(`wrong u32 integer \"${title}\": expected number, got type=${typeof n}`);\n if (!Number.isSafeInteger(n) || n < 0 || n > 0xffffffff)\n throw new RangeError(`wrong u32 integer \"${title}\": expected 0..4294967295, got ${n}`);\n return n;\n}\n/**\n * Checks if integer is in form of `1 << X`.\n * @param x - Integer to inspect.\n * @returns `true` when the value is a power of two.\n * @example\n * Validate that an FFT size is a power of two.\n *\n * ```ts\n * isPowerOfTwo(8);\n * ```\n */\nexport function isPowerOfTwo(x) {\n checkU32(x, 'x');\n return (x & (x - 1)) === 0 && x !== 0;\n}\n/**\n * @param n - Input value.\n * @returns Next power of two within the u32/array-length domain.\n * @throws If `n` is not a valid unsigned 32-bit integer. {@link Error}\n * @example\n * Round an integer up to the FFT size it needs.\n *\n * ```ts\n * nextPowerOfTwo(9);\n * ```\n */\nexport function nextPowerOfTwo(n) {\n checkU32(n);\n if (n <= 1)\n return 1;\n // FFT sizes here are used as JS array lengths, so `2^32` is not a meaningful result:\n // keep the fast u32 bit-twiddling path and fail explicitly instead of wrapping to 1.\n if (n > 0x8000_0000)\n throw new Error('nextPowerOfTwo overflow: result does not fit u32');\n return (1 << (log2(n - 1) + 1)) >>> 0;\n}\n/**\n * @param n - Value to reverse.\n * @param bits - Number of bits to use.\n * @returns Bit-reversed integer.\n * @throws If `n` is not a valid unsigned 32-bit integer. {@link Error}\n * @example\n * Reverse the low `bits` bits of one index.\n *\n * ```ts\n * reverseBits(3, 3);\n * ```\n */\nexport function reverseBits(n, bits) {\n checkU32(n);\n if (typeof bits !== 'number')\n throw new TypeError('\"bits\" expected number, got type=' + typeof bits);\n if (!Number.isSafeInteger(bits) || bits < 0 || bits > 32)\n throw new Error(`expected integer 0 <= bits <= 32, got ${bits}`);\n let reversed = 0;\n for (let i = 0; i < bits; i++, n >>>= 1)\n reversed = (reversed << 1) | (n & 1);\n // JS bitwise ops are signed i32; cast back so 32-bit reversals stay in the unsigned u32 domain.\n return reversed >>> 0;\n}\n/**\n * Similar to `bitLen(x)-1` but much faster for small integers, like indices.\n * @param n - Input value.\n * @returns Base-2 logarithm. For `n = 0`, the current implementation returns `-1`.\n * @example\n * Compute the radix-2 stage count for one transform size.\n *\n * ```ts\n * log2(8);\n * ```\n */\nexport function log2(n) {\n checkU32(n);\n return 31 - Math.clz32(n);\n}\n/**\n * Moves lowest bit to highest position, which at first step splits\n * array on even and odd indices, then it applied again to each part,\n * which is core of fft\n * @param values - Mutable coefficient array.\n * @returns Mutated input array.\n * @throws If the array length is not a positive power of two. {@link Error}\n * @example\n * Reorder coefficients into bit-reversed order in place.\n *\n * ```ts\n * const values = Uint8Array.from([0, 1, 2, 3]);\n * bitReversalInplace(values);\n * ```\n */\nexport function bitReversalInplace(values) {\n if (!values ||\n typeof values !== 'object' ||\n typeof values.length !== 'number')\n throw new TypeError('\"values\" expected array-like, got type=' + typeof values);\n const n = values.length;\n // Size-1 FFT is the identity, so bit-reversal must stay a no-op there instead of rejecting it.\n if (!isPowerOfTwo(n))\n throw new Error('expected positive power-of-two length, got ' + n);\n const bits = log2(n);\n for (let i = 0; i < n; i++) {\n const j = reverseBits(i, bits);\n if (i < j) {\n const tmp = values[i];\n values[i] = values[j];\n values[j] = tmp;\n }\n }\n return values;\n}\n/**\n * @param values - Input values.\n * @returns Reordered copy.\n * @throws If the array length is not a positive power of two. {@link Error}\n * @example\n * Return a reordered copy instead of mutating the input in place.\n *\n * ```ts\n * const reordered = bitReversalPermutation([0, 1, 2, 3]);\n * ```\n */\nexport function bitReversalPermutation(values) {\n aarray(values, 'values');\n return bitReversalInplace(values.slice());\n}\nconst _1n = /** @__PURE__ */ BigInt(1);\nfunction findGenerator(field) {\n let G = BigInt(2);\n for (; field.eql(field.pow(G, field.ORDER >> _1n), field.ONE); G++)\n ;\n return G;\n}\n/**\n * We limit roots up to 2**31, which is a lot: 2-billion polynomial should be rare.\n * @param field - Field implementation.\n * @param generator - Optional trusted non-quadratic-residue override for callers that already know the field.\n * @returns Roots-of-unity cache.\n * @example\n * Cache roots once, then ask for the omega table of one FFT size.\n *\n * ```ts\n * import { rootsOfUnity } from '@noble/curves/abstract/fft.js';\n * import { Field } from '@noble/curves/abstract/modular.js';\n * const roots = rootsOfUnity(Field(17n));\n * const omega = roots.omega(4);\n * ```\n */\nexport function rootsOfUnity(field, generator) {\n validateField(field);\n if (generator !== undefined && typeof generator !== 'bigint')\n throw new TypeError('\"generator\" expected bigint, got type=' + typeof generator);\n // Factor field.ORDER-1 as oddFactor * 2^powerOfTwo\n let oddFactor = field.ORDER - _1n;\n let powerOfTwo = 0;\n for (; (oddFactor & _1n) !== _1n; powerOfTwo++, oddFactor >>= _1n)\n ;\n // Find non quadratic residue\n let G = generator !== undefined ? BigInt(generator) : findGenerator(field);\n // Powers of generator\n const omegas = new Array(powerOfTwo + 1);\n omegas[powerOfTwo] = field.pow(G, oddFactor);\n for (let i = powerOfTwo; i > 0; i--)\n omegas[i - 1] = field.sqr(omegas[i]);\n // Compute all roots of unity for powers up to maxPower\n const rootsCache = [];\n const checkBits = (bits) => {\n checkU32(bits, 'bits');\n if (bits > 31 || bits > powerOfTwo)\n throw new Error('rootsOfUnity: wrong bits ' + bits + ' powerOfTwo=' + powerOfTwo);\n return bits;\n };\n const precomputeRoots = (maxPower) => {\n checkBits(maxPower);\n for (let power = maxPower; power >= 0; power--) {\n if (rootsCache[power])\n continue; // Skip if we've already computed roots for this power\n const above = rootsCache[power + 1];\n const rootsAtPower = [];\n if (above) {\n // \u03C9_{2^p} = \u03C9_{2^{p+1}}\u00B2, so the smaller table is the even-index stride of the bigger\n // one: only the largest requested power pays for the multiplication chain.\n for (let j = 0; j < 2 ** power; j++)\n rootsAtPower.push(above[2 * j]);\n }\n else {\n for (let j = 0, cur = field.ONE; j < 2 ** power; j++, cur = field.mul(cur, omegas[power]))\n rootsAtPower.push(cur);\n }\n rootsCache[power] = rootsAtPower;\n }\n return rootsCache[maxPower];\n };\n const brpCache = new Map();\n const inverseCache = new Map();\n // roots()/brp()/inverse() expose shared cached arrays by reference for speed; callers must treat them as read-only.\n // NOTE: we use bits instead of power, because power = 2**bits,\n // but power is not necessarily isPowerOfTwo(power)!\n return {\n info: { G, powerOfTwo, oddFactor },\n roots: (bits) => {\n const b = checkBits(bits);\n return precomputeRoots(b);\n },\n brp(bits) {\n const b = checkBits(bits);\n if (brpCache.has(b))\n return brpCache.get(b);\n else {\n const res = bitReversalPermutation(this.roots(b));\n brpCache.set(b, res);\n return res;\n }\n },\n inverse(bits) {\n const b = checkBits(bits);\n if (inverseCache.has(b))\n return inverseCache.get(b);\n else {\n // \u03C9\u1D3A = 1, so inv(\u03C9\u1D4F) = \u03C9\u1D3A\u207B\u1D4F: the inverse table is the reversed roots table.\n // Value-identical to field.invertBatch(roots), but skips its 3N muls + inversion.\n const r = this.roots(b);\n const res = [r[0]].concat(r.slice(1).reverse());\n inverseCache.set(b, res);\n return res;\n }\n },\n omega: (bits) => omegas[checkBits(bits)],\n clear: () => {\n rootsCache.splice(0, rootsCache.length);\n brpCache.clear();\n inverseCache.clear();\n },\n };\n}\n/**\n * Constructs different flavors of FFT. radix2 implementation of low level mutating API. Flavors:\n *\n * - DIT (Decimation-in-Time): Bottom-Up (leaves to root), Cooley-Tukey\n * - DIF (Decimation-in-Frequency): Top-Down (root to leaves), Gentleman-Sande\n *\n * DIT takes brp input, returns natural output.\n * DIF takes natural input, returns brp output.\n *\n * The output is actually identical. Time / frequence distinction is not meaningful\n * for Polynomial multiplication in fields.\n * Which means if protocol supports/needs brp output/inputs, then we can skip this step.\n *\n * Cyclic NTT: Rq = Zq[x]/(x^n-1). butterfly_DIT+loop_DIT OR butterfly_DIF+loop_DIT, roots are omega\n * Negacyclic NTT: Rq = Zq[x]/(x^n+1). butterfly_DIT+loop_DIF, at least for mlkem / mldsa\n *\n * `invertButterflies` indexes roots by a per-butterfly-group counter (`grp`): forward\n * (`dit: false`) reads `roots[grp]` with grp = 1..; inverse (`dit: true`) reads `roots[N - grp]`\n * with grp restarting at 1. With `skipStages: 0` one table serves both directions (\u03C9\u1D3A = 1 makes\n * the reversed walk self-inverse). With `skipStages > 0` the inverse walk starts at `N - 1`\n * instead of continuing where the skipped stages would have left off, so the caller must supply\n * a table shaped for that (ML-KEM: `\u03B6^BitRev7(i)` over all N=256 indices, whose aliased upper\n * half is exactly the FIPS 203 inverse walk).\n * @param F - Field operations.\n * @param coreOpts - FFT configuration. See {@link FFTCoreOpts}:\n * - `N`: Transform size. Must be a power of two.\n * - `roots`: Stage roots for the selected transform size.\n * - `dit`: Whether to run the DIT variant instead of DIF.\n * - `invertButterflies` (optional): Whether to invert butterfly placement.\n * - `skipStages` (optional): Number of initial stages to skip.\n * - `brp` (optional): Whether to apply bit-reversal permutation at the boundary.\n * @returns Low-level FFT loop.\n * @throws If the FFT options or cached roots are invalid for the requested size. {@link Error}\n * @example\n * Constructs different flavors of FFT.\n *\n * ```ts\n * import { FFTCore, rootsOfUnity } from '@noble/curves/abstract/fft.js';\n * import { Field } from '@noble/curves/abstract/modular.js';\n * const Fp = Field(17n);\n * const roots = rootsOfUnity(Fp).roots(2);\n * const loop = FFTCore(Fp, { N: 4, roots, dit: true });\n * const values = loop([1n, 2n, 3n, 4n]);\n * ```\n */\nexport const FFTCore = (F, coreOpts) => {\n validateObject(coreOpts, { N: 'number', roots: 'object', dit: 'boolean' }, { invertButterflies: 'boolean', skipStages: 'number', brp: 'boolean' }, 'coreOpts');\n const { N, roots, dit, invertButterflies = false, skipStages = 0, brp = true } = coreOpts;\n checkU32(N, 'coreOpts.N');\n const bits = log2(N);\n if (!isPowerOfTwo(N))\n throw new Error('FFT: Polynomial size should be power of two');\n checkU32(skipStages, 'coreOpts.skipStages');\n const maxSkipStages = bits === 0 ? 0 : bits - 1;\n // Skipping every stage leaves only boundary layout changes, not a valid FFT loop shape.\n if (skipStages > maxSkipStages)\n throw new Error(`FFT: wrong skipStages: expected 0 <= skipStages <= ${maxSkipStages}`);\n // Wrong-sized root tables can stay in-bounds for some loop shapes and silently compute nonsense.\n if (roots.length !== N)\n throw new Error(`FFT: wrong roots length: expected ${N}, got ${roots.length}`);\n const isDit = dit !== invertButterflies;\n return (values) => {\n if (values.length !== N)\n throw new Error('FFT: wrong Polynomial length');\n if (dit && brp)\n bitReversalInplace(values);\n for (let i = 0, g = 1; i < bits - skipStages; i++) {\n // For each stage s (sub-FFT length m = 2^s)\n const s = dit ? i + 1 + skipStages : bits - i;\n const m = 1 << s;\n const m2 = m >> 1;\n const stride = N >> s;\n // Loop over each subarray of length m\n for (let k = 0; k < N; k += m) {\n // Loop over each butterfly within the subarray\n for (let j = 0, grp = g++; j < m2; j++) {\n const rootPos = invertButterflies ? (dit ? N - grp : grp) : j * stride;\n const i0 = k + j;\n const i1 = k + j + m2;\n const omega = roots[rootPos];\n const b = values[i1];\n const a = values[i0];\n // Inlining gives us 10% perf in kyber vs functions\n if (isDit) {\n const t = F.mul(b, omega); // Standard DIT butterfly\n values[i0] = F.add(a, t);\n values[i1] = F.sub(a, t);\n }\n else if (invertButterflies) {\n values[i0] = F.add(b, a); // DIT loop + inverted butterflies (Kyber decode)\n values[i1] = F.mul(F.sub(b, a), omega);\n }\n else {\n values[i0] = F.add(a, b); // Standard DIF butterfly\n values[i1] = F.mul(F.sub(a, b), omega);\n }\n }\n }\n }\n if (!dit && brp)\n bitReversalInplace(values);\n return values;\n };\n};\n/**\n * NTT aka FFT over finite field (NOT over complex numbers).\n * Naming mirrors other libraries.\n * @param roots - Roots-of-unity cache.\n * @param opts - Field operations. See {@link FFTOpts}.\n * @returns Forward and inverse FFT helpers.\n * @example\n * NTT aka FFT over finite field (NOT over complex numbers).\n *\n * ```ts\n * import { FFT, rootsOfUnity } from '@noble/curves/abstract/fft.js';\n * import { Field } from '@noble/curves/abstract/modular.js';\n * const Fp = Field(17n);\n * const fft = FFT(rootsOfUnity(Fp), Fp);\n * const values = fft.direct([1n, 2n, 3n, 4n]);\n * ```\n */\nexport function FFT(roots, opts) {\n // Loops are cached per (size, direction, brp flags): FFTCore construction validates options\n // and allocates closures, which costs more than a small transform itself. The cached loop\n // closes over the root table active at first use; `roots.clear()` rebuilds value-identical\n // tables, so a stale reference stays correct.\n const loops = new Map();\n const getLoop = (N, rootsTable, key) => {\n const cached = loops.get(key);\n if (cached)\n return cached;\n const brpInput = !!(key & 2);\n const brpOutput = !!(key & 1);\n let loop;\n if (brpInput && brpOutput) {\n // we cannot optimize this case, but lets support it anyway\n const core = FFTCore(opts, { N, roots: rootsTable, dit: false, brp: false });\n loop = (values) => core(bitReversalInplace(values));\n }\n else if (brpInput)\n loop = FFTCore(opts, { N, roots: rootsTable, dit: true, brp: false });\n else if (brpOutput)\n loop = FFTCore(opts, { N, roots: rootsTable, dit: false, brp: false });\n else\n loop = FFTCore(opts, { N, roots: rootsTable, dit: true, brp: true }); // all natural\n loops.set(key, loop);\n return loop;\n };\n const loopKey = (bits, isInverse, brpInput, brpOutput) => (bits << 3) | (isInverse ? 4 : 0) | (brpInput ? 2 : 0) | (brpOutput ? 1 : 0);\n return {\n direct(values, brpInput = false, brpOutput = false) {\n const N = values.length;\n if (!isPowerOfTwo(N))\n throw new Error('FFT: Polynomial size should be power of two');\n const bits = log2(N);\n const key = loopKey(bits, false, brpInput, brpOutput);\n return getLoop(N, roots.roots(bits), key)(values.slice());\n },\n inverse(values, brpInput = false, brpOutput = false) {\n const N = values.length;\n if (!isPowerOfTwo(N))\n throw new Error('FFT: Polynomial size should be power of two');\n const bits = log2(N);\n const key = loopKey(bits, true, brpInput, brpOutput);\n const res = getLoop(N, roots.inverse(bits), key)(values.slice());\n const ivm = opts.inv(BigInt(values.length)); // scale\n // we can get brp output if we use dif instead of dit!\n for (let i = 0; i < res.length; i++)\n res[i] = opts.mul(res[i], ivm);\n // Allows to re-use non-inverted roots, but is VERY fragile\n // return [res[0]].concat(res.slice(1).reverse());\n // inverse calculated as pow(-1), which transforms into \u03C9^{-kn} (-> reverses indices)\n return res;\n },\n };\n}\nexport function poly(field, roots, create, fft, length) {\n validateField(field);\n const F = field;\n const _create = create ||\n ((len, elm) => new Array(len).fill(elm ?? F.ZERO));\n // `poly.mul(a, b)` distinguishes polynomial-vs-scalar at runtime, so keep accepted\n // polynomial containers concrete instead of trying to support arbitrary wrappers.\n const isPoly = (x) => {\n if (Array.isArray(x))\n return true;\n if (!ArrayBuffer.isView(x))\n return false;\n const v = x;\n return (typeof v.length === 'number' &&\n typeof v.slice === 'function' &&\n typeof v[Symbol.iterator] === 'function');\n };\n const checkPoly = (title, value) => {\n if (!isPoly(value))\n throw new TypeError(`\"${title}\" expected polynomial, got type=${typeof value}`);\n };\n const checkLength = (a, b) => {\n checkPoly('a', a);\n const L = a.length;\n if (b !== undefined) {\n checkPoly('b', b);\n if (b.length !== L)\n throw new Error(`poly: mismatched lengths ${L} vs ${b.length}`);\n }\n if (length !== undefined && L !== length)\n throw new Error(`poly: expected fixed length ${length}, got ${L}`);\n return L;\n };\n function findOmegaIndex(x, n, brp = false, weights) {\n if (!isPowerOfTwo(n))\n throw new Error('poly.lagrange: expected power of two length, got ' + n);\n // Explicit weights define the interpolation domain, including the Kronecker-\u03B4 shortcut.\n const omega = weights || (brp ? roots.brp(log2(n)) : roots.roots(log2(n)));\n for (let i = 0; i < n; i++)\n if (F.eql(x, omega[i]))\n return i;\n return -1;\n }\n // TODO: mutating versions for mlkem/mldsa\n return {\n roots,\n create: _create,\n length,\n extend: (a, len) => {\n checkLength(a);\n const out = _create(len, F.ZERO);\n // Plain arrays grow when writing past `out.length`, so cap the copy explicitly to keep\n // `extend()` consistent with typed arrays and with its documented truncate behavior.\n for (let i = 0; i < Math.min(a.length, len); i++)\n out[i] = a[i];\n return out;\n },\n degree: (a) => {\n checkLength(a);\n for (let i = a.length - 1; i >= 0; i--)\n if (!F.is0(a[i]))\n return i;\n return -1;\n },\n add: (a, b) => {\n const len = checkLength(a, b);\n const out = _create(len);\n for (let i = 0; i < len; i++)\n out[i] = F.add(a[i], b[i]);\n return out;\n },\n sub: (a, b) => {\n const len = checkLength(a, b);\n const out = _create(len);\n for (let i = 0; i < len; i++)\n out[i] = F.sub(a[i], b[i]);\n return out;\n },\n dot: (a, b) => {\n const len = checkLength(a, b);\n const out = _create(len);\n for (let i = 0; i < len; i++)\n out[i] = F.mul(a[i], b[i]);\n return out;\n },\n mul: (a, b) => {\n if (isPoly(b)) {\n const len = checkLength(a, b);\n if (fft) {\n const A = fft.direct(a, false, true);\n const B = fft.direct(b, false, true);\n for (let i = 0; i < A.length; i++)\n A[i] = F.mul(A[i], B[i]);\n return fft.inverse(A, true, false);\n }\n else {\n // NOTE: this is quadratic and mostly for compat tests with FFT\n const res = _create(len);\n for (let i = 0; i < len; i++) {\n for (let j = 0; j < len; j++) {\n const k = (i + j) % len; // wrap mod length\n res[k] = F.add(res[k], F.mul(a[i], b[j]));\n }\n }\n return res;\n }\n }\n else {\n const out = _create(checkLength(a));\n for (let i = 0; i < out.length; i++)\n out[i] = F.mul(a[i], b);\n return out;\n }\n },\n convolve(a, b) {\n checkPoly('a', a);\n checkPoly('b', b);\n const len = nextPowerOfTwo(a.length + b.length - 1);\n return this.mul(this.extend(a, len), this.extend(b, len));\n },\n shift(p, factor) {\n checkPoly('p', p);\n const out = _create(p.length);\n if (length !== undefined && p.length !== length)\n throw new Error(`poly: expected fixed length ${length}, got ${p.length}`);\n if (!p.length)\n return out;\n out[0] = p[0];\n for (let i = 1, power = F.ONE; i < p.length; i++) {\n power = F.mul(power, factor);\n out[i] = F.mul(p[i], power);\n }\n return out;\n },\n clone: (a) => {\n checkLength(a);\n const out = _create(a.length);\n for (let i = 0; i < a.length; i++)\n out[i] = a[i];\n return out;\n },\n eval: (a, basis) => {\n checkLength(a, basis);\n let acc = F.ZERO;\n for (let i = 0; i < a.length; i++)\n acc = F.add(acc, F.mul(a[i], basis[i]));\n return acc;\n },\n monomial: {\n basis: (x, n) => {\n const out = _create(n);\n let pow = F.ONE;\n for (let i = 0; i < n; i++) {\n out[i] = pow;\n pow = F.mul(pow, x);\n }\n return out;\n },\n eval: (a, x) => {\n checkLength(a);\n // Same as eval(a, monomialBasis(x, a.length)), but it is faster this way\n let acc = F.ZERO;\n for (let i = a.length - 1; i >= 0; i--)\n acc = F.add(F.mul(acc, x), a[i]);\n return acc;\n },\n },\n lagrange: {\n basis: (x, n, brp = false, weights) => {\n if (!isPowerOfTwo(n))\n throw new Error('poly.lagrange: expected power of two length, got ' + n);\n const bits = log2(n);\n const cache = weights || (brp ? roots.brp(bits) : roots.roots(bits)); // [\u03C9\u2070, \u03C9\u00B9, ..., \u03C9\u207F\u207B\u00B9]\n const out = _create(n);\n // Fast Kronecker-\u03B4 shortcut\n const idx = findOmegaIndex(x, n, brp, weights);\n if (idx !== -1) {\n out[idx] = F.ONE;\n return out;\n }\n const tm = F.pow(x, BigInt(n));\n const c = F.mul(F.sub(tm, F.ONE), F.inv(BigInt(n))); // c = (x\u207F - 1)/n\n const denom = _create(n);\n for (let i = 0; i < n; i++)\n denom[i] = F.sub(x, cache[i]);\n const inv = F.invertBatch(denom);\n for (let i = 0; i < n; i++)\n out[i] = F.mul(c, F.mul(cache[i], inv[i]));\n return out;\n },\n eval(a, x, brp = false) {\n checkLength(a);\n const idx = findOmegaIndex(x, a.length, brp);\n if (idx !== -1)\n return a[idx]; // fast path\n const L = this.basis(x, a.length, brp); // L\u1D62(x)\n let acc = F.ZERO;\n for (let i = 0; i < a.length; i++)\n if (!F.is0(a[i]))\n acc = F.add(acc, F.mul(a[i], L[i]));\n return acc;\n },\n },\n vanishing(roots) {\n checkPoly('roots', roots);\n if (length !== undefined && roots.length !== length)\n throw new Error(`poly: expected fixed length ${length}, got ${roots.length}`);\n const out = _create(roots.length + 1, F.ZERO);\n out[0] = F.ONE;\n for (const r of roots) {\n const neg = F.neg(r);\n for (let j = out.length - 1; j > 0; j--)\n out[j] = F.add(F.mul(out[j], neg), out[j - 1]);\n out[0] = F.mul(out[0], neg);\n }\n return out;\n },\n };\n}\n", "/**\n * NIST P256, P384, P521 curves.\n * https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256\n * @module\n */\n/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */\nimport { sha256, sha384, sha512 } from '@noble/hashes/sha2.js';\nimport { createFROST } from \"./abstract/frost.js\";\nimport { createHasher, mapToCurveSimpleSWU } from \"./abstract/hash-to-curve.js\";\nimport { createOPRF } from \"./abstract/oprf.js\";\nimport { ecdsa, weierstrass, } from \"./abstract/weierstrass.js\";\nimport {} from \"./utils.js\";\n// p = 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n - 1n\n// a = Fp256.create(BigInt('-3'));\nconst p256_CURVE = /* @__PURE__ */ (() => ({\n p: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),\n n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),\n h: BigInt(1),\n a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),\n b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),\n Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),\n Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),\n}))();\n// p = 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n\nconst p384_CURVE = /* @__PURE__ */ (() => ({\n p: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff'),\n n: BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973'),\n h: BigInt(1),\n a: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc'),\n b: BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef'),\n Gx: BigInt('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7'),\n Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),\n}))();\n// p = 2n**521n - 1n\nconst p521_CURVE = /* @__PURE__ */ (() => ({\n p: BigInt('0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'),\n n: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'),\n h: BigInt(1),\n a: BigInt('0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc'),\n b: BigInt('0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00'),\n Gx: BigInt('0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'),\n Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),\n}))();\nfunction createSWU(Point, opts) {\n let map;\n // RFC 9380's NIST suites here all use m = 1, so createHasher passes one field element per map.\n // Building the SWU sqrt-ratio helper eagerly adds noticeable `nist.js` import cost, so defer it\n // to first use; after that the cached mapper is reused directly.\n return (scalars) => (map || (map = mapToCurveSimpleSWU(Point.Fp, opts)))(scalars[0]);\n}\n// NIST P256\nconst p256_Point = /* @__PURE__ */ weierstrass(p256_CURVE);\n/**\n * NIST P256 (aka secp256r1, prime256v1) curve, ECDSA and ECDH methods.\n * Hashes inputs with sha256 by default.\n *\n * @example\n * Generate one P-256 keypair, sign a message, and verify it.\n *\n * ```js\n * import { p256 } from '@noble/curves/nist.js';\n * const { secretKey, publicKey } = p256.keygen();\n * const recovered = p256.getPublicKey(secretKey);\n * const peer = p256.keygen();\n * const shared = p256.getSharedSecret(secretKey, peer.publicKey);\n * const msg = new TextEncoder().encode('hello noble');\n * const sig = p256.sign(msg, secretKey, { lowS: true, prehash: true });\n * const isValid = p256.verify(sig, msg, publicKey);\n * // const sigKeccak = p256.sign(keccak256(msg), secretKey, { prehash: false });\n * ```\n */\nexport const p256 = /* @__PURE__ */ ecdsa(p256_Point, sha256);\n/**\n * Hashing / encoding to p256 points / field. RFC 9380 methods.\n * @example\n * Hash one message onto the P-256 curve.\n *\n * ```ts\n * const point = p256_hasher.hashToCurve(new TextEncoder().encode('hello noble'));\n * ```\n */\nexport const p256_hasher = /* @__PURE__ */ (() => {\n return createHasher(p256_Point, createSWU(p256_Point, {\n A: p256_CURVE.a,\n B: p256_CURVE.b,\n Z: p256_Point.Fp.neg(BigInt(10)),\n }), {\n DST: 'P256_XMD:SHA-256_SSWU_RO_',\n encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',\n p: p256_CURVE.p,\n m: 1,\n k: 128,\n expand: 'xmd',\n hash: sha256,\n });\n})();\n/**\n * p256 OPRF, defined in RFC 9497.\n * @example\n * Run one blind/evaluate/finalize OPRF round over P-256.\n *\n * ```ts\n * const input = new TextEncoder().encode('hello noble');\n * const keys = p256_oprf.oprf.generateKeyPair();\n * const blind = p256_oprf.oprf.blind(input);\n * const evaluated = p256_oprf.oprf.blindEvaluate(keys.secretKey, blind.blinded);\n * const output = p256_oprf.oprf.finalize(input, blind.blind, evaluated);\n * ```\n */\nexport const p256_oprf = /* @__PURE__ */ (() => createOPRF({\n name: 'P256-SHA256',\n Point: p256_Point,\n hash: sha256,\n hashToGroup: p256_hasher.hashToCurve,\n hashToScalar: p256_hasher.hashToScalar,\n}))();\n/**\n * FROST threshold signatures over p256. RFC 9591.\n * @example\n * Create one trusted-dealer package for 2-of-3 p256 signing.\n *\n * ```ts\n * const alice = p256_FROST.Identifier.derive('alice@example.com');\n * const bob = p256_FROST.Identifier.derive('bob@example.com');\n * const carol = p256_FROST.Identifier.derive('carol@example.com');\n * const deal = p256_FROST.trustedDealer({ min: 2, max: 3 }, [alice, bob, carol]);\n * ```\n */\nexport const p256_FROST = /* @__PURE__ */ (() => createFROST({\n name: 'FROST-P256-SHA256-v1',\n Point: p256_Point,\n hashToScalar: p256_hasher.hashToScalar,\n hash: sha256,\n}))();\nconst p384_Point = /* @__PURE__ */ weierstrass(p384_CURVE);\n/**\n * NIST P384 (aka secp384r1) curve, ECDSA and ECDH methods. Hashes inputs with sha384 by default.\n * @example\n * Generate one P-384 keypair, sign a message, and verify it.\n *\n * ```ts\n * const { secretKey, publicKey } = p384.keygen();\n * const msg = new TextEncoder().encode('hello noble');\n * const sig = p384.sign(msg, secretKey);\n * const isValid = p384.verify(sig, msg, publicKey);\n * ```\n */\nexport const p384 = /* @__PURE__ */ ecdsa(p384_Point, sha384);\n/**\n * Hashing / encoding to p384 points / field. RFC 9380 methods.\n * @example\n * Hash one message onto the P-384 curve.\n *\n * ```ts\n * const point = p384_hasher.hashToCurve(new TextEncoder().encode('hello noble'));\n * ```\n */\nexport const p384_hasher = /* @__PURE__ */ (() => {\n return createHasher(p384_Point, createSWU(p384_Point, {\n A: p384_CURVE.a,\n B: p384_CURVE.b,\n Z: p384_Point.Fp.neg(BigInt(12)),\n }), {\n DST: 'P384_XMD:SHA-384_SSWU_RO_',\n encodeDST: 'P384_XMD:SHA-384_SSWU_NU_',\n p: p384_CURVE.p,\n m: 1,\n k: 192,\n expand: 'xmd',\n hash: sha384,\n });\n})();\n/**\n * p384 OPRF, defined in RFC 9497.\n * @example\n * Run one blind/evaluate/finalize OPRF round over P-384.\n *\n * ```ts\n * const input = new TextEncoder().encode('hello noble');\n * const keys = p384_oprf.oprf.generateKeyPair();\n * const blind = p384_oprf.oprf.blind(input);\n * const evaluated = p384_oprf.oprf.blindEvaluate(keys.secretKey, blind.blinded);\n * const output = p384_oprf.oprf.finalize(input, blind.blind, evaluated);\n * ```\n */\nexport const p384_oprf = /* @__PURE__ */ (() => createOPRF({\n name: 'P384-SHA384',\n Point: p384_Point,\n hash: sha384,\n hashToGroup: p384_hasher.hashToCurve,\n hashToScalar: p384_hasher.hashToScalar,\n}))();\n// NIST P521\n// RFC 7518 fixes the canonical JWK/JOSE width at 66 bytes:\n// - Section 3.4 says ECDSA octet strings must not omit leading zero octets\n// - Sections 6.2.1.2/6.2.1.3 say P-521 coordinates \"x\"/\"y\" must be 66 octets\n// - Section 6.2.2.1 says private scalar \"d\" must be ceil(log2(n)/8) octets, i.e. 66 for P-521\n// NIST FIPS 186-5 Appendix A.3.3 also routes deterministic ECDSA private keys through Appendix\n// B.2.3, whose Integer-to-Octet-String output has explicit fixed length L; for P-521 that is the\n// same 66-byte order width.\n// RFC 6979 matches that width too: private key x is an integer, while `int2octets(x)` uses\n// rlen = 8 * ceil(qlen/8); for P-521, qlen = 521 so the canonical octet width is 66 bytes.\n// Wycheproof ECDH stores private values as integers, not fixed-width scalar bytes, so it does not\n// require a dedicated 65-byte parser path; the repo tests now normalize those integer fixtures to\n// the canonical 66-byte width before use. There is no good standards or oracle reason to accept\n// exactly 65 bytes here: the coherent choices are canonical 66 only, or a broader integer-style\n// parser across many widths. Since this field parser is fixed-width, keep it canonical and use the\n// default exact-66-byte scalar field path.\n// A dedicated MersenneField primitive would allow speed-ups here: +40% getPublicKey, +23% sign,\n// +53% verify, +53% getSharedSecret.\nconst p521_Point = /* @__PURE__ */ weierstrass(p521_CURVE);\n/**\n * NIST P521 (aka secp521r1) curve, ECDSA and ECDH methods. Hashes inputs with sha512 by default.\n * Deterministic `keygen(seed)` expects 99 seed bytes here because the generic scalar-derivation\n * helper uses `getMinHashLength(n)`, not the 66-byte canonical secret-key width.\n * @example\n * Generate one P-521 keypair, sign a message, and verify it.\n *\n * ```ts\n * const { secretKey, publicKey } = p521.keygen();\n * const msg = new TextEncoder().encode('hello noble');\n * const sig = p521.sign(msg, secretKey);\n * const isValid = p521.verify(sig, msg, publicKey);\n * ```\n */\nexport const p521 = /* @__PURE__ */ ecdsa(p521_Point, sha512);\n/**\n * Hashing / encoding to p521 points / field. RFC 9380 methods.\n * @example\n * Hash one message onto the P-521 curve.\n *\n * ```ts\n * const point = p521_hasher.hashToCurve(new TextEncoder().encode('hello noble'));\n * ```\n */\nexport const p521_hasher = /* @__PURE__ */ (() => {\n return createHasher(p521_Point, createSWU(p521_Point, {\n A: p521_CURVE.a,\n B: p521_CURVE.b,\n Z: p521_Point.Fp.neg(BigInt(4)),\n }), {\n DST: 'P521_XMD:SHA-512_SSWU_RO_',\n encodeDST: 'P521_XMD:SHA-512_SSWU_NU_',\n p: p521_CURVE.p,\n m: 1,\n k: 256,\n expand: 'xmd',\n hash: sha512,\n });\n})();\n/**\n * p521 OPRF, defined in RFC 9497.\n * @example\n * Run one blind/evaluate/finalize OPRF round over P-521.\n *\n * ```ts\n * const input = new TextEncoder().encode('hello noble');\n * const keys = p521_oprf.oprf.generateKeyPair();\n * const blind = p521_oprf.oprf.blind(input);\n * const evaluated = p521_oprf.oprf.blindEvaluate(keys.secretKey, blind.blinded);\n * const output = p521_oprf.oprf.finalize(input, blind.blind, evaluated);\n * ```\n */\nexport const p521_oprf = /* @__PURE__ */ (() => createOPRF({\n name: 'P521-SHA512',\n Point: p521_Point,\n hash: sha512,\n hashToGroup: p521_hasher.hashToCurve,\n hashToScalar: p521_hasher.hashToScalar, // produces L=98 just like in RFC\n}))();\n", "/**\n * SHA3 (keccak) hash function, based on a new \"Sponge function\" design.\n * Different from older hashes, the internal state is bigger than output size.\n *\n * Check out\n * {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf | FIPS-202},\n * {@link https://keccak.team/keccak.html | Website}, and\n * {@link https://crypto.stackexchange.com/q/15727 | the differences between\n * SHA-3 and Keccak}.\n *\n * Check out `sha3-addons` module for cSHAKE, k12, and others.\n * @module\n */\nimport { split } from \"./_u64.js\";\n// prettier-ignore\nimport { abool, abytes, aexists, anumber, aoutput, checkOpts, clean, createHasher, oidNist, swap32IfBE, u32 } from \"./utils.js\";\n// No __PURE__ annotations in sha3 header:\n// EVERYTHING is in fact used on every export.\n// Various per round constants calculations\nconst _0n = BigInt(0);\nconst _1n = BigInt(1);\nconst _2n = BigInt(2);\nconst _7n = BigInt(7);\nconst _256n = BigInt(256);\n// FIPS 202 Algorithm 5 rc(): when the outgoing bit is 1, the 8-bit LFSR xors\n// taps 0, 4, 5, and 6, which compresses to the feedback mask `0x71`.\nconst _0x71n = BigInt(0x71);\nconst SHA3_PI = [];\nconst SHA3_ROTL = [];\nconst _SHA3_IOTA = []; // no pure annotation: var is always used\nfor (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {\n // Pi\n [x, y] = [y, (2 * x + 3 * y) % 5];\n SHA3_PI.push(2 * (5 * y + x));\n // Rotational\n SHA3_ROTL.push((((round + 1) * (round + 2)) / 2) % 64);\n // Iota\n let t = _0n;\n for (let j = 0; j < 7; j++) {\n R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n;\n if (R & _2n)\n t ^= _1n << ((_1n << BigInt(j)) - _1n);\n }\n _SHA3_IOTA.push(t);\n}\nconst IOTAS = split(_SHA3_IOTA, true);\n// `split(..., true)` keeps the local little-endian lane-word layout used by\n// `state32`, so these `H` / `L` tables follow the file's first-word /\n// second-word lane slots rather than `_u64.ts`'s usual high/low naming.\nconst SHA3_IOTA_H = IOTAS[0];\nconst SHA3_IOTA_L = IOTAS[1];\n// 64-bit left rotates as u32 pairs. Inlined here (not imported from _u64) so V8 can\n// inline them into keccakP \u2014 the import path costs ~24% on sha3_256. SHA3 is the only\n// consumer of left-rotates; other hashes use right-rotates from _u64.\n// Valid for s in 1..31 (SH/SL) and 33..63 (BH/BL); keccak never rotates by 0/32/64.\nconst rotlSH = (h, l, s) => (h << s) | (l >>> (32 - s));\nconst rotlSL = (h, l, s) => (l << s) | (h >>> (32 - s));\nconst rotlBH = (h, l, s) => (l << (s - 32)) | (h >>> (64 - s));\nconst rotlBL = (h, l, s) => (h << (s - 32)) | (l >>> (64 - s));\nconst rotlH = (h, l, s) => (s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s));\nconst rotlL = (h, l, s) => (s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s));\n// Reused Theta scratch buffer (column parities), same pattern as SHA256_W in sha2.\n// keccakP never calls user code, so the shared buffer cannot be observed mid-permutation.\nconst B = new Uint32Array(5 * 2);\n/**\n * `keccakf1600` internal permutation, additionally allows adjusting the round count.\n * @param s - 5x5 Keccak state encoded as 25 lanes split into 50 uint32 words\n * in this file's local little-endian lane-word order\n * @param rounds - number of rounds to execute\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If `rounds` is outside the supported `1..24` range. {@link Error}\n * @example\n * Permute a Keccak state with the default 24 rounds.\n * ```ts\n * keccakP(new Uint32Array(50));\n * ```\n */\nexport function keccakP(s, rounds = 24) {\n if (!(s instanceof Uint32Array))\n throw new TypeError('\"s\" expected Uint32Array(50), got type=' + typeof s);\n if (s.length !== 50)\n throw new RangeError('\"s\" expected Uint32Array(50), got length=' + s.length);\n anumber(rounds, 'rounds');\n // This implementation precomputes only the standard Keccak-f[1600] 24-round Iota table.\n if (rounds < 1 || rounds > 24)\n throw new Error('\"rounds\" expected integer 1..24');\n // NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js)\n for (let round = 24 - rounds; round < 24; round++) {\n // Theta \u03B8\n for (let x = 0; x < 10; x++)\n B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];\n for (let x = 0; x < 10; x += 2) {\n const idx1 = (x + 8) % 10;\n const idx0 = (x + 2) % 10;\n const B0 = B[idx0];\n const B1 = B[idx0 + 1];\n const Th = rotlH(B0, B1, 1) ^ B[idx1];\n const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];\n for (let y = 0; y < 50; y += 10) {\n s[x + y] ^= Th;\n s[x + y + 1] ^= Tl;\n }\n }\n // Rho (\u03C1) and Pi (\u03C0)\n let curH = s[2];\n let curL = s[3];\n for (let t = 0; t < 24; t++) {\n const shift = SHA3_ROTL[t];\n const Th = rotlH(curH, curL, shift);\n const Tl = rotlL(curH, curL, shift);\n const PI = SHA3_PI[t];\n curH = s[PI];\n curL = s[PI + 1];\n s[PI] = Th;\n s[PI + 1] = Tl;\n }\n // Chi (\u03C7)\n // Same as:\n // for (let x = 0; x < 10; x++) B[x] = s[y + x];\n // for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];\n for (let y = 0; y < 50; y += 10) {\n const b0 = s[y], b1 = s[y + 1], b2 = s[y + 2], b3 = s[y + 3];\n s[y] ^= ~s[y + 2] & s[y + 4];\n s[y + 1] ^= ~s[y + 3] & s[y + 5];\n s[y + 2] ^= ~s[y + 4] & s[y + 6];\n s[y + 3] ^= ~s[y + 5] & s[y + 7];\n s[y + 4] ^= ~s[y + 6] & s[y + 8];\n s[y + 5] ^= ~s[y + 7] & s[y + 9];\n s[y + 6] ^= ~s[y + 8] & b0;\n s[y + 7] ^= ~s[y + 9] & b1;\n s[y + 8] ^= ~b0 & b2;\n s[y + 9] ^= ~b1 & b3;\n }\n // Iota (\u03B9)\n s[0] ^= SHA3_IOTA_H[round];\n s[1] ^= SHA3_IOTA_L[round];\n }\n clean(B);\n}\n/**\n * Keccak sponge function.\n * @param blockLen - absorb/squeeze rate in bytes\n * @param suffix - domain separation suffix byte\n * @param outputLen - default digest length in bytes. This base sponge only\n * requires a non-negative integer; wrappers that need positive output\n * lengths must enforce that themselves.\n * @param enableXOF - whether XOF output is allowed\n * @param rounds - number of Keccak-f rounds\n * @example\n * Build a sponge state, absorb bytes, then finalize a digest.\n * ```ts\n * const hash = new Keccak(136, 0x06, 32);\n * hash.update(new Uint8Array([1, 2, 3]));\n * hash.digest();\n * ```\n */\nexport class Keccak {\n state;\n pos = 0;\n posOut = 0;\n finished = false;\n state32;\n destroyed = false;\n blockLen;\n suffix;\n outputLen;\n canXOF;\n enableXOF = false;\n rounds;\n // NOTE: we accept arguments in bytes instead of bits here.\n constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {\n anumber(blockLen, 'blockLen');\n anumber(suffix, 'suffix');\n anumber(rounds, 'rounds');\n abool(enableXOF, 'enableXOF');\n this.blockLen = blockLen;\n this.suffix = suffix;\n this.outputLen = outputLen;\n this.enableXOF = enableXOF;\n this.canXOF = enableXOF;\n this.rounds = rounds;\n // Can be passed from user as dkLen\n anumber(outputLen, 'outputLen');\n // Only keccak-f1600 is supported: 1600 bits (5x5 matrix of 64bit) === 200 bytes of state.\n if (!(0 < blockLen && blockLen < 200))\n throw new Error('\"blockLen\" must be 1..199');\n this.state = new Uint8Array(200);\n this.state32 = u32(this.state);\n }\n clone() {\n return this._cloneInto();\n }\n keccak() {\n swap32IfBE(this.state32);\n keccakP(this.state32, this.rounds);\n swap32IfBE(this.state32);\n this.posOut = 0;\n this.pos = 0;\n }\n update(data) {\n aexists(this);\n abytes(data);\n const { blockLen, state, state32 } = this;\n const len = data.length;\n // Absorb full blocks with u32 XORs when both sides are 4-byte aligned.\n // XOR of same-position words equals XOR of same-position bytes, so this is endianness-safe.\n const canUseU32 = blockLen % 4 === 0 && data.byteOffset % 4 === 0;\n const blockLen32 = blockLen / 4;\n const data32 = canUseU32 && len >= blockLen ? u32(data) : undefined;\n for (let pos = 0; pos < len;) {\n if (data32 !== undefined && this.pos === 0 && pos % 4 === 0 && len - pos >= blockLen) {\n for (let i = 0, o = pos / 4; i < blockLen32; i++)\n state32[i] ^= data32[o + i];\n pos += blockLen;\n // Subclasses (_KeccakPRG) read `this.pos` inside their `keccak()` override,\n // so it must reflect the fully-absorbed block before the permutation fires.\n this.pos = blockLen;\n this.keccak();\n continue;\n }\n const take = Math.min(blockLen - this.pos, len - pos);\n for (let i = 0; i < take; i++)\n state[this.pos++] ^= data[pos++];\n if (this.pos === blockLen)\n this.keccak();\n }\n return this;\n }\n finish() {\n if (this.finished)\n return;\n this.finished = true;\n const { state, suffix, pos, blockLen } = this;\n // FIPS 202 appends the SHA3/SHAKE domain-separation suffix before pad10*1.\n // These byte values already include the first padding bit, while the\n // final `0x80` below supplies the closing `1` bit in the last rate byte.\n state[pos] ^= suffix;\n // If that combined suffix lands in the last rate byte and already sets\n // bit 7, absorb it first so the final pad10*1 bit can be xored into a\n // fresh block.\n if ((suffix & 0x80) !== 0 && pos === blockLen - 1)\n this.keccak();\n state[blockLen - 1] ^= 0x80;\n this.keccak();\n }\n writeInto(out) {\n aexists(this, false);\n abytes(out);\n this.finish();\n const bufferOut = this.state;\n const { blockLen } = this;\n for (let pos = 0, len = out.length; pos < len;) {\n if (this.posOut >= blockLen)\n this.keccak();\n const take = Math.min(blockLen - this.posOut, len - pos);\n out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);\n this.posOut += take;\n pos += take;\n }\n return out;\n }\n xofInto(out) {\n // Plain SHA3/Keccak usage with XOF is probably a mistake, but this base\n // class is also reused by SHAKE/cSHAKE/KMAC/TupleHash/ParallelHash/\n // TurboSHAKE/KangarooTwelve wrappers that intentionally enable XOF.\n if (!this.enableXOF)\n throw new Error('XOF is not enabled');\n return this.writeInto(out);\n }\n xof(bytes) {\n anumber(bytes);\n return this.xofInto(new Uint8Array(bytes));\n }\n digestInto(out) {\n aoutput(out, this);\n if (this.finished)\n throw new Error('digest() was already called');\n // `aoutput(...)` allows oversized buffers; digestInto() must fill only the advertised digest.\n this.writeInto(out.length === this.outputLen ? out : out.subarray(0, this.outputLen));\n this.destroy();\n }\n digest() {\n const out = new Uint8Array(this.outputLen);\n this.digestInto(out);\n return out;\n }\n destroy() {\n this.destroyed = true;\n clean(this.state);\n }\n _cloneInto(to) {\n const { blockLen, suffix, outputLen, rounds, enableXOF } = this;\n to ||= new Keccak(blockLen, suffix, outputLen, enableXOF, rounds);\n // Reused destinations can come from a different rate/capacity variant, so clone must rewrite\n // the sponge geometry as well as the state words.\n to.blockLen = blockLen;\n to.state32.set(this.state32);\n // Sponge padding and XOF output are positional, so both offsets are part of the clone state.\n to.pos = this.pos;\n to.posOut = this.posOut;\n to.finished = this.finished;\n to.rounds = rounds;\n // Suffix can change in cSHAKE\n to.suffix = suffix;\n to.outputLen = outputLen;\n to.enableXOF = enableXOF;\n // Clones must preserve the public capability bit too; `_KMAC` reuses this path and deep clone\n // tests compare instance fields directly, so leaving `canXOF` behind makes the clone lie.\n to.canXOF = this.canXOF;\n to.destroyed = this.destroyed;\n return to;\n }\n}\nconst genKeccak = (suffix, blockLen, outputLen, info = {}) => createHasher(() => new Keccak(blockLen, suffix, outputLen), info);\n/**\n * SHA3-224 hash function.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA3-224.\n * ```ts\n * sha3_224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha3_224 = /* @__PURE__ */ genKeccak(0x06, 144, 28, \n/* @__PURE__ */ oidNist(0x07));\n/**\n * SHA3-256 hash function. Different from keccak-256.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA3-256.\n * ```ts\n * sha3_256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha3_256 = /* @__PURE__ */ genKeccak(0x06, 136, 32, \n/* @__PURE__ */ oidNist(0x08));\n/**\n * SHA3-384 hash function.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA3-384.\n * ```ts\n * sha3_384(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha3_384 = /* @__PURE__ */ genKeccak(0x06, 104, 48, \n/* @__PURE__ */ oidNist(0x09));\n/**\n * SHA3-512 hash function.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA3-512.\n * ```ts\n * sha3_512(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha3_512 = /* @__PURE__ */ genKeccak(0x06, 72, 64, \n/* @__PURE__ */ oidNist(0x0a));\n/**\n * Keccak-224 hash function.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with Keccak-224.\n * ```ts\n * keccak_224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const keccak_224 = /* @__PURE__ */ genKeccak(0x01, 144, 28);\n/**\n * Keccak-256 hash function. Different from SHA3-256.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with Keccak-256.\n * ```ts\n * keccak_256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const keccak_256 = /* @__PURE__ */ genKeccak(0x01, 136, 32);\n/**\n * Keccak-384 hash function.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with Keccak-384.\n * ```ts\n * keccak_384(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const keccak_384 = /* @__PURE__ */ genKeccak(0x01, 104, 48);\n/**\n * Keccak-512 hash function.\n * @param msg - message bytes to hash\n * @param opts - Reserved hash options.\n * @returns Digest bytes.\n * @example\n * Hash a message with Keccak-512.\n * ```ts\n * keccak_512(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const keccak_512 = /* @__PURE__ */ genKeccak(0x01, 72, 64);\nconst genShake = (suffix, blockLen, outputLen, info = {}) => createHasher((opts = {}) => {\n opts = checkOpts({}, opts);\n return new Keccak(blockLen, suffix, opts.dkLen === undefined ? outputLen : opts.dkLen, true);\n}, info);\n/**\n * SHAKE128 XOF with 128-bit security and a 16-byte default output.\n * @param msg - message bytes to hash\n * @param opts - Optional output-length override. See {@link ShakeOpts}.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHAKE128.\n * ```ts\n * shake128(new Uint8Array([97, 98, 99]), { dkLen: 32 });\n * ```\n */\nexport const shake128 = \n/* @__PURE__ */\ngenShake(0x1f, 168, 16, /* @__PURE__ */ oidNist(0x0b));\n/**\n * SHAKE256 XOF with 256-bit security and a 32-byte default output.\n * @param msg - message bytes to hash\n * @param opts - Optional output-length override. See {@link ShakeOpts}.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHAKE256.\n * ```ts\n * shake256(new Uint8Array([97, 98, 99]), { dkLen: 64 });\n * ```\n */\nexport const shake256 = \n/* @__PURE__ */\ngenShake(0x1f, 136, 32, /* @__PURE__ */ oidNist(0x0c));\n/**\n * SHAKE128 XOF with 256-bit output (NIST version).\n * @param msg - message bytes to hash\n * @param opts - Optional output-length override. See {@link ShakeOpts}.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHAKE128 using a 32-byte default output.\n * ```ts\n * shake128_32(new Uint8Array([97, 98, 99]), { dkLen: 32 });\n * ```\n */\nexport const shake128_32 = \n/* @__PURE__ */\ngenShake(0x1f, 168, 32, /* @__PURE__ */ oidNist(0x0b));\n/**\n * SHAKE256 XOF with 512-bit output (NIST version).\n * @param msg - message bytes to hash\n * @param opts - Optional output-length override. See {@link ShakeOpts}.\n * @returns Digest bytes.\n * @example\n * Hash a message with SHAKE256 using a 64-byte default output.\n * ```ts\n * shake256_64(new Uint8Array([97, 98, 99]), { dkLen: 64 });\n * ```\n */\nexport const shake256_64 = \n/* @__PURE__ */\ngenShake(0x1f, 136, 64, /* @__PURE__ */ oidNist(0x0c));\n", "/**\n * Utilities for hex, bytearray and number handling.\n * @module\n */\n/*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) */\nimport { abytes, abytes as abytes_, ahash as ahash_, anumber, bytesToHex, concatBytes, isBytes, isLE, randomBytes as randb, } from '@noble/hashes/utils.js';\n/**\n * Asserts that a value is a byte array and optionally checks its length.\n * Returns the original reference unchanged on success, and currently also accepts Node `Buffer`\n * values through the upstream validator.\n * This helper throws on malformed input, so APIs that must return `false` need to guard lengths\n * before decoding or before calling it.\n * @example\n * Validate that a value is a byte array with the expected length.\n * ```ts\n * abytes(new Uint8Array([1]), 1);\n * ```\n */\nconst abytesDoc = abytes;\nexport { abytesDoc as abytes };\n/**\n * Concatenates byte arrays into a new `Uint8Array`.\n * Zero arguments return an empty `Uint8Array`.\n * Invalid segments throw before allocation because each argument is validated first.\n * @example\n * Concatenate two byte arrays into one result.\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nconst concatBytesDoc = concatBytes;\nexport { concatBytesDoc as concatBytes };\n/**\n * Returns cryptographically secure random bytes.\n * Requires `globalThis.crypto.getRandomValues` and throws if that API is unavailable.\n * `bytesLength` is validated by the upstream helper as a non-negative integer before allocation,\n * so negative and fractional values both throw instead of truncating through JS `ToIndex`.\n * @param bytesLength - Number of random bytes to generate.\n * @returns Fresh random bytes.\n * @example\n * Generate a fresh random seed.\n * ```ts\n * const seed = randomBytes(4);\n * ```\n */\nexport const randomBytes = randb;\nexport function aarray(item, title, inner = () => { }) {\n if (!Array.isArray(item))\n throw new TypeError(`\"${title}\" expected array, got type=${typeof item}`);\n for (let i = 0; i < item.length; i++)\n inner(item[i], `${title}[${i}]`);\n return item;\n}\nexport function aobject(value, title = 'object') {\n if (value === null || typeof value !== 'object' || Array.isArray(value))\n throw new TypeError(title === 'object'\n ? 'expected valid options object'\n : `\"${title}\" expected object, got type=${typeof value}`);\n return value;\n}\n/**\n * Compares two byte arrays in a length-constant way for equal lengths.\n * Inputs are validated as byte arrays; unequal lengths return `false` immediately.\n * @param a - First byte array.\n * @param b - Second byte array.\n * @returns Whether both arrays contain the same bytes.\n * @example\n * Compare two byte arrays for equality.\n * ```ts\n * equalBytes(new Uint8Array([1]), new Uint8Array([1]));\n * ```\n */\nexport function equalBytes(a, b) {\n a = abytes(a);\n b = abytes(b);\n if (a.length !== b.length)\n return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++)\n diff |= a[i] ^ b[i];\n return diff === 0;\n}\n/**\n * Copies bytes into a fresh `Uint8Array`.\n * Returns a detached plain `Uint8Array` after validating that the input is real bytes.\n * @param bytes - Source bytes.\n * @returns Copy of the input bytes.\n * @example\n * Copy bytes into a fresh array.\n * ```ts\n * copyBytes(new Uint8Array([1, 2]));\n * ```\n */\nexport function copyBytes(bytes) {\n // The typed-array constructor copies a typed-array source through its internal byte storage.\n // Unlike `Uint8Array.from`, it does not invoke a subclass-overridden iterator. Keep the explicit\n // validation because the constructor itself would also accept arrays and other typed arrays.\n return new Uint8Array(abytes(bytes));\n}\n/**\n * Byte-swaps each 64-bit lane in place.\n * Falcon's exact binary64 tables are stored as little-endian byte payloads, so BE runtimes need\n * this boundary helper before aliasing them as host `Float64Array` lanes.\n * @param arr - Byte buffer whose length is a multiple of 8.\n * @returns The same buffer after in-place 64-bit lane byte swaps.\n * @example\n * Byte-swap one 64-bit lane in place.\n * ```ts\n * byteSwap64(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]));\n * ```\n */\nexport function byteSwap64(arr) {\n const bytes = new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);\n for (let i = 0; i < bytes.length; i += 8) {\n const a0 = bytes[i + 0];\n const a1 = bytes[i + 1];\n const a2 = bytes[i + 2];\n const a3 = bytes[i + 3];\n bytes[i + 0] = bytes[i + 7];\n bytes[i + 1] = bytes[i + 6];\n bytes[i + 2] = bytes[i + 5];\n bytes[i + 3] = bytes[i + 4];\n bytes[i + 4] = a3;\n bytes[i + 5] = a2;\n bytes[i + 6] = a1;\n bytes[i + 7] = a0;\n }\n return arr;\n}\n/**\n * Byte-swaps 64-bit lanes on big-endian runtimes and returns the input unchanged on little-endian.\n * This keeps Falcon's binary64 tables in canonical little-endian order before aliasing them as\n * `Float64Array` lanes on the current host.\n * @param arr - Buffer to pass through or swap in place.\n * @returns The same buffer, normalized for Falcon's little-endian table layout.\n * @example\n * Normalize one host-endian buffer for Falcon's float tables.\n * ```ts\n * baswap64If(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]));\n * ```\n */\nexport const baswap64If = isLE\n ? (arr) => arr\n : byteSwap64;\n/**\n * Validates that an options bag is a plain object.\n * @param opts - Options object to validate.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Validate that an options bag is a plain object.\n * ```ts\n * validateOpts({});\n * ```\n */\nexport function validateOpts(opts) {\n // Arrays silently passed here before, but these call sites expect named option-bag fields.\n if (isBytes(opts))\n throw new TypeError('\"opts\" expected object, got Uint8Array');\n aobject(opts, 'opts');\n const proto = Object.getPrototypeOf(opts);\n // Options are security parameters, not general class instances. Restricting the bag to own\n // properties prevents values injected through Object.prototype (or a custom shared prototype)\n // from silently changing signing behavior. Null-prototype records remain supported.\n if (proto !== null && proto !== Object.prototype)\n throw new TypeError('\"opts\" expected a plain object');\n}\n// Frozen because they are exported: an unfrozen array export lets anything in the\n// process push a key onto the accepted set and silently re-open exactly the hole this\n// validation closes.\n/** Keys accepted by `verify`. */\nexport const VER_OPT_KEYS = /* @__PURE__ */ Object.freeze([\n 'context',\n]);\n/** Keys accepted by `sign`. */\nexport const SIG_OPT_KEYS = /* @__PURE__ */ Object.freeze([\n 'context',\n 'extraEntropy',\n]);\n/**\n * Rejects option keys the caller did not mean to set.\n *\n * Validating the types of known keys while ignoring unknown ones makes a typo\n * indistinguishable from an omission, and for these options an omission is a\n * security downgrade rather than a no-op: `{ ctx }` instead of `{ context }` signs\n * with no domain separation, succeeds, and verifies for anyone who also supplies\n * none. Nothing at any layer reports it. TypeScript catches this through excess\n * property checks, so the exposure is JavaScript callers specifically.\n *\n * @param opts - Options object to check.\n * @param allowed - The keys this call site accepts.\n * Returns a frozen null-prototype snapshot so later reads cannot fall through to a polluted\n * prototype. Like `checkOpts()` in noble-hashes, only enumerable own properties are copied.\n * @throws If any other copied key is present or the bag has a custom prototype. {@link TypeError}\n * @returns Sanitized snapshot of the enumerable own options.\n * @example\n * Accept a known option key. A key the list does not name, such as `ctx`, throws instead.\n * ```ts\n * import { checkOptKeys } from '@noble/post-quantum/utils.js';\n * checkOptKeys({ context: new Uint8Array() }, ['context']);\n * ```\n */\nexport function checkOptKeys(opts, allowed) {\n validateOpts(opts);\n // Snapshot once before validation: Object.assign follows the same own-enumerable option-bag\n // semantics as noble-hashes, while the null prototype keeps omitted fields immune to pollution.\n const normalized = Object.assign(Object.create(null), opts);\n for (const [k, v] of Object.entries(normalized)) {\n // `undefined` means unset everywhere else in these validators, and building an options bag by\n // spread is a normal way to reach these calls, so present-but-undefined stays equivalent to\n // omission.\n if (v === undefined)\n continue;\n if (!allowed.includes(k))\n throw new TypeError('unexpected option \"' + String(k) + '\"; expected one of: ' + allowed.join(', '));\n }\n return Object.freeze(normalized);\n}\n/**\n * Validates common verification options.\n * `context` itself is validated with `abytes(...)`, and individual algorithms may narrow support\n * further after this shared plain-object gate.\n * @param opts - Verification options. See {@link VerOpts}.\n * @param allowed - Keys this call site accepts. Defaults to {@link VER_OPT_KEYS}; surfaces that\n * take extra keys, or take fewer, pass their own list.\n * @throws On wrong argument types. {@link TypeError}\n * @returns Frozen null-prototype snapshot of the validated options.\n * @example\n * Validate common verification options.\n * ```ts\n * validateVerOpts({ context: new Uint8Array([1]) });\n * ```\n */\nexport function validateVerOpts(opts, allowed = VER_OPT_KEYS) {\n const normalized = checkOptKeys(opts, allowed);\n if (normalized.context !== undefined)\n abytes(normalized.context, undefined, 'opts.context');\n return normalized;\n}\n/**\n * Validates common signing options.\n * `extraEntropy` is validated with `abytes(...)`; exact lengths and extra algorithm-specific\n * restrictions are enforced later by callers.\n * @param opts - Signing options. See {@link SigOpts}.\n * @param allowed - Keys this call site accepts. Defaults to {@link SIG_OPT_KEYS}; surfaces that\n * take extra keys, or take fewer, pass their own list.\n * @throws On wrong argument types. {@link TypeError}\n * @returns Frozen null-prototype snapshot of the validated options.\n * @example\n * Validate common signing options.\n * ```ts\n * validateSigOpts({ extraEntropy: new Uint8Array([1]) });\n * ```\n */\nexport function validateSigOpts(opts, allowed = SIG_OPT_KEYS) {\n const normalized = checkOptKeys(opts, allowed);\n if (normalized.context !== undefined)\n abytes(normalized.context, undefined, 'opts.context');\n if (normalized.extraEntropy !== false && normalized.extraEntropy !== undefined)\n abytes(normalized.extraEntropy, undefined, 'opts.extraEntropy');\n return normalized;\n}\n/**\n * Builds a fixed-layout coder from byte lengths and nested coders.\n * Raw-length fields decode as zero-copy `subarray(...)` views, and nested coders may preserve that\n * aliasing too. Nested coder `encode(...)` results are treated as owned scratch: `splitCoder`\n * copies them into the output and then zeroizes them with `fill(0)`. If a nested encoder forwards\n * caller-owned bytes, it must do so only after detaching them into a disposable copy.\n * @param label - Label used in validation errors.\n * @param lengths - Field lengths or nested coders.\n * @returns Composite fixed-length coder.\n * @example\n * Build a fixed-layout coder from byte lengths and nested coders.\n * ```ts\n * splitCoder('demo', 1, 2).encode([new Uint8Array([1]), new Uint8Array([2, 3])]);\n * ```\n */\nexport function splitCoder(label, ...lengths) {\n const getLength = (c) => typeof c === 'number' ? c : c.bytesLen;\n const bytesLen = lengths.reduce((sum, a) => sum + getLength(a), 0);\n return {\n bytesLen,\n encode: (bufs) => {\n const res = new Uint8Array(bytesLen);\n for (let i = 0, pos = 0; i < lengths.length; i++) {\n const c = lengths[i];\n const l = getLength(c);\n const b = typeof c === 'number' ? bufs[i] : c.encode(bufs[i]);\n abytes_(b, l, label);\n res.set(b, pos);\n if (typeof c !== 'number')\n b.fill(0); // clean\n pos += l;\n }\n return res;\n },\n decode: (buf) => {\n abytes_(buf, bytesLen, label);\n const res = [];\n for (const c of lengths) {\n const l = getLength(c);\n const b = buf.subarray(0, l);\n res.push(typeof c === 'number' ? b : c.decode(b));\n buf = buf.subarray(l);\n }\n return res;\n },\n };\n}\n// nano-packed.array (fixed size)\n/**\n * Builds a fixed-length vector coder from another fixed-length coder.\n * Element decoding receives `subarray(...)` views, so aliasing depends on the element coder.\n * Element coder `encode(...)` results are treated as owned scratch: `vecCoder` copies them into\n * the output and then zeroizes them with `fill(0)`. If an element encoder forwards caller-owned\n * bytes, it must do so only after detaching them into a disposable copy. `vecCoder` also trusts\n * the `BytesCoderLen` contract: each encoded element must already be exactly `c.bytesLen` bytes.\n * @param c - Element coder.\n * @param vecLen - Number of elements in the vector.\n * @returns Fixed-length vector coder.\n * @example\n * Build a fixed-length vector coder from another fixed-length coder.\n * ```ts\n * vecCoder(\n * { bytesLen: 1, encode: (n: number) => Uint8Array.of(n), decode: (b: Uint8Array) => b[0] || 0 },\n * 2\n * ).encode([1, 2]);\n * ```\n */\nexport function vecCoder(c, vecLen) {\n const coder = c;\n const bytesLen = vecLen * coder.bytesLen;\n return {\n bytesLen,\n encode: (u) => {\n const uArr = aarray(u, 'u');\n if (uArr.length !== vecLen)\n throw new RangeError(`vecCoder.encode: wrong length=${uArr.length}. Expected: ${vecLen}`);\n const res = new Uint8Array(bytesLen);\n for (let i = 0, pos = 0; i < uArr.length; i++) {\n const b = coder.encode(uArr[i]);\n res.set(b, pos);\n b.fill(0); // clean\n pos += b.length;\n }\n return res;\n },\n decode: (a) => {\n abytes_(a, bytesLen);\n const r = [];\n for (let i = 0; i < a.length; i += coder.bytesLen)\n r.push(coder.decode(a.subarray(i, i + coder.bytesLen)));\n return r;\n },\n };\n}\n/**\n * Overwrites supported typed-array inputs with zeroes in place.\n * Accepts direct typed arrays and one-level arrays of them.\n * @param list - Typed arrays or one-level lists of typed arrays to clear.\n * @example\n * Overwrite typed arrays with zeroes.\n * ```ts\n * const buf = Uint8Array.of(1, 2, 3);\n * cleanBytes(buf);\n * ```\n */\nexport function cleanBytes(...list) {\n for (const t of list) {\n if (Array.isArray(t))\n for (const b of t)\n b.fill(0);\n else\n t.fill(0);\n }\n}\n/**\n * Creates a 32-bit mask with the lowest `bits` bits set.\n * @param bits - Number of low bits to keep.\n * @returns Bit mask with `bits` ones.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Create a low-bit mask for packed-field operations.\n * ```ts\n * const mask = getMask(4);\n * ```\n */\nexport function getMask(bits) {\n anumber(bits, 'bits');\n if (bits > 32)\n throw new RangeError('\"bits\" expected <= 32, got ' + bits);\n // JS shifts are modulo 32, so bit 32 needs an explicit full-width mask.\n return bits === 32 ? 0xffffffff : ~(-1 << bits) >>> 0;\n}\n/** Shared empty byte array used as the default context. */\nexport const EMPTY = /* @__PURE__ */ Uint8Array.of();\n/**\n * Builds the domain-separated message payload for the pure sign/verify paths.\n * Context length `255` is valid; only `ctx.length > 255` is rejected.\n * @param msg - Message bytes.\n * @param ctx - Optional context bytes.\n * @returns Domain-separated message payload.\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Build the domain-separated payload before direct signing.\n * ```ts\n * const payload = getMessage(new Uint8Array([1, 2]));\n * ```\n */\nexport function getMessage(msg, ctx = EMPTY) {\n abytes_(msg, undefined, 'msg');\n abytes_(ctx, undefined, 'ctx');\n if (ctx.length > 255)\n throw new RangeError('context should be 255 bytes or less');\n return concatBytes(new Uint8Array([0, ctx.length]), ctx, msg);\n}\n// DER tag+length plus the shared NIST hash OID arc 2.16.840.1.101.3.4.2.* used by the\n// FIPS 204 / FIPS 205 pre-hash wrappers; the final byte selects SHA-256, SHA-512, SHAKE128,\n// SHAKE256, or another approved hash/XOF under that subtree.\n// 06 09 60 86 48 01 65 03 04 02\nconst oidNistP = /* @__PURE__ */ Uint8Array.from([6, 9, 0x60, 0x86, 0x48, 1, 0x65, 3, 4, 2]);\n/**\n * Output length, in bytes, that each XOF OID under this arc denotes.\n *\n * Unlike a fixed hash, an XOF's OID is a promise about the digest length: RFC 8702\n * defines id-shake128 as SHAKE128 with 256-bit output and id-shake256 as SHAKE256 with\n * 512-bit output, and FIPS 204 / FIPS 205 use exactly those pairings for pre-hash. Both\n * bare noble-hashes defaults are half these values, so neither can be signed under its\n * own OID.\n */\nconst XOF_OID_OUTPUT_LEN = /* @__PURE__ */ (() => ({\n '060960864801650304020b': 32, // id-shake128, SHAKE128(M, 256)\n '060960864801650304020c': 64, // id-shake256, SHAKE256(M, 512)\n}))();\n/**\n * Validates that a hash exposes a NIST hash OID and enough collision resistance.\n * Current accepted surface is broader than the FIPS algorithm tables: any hash/XOF under the NIST\n * `2.16.840.1.101.3.4.2.*` subtree is accepted if its effective `outputLen` is strong enough.\n * XOF callers must pass a callable whose `outputLen` matches the digest length they actually intend\n * to sign; bare `shake128` / `shake256` defaults are too short for the stronger prehash modes.\n * @param hash - Hash function to validate.\n * @param requiredStrength - Minimum required collision-resistance strength in bits.\n * @throws If the hash metadata or collision resistance is insufficient. {@link Error}\n * @example\n * Validate that a hash exposes a NIST hash OID and enough collision resistance.\n * ```ts\n * import { sha256 } from '@noble/hashes/sha2.js';\n * import { checkHash } from '@noble/post-quantum/utils.js';\n * checkHash(sha256, 128);\n * ```\n */\nexport function checkHash(hash, requiredStrength = 0) {\n if (typeof hash !== 'function' || typeof hash.create !== 'function')\n throw new TypeError('\"hash\" expected hash function, got type=' + typeof hash);\n ahash_(hash);\n anumber(requiredStrength, 'requiredStrength');\n const oid = hash.oid;\n abytes_(oid, undefined, 'hash.oid');\n if (!equalBytes(oid.subarray(0, 10), oidNistP))\n throw new Error('\"hash.oid\" is invalid: expected NIST hash');\n // FIPS 204 / FIPS 205 require both collision and second-preimage strength; for approved NIST\n // hashes/XOFs under this OID subtree, the collision bound from the configured digest length is\n // the tighter runtime check, so enforce that lower bound here.\n // XOFs under this arc are identified by an OID that fixes their output length:\n // FIPS 204 \u00A75.4.1 (SHAKE128) and FIPS 205 \u00A710.2.2 (both SHAKEs), matching RFC 8702, pair\n // id-shake128 with SHAKE128(M, 256) and id-shake256 with SHAKE256(M, 512). getMessagePrehash embeds\n // hash.oid beside hash(msg), so a shorter digest signs an M' that claims a length\n // it does not have: noble-hashes' bare shake256 defaults to 32 bytes and cleared\n // the collision bound at the 128-bit level, producing signatures a conformant\n // verifier rejects because it recomputes 512 bits. Check the length the OID\n // denotes rather than the generic bound.\n const xofLen = XOF_OID_OUTPUT_LEN[bytesToHex(oid)];\n if (xofLen !== undefined && hash.outputLen !== xofLen) {\n throw new Error('Pre-hash XOF output length must be ' + xofLen + ' bytes for this OID, got: ' + hash.outputLen);\n }\n const collisionResistance = (hash.outputLen * 8) / 2;\n if (requiredStrength > collisionResistance) {\n throw new Error('Pre-hash security strength too low: ' +\n collisionResistance +\n ', required: ' +\n requiredStrength);\n }\n}\n/**\n * Builds the domain-separated prehash payload for the prehash sign/verify paths.\n * Callers are expected to vet `hash.oid` first, e.g. via `checkHash(...)`; calling this helper\n * directly with a hash object that lacks `oid` currently throws later inside `concatBytes(...)`.\n * Context length `255` is valid; only `ctx.length > 255` is rejected.\n * @param hash - Prehash function.\n * @param msg - Message bytes.\n * @param ctx - Optional context bytes.\n * @returns Domain-separated prehash payload.\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Build the domain-separated prehash payload for external hashing.\n * ```ts\n * import { sha256 } from '@noble/hashes/sha2.js';\n * import { getMessagePrehash } from '@noble/post-quantum/utils.js';\n * getMessagePrehash(sha256, new Uint8Array([1, 2]));\n * ```\n */\nexport function getMessagePrehash(hash, msg, ctx = EMPTY) {\n checkHash(hash);\n abytes_(msg, undefined, 'msg');\n abytes_(ctx, undefined, 'ctx');\n if (ctx.length > 255)\n throw new RangeError('context should be 255 bytes or less');\n const hashed = hash(msg);\n return concatBytes(new Uint8Array([1, ctx.length]), ctx, hash.oid, hashed);\n}\n/**\n * Asserts something is a string.\n * @param value - Value to validate.\n * @param title - Label included in thrown errors.\n * @returns The validated string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Validate a label string.\n *\n * ```ts\n * astring('example', 'label');\n * ```\n */\nexport function astring(value, title = '') {\n if (typeof value !== 'string') {\n const prefix = title && `\"${title}\" `;\n throw new TypeError(prefix + 'expected string, got type=' + typeof value);\n }\n return value;\n}\n", "/**\n * Internal methods for lattice-based ML-KEM and ML-DSA.\n * @module\n */\n/*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) */\nimport { FFTCore, reverseBits } from '@noble/curves/abstract/fft.js';\nimport { shake128, shake256 } from '@noble/hashes/sha3.js';\nimport { cleanBytes, getMask, } from \"./utils.js\";\n/**\n * Creates shared modular arithmetic, NTT, and packing helpers for CRYSTALS schemes.\n * @param opts - Polynomial and transform parameters. See {@link CrystalOpts}.\n * @returns CRYSTALS arithmetic and encoding helpers.\n * @example\n * Create shared modular arithmetic and NTT helpers for a CRYSTALS parameter set.\n * ```ts\n * const crystals = genCrystals({\n * newPoly: (n) => new Uint16Array(n),\n * N: 256,\n * Q: 3329,\n * F: 3303,\n * ROOT_OF_UNITY: 17,\n * brvBits: 7,\n * isKyber: true,\n * });\n * const reduced = crystals.mod(-1);\n * ```\n */\nexport const genCrystals = (opts) => {\n // isKyber: true means Kyber, false means Dilithium\n const { newPoly, N, Q, F, ROOT_OF_UNITY, brvBits, isKyber } = opts;\n // Normalize JS `%` into the canonical Z_m representative `[0, modulo-1]` expected by\n // FIPS 203 \u00A72.3 / FIPS 204 \u00A72.3 before downstream mod-q arithmetic.\n const mod = (a, modulo = Q) => {\n const result = (a % modulo) | 0;\n return (result >= 0 ? result | 0 : (modulo + result) | 0) | 0;\n };\n // FIPS 204 \u00A77.4 uses the centered `mod \u00B1` representative for low bits, keeping the\n // positive midpoint when `modulo` is even.\n // Center to `[-floor((modulo-1)/2), floor(modulo/2)]`.\n const smod = (a, modulo = Q) => {\n const r = mod(a, modulo) | 0;\n return (r > modulo >> 1 ? (r - modulo) | 0 : r) | 0;\n };\n // Kyber uses the FIPS 203 Appendix A `BitRev_7` table here via the first 128 entries, while\n // Dilithium uses the FIPS 204 \u00A77.5 / Appendix B `BitRev_8` zetas table over all 256 entries.\n function getZettas() {\n const out = newPoly(N);\n for (let i = 0; i < N; i++) {\n const b = reverseBits(i, brvBits);\n const p = BigInt(ROOT_OF_UNITY) ** BigInt(b) % BigInt(Q);\n out[i] = Number(p) | 0;\n }\n return out;\n }\n const nttZetas = getZettas();\n // Number-Theoretic Transform\n // Explained: https://electricdusk.com/ntt.html\n // Kyber has slightly different params, since there is no 512th primitive root of unity mod q,\n // only 256th primitive root of unity mod. Which also complicates MultiplyNTT.\n const inv = (_a) => {\n throw new Error('not implemented');\n };\n // ML-KEM (Kyber) polynomials always enter the transform reduced to [0, Q), so add/sub only\n // need one conditional correction instead of `%`; measured ~20% faster NTT there.\n // ML-DSA keeps the generic mod() path on purpose: its first forward stage sees centered\n // (negative) coefficients, and `sub(a, t)` can drop below -Q (t is a mul output in [0, Q)),\n // so a single correction is not enough. A guarded fast path with mod() fallback was measured\n // slower than plain `%` for the 23-bit Q (V8 int32 modulo is one div; the branches lose).\n const field = isKyber\n ? {\n add: (a, b) => {\n const r = (a + b) | 0;\n return r >= Q ? (r - Q) | 0 : r;\n },\n sub: (a, b) => {\n const r = (a - b) | 0;\n return r < 0 ? (r + Q) | 0 : r;\n },\n mul: (a, b) => mod((a | 0) * (b | 0)) | 0,\n inv,\n }\n : {\n add: (a, b) => mod((a | 0) + (b | 0)) | 0,\n sub: (a, b) => mod((a | 0) - (b | 0)) | 0,\n mul: (a, b) => mod((a | 0) * (b | 0)) | 0,\n inv,\n };\n const nttOpts = {\n N,\n roots: nttZetas,\n invertButterflies: true,\n skipStages: isKyber ? 1 : 0,\n brp: false,\n };\n const dif = FFTCore(field, { dit: false, ...nttOpts });\n const dit = FFTCore(field, { dit: true, ...nttOpts });\n const NTT = {\n encode: (r) => {\n return dif(r);\n },\n decode: (r) => {\n dit(r);\n // The inverse-NTT normalization factor is family-specific: FIPS 203 Algorithm 10 line 14\n // uses `128^-1 mod q` for Kyber, while FIPS 204 Algorithm 42 lines 21-23 use `256^-1 mod q`.\n // kyber uses 128 here, because brv && stuff\n for (let i = 0; i < r.length; i++)\n r[i] = mod(F * r[i]);\n return r;\n },\n };\n // Pack one little-endian `d`-bit word per coefficient, matching FIPS 203 ByteEncode /\n // ByteDecode and the FIPS 204 BitsToBytes-based polynomial packing helpers.\n const bitsCoder = (d, c) => {\n // Validate the carry shape once: JS bitwise operations silently truncate wider accumulators.\n for (let i = 0, bufLen = 0; i < N; i++) {\n bufLen += d;\n if (bufLen > 32)\n getMask(bufLen);\n bufLen %= 8;\n }\n const mask = getMask(d);\n const bytesLen = d * (N / 8);\n return {\n bytesLen,\n encode: (poly_) => {\n const poly = poly_;\n const r = new Uint8Array(bytesLen);\n for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < poly.length; i++) {\n buf |= (c.encode(poly[i]) & mask) << bufLen;\n bufLen += d;\n // Take the low byte directly: `& 0xff` matches the previous getMask(bufLen) result\n // after Uint8Array truncation, without a validated function call per output byte.\n for (; bufLen >= 8; bufLen -= 8, buf >>= 8)\n r[pos++] = buf & 0xff;\n }\n return r;\n },\n decode: (bytes) => {\n const r = newPoly(N);\n for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < bytes.length; i++) {\n buf |= bytes[i] << bufLen;\n bufLen += 8;\n for (; bufLen >= d; bufLen -= d, buf >>= d)\n r[pos++] = c.decode(buf & mask);\n }\n return r;\n },\n };\n };\n return {\n mod,\n smod,\n nttZetas: nttZetas,\n NTT: {\n encode: (r) => NTT.encode(r),\n decode: (r) => NTT.decode(r),\n },\n bitsCoder: bitsCoder,\n };\n};\nconst createXofShake = (shake) => (seed, blockLen) => {\n if (!blockLen)\n blockLen = shake.blockLen;\n // Optimizations that won't mater:\n // - cached seed update (two .update(), on start and on the end)\n // - another cache which cloned into working copy\n // Faster than multiple updates, since seed less than blockLen\n const _seed = new Uint8Array(seed.length + 2);\n _seed.set(seed);\n const seedLen = seed.length;\n const buf = new Uint8Array(blockLen); // == shake128.blockLen\n let h = shake.create({});\n let calls = 0;\n let xofs = 0;\n return {\n stats: () => ({ calls, xofs }),\n get: (x, y) => {\n // Rebind to `seed || x || y` so callers can implement the spec's per-coordinate\n // SHAKE inputs like `rho || j || i` and `rho || IntegerToBytes(counter, 2)`.\n _seed[seedLen + 0] = x;\n _seed[seedLen + 1] = y;\n h.destroy();\n h = shake.create({}).update(_seed);\n calls++;\n return () => {\n xofs++;\n return h.xofInto(buf);\n };\n },\n clean: () => {\n h.destroy();\n cleanBytes(buf, _seed);\n },\n };\n};\n/**\n * SHAKE128-based extendable-output reader factory used by ML-KEM.\n * `get(x, y)` selects one coordinate pair at a time; calling it again invalidates previously\n * returned readers, and each squeeze reuses one mutable internal output buffer.\n * @param seed - Seed bytes for the reader.\n * @param blockLen - Optional output block length.\n * @returns Stateful XOF reader.\n * @example\n * Build the ML-KEM SHAKE128 matrix expander and read one block.\n * ```ts\n * import { randomBytes } from '@noble/post-quantum/utils.js';\n * import { XOF128 } from '@noble/post-quantum/_crystals.js';\n * const reader = XOF128(randomBytes(32));\n * const block = reader.get(0, 0)();\n * ```\n */\nexport const XOF128 = /* @__PURE__ */ createXofShake(shake128);\n/**\n * SHAKE256-based extendable-output reader factory used by ML-DSA.\n * `get(x, y)` appends raw one-byte coordinates to the seed, invalidates previously returned\n * readers, and reuses one mutable internal output buffer for each squeeze.\n * @param seed - Seed bytes for the reader.\n * @param blockLen - Optional output block length.\n * @returns Stateful XOF reader.\n * @example\n * Build the ML-DSA SHAKE256 coefficient expander and read one block.\n * ```ts\n * import { randomBytes } from '@noble/post-quantum/utils.js';\n * import { XOF256 } from '@noble/post-quantum/_crystals.js';\n * const reader = XOF256(randomBytes(32));\n * const block = reader.get(0, 0)();\n * ```\n */\nexport const XOF256 = /* @__PURE__ */ createXofShake(shake256);\n", "/**\n * ML-KEM: Module Lattice-based Key Encapsulation Mechanism from\n * [FIPS-203](https://csrc.nist.gov/pubs/fips/203/ipd). A.k.a. CRYSTALS-Kyber.\n *\n * Key encapsulation is similar to DH / ECDH (think X25519), with important differences:\n * * Unlike in ECDH, we can't verify if it was \"Bob\" who've sent the shared secret\n * * Unlike ECDH, it is probabalistic and relies on quality of randomness (CSPRNG).\n * * Decapsulation never throws an error, even when shared secret was\n * encrypted by a different public key. It will just return a different shared secret.\n *\n * There are some concerns with regards to security: see\n * [djb blog](https://blog.cr.yp.to/20231003-countcorrectly.html) and\n * [mailing list](https://groups.google.com/a/list.nist.gov/g/pqc-forum/c/W2VOzy0wz_E).\n *\n * Has similar internals to ML-DSA, but their keys and params are different.\n *\n * Check out [official site](https://www.pq-crystals.org/kyber/resources.shtml),\n * [repo](https://github.com/pq-crystals/kyber),\n * [spec](https://datatracker.ietf.org/doc/draft-cfrg-schwabe-kyber/).\n * @module\n */\n/*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) */\nimport { sha3_256, sha3_512, shake256 } from '@noble/hashes/sha3.js';\nimport { swap32IfBE, u32 } from '@noble/hashes/utils.js';\nimport { genCrystals, XOF128 } from \"./_crystals.js\";\nimport { abytes, cleanBytes, copyBytes, equalBytes, getMask, randomBytes, splitCoder, vecCoder, } from \"./utils.js\";\n/** Key encapsulation mechanism interface */\nconst N = 256; // Kyber (not FIPS-203) supports different lengths, but all std modes were using 256\nconst Q = 3329; // 13*(2**8)+1, modulo prime\nconst F = 3303; // 3303 \u2261 128**(\u22121) mod q (FIPS-203)\nconst ROOT_OF_UNITY = 17; // \u03B6 = 17 \u2208 Zq is a primitive 256-th root of unity modulo Q. \u03B6**128 \u2261\u22121\n// treeshake: keep genCrystals behind the object so PARAMS-only bundles can drop it entirely.\n// Shared CRYSTALS helper in the ML-KEM branch: Kyber mode, 7-bit bit-reversal,\n// and Uint16Array polys because current coefficients stay reduced modulo q.\nconst crystals = /* @__PURE__ */ genCrystals({\n N,\n Q,\n F,\n ROOT_OF_UNITY,\n newPoly: (n) => new Uint16Array(n),\n brvBits: 7,\n isKyber: true,\n});\n/** Internal params of ML-KEM versions */\n// prettier-ignore\n/** Built-in ML-KEM parameter presets keyed by the public export names\n * `ml_kem512` / `ml_kem768` / `ml_kem1024`.\n * `RBGstrength` is Table 2's required randomness-source strength in bits,\n * not a generic security label.\n */\nexport const PARAMS = /* @__PURE__ */ (() => Object.freeze({\n 512: Object.freeze({ N, Q, K: 2, ETA1: 3, ETA2: 2, du: 10, dv: 4, RBGstrength: 128 }),\n 768: Object.freeze({ N, Q, K: 3, ETA1: 2, ETA2: 2, du: 10, dv: 4, RBGstrength: 192 }),\n 1024: Object.freeze({ N, Q, K: 4, ETA1: 2, ETA2: 2, du: 11, dv: 5, RBGstrength: 256 }),\n}))();\n// FIPS-203: compress/decompress\nconst compress = (d) => {\n // d=12 is the ByteEncode12/ByteDecode12 path, not lossy compression.\n // ByteDecode12 interprets each 12-bit word modulo q; without that reduction the public-key\n // modulus check in encapsulate() becomes a no-op for malformed coefficients like 4095.\n if (d >= 12)\n return { encode: (i) => i, decode: (i) => (i >= Q ? i - Q : i) };\n // Comments map to python implementation in RFC (draft-cfrg-schwabe-kyber)\n // const round = (i: number) => Math.floor(i + 0.5) | 0;\n const a = 2 ** (d - 1);\n return {\n // This only matches standalone Compress_d after bitsCoder masks the result into Z_(2^d).\n encode: (i) => ((i << d) + Q / 2) / Q,\n // const decompress = (i: number) => round((Q / 2 ** d) * i);\n decode: (i) => (i * Q + a) >>> d,\n };\n};\n// Raw ByteEncode_d / ByteDecode_d from FIPS 203 operate on d-bit words directly.\n// That differs from `polyCoder(d)` for d<12, where noble folds packing together with the lossy\n// ciphertext compression step used by u/v. Tests that exercise the spec's raw packing surface need\n// this exact non-lossy variant instead.\nconst byteCoder = (d) => crystals.bitsCoder(d, d === 12\n ? { encode: (i) => i, decode: (i) => (i >= Q ? i - Q : i) }\n : { encode: (i) => i, decode: (i) => i });\n// NOTE: we merge encoding and compress because it is faster, also both require same d param\n// d=12 is the ByteEncode12/ByteDecode12 path rather than compression, and caller-side\n// public-key modulus checks route through this helper's decode/encode roundtrip.\n// Converts between bytes and d-bits compressed representation.\n// Kinda like convertRadix2 from @scure/base.\n// decode(encode(t)) == t, but there is loss of information on encode(decode(t))\nconst polyCoder = (d) => (d === 12 ? byteCoder(12) : crystals.bitsCoder(d, compress(d)));\n// Coefficients always stay reduced in [0, Q) here (samplers, NTT and coders all reduce),\n// so one conditional correction replaces the generic mod().\nfunction polyAdd(a_, b_) {\n const a = a_;\n const b = b_;\n // Mutates `a` in place; callers must pass two N=256 polynomials.\n for (let i = 0; i < N; i++) {\n const r = a[i] + b[i]; // a += b\n a[i] = r >= Q ? r - Q : r;\n }\n}\nfunction polySub(a_, b_) {\n const a = a_;\n const b = b_;\n // Mutates `a` in place; callers must pass two N=256 polynomials.\n for (let i = 0; i < N; i++) {\n const r = a[i] - b[i]; // a -= b\n a[i] = r < 0 ? r + Q : r;\n }\n}\n// FIPS-203: Computes the product of two degree-one polynomials with respect to a quadratic modulus\nfunction BaseCaseMultiply(a0, a1, b0, b1, zeta) {\n // `zeta` here is Algorithm 11's \u03B3 = \u03B6^(2BitRev_7(i)+1).\n // Reduce a1*b1 before multiplying by zeta: a1*b1*zeta would reach ~2^35, forcing JS engines\n // into slow float fmod; with the extra reduction every intermediate fits int32.\n const c0 = crystals.mod(crystals.mod(a1 * b1) * zeta + a0 * b0);\n const c1 = crystals.mod(a0 * b1 + a1 * b0);\n return { c0, c1 };\n}\n// FIPS-203: Computes the product (in the ring Tq) of two NTT representations.\n// Works in place on `f`; `g` is read-only and both inputs must already be in NTT form.\nfunction MultiplyNTTs(f_, g_) {\n const f = f_;\n const g = g_;\n for (let i = 0; i < N / 2; i++) {\n let z = crystals.nttZetas[64 + (i >> 1)];\n if (i & 1)\n z = -z;\n const { c0, c1 } = BaseCaseMultiply(f[2 * i + 0], f[2 * i + 1], g[2 * i + 0], g[2 * i + 1], z);\n f[2 * i + 0] = c0;\n f[2 * i + 1] = c1;\n }\n return f;\n}\n// Return poly in NTT representation\nfunction SampleNTT(xof_) {\n const xof = xof_;\n // The reader must already bind the Algorithm 7 seed||j||i bytes\n // and return block lengths divisible by 3.\n const r = new Uint16Array(N);\n for (let j = 0; j < N;) {\n const b = xof();\n if (b.length % 3)\n throw new Error('SampleNTT: unaligned block');\n for (let i = 0; j < N && i + 3 <= b.length; i += 3) {\n const d1 = ((b[i + 0] >> 0) | (b[i + 1] << 8)) & 0xfff;\n const d2 = ((b[i + 1] >> 4) | (b[i + 2] << 4)) & 0xfff;\n if (d1 < Q)\n r[j++] = d1;\n if (j < N && d2 < Q)\n r[j++] = d2;\n }\n }\n return r;\n}\n// Sampling from the centered binomial distribution\n// Returns poly with small coefficients (noise/errors) stored modulo q in ordinary coefficient form.\n// Current callers only use Table 2 eta values {2,3} and PRF outputs of exactly 64*eta bytes.\nconst sampleCBDBytes = (buf, eta) => {\n const r = new Uint16Array(N);\n // CBD consumes the PRF bitstream in little-endian byte order; normalize the word view on BE,\n // then swap it back so callers still observe `buf` as read-only.\n const b32 = u32(buf);\n swap32IfBE(b32);\n let len = 0;\n for (let i = 0, p = 0, bb = 0, t0 = 0; i < b32.length; i++) {\n let b = b32[i];\n for (let j = 0; j < 32; j++) {\n bb += b & 1;\n b >>= 1;\n len += 1;\n if (len === eta) {\n t0 = bb;\n bb = 0;\n }\n else if (len === 2 * eta) {\n r[p++] = crystals.mod(t0 - bb);\n bb = 0;\n len = 0;\n }\n }\n }\n swap32IfBE(b32);\n if (len)\n throw new Error(`sampleCBD: leftover bits: ${len}`);\n return r;\n};\nfunction sampleCBD(PRF_, seed, nonce, eta) {\n const PRF = PRF_;\n return sampleCBDBytes(PRF((eta * N) / 4, seed, nonce), eta);\n}\n// K-PKE\n// Internal ML-KEM subroutine only: exact 32-byte `seed` / `msg` inputs\n// come from Algorithms 13-15, and the helper mutates decoded temporary\n// polynomials in place while leaving caller byte arrays unchanged.\nconst genKPKE = (opts_) => {\n const opts = opts_;\n const { K, PRF, XOF, HASH512, ETA1, ETA2, du, dv } = opts;\n const poly1 = polyCoder(1);\n const polyV = polyCoder(dv);\n const polyU = polyCoder(du);\n const publicCoder = splitCoder('publicKey', vecCoder(polyCoder(12), K), 32);\n const secretCoder = vecCoder(polyCoder(12), K);\n const cipherCoder = splitCoder('ciphertext', vecCoder(polyU, K), polyV);\n const seedCoder = splitCoder('seed', 32, 32);\n // Algorithm 14 (K-PKE.Encrypt) core, after ek parsing. `tHat` and every poly returned by\n // `getA(i, j)` are treated as disposable scratch: they are mutated in place and wiped/dropped,\n // so callers holding cached copies must pass fresh copies.\n const encryptCore = (tHat, getA, msg, seed) => {\n const rHat = [];\n for (let i = 0; i < K; i++)\n rHat.push(crystals.NTT.encode(sampleCBD(PRF, seed, i, ETA1)));\n const tmp2 = new Uint16Array(N);\n const u = [];\n for (let i = 0; i < K; i++) {\n const e1 = sampleCBD(PRF, seed, K + i, ETA2);\n const tmp = new Uint16Array(N);\n for (let j = 0; j < K; j++) {\n const aij = getA(i, j); // A[j][i], inplace transpose access\n polyAdd(tmp, MultiplyNTTs(aij, rHat[j])); // t += aij * rHat[j]\n }\n polyAdd(e1, crystals.NTT.decode(tmp)); // e1 += tmp\n u.push(e1);\n polyAdd(tmp2, MultiplyNTTs(tHat[i], rHat[i])); // t2 += tHat[i] * rHat[i]\n cleanBytes(tmp);\n }\n const e2 = sampleCBD(PRF, seed, 2 * K, ETA2);\n polyAdd(e2, crystals.NTT.decode(tmp2)); // e2 += tmp2\n const v = poly1.decode(msg); // encode plaintext m into polynomial v\n polyAdd(v, e2); // v += e2\n cleanBytes(tHat, rHat, tmp2, e2);\n return cipherCoder.encode([u, v]);\n };\n return {\n secretCoder,\n lengths: {\n secretKey: secretCoder.bytesLen,\n publicKey: publicCoder.bytesLen,\n cipherText: cipherCoder.bytesLen,\n },\n keygen: (seed) => {\n abytes(seed, 32, 'seed');\n const seedDst = new Uint8Array(33);\n seedDst.set(seed);\n // FIPS 203 Algorithm 13 appends the parameter-set byte `k`\n // before `G(d || k)`, so expanding the same 32-byte seed\n // under a different ML-KEM parameter set yields unrelated keys.\n seedDst[32] = K;\n const seedHash = HASH512(seedDst);\n const [rho, sigma] = seedCoder.decode(seedHash);\n const sHat = [];\n const tHat = [];\n for (let i = 0; i < K; i++)\n sHat.push(crystals.NTT.encode(sampleCBD(PRF, sigma, i, ETA1)));\n const x = XOF(rho);\n for (let i = 0; i < K; i++) {\n const e = crystals.NTT.encode(sampleCBD(PRF, sigma, K + i, ETA1));\n for (let j = 0; j < K; j++) {\n const aji = SampleNTT(x.get(j, i)); // A[i][j], inplace\n polyAdd(e, MultiplyNTTs(aji, sHat[j]));\n }\n tHat.push(e); // t \u2190 A \u25E6 s + e\n }\n x.clean();\n const res = {\n publicKey: publicCoder.encode([tHat, rho]),\n secretKey: secretCoder.encode(sHat),\n };\n cleanBytes(rho, sigma, sHat, tHat, seedDst, seedHash);\n return res;\n },\n encrypt: (publicKey, msg, seed) => {\n const [tHat, rho] = publicCoder.decode(publicKey);\n const x = XOF(rho);\n const res = encryptCore(tHat, (i, j) => SampleNTT(x.get(i, j)), msg, seed);\n x.clean();\n return res;\n },\n // Expands the full \u00C2 matrix (public data derived from rho) once, so repeated encryptions\n // against the same ek skip the K\u00B2 SampleNTT XOF expansions. Cached polys are copied per\n // call because encryptCore mutates its inputs in place.\n prepare: (publicKey) => {\n const [tHat, rho] = publicCoder.decode(publicKey);\n const x = XOF(rho);\n const A = [];\n for (let i = 0; i < K; i++)\n for (let j = 0; j < K; j++)\n A.push(SampleNTT(x.get(i, j)));\n x.clean();\n return {\n encrypt: (msg, seed) => encryptCore(tHat.map((p) => p.slice()), (i, j) => A[i * K + j].slice(), msg, seed),\n clean: () => cleanBytes(tHat, A),\n };\n },\n decrypt: (cipherText, privateKey) => {\n const [u, v] = cipherCoder.decode(cipherText);\n const sk = secretCoder.decode(privateKey); // s \u2190 ByteDecode_12(dkPKE)\n const tmp = new Uint16Array(N);\n // tmp += sk[i] * u[i]\n for (let i = 0; i < K; i++)\n polyAdd(tmp, MultiplyNTTs(sk[i], crystals.NTT.encode(u[i])));\n polySub(v, crystals.NTT.decode(tmp)); // w = v' - tmp\n // `v` now holds w, from which the plaintext is just a 1-bit threshold away, so wipe it too.\n // encode() allocates its own buffer, so the returned bytes do not alias `v`.\n const res = poly1.encode(v);\n cleanBytes(tmp, sk, u, v);\n return res;\n },\n };\n};\n/**\n * Public ML-KEM wrapper over the internal K-PKE subroutine.\n * `keygen(seed)` and `encapsulate(publicKey, msg)` are deterministic/test-oriented hooks that map\n * more directly to Algorithms 16-17 than to the pure no-input / random-internal Algorithms 19-20.\n * `encapsulate`'s optional `msg` is the 32-byte message randomness `m` of Algorithm 17, the\n * pre-image the shared secret is derived from, NOT a plaintext to encrypt: ML-KEM is a key\n * encapsulation mechanism, not a cipher. Omit it to draw fresh randomness; pass it only to\n * reproduce a known-answer vector, and only as 32 uniformly random bytes, since a low-entropy or\n * reused value makes the shared secret predictable. The same holds for `keygen`'s optional `seed`.\n * decapsulate() tries to follow the Algorithms 18/21 implicit-reject structure as closely as\n * practical here by re-encrypting, comparing ciphertexts, returning `Khat` on match or `Kbar` on\n * mismatch, and zeroizing the non-returned shared-secret candidate; JS/JIT still provides no\n * constant-time guarantees for that path.\n */\nfunction createKyber(opts) {\n const rawOpts = opts;\n const KPKE = genKPKE(rawOpts);\n const { HASH256, HASH512, KDF } = rawOpts;\n const { secretCoder: KPKESecretCoder, lengths } = KPKE;\n const secretCoder = splitCoder('secretKey', lengths.secretKey, lengths.publicKey, 32, 32);\n const msgLen = 32;\n const seedLen = 64;\n // FIPS-203 includes additional verification check for modulus\n const validateModulus = (publicKey, fn) => {\n const eke = publicKey.subarray(0, 384 * rawOpts.K);\n // Copy because of inplace encoding\n const ek = KPKESecretCoder.encode(KPKESecretCoder.decode(copyBytes(eke)));\n // (Modulus check.) Perform the computation ek \u2190 ByteEncode12(ByteDecode12(eke)).\n // If ek = \u0338 eke, the input is invalid. (See Section 4.2.1.)\n const ok = equalBytes(ek, eke);\n cleanBytes(ek);\n if (!ok)\n throw new Error(`ML-KEM.${fn}: wrong publicKey modulus`);\n };\n const kemLengths = Object.freeze({\n ...lengths,\n seed: 64,\n msg: msgLen,\n msgRand: msgLen,\n secretKey: secretCoder.bytesLen,\n });\n return Object.freeze({\n info: Object.freeze({ type: 'ml-kem' }),\n lengths: kemLengths,\n keygen: (seed) => {\n // A generated seed carries z (the implicit-rejection secret) and must be wiped once the\n // secret key holds a copy, matching ml-dsa / slh-dsa / falcon keygen. A caller-supplied\n // seed is the caller's to manage (and the immutability test requires it stay untouched).\n const ownSeed = seed === undefined;\n const s = ownSeed ? randomBytes(seedLen) : seed;\n let sk;\n let publicKeyHash;\n try {\n abytes(s, seedLen, 'seed');\n const keys = KPKE.keygen(s.subarray(0, 32));\n const publicKey = keys.publicKey;\n sk = keys.secretKey;\n publicKeyHash = HASH256(publicKey);\n // (dkPKE||ek||H(ek)||z)\n const secretKey = secretCoder.encode([sk, publicKey, publicKeyHash, s.subarray(32)]);\n return {\n publicKey: publicKey,\n secretKey: secretKey,\n };\n }\n finally {\n if (sk !== undefined)\n cleanBytes(sk);\n if (publicKeyHash !== undefined)\n cleanBytes(publicKeyHash);\n if (ownSeed)\n cleanBytes(s);\n }\n },\n getPublicKey: (secretKey) => {\n const [_sk, publicKey, _publicKeyHash, _z] = secretCoder.decode(secretKey);\n return Uint8Array.from(publicKey);\n },\n encapsulate: (publicKey, msg) => {\n // A generated message is the preimage of the shared secret (K = G(m || H(ek))[0:32]) and\n // must be wiped. A caller-supplied message is the deterministic-randomness hook and the\n // caller's to manage (the immutability test requires it stay untouched).\n const ownMsg = msg === undefined;\n const m = ownMsg ? randomBytes(msgLen) : msg;\n let kr;\n try {\n abytes(publicKey, lengths.publicKey, 'publicKey');\n abytes(m, msgLen, 'message');\n validateModulus(publicKey, 'encapsulate');\n // derive randomness\n kr = HASH512.create().update(m).update(HASH256(publicKey)).digest();\n const cipherText = KPKE.encrypt(publicKey, m, kr.subarray(32, 64));\n return {\n cipherText: cipherText,\n sharedSecret: kr.subarray(0, 32),\n };\n }\n finally {\n if (kr !== undefined)\n cleanBytes(kr.subarray(32));\n if (ownMsg)\n cleanBytes(m);\n }\n },\n decapsulate: (cipherText, secretKey) => {\n abytes(secretKey, secretCoder.bytesLen, 'secretKey'); // 768*k + 96\n abytes(cipherText, lengths.cipherText, 'cipherText'); // 32(du*k + dv)\n // test \u2190 H(dk[384\uD835\uDC58 \u2236 768\uD835\uDC58 + 32])) .\n const k768 = secretCoder.bytesLen - 96;\n const start = k768 + 32;\n const test = HASH256(secretKey.subarray(k768 / 2, start));\n // If test \u2260 dk[768\uD835\uDC58 + 32 \u2236 768\uD835\uDC58 + 64], then input checking has failed.\n if (!equalBytes(test, secretKey.subarray(start, start + 32)))\n throw new Error('invalid secretKey: hash check failed');\n const [sk, publicKey, publicKeyHash, z] = secretCoder.decode(secretKey);\n const msg = KPKE.decrypt(cipherText, sk);\n // derive randomness, Khat, rHat = G(mHat || h)\n const kr = HASH512.create().update(msg).update(publicKeyHash).digest();\n const Khat = kr.subarray(0, 32);\n // re-encrypt using the derived randomness\n const cipherText2 = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));\n // if ciphertexts do not match, \u201Cimplicitly reject\u201D\n const isValid = equalBytes(cipherText, cipherText2);\n const Kbar = KDF.create({ dkLen: 32 }).update(z).update(cipherText).digest();\n // kr[32:64] is the derived K-PKE encryption randomness: wipe it like encapsulate() does.\n cleanBytes(msg, cipherText2, kr.subarray(32), !isValid ? Khat : Kbar);\n return (isValid ? Khat : Kbar);\n },\n /**\n * Experimental prototype: pre-expand a public key so repeated encapsulate/decapsulate\n * against the same key skip re-validation, H(ek), t\u0302 decoding and the K\u00B2 SampleNTT\n * XOF expansions of \u00C2. Only public data is cached; see {@link KEMPrepared}.\n */\n prepare: (publicKey) => {\n abytes(publicKey, lengths.publicKey, 'publicKey');\n validateModulus(publicKey, 'prepare');\n const ek = copyBytes(publicKey); // detach from the caller before caching\n const publicKeyHash = HASH256(ek);\n const cached = KPKE.prepare(ek);\n return Object.freeze({\n publicKey: ek,\n encapsulate: (msg) => {\n // As in the non-prepared encapsulate: a generated message is the shared-secret\n // preimage and is wiped; a caller-supplied one is left untouched.\n const ownMsg = msg === undefined;\n const m = ownMsg ? randomBytes(msgLen) : msg;\n let kr;\n try {\n abytes(m, msgLen, 'message');\n kr = HASH512.create().update(m).update(publicKeyHash).digest();\n const cipherText = cached.encrypt(m, kr.subarray(32, 64));\n return {\n cipherText: cipherText,\n sharedSecret: kr.subarray(0, 32),\n };\n }\n finally {\n if (kr !== undefined)\n cleanBytes(kr.subarray(32));\n if (ownMsg)\n cleanBytes(m);\n }\n },\n decapsulate: (cipherText, secretKey) => {\n abytes(secretKey, secretCoder.bytesLen, 'secretKey');\n abytes(cipherText, lengths.cipherText, 'cipherText');\n const [sk, ekEmbedded, storedHash, z] = secretCoder.decode(secretKey);\n // Under KEMPrepared's read-only publicKey contract, bind dk to the prepared key.\n // Together with publicKeyHash = H(ek) computed in prepare(), this is equivalent to (and\n // stronger than) FIPS 203 \u00A77.3's `H(dk[384k : 768k+32]) == dk[768k+32 : 768k+64]`.\n if (!equalBytes(ekEmbedded, ek) || !equalBytes(storedHash, publicKeyHash))\n throw new Error('ML-KEM.decapsulate: secretKey does not match prepared publicKey');\n const msg = KPKE.decrypt(cipherText, sk);\n // derive randomness, Khat, rHat = G(mHat || h)\n const kr = HASH512.create().update(msg).update(publicKeyHash).digest();\n const Khat = kr.subarray(0, 32);\n // re-encrypt using the derived randomness and cached \u00C2/t\u0302\n const cipherText2 = cached.encrypt(msg, kr.subarray(32, 64));\n // if ciphertexts do not match, \u201Cimplicitly reject\u201D\n const isValid = equalBytes(cipherText, cipherText2);\n const Kbar = KDF.create({ dkLen: 32 }).update(z).update(cipherText).digest();\n cleanBytes(msg, cipherText2, kr.subarray(32), !isValid ? Khat : Kbar);\n return (isValid ? Khat : Kbar);\n },\n clean: cached.clean,\n });\n },\n });\n}\n// FIPS 203's PRF_eta binding: current callers use only 32-byte keys, one-byte nonces,\n// and dkLen values {128, 192}; out-of-range nonce numbers still wrap modulo 256 here.\nfunction shakePRF(dkLen, key, nonce) {\n return shake256\n .create({ dkLen })\n .update(key)\n .update(new Uint8Array([nonce]))\n .digest();\n}\n// Fixed ML-KEM hash/XOF bindings. `KDF` here is the spec's fixed 32-byte `J` call,\n// and swapping any field changes the scheme rather than tuning an internal dependency.\nconst opts = /* @__PURE__ */ (() => ({\n HASH256: sha3_256,\n HASH512: sha3_512,\n KDF: shake256,\n XOF: XOF128,\n PRF: shakePRF,\n}))();\n// Parameter-set instantiation step for the spec's \"ML-KEM-x\" names; current correctness relies\n// on the internal PARAMS rows rather than local validation of arbitrary KEMParam objects.\nconst mk = (params) => createKyber({\n ...opts,\n ...params,\n});\n/**\n * ML-KEM-512: Table 2 row `k=2, \u03B71=3, \u03B72=2, du=10, dv=4`; Table 3 sizes `800/1632/768/32`.\n * The ASD lifecycle note here is external policy guidance, not a FIPS 203 requirement.\n * @example\n * Generate deterministic ML-KEM-512 keys, encapsulate a shared secret, and decapsulate it.\n * ```ts\n * import { ml_kem512 } from '@noble/post-quantum/ml-kem.js';\n * const seed = new Uint8Array(ml_kem512.lengths.seed!);\n * const { secretKey, publicKey } = ml_kem512.keygen(seed);\n * const msg = new Uint8Array(ml_kem512.lengths.msgRand!);\n * const { cipherText, sharedSecret } = ml_kem512.encapsulate(publicKey, msg);\n * const recovered = ml_kem512.decapsulate(cipherText, secretKey);\n * const publicKey2 = ml_kem512.getPublicKey(secretKey);\n * ```\n */\nexport const ml_kem512 = /* @__PURE__ */ (() => mk(PARAMS[512]))();\n/**\n * ML-KEM-768: Table 2 row `k=3, \u03B71=2, \u03B72=2, du=10, dv=4`; Table 3 sizes `1184/2400/1088/32`.\n * The ASD lifecycle note here is external policy guidance, not a FIPS 203 requirement.\n */\nexport const ml_kem768 = /* @__PURE__ */ (() => mk(PARAMS[768]))();\n/**\n * ML-KEM-1024: Table 2 row `k=4, \u03B71=2, \u03B72=2, du=11, dv=5`; Table 3 sizes `1568/3168/1568/32`.\n * The ASD lifecycle note here is external policy guidance, not a FIPS 203 requirement.\n */\nexport const ml_kem1024 = /* @__PURE__ */ (() => mk(PARAMS[1024]))();\n// NOTE: for tests only, don't use. This keeps the exact internal ML-KEM math surfaces available\n// without re-implementing them in separate test code.\nexport const __tests = /* @__PURE__ */ (() => Object.freeze({\n Compress_d: (x, d) => {\n if (d < 1 || d > 11)\n throw new Error(`Compress_d: expected d in [1..11], got ${d}`);\n return compress(d).encode(x) & getMask(d);\n },\n Decompress_d: (y, d) => {\n if (d < 1 || d > 11)\n throw new Error(`Decompress_d: expected d in [1..11], got ${d}`);\n return compress(d).decode(y);\n },\n ByteEncode_d: (F, d) => {\n if (d < 1 || d > 12)\n throw new Error(`ByteEncode_d: expected d in [1..12], got ${d}`);\n return byteCoder(d).encode(F);\n },\n ByteDecode_d: (B, d) => {\n if (d < 1 || d > 12)\n throw new Error(`ByteDecode_d: expected d in [1..12], got ${d}`);\n return byteCoder(d).decode(B);\n },\n NTT: (f) => crystals.NTT.encode(Uint16Array.from(f)),\n NTT_inv: (fHat) => crystals.NTT.decode(Uint16Array.from(fHat)),\n MultiplyNTTs: (fHat, gHat) => MultiplyNTTs(Uint16Array.from(fHat), Uint16Array.from(gHat)),\n SamplePolyCBD: (B, eta) => {\n abytes(B, 64 * eta, 'B');\n return sampleCBDBytes(B, eta);\n },\n SampleNTT: (B) => {\n abytes(B, 34, 'B');\n const xof = XOF128(B.subarray(0, 32));\n try {\n return SampleNTT(xof.get(B[32], B[33]));\n }\n finally {\n xof.clean();\n }\n },\n}))();\n", "/**\n * Post-Quantum Hybrid Cryptography\n *\n * The current implementation is flawed and likely redundant. We should offer\n * a small, generic API to compose hybrid schemes instead of reimplementing\n * protocol-specific logic (SSH, GPG, etc.) with ad hoc encodings.\n *\n * 1. Core Issues\n * - sign/verify: implemented as two separate operations with different keys.\n * - EC getSharedSecret: could be refactored into a proper KEM.\n * - Multiple calls: keys, signatures, and shared secrets could be\n * concatenated to reduce the number of API invocations.\n * - Reinvention: most libraries add strange domain separations and\n * encodings instead of simple byte concatenation.\n *\n * 2. API Goals\n * - Provide primitives to build hybrids generically.\n * - Avoid embedding SSH- or GPG-specific formats in the core API.\n *\n * 3. Edge Cases\n * \u2022 Variable-length signatures:\n * - DER-encoded (Weierstrass curves).\n * - Falcon (unpadded).\n * - Concatenation works only if length is fixed; otherwise a length\n * prefix is required (but that breaks compatibility).\n *\n * \u2022 getSharedSecret:\n * - Default: non-KEM (authenticated ECDH).\n * - KEM conversion: generate a random SK to remove implicit auth.\n *\n * 4. Common Pitfalls\n * - Seed expansion:\n * \u2022 Expanding a small seed into multiple keys reduces entropy.\n * \u2022 API should allow identity mapping (no expansion).\n *\n * - Skipping full point encoding:\n * \u2022 Some omit the compression byte (parity) for WebCrypto compatibility.\n * \u2022 Better: hash the raw secret; coordinate output is already non-uniform.\n * \u2022 Some curves (e.g., X448) produce secrets that must be re-hashed to match\n * symmetric-key lengths.\n *\n * - Combiner inconsistencies:\n * \u2022 Different domain separations and encodings across libraries.\n * \u2022 Should live at the application layer, since key lengths vary.\n *\n * 5. Protocol Examples\n * - SSH:\n * \u2022 Concatenate keys.\n * \u2022 Combiner: SHA-512.\n *\n * - GPG:\n * \u2022 Concatenate keys.\n * \u2022 Combiner:\n * SHA3-256(kemShare || ecdhShare || ciphertext || pubKey || algId || domSep || len(domSep))\n *\n * - TLS:\n * \u2022 Transcript-based derivation (HKDF).\n *\n * 6. Relevant Specs & Implementations\n * - IETF Hybrid KEM drafts:\n * \u2022 draft-irtf-cfrg-hybrid-kems\n * \u2022 draft-connolly-cfrg-xwing-kem\n * \u2022 draft-westerbaan-tls-xyber768d00\n *\n * - PQC Libraries:\n * \u2022 superdilithium (cyph/pqcrypto.js) \u2013 low adoption.\n * \u2022 hybrid-pqc (DogeProtocol, quantumcoinproject) \u2013 complex encodings.\n *\n * 7. Signatures\n * - Ed25519: fixed-size, easy to support.\n * - Variable-size: introduces custom format requirements; best left to\n * higher-level code.\n *\n * @module\n */\n/*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) */\nimport {} from '@noble/curves/abstract/edwards.js';\nimport {} from '@noble/curves/abstract/montgomery.js';\nimport {} from '@noble/curves/abstract/weierstrass.js';\nimport { x25519 } from '@noble/curves/ed25519.js';\nimport { p256, p384 } from '@noble/curves/nist.js';\nimport { abool, afunction, asciiToBytes, bytesToNumberBE, bytesToNumberLE, concatBytes, numberToBytesBE, } from '@noble/curves/utils.js';\nimport { expand, extract } from '@noble/hashes/hkdf.js';\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { sha3_256, shake256 } from '@noble/hashes/sha3.js';\nimport { abytes, ahash, anumber, isBytes } from '@noble/hashes/utils.js';\nimport { ml_kem1024, ml_kem768 } from \"./ml-kem.js\";\nimport { aobject, astring, cleanBytes, copyBytes, randomBytes, splitCoder, validateSigOpts, validateVerOpts, } from \"./utils.js\";\nconst validateKEM = (kem, title) => {\n const k = aobject(kem, title);\n aobject(k.lengths, `${title}.lengths`);\n afunction(k.keygen, `${title}.keygen`);\n afunction(k.getPublicKey, `${title}.getPublicKey`);\n afunction(k.encapsulate, `${title}.encapsulate`);\n afunction(k.decapsulate, `${title}.decapsulate`);\n return k;\n};\nconst validateSigner = (signer, title) => {\n const s = aobject(signer, title);\n aobject(s.lengths, `${title}.lengths`);\n afunction(s.keygen, `${title}.keygen`);\n afunction(s.getPublicKey, `${title}.getPublicKey`);\n afunction(s.sign, `${title}.sign`);\n afunction(s.verify, `${title}.verify`);\n return s;\n};\n// Can re-use if decide to signatures support, on other hand getSecretKey is specific and ugly\nfunction ecKeygen(curve, allowZeroKey = false) {\n const c = aobject(curve, 'curve');\n aobject(c.lengths, 'curve.lengths');\n afunction(c.keygen, 'curve.keygen');\n afunction(c.getPublicKey, 'curve.getPublicKey');\n abool(allowZeroKey, 'allowZeroKey');\n const lengths = curve.lengths;\n let keygen = curve.keygen;\n if (allowZeroKey) {\n // Only the ECDSA/Weierstrass branch uses raw scalar-byte secret keys here. Edwards seeds are\n // hashed/pruned and Montgomery keys are clamped byte strings, so forcing Point.Fn semantics on\n // those curves would change key construction instead of just relaxing scalar range handling.\n if (!('getSharedSecret' in curve && 'sign' in curve && 'verify' in curve))\n throw new Error('allowZeroKey requires a Weierstrass curve');\n // This legacy flag is really \"skip the +1 shift\" for vector matching, not \"accept scalar 0\".\n // It swaps seeded Weierstrass keygen from reduction into [1, ORDER) to direct reduction into\n // [0, ORDER), which preserves exact reduced bytes but still leaves scalar 0 invalid.\n // This is ugly, but we need to return exact results here.\n const wCurve = curve;\n const Fn = wCurve.Point.Fn;\n // Unlike noble-curves' seeded Weierstrass keygen, this path removes the post-reduction +1.\n // That is enough to match exact reduced-vector bytes, but an all-zero seed still reduces to\n // scalar 0 here and getPublicKey(secretKey) throws instead of \"allowing zero\".\n keygen = (seed = randomBytes(lengths.seed)) => {\n abytes(seed, lengths.seed, 'seed');\n const seedScalar = Fn.isLE ? bytesToNumberLE(seed) : bytesToNumberBE(seed);\n // Reduce directly into [0, ORDER); scalar 0 still stays invalid.\n const secretKey = Fn.toBytes(Fn.create(seedScalar));\n return {\n secretKey: secretKey,\n publicKey: curve.getPublicKey(secretKey),\n };\n };\n }\n return {\n lengths: { secretKey: lengths.secretKey, publicKey: lengths.publicKey, seed: lengths.seed },\n keygen: (seed) => keygen(seed),\n getPublicKey: (secretKey) => curve.getPublicKey(secretKey),\n };\n}\n/**\n * Wraps an ECDH-capable curve as a KEM.\n * Shared secrets stay in the wrapped curve's raw ECDH byte format with no built-in KDF.\n *\n * SECURITY: this is a low-level component adapter, not a standalone IND-CCA-secure KEM. It does\n * not bind the encapsulation or recipient public key into the secret, so distinct accepted point\n * encodings can produce the same output. Use it only inside a construction whose specified\n * combiner binds those values, or use a standardized DHKEM with labeled extract-and-expand.\n *\n * On SEC 1 / Weierstrass curves, that means the compressed shared-point body without the\n * 1-byte `0x02` / `0x03` prefix.\n * The X25519 path also leaves RFC 7748's optional all-zero shared-secret check to callers.\n * @param curve - Curve with `getSharedSecret`.\n * @param allowZeroKey - Legacy vector-matching toggle for Weierstrass keygen.\n * On Weierstrass curves this removes the usual post-reduction `+1` shift, changing seeded scalar\n * reduction from `[1, ORDER)` to direct reduction into `[0, ORDER)`. It does not make scalar zero\n * valid: an all-zero seed still derives scalar `0` and throws in `curve.getPublicKey(...)`.\n * Only supported on Weierstrass/ECDSA curves.\n * @returns KEM wrapper over the curve.\n * @throws If the curve does not expose `getSharedSecret`. {@link Error}\n * @example\n * Wrap an ECDH-capable curve as a generic KEM.\n * ```ts\n * import { x25519 } from '@noble/curves/ed25519.js';\n * import { _ecdhKem } from '@noble/post-quantum/hybrid.js';\n * const kem = _ecdhKem(x25519);\n * const publicKeyLen = kem.lengths.publicKey;\n * ```\n */\nexport function _ecdhKem(curve, allowZeroKey = false) {\n const kg = ecKeygen(curve, allowZeroKey);\n if (!curve.getSharedSecret)\n throw new Error('wrong curve'); // ed25519 doesn't have one!\n // Standalone (not `this.decapsulate`) so encapsulate works even when methods are destructured.\n const decapsulate = (cipherText, secretKey) => {\n const res = curve.getSharedSecret(secretKey, cipherText);\n return (curve.lengths.publicKeyHasPrefix ? res.subarray(1) : res);\n };\n return {\n lengths: { ...kg.lengths, msg: kg.lengths.seed, cipherText: kg.lengths.publicKey },\n keygen: kg.keygen,\n getPublicKey: kg.getPublicKey,\n encapsulate(publicKey, rand = randomBytes(curve.lengths.seed)) {\n // Some curve.keygen(seed) paths reuse the provided seed buffer as secretKey; detach caller\n // randomness first so cleanBytes() only wipes wrapper-owned material.\n const seed = copyBytes(rand);\n let ek = undefined;\n try {\n ek = kg.keygen(seed).secretKey;\n const sharedSecret = decapsulate(publicKey, ek);\n const cipherText = curve.getPublicKey(ek);\n return { sharedSecret, cipherText };\n }\n finally {\n // Invalid peer public keys can make decapsulation throw; wipe both the detached seed and\n // derived ephemeral secret key even when encapsulation aborts before returning.\n cleanBytes(seed);\n if (ek)\n cleanBytes(ek);\n }\n },\n decapsulate,\n };\n}\n/**\n * Wraps a curve signer as a generic `Signer`.\n * Signatures stay in the wrapped curve's native byte encoding.\n * This wrapper does not normalize or document which per-curve signing options are meaningful.\n * @param curve - Curve with `sign` and `verify`.\n * @param allowZeroKey - Legacy vector-matching toggle for Weierstrass keygen.\n * On Weierstrass curves this removes the usual post-reduction `+1` shift, changing seeded scalar\n * reduction from `[1, ORDER)` to direct reduction into `[0, ORDER)`. It does not make scalar zero\n * valid: an all-zero seed still derives scalar `0` and throws in `curve.getPublicKey(...)`.\n * Only supported on Weierstrass/ECDSA curves.\n * @returns Signer wrapper over the curve.\n * @throws If the curve does not expose `sign` and `verify`. {@link Error}\n * @example\n * Wrap a curve signer as a generic signer.\n * ```ts\n * import { ed25519 } from '@noble/curves/ed25519.js';\n * import { ecSigner } from '@noble/post-quantum/hybrid.js';\n * const signer = ecSigner(ed25519);\n * const sigLen = signer.lengths.signature;\n * ```\n */\nexport function ecSigner(curve, allowZeroKey = false) {\n const kg = ecKeygen(curve, allowZeroKey);\n if (!curve.sign || !curve.verify)\n throw new Error('wrong curve'); // ed25519 doesn't have one!\n return {\n lengths: { ...kg.lengths, signature: curve.lengths.signature, signRand: 0 },\n keygen: kg.keygen,\n getPublicKey: kg.getPublicKey,\n sign: (message, secretKey, opts = {}) => {\n opts = validateSigOpts(opts);\n // This generic wrapper intentionally keeps the Signer contract to message + key only.\n // Backend-specific knobs like ECDSA extraEntropy or Ed25519ctx context cannot be forwarded\n // uniformly through combineSigners(), so callers that need them must use the curve directly.\n if (opts.extraEntropy !== undefined)\n throw new Error('ecSigner does not support extraEntropy; use the underlying curve directly');\n if (opts.context !== undefined)\n throw new Error('ecSigner does not support context; use the underlying curve directly');\n return curve.sign(message, secretKey);\n },\n /** Verify one wrapped curve signature.\n * Returns the wrapped curve's `verify()` result for well-formed inputs. Throws on unsupported\n * generic opts and lets wrapped-curve malformed-input errors escape unchanged.\n */\n verify: (signature, message, publicKey, opts = {}) => {\n opts = validateVerOpts(opts);\n if (opts.context !== undefined)\n throw new Error('ecSigner does not support context; use the underlying curve directly');\n return curve.verify(signature, message, publicKey);\n },\n };\n}\nfunction positiveLength(value, title) {\n const length = anumber(value, title);\n if (length === 0)\n throw new RangeError(`\"${title}\" expected integer greater than 0, got 0`);\n return length;\n}\nfunction splitLengths(lst, name) {\n // Preserve caller order exactly; raw numeric fields still decode as splitCoder() subarray views.\n const coder = splitCoder(name, ...lst.map((i) => {\n if (typeof i.lengths[name] !== 'number')\n throw new Error('wrong length: ' + name);\n return positiveLength(i.lengths[name], name);\n }));\n positiveLength(coder.bytesLen, name);\n return coder;\n}\n// It is XOF for most cases, but can be more complex!\n/**\n * Adapts an XOF into an `ExpandSeed` callback.\n * The returned callback interprets its second argument as an output byte length passed as `dkLen`.\n * @param xof - Extendable-output hash function.\n * @returns Seed expander using `dkLen`.\n * @example\n * Adapt an XOF into a seed expander.\n * ```ts\n * import { shake256 } from '@noble/hashes/sha3.js';\n * import { expandSeedXof } from '@noble/post-quantum/hybrid.js';\n * const expandSeed = expandSeedXof(shake256);\n * const seed = expandSeed(new Uint8Array([1]), 4);\n * ```\n */\nexport function expandSeedXof(xof) {\n // Forward the caller seed directly: XOFs are expected to treat inputs as read-only, and this\n // adapter only translates the requested byte length into the hash API's `dkLen` option.\n return ((seed, seedLen) => xof(seed, { dkLen: seedLen }));\n}\nfunction combineKeys(realSeedLen, // how much bytes expandSeed expects\nexpandSeed_, ...ck_) {\n const expandSeed = expandSeed_;\n const ck = ck_;\n const seedCoder = splitLengths(ck, 'seed');\n const pkCoder = splitLengths(ck, 'publicKey');\n // Allows to use identity functions for combiner/expandSeed\n const rootSeedLen = positiveLength(realSeedLen === undefined ? seedCoder.bytesLen : realSeedLen, 'realSeedLen');\n function expandDecapsulationKey(seed) {\n abytes(seed, rootSeedLen);\n const expandedRaw = expandSeed(seed, seedCoder.bytesLen);\n // Identity/subarray expanders can hand back caller-owned seed storage. Detach those outputs so\n // later cleanup can wipe the expanded schedule without mutating the caller's root seed bytes.\n const expandedSeed = expandedRaw.buffer === seed.buffer ? copyBytes(expandedRaw) : expandedRaw;\n const expanded = [];\n const keySecret = [];\n const secretKey = [];\n const publicKey = [];\n let ok = false;\n try {\n // seedCoder.decode() returns zero-copy slices into expandedSeed and can throw before child\n // keygen() runs, so keep the raw expanded buffer separate and copy each child seed before any\n // later cleanup wipes the shared backing bytes.\n for (const part of seedCoder.decode(expandedSeed))\n expanded.push(copyBytes(part));\n for (let i = 0; i < ck.length; i++) {\n const keys = ck[i].keygen(expanded[i]);\n keySecret.push(keys.secretKey);\n secretKey.push(copyBytes(keys.secretKey));\n publicKey.push(keys.publicKey);\n }\n ok = true;\n return { secretKey, publicKey };\n }\n finally {\n // Child keygen() can throw after deriving only a prefix of the composite key schedule. Keep\n // the exported copies on success, but wipe all temporary and partially built secret material\n // on either path so failures do not strand derived child seeds in memory.\n cleanBytes(expandedSeed, expanded, keySecret);\n if (!ok)\n cleanBytes(secretKey);\n }\n }\n // Standalone (not a method) so getPublicKey / destructured usage never depends on `this`.\n const keygen = (seed) => {\n // Detach the root: the exported secretKey must not alias caller-owned seed bytes, so later\n // caller mutation of the seed cannot silently change the secret key (and vice versa).\n const root = seed === undefined ? randomBytes(rootSeedLen) : copyBytes(seed);\n let res;\n try {\n const { publicKey: pk, secretKey } = expandDecapsulationKey(root);\n try {\n res = {\n secretKey: root,\n publicKey: pkCoder.encode(pk),\n };\n }\n finally {\n // The exported secretKey is the (detached) root seed; child secret keys are internal\n // expansion outputs that are cleaned whether encoding succeeds or throws.\n cleanBytes(pk, secretKey);\n }\n return res;\n }\n finally {\n if (!res)\n cleanBytes(root);\n }\n };\n return {\n info: { lengths: { seed: rootSeedLen, publicKey: pkCoder.bytesLen, secretKey: rootSeedLen } },\n // Composite secret keys are root seeds, so public-key derivation reruns key expansion from\n // that seed instead of decoding a packed child-secret-key structure.\n getPublicKey: (secretKey) => {\n const keys = keygen(secretKey);\n // keygen detaches its exported root; getPublicKey discards that half of the result.\n cleanBytes(keys.secretKey);\n return keys.publicKey;\n },\n keygen,\n expandDecapsulationKey,\n realSeedLen: rootSeedLen,\n };\n}\n// This generic function that combines multiple KEMs into single one\n/**\n * Combines multiple KEMs into one composite KEM.\n * @param realSeedLen - Positive input seed length expected by `expandSeed`, or `undefined` to use\n * the sum of component seed lengths. Callers remain responsible for choosing a security-appropriate\n * size.\n * @param realMsgLen - Positive shared-secret length returned by `combiner`, or `undefined` to use\n * the sum of component message lengths.\n * @param expandSeed - Seed expander used to derive per-KEM seeds.\n * @param combiner - Combines the per-KEM outputs into one shared secret.\n * @param kems - At least one KEM implementation. A construction advertised as hybrid normally\n * supplies two or more.\n * @returns Composite KEM.\n * @throws On wrong argument types. {@link TypeError}\n * @throws If there are no components or any required length resolves to zero. {@link RangeError}\n * @example\n * Combine multiple KEMs into one composite KEM.\n * ```ts\n * import { shake256 } from '@noble/hashes/sha3.js';\n * import { combineKEMS, expandSeedXof } from '@noble/post-quantum/hybrid.js';\n * import { ml_kem768 } from '@noble/post-quantum/ml-kem.js';\n * const hybrid = combineKEMS(\n * 32,\n * 32,\n * expandSeedXof(shake256),\n * (_pk, _ct, sharedSecrets) => sharedSecrets[0],\n * ml_kem768,\n * ml_kem768\n * );\n * const { publicKey } = hybrid.keygen();\n * ```\n */\nexport function combineKEMS(realSeedLen, // how much bytes expandSeed expects\nrealMsgLen, // how much bytes combiner returns\nexpandSeed, combiner, ...kems) {\n if (realSeedLen !== undefined)\n positiveLength(realSeedLen, 'realSeedLen');\n if (realMsgLen !== undefined)\n positiveLength(realMsgLen, 'realMsgLen');\n if (typeof expandSeed !== 'function')\n throw new TypeError('\"expandSeed\" expected function, got type=' + typeof expandSeed);\n if (typeof combiner !== 'function')\n throw new TypeError('\"combiner\" expected function, got type=' + typeof combiner);\n const rawCombiner = combiner;\n const rawKems = kems;\n if (rawKems.length === 0)\n throw new RangeError('combineKEMS requires at least one KEM');\n for (let i = 0; i < rawKems.length; i++)\n validateKEM(rawKems[i], `kems[${i}]`);\n const keys = combineKeys(realSeedLen, expandSeed, ...rawKems);\n const ctCoder = splitLengths(rawKems, 'cipherText');\n const pkCoder = splitLengths(rawKems, 'publicKey');\n const msgCoder = splitLengths(rawKems, 'msg');\n const sharedSecretLen = positiveLength(realMsgLen === undefined ? msgCoder.bytesLen : realMsgLen, 'realMsgLen');\n const lengths = Object.freeze({\n ...keys.info.lengths,\n msg: sharedSecretLen,\n msgRand: msgCoder.bytesLen,\n cipherText: ctCoder.bytesLen,\n });\n const combine = (publicKeys, cipherTexts, sharedSecrets) => {\n const combined = rawCombiner(publicKeys, cipherTexts, sharedSecrets);\n try {\n return copyBytes(abytes(combined, sharedSecretLen, 'sharedSecret'));\n }\n catch (error) {\n if (isBytes(combined)) {\n // A combiner may return any callback argument. Public keys during encapsulation and\n // ciphertexts during decapsulation are views into caller-owned inputs, so wipe an invalid\n // byte result only when its range does not overlap either public argument vector. Child\n // shared-secret aliases are already wiped by the operation's outer finally block.\n const overlaps = (value) => combined.buffer === value.buffer &&\n combined.byteOffset < value.byteOffset + value.byteLength &&\n value.byteOffset < combined.byteOffset + combined.byteLength;\n const aliasesPublicInput = publicKeys.some(overlaps) ||\n cipherTexts.some(overlaps);\n if (!aliasesPublicInput)\n cleanBytes(combined);\n }\n throw error;\n }\n };\n return Object.freeze({\n lengths,\n getPublicKey: keys.getPublicKey,\n keygen: keys.keygen,\n encapsulate(pk, randomness = randomBytes(msgCoder.bytesLen)) {\n const pks = pkCoder.decode(pk);\n const rand = msgCoder.decode(randomness);\n const sharedSecret = [];\n const cipherText = [];\n try {\n for (let i = 0; i < rawKems.length; i++) {\n const enc = rawKems[i].encapsulate(pks[i], rand[i]);\n sharedSecret.push(enc.sharedSecret);\n cipherText.push(enc.cipherText);\n }\n // Validate and detach public ciphertexts before deriving a final secret from them. This\n // also ensures a malformed child cannot make us allocate and then strand a combined key.\n const encodedCipherText = ctCoder.encode(cipherText);\n return {\n // Detach the combiner result before cleanup: a caller-provided combiner may alias one of\n // the child sharedSecret buffers, and those child buffers are zeroized immediately below.\n sharedSecret: combine(pks, cipherText, sharedSecret),\n cipherText: encodedCipherText,\n };\n }\n finally {\n // Child encapsulation or combiner failures can happen after some components already\n // returned secret material; zeroize whatever was produced before propagating the error.\n cleanBytes(sharedSecret, cipherText);\n }\n },\n decapsulate(ct, seed) {\n const cts = ctCoder.decode(ct);\n const { publicKey, secretKey } = keys.expandDecapsulationKey(seed);\n const sharedSecret = [];\n try {\n // Child decapsulate() is inside the try: it can throw on an attacker-supplied ciphertext\n // (e.g. a low-order X25519 point), and by then the expanded child secret keys \u2014 plus any\n // child shared secrets already produced \u2014 are live and must still be wiped.\n for (let i = 0; i < rawKems.length; i++)\n sharedSecret.push(rawKems[i].decapsulate(cts[i], secretKey[i]));\n // Detach the decapsulation result before cleanup: the combiner may hand back one of the\n // child shared-secret buffers, and those temporary buffers are zeroized below.\n return combine(publicKey, cts, sharedSecret);\n }\n finally {\n // Decapsulation only needs the expanded child secret keys and child shared secrets for this\n // call; keep the caller/root seed intact, but wipe all derived material even on errors.\n cleanBytes(secretKey, sharedSecret);\n }\n },\n });\n}\n// There is no specs for this, but can be useful\n// realSeedLen: how much bytes expandSeed expects.\n/**\n * Combines multiple signers into one composite signer.\n * @param realSeedLen - Positive input seed length expected by `expandSeed`, or `undefined` to use\n * the sum of component seed lengths. Callers remain responsible for choosing a security-appropriate\n * size.\n * @param expandSeed - Seed expander used to derive per-signer seeds.\n * @param signers - At least one signer. A construction advertised as hybrid normally supplies two\n * or more.\n * @returns Composite signer.\n * @throws On wrong argument types. {@link TypeError}\n * @throws If there are no components or any required length resolves to zero. {@link RangeError}\n * @example\n * Combine multiple signers into one composite signer.\n * ```ts\n * import { shake256 } from '@noble/hashes/sha3.js';\n * import { combineSigners, expandSeedXof } from '@noble/post-quantum/hybrid.js';\n * import { ml_dsa44 } from '@noble/post-quantum/ml-dsa.js';\n * const hybrid = combineSigners(32, expandSeedXof(shake256), ml_dsa44, ml_dsa44);\n * const seed = new Uint8Array(hybrid.lengths.seed!).fill(1);\n * const { secretKey, publicKey } = hybrid.keygen(seed);\n * const msg = new TextEncoder().encode('hello noble');\n * const sig = hybrid.sign(msg, secretKey);\n * const isValid = hybrid.verify(sig, msg, publicKey);\n * ```\n */\nexport function combineSigners(realSeedLen, expandSeed, ...signers) {\n if (realSeedLen !== undefined)\n positiveLength(realSeedLen, 'realSeedLen');\n if (typeof expandSeed !== 'function')\n throw new TypeError('\"expandSeed\" expected function, got type=' + typeof expandSeed);\n const rawSigners = signers;\n if (rawSigners.length === 0)\n throw new RangeError('combineSigners requires at least one signer');\n for (let i = 0; i < rawSigners.length; i++)\n validateSigner(rawSigners[i], `signers[${i}]`);\n const keys = combineKeys(realSeedLen, expandSeed, ...rawSigners);\n const sigCoder = splitLengths(rawSigners, 'signature');\n const pkCoder = splitLengths(rawSigners, 'publicKey');\n return {\n lengths: { ...keys.info.lengths, signature: sigCoder.bytesLen, signRand: 0 },\n getPublicKey: keys.getPublicKey,\n keygen: keys.keygen,\n sign(message, seed, opts = {}) {\n opts = validateSigOpts(opts);\n // This generic wrapper intentionally keeps the composite signer contract to message + root\n // seed only. Per-signer opts like context or extraEntropy cannot be preserved uniformly\n // across mixed backends, so callers that need them must use the underlying signer directly.\n if (opts.extraEntropy !== undefined)\n throw new Error('combineSigners does not support extraEntropy; use the underlying signer directly');\n if (opts.context !== undefined)\n throw new Error('combineSigners does not support context; use the underlying signer directly');\n const { secretKey } = keys.expandDecapsulationKey(seed);\n try {\n const sigs = rawSigners.map((i, j) => i.sign(message, secretKey[j]));\n return sigCoder.encode(sigs);\n }\n finally {\n // Composite secret keys are root seeds; the per-signer child secret keys are temporary\n // expansion outputs and must not stay live after the combined signature is produced.\n cleanBytes(secretKey);\n }\n },\n /** Verify one combined signature.\n * Wrong-length aggregate signatures return `false` (matching ml-dsa / slh-dsa behavior), as\n * does any failing child verify. Throws on unsupported generic opts or malformed publicKey.\n */\n verify: (signature, message, publicKey, opts = {}) => {\n opts = validateVerOpts(opts);\n if (opts.context !== undefined)\n throw new Error('combineSigners does not support context; use the underlying signer directly');\n // Malformed signature *length* is a verification failure, not a thrown type error \u2014\n // consistent with ml-dsa / slh-dsa. Must run before sigCoder.decode, which throws.\n // Preserve TypeError for non-byte API arguments before treating byte lengths as invalid.\n abytes(signature, undefined, 'signature');\n // A signature failure must not hide malformed aggregate public-key bytes.\n const pks = pkCoder.decode(publicKey);\n if (signature.length !== sigCoder.bytesLen)\n return false;\n const sigs = sigCoder.decode(signature);\n for (let i = 0; i < rawSigners.length; i++) {\n if (!rawSigners[i].verify(sigs[i], message, pks[i]))\n return false;\n }\n return true;\n },\n };\n}\n/**\n * Builds a QSF hybrid KEM preset from a PQ KEM and an elliptic-curve KEM.\n * The combined shared-secret length follows `kdf.outputLen`; the built-in presets use 32-byte\n * SHA3-256 output, while custom `kdf` choices inherit their own digest size.\n * Its combiner hashes `ss0 || ss1 || ct1 || pk1 || label`, not the full\n * `(c1, c2, ek1, ek2)` example input shape from SP 800-227 equation (15).\n * Labels are encoded with `asciiToBytes()`, so non-ASCII labels are rejected.\n * @param label - Domain-separation label.\n * @param pqc - Post-quantum KEM.\n * @param curveKEM - Classical curve KEM.\n * @param xof - XOF used for seed expansion.\n * @param kdf - Hash used for the final combiner.\n * @returns Hybrid KEM.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Build a QSF hybrid KEM preset from a PQ KEM and an elliptic-curve KEM.\n * ```ts\n * import { p256 } from '@noble/curves/nist.js';\n * import { sha3_256, shake256 } from '@noble/hashes/sha3.js';\n * import { QSF, _ecdhKem } from '@noble/post-quantum/hybrid.js';\n * import { ml_kem768 } from '@noble/post-quantum/ml-kem.js';\n * const kem = QSF('example', ml_kem768, _ecdhKem(p256, true), shake256, sha3_256);\n * const publicKeyLen = kem.lengths.publicKey;\n * ```\n */\nexport function QSF(label, pqc, curveKEM, xof, kdf) {\n astring(label, 'label');\n validateKEM(pqc, 'pqc');\n validateKEM(curveKEM, 'curveKEM');\n if (typeof xof !== 'function' || typeof xof.create !== 'function')\n throw new TypeError('\"xof\" expected hash function, got type=' + typeof xof);\n ahash(xof);\n if (typeof kdf !== 'function' || typeof kdf.create !== 'function')\n throw new TypeError('\"kdf\" expected hash function, got type=' + typeof kdf);\n ahash(kdf);\n return combineKEMS(32, kdf.outputLen, expandSeedXof(xof), (pk, ct, ss) => kdf(concatBytes(ss[0], ss[1], ct[1], pk[1], asciiToBytes(label))), pqc, curveKEM);\n}\n/** QSF preset combining ML-KEM-768 with P-256. */\nexport const QSF_ml_kem768_p256 = /* @__PURE__ */ (() => QSF('QSF-KEM(ML-KEM-768,P-256)-XOF(SHAKE256)-KDF(SHA3-256)', ml_kem768, _ecdhKem(p256, true), shake256, sha3_256))();\n/** QSF preset combining ML-KEM-1024 with P-384. */\nexport const QSF_ml_kem1024_p384 = /* @__PURE__ */ (() => QSF('QSF-KEM(ML-KEM-1024,P-384)-XOF(SHAKE256)-KDF(SHA3-256)', ml_kem1024, _ecdhKem(p384, true), shake256, sha3_256))();\n/**\n * Builds the \"KitchenSink\" hybrid KEM combiner.\n * The current builder always derives a fixed 32-byte output,\n * regardless of the hash's native output size.\n * Its HKDF extract step uses implicit zero salt with IKM\n * `hybrid_prk || ss0 || ss1 || ct0 || pk0 || ct1 || pk1 || label`.\n * Its HKDF expand step fixes `info` to `len || 'shared_secret' || ''`.\n * Labels are encoded with `asciiToBytes()`, so non-ASCII labels are rejected.\n * @param label - Domain-separation label.\n * @param pqc - Post-quantum KEM.\n * @param curveKEM - Classical curve KEM.\n * @param xof - XOF used for seed expansion.\n * @param hash - Hash used for HKDF extraction and expansion.\n * @returns Hybrid KEM.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Build the \"KitchenSink\" hybrid KEM combiner.\n * ```ts\n * import { sha256 } from '@noble/hashes/sha2.js';\n * import { shake256 } from '@noble/hashes/sha3.js';\n * import { createKitchenSink, _ecdhKem } from '@noble/post-quantum/hybrid.js';\n * import { ml_kem768 } from '@noble/post-quantum/ml-kem.js';\n * import { x25519 } from '@noble/curves/ed25519.js';\n * const kem = createKitchenSink('example', ml_kem768, _ecdhKem(x25519), shake256, sha256);\n * const publicKeyLen = kem.lengths.publicKey;\n * ```\n */\nexport function createKitchenSink(label, pqc, curveKEM, xof, hash) {\n astring(label, 'label');\n validateKEM(pqc, 'pqc');\n validateKEM(curveKEM, 'curveKEM');\n if (typeof xof !== 'function' || typeof xof.create !== 'function')\n throw new TypeError('\"xof\" expected hash function, got type=' + typeof xof);\n ahash(xof);\n if (typeof hash !== 'function' || typeof hash.create !== 'function')\n throw new TypeError('\"hash\" expected hash function, got type=' + typeof hash);\n ahash(hash);\n return combineKEMS(32, 32, expandSeedXof(xof), (pk, ct, ss) => {\n const preimage = concatBytes(ss[0], ss[1], ct[0], pk[0], ct[1], pk[1], asciiToBytes(label));\n const len = 32;\n const ikm = concatBytes(asciiToBytes('hybrid_prk'), preimage);\n const prk = extract(hash, ikm);\n const info = concatBytes(numberToBytesBE(len, 2), asciiToBytes('shared_secret'), asciiToBytes(''));\n const res = expand(hash, prk, info, len);\n cleanBytes(prk, info, ikm, preimage);\n return res;\n }, pqc, curveKEM);\n}\n// Internal alias only: this stays exactly `_ecdhKem(x25519)`\n// and inherits that wrapper's mutation/oracle behavior.\nconst x25519kem = /* @__PURE__ */ _ecdhKem(x25519);\n/** KitchenSink preset combining ML-KEM-768 with X25519.\n * Caller randomness splits into 32 ML-KEM coins plus a 32-byte X25519 ephemeral-secret seed.\n */\nexport const KitchenSink_ml_kem768_x25519 = /* @__PURE__ */ (() => createKitchenSink('KitchenSink-KEM(ML-KEM-768,X25519)-XOF(SHAKE256)-KDF(HKDF-SHA-256)', ml_kem768, x25519kem, shake256, sha256))();\n// Always X25519 and ML-KEM - 768, no point to export\n/** X25519 + ML-KEM-768 hybrid preset.\n * Uses the hard-coded domain-separation label `\\\\.//^\\\\` and hashes only `ct1 || pk1`\n * from the X25519 side in addition to the two component shared secrets.\n */\nexport const ml_kem768_x25519 = /* @__PURE__ */ (() => combineKEMS(32, 32, expandSeedXof(shake256), \n// Awesome label, so much escaping hell in a single line.\n(pk, ct, ss) => sha3_256(concatBytes(ss[0], ss[1], ct[1], pk[1], asciiToBytes('\\\\.//^\\\\'))), ml_kem768, x25519kem))();\n/**\n * Internal SEC 1-style KEM wrapper for NIST curves.\n * `nseed` is only the rejection-sampling byte budget for deriving one nonzero scalar:\n * current presets use `128` bytes for P-256 and `48` bytes for P-384.\n * `decapsulate()` returns the uncompressed shared point body `x || y` without the `0x04`\n * prefix, not the SEC 1 `x_P`-only primitive output, because current hybrid combiners hash\n * both coordinates.\n */\nfunction nistCurveKem(curve, scalarLen, elemLen, nseed) {\n const Fn = curve.Point.Fn;\n if (!Fn)\n throw new Error('no Point.Fn');\n // Scan scalar-sized windows until one decodes to a nonzero scalar in `[1, n-1]`; if every\n // window is zero or out of range, fail instead of silently reducing modulo `n`.\n function rejectionSampling(seed) {\n let sk;\n for (let start = 0, end = scalarLen;; start = end, end += scalarLen) {\n if (end > seed.length)\n throw new Error('rejection sampling failed');\n sk = Fn.fromBytes(seed.subarray(start, end), true);\n if (Fn.isValidNot0(sk))\n break;\n }\n const secretKey = Fn.toBytes(Fn.create(sk));\n const publicKey = curve.getPublicKey(secretKey, false);\n return { secretKey, publicKey };\n }\n // Standalone (not `this.decapsulate`) so encapsulate works even when methods are destructured.\n const decapsulate = (cipherText, secretKey) => {\n const full = curve.getSharedSecret(secretKey, cipherText);\n return full.subarray(1);\n };\n return {\n lengths: {\n secretKey: scalarLen,\n publicKey: elemLen,\n seed: nseed,\n msg: nseed,\n cipherText: elemLen,\n },\n keygen(seed = randomBytes(nseed)) {\n abytes(seed, nseed, 'seed');\n return rejectionSampling(seed);\n },\n getPublicKey(secretKey) {\n return curve.getPublicKey(secretKey, false);\n },\n encapsulate(publicKey, rand = randomBytes(nseed)) {\n abytes(rand, nseed, 'rand');\n let ek = undefined;\n try {\n ek = rejectionSampling(rand).secretKey;\n const sharedSecret = decapsulate(publicKey, ek);\n const cipherText = curve.getPublicKey(ek, false);\n return { sharedSecret, cipherText };\n }\n finally {\n // Rejection-sampled NIST-curve ephemeral secret keys are temporary encapsulation state and\n // must be wiped even if peer-key validation or shared-secret derivation throws.\n if (ek)\n cleanBytes(ek);\n }\n },\n decapsulate,\n };\n}\n/**\n * Internal ML-KEM + NIST-curve combiner.\n * `nseed` controls only the curve-side rejection-sampling budget; it is expanded from the\n * 32-byte root seed and is not itself part of the exported secret-key length.\n * The domain-separation `label` is used only in the final `sha3_256` combiner, not in\n * `shake256(seed, { dkLen: 64 + nseed })`,\n * and the combiner hashes `ss0 || ss1 || ct1 || pk1 || label`.\n */\nfunction concreteHybridKem(label, mlkem, curve, nseed) {\n const { secretKey: scalarLen, publicKeyUncompressed: elemLen } = curve.lengths;\n if (!scalarLen || !elemLen)\n throw new Error('wrong curve');\n const curveKem = nistCurveKem(curve, scalarLen, elemLen, nseed);\n const mlkemSeedLen = 64;\n const totalSeedLen = mlkemSeedLen + nseed;\n return combineKEMS(32, 32, (seed) => {\n abytes(seed, 32);\n // One SHAKE256 stream split by the seed coder as mlkemSeed (64) || curveSeed (nseed).\n // Returned directly: the previous concatBytes of two adjacent subarrays produced an\n // identical copy while leaving this original buffer unwiped; expandDecapsulationKey\n // wipes the returned buffer after the child seeds are copied out.\n return shake256(seed, { dkLen: totalSeedLen });\n }, (pk, ct, ss) => sha3_256(concatBytes(ss[0], ss[1], ct[1], pk[1], asciiToBytes(label))), mlkem, curveKem);\n}\n/** P-256 + ML-KEM-768 hybrid preset. */\nexport const ml_kem768_p256 = /* @__PURE__ */ (() => concreteHybridKem('MLKEM768-P256', ml_kem768, p256, 128))();\n/** P-384 + ML-KEM-1024 hybrid preset. */\nexport const ml_kem1024_p384 = /* @__PURE__ */ (() => concreteHybridKem('MLKEM1024-P384', ml_kem1024, p384, 48))();\n", "/**\n * @file e2ee.ts\n * @description Zero-Knowledge End-to-End Encryption (E2EE) cryptographic primitives.\n * Implements Web Crypto ECDH P-256 and Post-Quantum P256-MLKEM768 (NIST FIPS 203) hybrid encryption.\n */\n\nimport { ml_kem768_p256 } from \"@noble/post-quantum/hybrid.js\";\n\nexport interface EncryptedPayload {\n ephem_pk: JsonWebKey;\n iv: string;\n ciphertext: string;\n}\n\n/** Minimal compact payload for periodic envelope encryption (no redundant version field) */\nexport interface CompactEncryptedLogPayload {\n iv: string; // Base64 (12-byte AES-GCM IV)\n ciphertext: string; // Base64 (AES-256-GCM ciphertext + 16-byte tag)\n}\n\nexport interface PqcKeyPair {\n publicKeyBase64: string; // 1,249 bytes Base64\n secretKeyBase64: string; // 32 bytes seed Base64\n}\n\nexport interface PqcProfilePublicKey {\n alg: \"P256-MLKEM768\";\n pqc_pk: string; // Base64 1,249 bytes\n}\n\n\nexport interface SensitiveLogData {\n domain: string;\n client_ip: string;\n geo_country?: string | null;\n answer?: string | null;\n dest_geoip?: string | null;\n dest_country_code?: string | null;\n dest_country?: string | null;\n dest_isp?: string | null;\n ecs?: string | null;\n upstream?: string | null;\n reason?: string | null;\n}\n\nexport interface WrappedKeyData {\n encryptedSk: string;\n iv: string;\n}\n\n/**\n * Universal base64 encoder supporting Web Workers, Cloudflare Workers, Node.js, and Browsers.\n */\nexport function toBase64(bytes: Uint8Array | ArrayBuffer): string {\n const u8 = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(u8).toString(\"base64\");\n }\n let binary = \"\";\n for (let i = 0; i < u8.length; i++) {\n binary += String.fromCharCode(u8[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Universal base64 decoder supporting Web Workers, Cloudflare Workers, Node.js, and Browsers.\n */\nexport function fromBase64(base64: string): Uint8Array {\n if (typeof Buffer !== \"undefined\") {\n return new Uint8Array(Buffer.from(base64, \"base64\"));\n }\n const binary = atob(base64);\n const u8 = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n u8[i] = binary.charCodeAt(i);\n }\n return u8;\n}\n\n/**\n * Generates an ECDH P-256 keypair for log encryption and decryption.\n */\nexport async function generateLogKeyPair(): Promise<{\n publicKeyJwk: JsonWebKey;\n privateKeyJwk: JsonWebKey;\n}> {\n const subtle = crypto.subtle;\n const keyPair = (await subtle.generateKey(\n { name: \"ECDH\", namedCurve: \"P-256\" },\n true,\n [\"deriveKey\", \"deriveBits\"]\n )) as CryptoKeyPair;\n\n const publicKeyJwk = (await subtle.exportKey(\"jwk\", keyPair.publicKey)) as JsonWebKey;\n const privateKeyJwk = (await subtle.exportKey(\"jwk\", keyPair.privateKey)) as JsonWebKey;\n\n return { publicKeyJwk, privateKeyJwk };\n}\n\n/**\n * Encrypts sensitive DNS log data using recipient's ECDH P-256 public key.\n *\n * @param recipientPublicKeyJwk The recipient's ECDH P-256 public key.\n * @param sensitiveData The sensitive fields of the DNS query log.\n * @returns JSON-stringified EncryptedPayload containing ephem_pk, iv, and ciphertext.\n */\nexport async function encryptSensitiveLogData(\n recipientPublicKeyJwk: JsonWebKey,\n sensitiveData: SensitiveLogData\n): Promise<string> {\n const subtle = crypto.subtle;\n\n // 1. Generate ephemeral ECDH P-256 key pair\n const ephemKeyPair = (await subtle.generateKey(\n { name: \"ECDH\", namedCurve: \"P-256\" },\n true,\n [\"deriveKey\"]\n )) as CryptoKeyPair;\n\n // 2. Import recipient public key\n const importedRecipientPub = await subtle.importKey(\n \"jwk\",\n recipientPublicKeyJwk,\n { name: \"ECDH\", namedCurve: \"P-256\" },\n false,\n []\n );\n\n // 3. Derive 256-bit AES-GCM shared key\n const aesKey = await subtle.deriveKey(\n { name: \"ECDH\", public: importedRecipientPub, $public: importedRecipientPub } as any,\n ephemKeyPair.privateKey,\n { name: \"AES-GCM\", length: 256 },\n false,\n [\"encrypt\"]\n );\n\n // 4. Encrypt sensitive payload\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const plaintextBytes = new TextEncoder().encode(JSON.stringify(sensitiveData));\n const ciphertextBuffer = await subtle.encrypt(\n { name: \"AES-GCM\", iv },\n aesKey,\n plaintextBytes\n );\n\n // 5. Export ephemeral public key\n const ephemPkJwk = (await subtle.exportKey(\"jwk\", ephemKeyPair.publicKey)) as JsonWebKey;\n\n const payload: EncryptedPayload = {\n ephem_pk: ephemPkJwk,\n iv: toBase64(iv),\n ciphertext: toBase64(ciphertextBuffer),\n };\n\n return JSON.stringify(payload);\n}\n\n/**\n * Decrypts sensitive DNS log data using the user's ECDH P-256 private key.\n *\n * @param recipientPrivateKeyJwk The user's ECDH P-256 private key.\n * @param encryptedPayloadStr The JSON-stringified EncryptedPayload.\n * @returns Decrypted SensitiveLogData.\n */\nexport async function decryptSensitiveLogData(\n recipientPrivateKeyJwk: JsonWebKey,\n encryptedPayloadStr: string\n): Promise<SensitiveLogData> {\n const subtle = crypto.subtle;\n const payload: EncryptedPayload = JSON.parse(encryptedPayloadStr);\n\n // 1. Import recipient private key\n const importedRecipientPriv = await subtle.importKey(\n \"jwk\",\n recipientPrivateKeyJwk,\n { name: \"ECDH\", namedCurve: \"P-256\" },\n false,\n [\"deriveKey\"]\n );\n\n // 2. Import ephemeral public key\n const importedEphemPub = await subtle.importKey(\n \"jwk\",\n payload.ephem_pk,\n { name: \"ECDH\", namedCurve: \"P-256\" },\n false,\n []\n );\n\n // 3. Derive 256-bit AES-GCM shared key\n const aesKey = await subtle.deriveKey(\n { name: \"ECDH\", public: importedEphemPub, $public: importedEphemPub } as any,\n importedRecipientPriv,\n { name: \"AES-GCM\", length: 256 },\n false,\n [\"decrypt\"]\n );\n\n // 4. Decrypt ciphertext\n const iv = fromBase64(payload.iv);\n const ciphertext = fromBase64(payload.ciphertext);\n\n const decryptedBytes = await subtle.decrypt(\n { name: \"AES-GCM\", iv },\n aesKey,\n ciphertext\n );\n\n return JSON.parse(new TextDecoder().decode(decryptedBytes)) as SensitiveLogData;\n}\n\n/**\n * Wraps (encrypts) the user's log private key with a Passkey-derived Key Encryption Key (KEK).\n *\n * @param privateKeyJwk The user's ECDH P-256 private key in JWK format.\n * @param kekRaw 256-bit raw key derived from Passkey assertion / PRF.\n * @returns Base64-encoded encrypted private key and IV.\n */\nexport async function wrapPrivateKeyWithKek(\n privateKeyJwk: JsonWebKey,\n kekRaw: Uint8Array\n): Promise<WrappedKeyData> {\n const subtle = crypto.subtle;\n const kek = await subtle.importKey(\n \"raw\",\n kekRaw,\n { name: \"AES-GCM\" },\n false,\n [\"encrypt\"]\n );\n\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const plaintext = new TextEncoder().encode(JSON.stringify(privateKeyJwk));\n\n const encryptedBuffer = await subtle.encrypt(\n { name: \"AES-GCM\", iv },\n kek,\n plaintext\n );\n\n return {\n encryptedSk: toBase64(encryptedBuffer),\n iv: toBase64(iv),\n };\n}\n\n/**\n * Unwraps (decrypts) the user's log private key using a Passkey-derived Key Encryption Key (KEK).\n *\n * @param encryptedSkBase64 Base64-encoded encrypted private key.\n * @param ivBase64 Base64-encoded IV.\n * @param kekRaw 256-bit raw key derived from Passkey assertion / PRF.\n * @returns Decrypted ECDH P-256 private key in JWK format.\n */\nexport async function unwrapPrivateKeyWithKek(\n encryptedSkBase64: string,\n ivBase64: string,\n kekRaw: Uint8Array\n): Promise<JsonWebKey> {\n const subtle = crypto.subtle;\n const kek = await subtle.importKey(\n \"raw\",\n kekRaw,\n { name: \"AES-GCM\" },\n false,\n [\"decrypt\"]\n );\n\n const iv = fromBase64(ivBase64);\n const ciphertext = fromBase64(encryptedSkBase64);\n\n const decryptedBuffer = await subtle.decrypt(\n { name: \"AES-GCM\", iv },\n kek,\n ciphertext\n );\n\n return JSON.parse(new TextDecoder().decode(decryptedBuffer)) as JsonWebKey;\n}\n\nexport interface RecoveryWrappedKeyData {\n encryptedSk: string;\n iv: string;\n salt: string;\n}\n\n/**\n * Derives a 256-bit Key Encryption Key (KEK) from a normalized plaintext Recovery Key using PBKDF2-HMAC-SHA256.\n *\n * @param plaintextRecoveryKey - Plaintext 30-digit or raw recovery key\n * @param salt - 16-byte random salt\n * @returns CryptoKey for AES-GCM\n */\nexport async function deriveRecoveryKek(\n plaintextRecoveryKey: string,\n salt: Uint8Array\n): Promise<CryptoKey> {\n const subtle = crypto.subtle;\n const normalized = plaintextRecoveryKey.replace(/[-\\s]/g, \"\").toUpperCase().trim();\n const data = new TextEncoder().encode(normalized);\n\n const baseKey = await subtle.importKey(\n \"raw\",\n data,\n { name: \"PBKDF2\" },\n false,\n [\"deriveKey\"]\n );\n\n return subtle.deriveKey(\n {\n name: \"PBKDF2\",\n salt: salt as BufferSource,\n iterations: 100000,\n hash: \"SHA-256\",\n },\n baseKey,\n { name: \"AES-GCM\", length: 256 },\n false,\n [\"encrypt\", \"decrypt\"]\n );\n}\n\n/**\n * Wraps (encrypts) the user's ECDH P-256 private key using a Recovery Key KEK.\n *\n * @param privateKeyJwk - User's private key in JWK format\n * @param plaintextRecoveryKey - Plaintext recovery key\n * @returns Base64 encoded encryptedSk, iv, and salt\n */\nexport async function wrapPrivateKeyWithRecoveryKey(\n privateKeyJwk: JsonWebKey,\n plaintextRecoveryKey: string\n): Promise<RecoveryWrappedKeyData> {\n const salt = crypto.getRandomValues(new Uint8Array(16));\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const kek = await deriveRecoveryKek(plaintextRecoveryKey, salt);\n\n const plaintext = new TextEncoder().encode(JSON.stringify(privateKeyJwk));\n const ciphertextBuffer = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n kek,\n plaintext\n );\n\n return {\n encryptedSk: toBase64(ciphertextBuffer),\n iv: toBase64(iv),\n salt: toBase64(salt),\n };\n}\n\n/**\n * Unwraps (decrypts) the user's ECDH P-256 private key using their Recovery Key.\n *\n * @param encryptedSkBase64 - Base64 encoded encrypted private key\n * @param ivBase64 - Base64 encoded IV\n * @param saltBase64 - Base64 encoded salt\n * @param plaintextRecoveryKey - Plaintext recovery key entered by user\n * @returns Decrypted JsonWebKey\n */\nexport async function unwrapPrivateKeyWithRecoveryKey(\n encryptedSkBase64: string,\n ivBase64: string,\n saltBase64: string,\n plaintextRecoveryKey: string\n): Promise<JsonWebKey> {\n const salt = fromBase64(saltBase64);\n const iv = fromBase64(ivBase64);\n const ciphertext = fromBase64(encryptedSkBase64);\n const kek = await deriveRecoveryKek(plaintextRecoveryKey, salt);\n\n const decryptedBuffer = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv },\n kek,\n ciphertext\n );\n\n return JSON.parse(new TextDecoder().decode(decryptedBuffer)) as JsonWebKey;\n}\n\n/* ========================================================================== */\n/* Post-Quantum Cryptography (P256-MLKEM768 / NIST FIPS 203) */\n/* ========================================================================== */\n\n/**\n * Generates a post-quantum hybrid P256-MLKEM768 key pair.\n *\n * @returns Object with base64-encoded 1249-byte public key and 32-byte seed secret key.\n */\nexport function generatePqcLogKeyPair(): PqcKeyPair {\n const keys = ml_kem768_p256.keygen();\n return {\n publicKeyBase64: toBase64(keys.publicKey),\n secretKeyBase64: toBase64(keys.secretKey),\n };\n}\n\n/**\n * Encapsulates a shared secret using recipient's P256-MLKEM768 public key.\n *\n * @param publicKeyBytes 1249-byte hybrid public key\n * @returns 1153-byte KEM ciphertext (Base64) and 32-byte high-entropy shared secret\n */\nexport function encapsulatePqcDek(publicKeyBytes: Uint8Array): {\n kemCtBase64: string;\n sharedSecret: Uint8Array;\n} {\n const res = ml_kem768_p256.encapsulate(publicKeyBytes);\n return {\n kemCtBase64: toBase64(res.cipherText),\n sharedSecret: res.sharedSecret,\n };\n}\n\n/**\n * Decapsulates a shared secret using recipient's P256-MLKEM768 secret key.\n *\n * @param kemCtBytes 1153-byte KEM ciphertext\n * @param secretKeyBytes 32-byte seed secret key\n * @returns 32-byte shared secret\n */\nexport function decapsulatePqcDek(\n kemCtBytes: Uint8Array,\n secretKeyBytes: Uint8Array\n): Uint8Array {\n return ml_kem768_p256.decapsulate(kemCtBytes, secretKeyBytes);\n}\n\n/**\n * Derives a 256-bit AES-GCM DEK from a 32-byte KEM shared secret via HKDF-SHA256.\n *\n * @param sharedSecret 32-byte high-entropy shared secret\n * @returns CryptoKey handle for AES-GCM encrypt/decrypt\n */\nexport async function deriveDekFromSharedSecret(sharedSecret: Uint8Array): Promise<CryptoKey> {\n const subtle = crypto.subtle;\n const hkdfKey = await subtle.importKey(\n \"raw\",\n sharedSecret as BufferSource,\n { name: \"HKDF\" },\n false,\n [\"deriveKey\"]\n );\n\n return await subtle.deriveKey(\n {\n name: \"HKDF\",\n hash: \"SHA-256\",\n salt: new Uint8Array(0),\n info: new TextEncoder().encode(\"obex-pqc-dek-v2\"),\n },\n hkdfKey,\n { name: \"AES-GCM\", length: 256 },\n false,\n [\"encrypt\", \"decrypt\"]\n );\n}\n\n/**\n * Encrypts sensitive DNS log data using a pre-derived AES-256-GCM DEK.\n * Produces a minimal compact payload containing only iv and ciphertext.\n *\n * @param dek 256-bit AES-GCM CryptoKey\n * @param sensitiveData Plaintext sensitive DNS log fields\n * @returns JSON-serialized CompactEncryptedLogPayload ({ iv, ciphertext })\n */\nexport async function encryptSensitiveLogDataWithDek(\n dek: CryptoKey,\n sensitiveData: SensitiveLogData\n): Promise<string> {\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const plaintextBytes = new TextEncoder().encode(JSON.stringify(sensitiveData));\n const ciphertextBuffer = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n dek,\n plaintextBytes\n );\n\n const payload: CompactEncryptedLogPayload = {\n iv: toBase64(iv),\n ciphertext: toBase64(ciphertextBuffer),\n };\n\n return JSON.stringify(payload);\n}\n\n/**\n * Decrypts sensitive DNS log data using a pre-derived AES-256-GCM DEK.\n *\n * @param dek 256-bit AES-GCM CryptoKey\n * @param encryptedPayloadStr JSON-serialized CompactEncryptedLogPayload ({ iv, ciphertext })\n * @returns Plaintext SensitiveLogData\n */\nexport async function decryptSensitiveLogDataWithDek(\n dek: CryptoKey,\n encryptedPayloadStr: string\n): Promise<SensitiveLogData> {\n const payload: CompactEncryptedLogPayload = JSON.parse(encryptedPayloadStr);\n const iv = fromBase64(payload.iv);\n const ciphertext = fromBase64(payload.ciphertext);\n\n const decryptedBytes = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: iv as BufferSource },\n dek,\n ciphertext as BufferSource\n );\n\n return JSON.parse(new TextDecoder().decode(decryptedBytes)) as SensitiveLogData;\n}\n\n/**\n * Wraps (encrypts) the user's 32-byte PQC secret key seed with a Passkey-derived Key Encryption Key (KEK).\n *\n * @param secretKeyBytes 32-byte PQC secret key seed\n * @param kekRaw 256-bit raw key derived from Passkey assertion / PRF\n * @returns Base64-encoded encrypted secret key and IV\n */\nexport async function wrapPqcPrivateKeyWithKek(\n secretKeyBytes: Uint8Array,\n kekRaw: Uint8Array\n): Promise<WrappedKeyData> {\n const subtle = crypto.subtle;\n const kek = await subtle.importKey(\n \"raw\",\n kekRaw as BufferSource,\n { name: \"AES-GCM\" },\n false,\n [\"encrypt\"]\n );\n\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const encryptedBuffer = await subtle.encrypt(\n { name: \"AES-GCM\", iv },\n kek,\n secretKeyBytes as BufferSource\n );\n\n return {\n encryptedSk: toBase64(encryptedBuffer),\n iv: toBase64(iv),\n };\n}\n\n/**\n * Unwraps (decrypts) the user's 32-byte PQC secret key seed using a Passkey-derived Key Encryption Key (KEK).\n *\n * @param encryptedSkBase64 Base64-encoded encrypted secret key\n * @param ivBase64 Base64-encoded IV\n * @param kekRaw 256-bit raw key derived from Passkey assertion / PRF\n * @returns 32-byte PQC secret key seed\n */\nexport async function unwrapPqcPrivateKeyWithKek(\n encryptedSkBase64: string,\n ivBase64: string,\n kekRaw: Uint8Array\n): Promise<Uint8Array> {\n const subtle = crypto.subtle;\n const kek = await subtle.importKey(\n \"raw\",\n kekRaw as BufferSource,\n { name: \"AES-GCM\" },\n false,\n [\"decrypt\"]\n );\n\n const iv = fromBase64(ivBase64);\n const ciphertext = fromBase64(encryptedSkBase64);\n\n const decryptedBuffer = await subtle.decrypt(\n { name: \"AES-GCM\", iv: iv as BufferSource },\n kek,\n ciphertext as BufferSource\n );\n\n return new Uint8Array(decryptedBuffer);\n}\n\n/**\n * Wraps (encrypts) the user's 32-byte PQC secret key seed using a Recovery Key KEK.\n *\n * @param secretKeyBytes 32-byte PQC secret key seed\n * @param plaintextRecoveryKey Plaintext recovery key\n * @returns Base64 encoded encryptedSk, iv, and salt\n */\nexport async function wrapPqcPrivateKeyWithRecoveryKey(\n secretKeyBytes: Uint8Array,\n plaintextRecoveryKey: string\n): Promise<RecoveryWrappedKeyData> {\n const salt = crypto.getRandomValues(new Uint8Array(16));\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const kek = await deriveRecoveryKek(plaintextRecoveryKey, salt);\n\n const ciphertextBuffer = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n kek,\n secretKeyBytes as BufferSource\n );\n\n return {\n encryptedSk: toBase64(ciphertextBuffer),\n iv: toBase64(iv),\n salt: toBase64(salt),\n };\n}\n\n/**\n * Unwraps (decrypts) the user's 32-byte PQC secret key seed using their Recovery Key.\n *\n * @param encryptedSkBase64 Base64 encoded encrypted secret key\n * @param ivBase64 Base64 encoded IV\n * @param saltBase64 Base64 encoded salt\n * @param plaintextRecoveryKey Plaintext recovery key entered by user\n * @returns 32-byte PQC secret key seed\n */\nexport async function unwrapPqcPrivateKeyWithRecoveryKey(\n encryptedSkBase64: string,\n ivBase64: string,\n saltBase64: string,\n plaintextRecoveryKey: string\n): Promise<Uint8Array> {\n const salt = fromBase64(saltBase64);\n const iv = fromBase64(ivBase64);\n const ciphertext = fromBase64(encryptedSkBase64);\n const kek = await deriveRecoveryKek(plaintextRecoveryKey, salt);\n\n const decryptedBuffer = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: iv as BufferSource },\n kek,\n ciphertext as BufferSource\n );\n\n return new Uint8Array(decryptedBuffer);\n}\n\n", "/**\n * @file dekManager.ts\n * @description Zero-Knowledge Post-Quantum DEK (Data Encryption Key) Lifecycle Manager.\n * Operates as an independent key producer: generates and rotates hybrid KEM keys per isolate,\n * persists kem_keys (containing ONLY client-decapsulatable ciphertext kem_ct) to D1,\n * and maintains an active in-memory pool for the lifetime of the Worker isolate.\n *\n * NOTE: The server NEVER persists the plaintext DEK or any server-decryptable wrapper.\n * Once the isolate terminates, ephemeral shared secrets are destroyed from server memory,\n * ensuring strict forward secrecy and zero-knowledge data-at-rest encryption.\n */\n\nimport { D1Database } from \"@cloudflare/workers-types\";\nimport {\n encapsulatePqcDek,\n deriveDekFromSharedSecret,\n fromBase64,\n} from \"./e2ee\";\nimport { generateLogId } from \"../../models/log/core\";\n\nexport interface ActiveDek {\n kemKeyId: string;\n dek: CryptoKey;\n profileId: string;\n createdAt: number;\n expiresAt: number;\n}\n\n/** 1 hour DEK lifetime in milliseconds */\nexport const DEK_LIFETIME_MS = 3600 * 1000;\n\n/** In-memory pool of currently active DEKs per profile in this isolate */\nconst activeDekMap = new Map<string, ActiveDek>();\n\n/** Mutex to deduplicate concurrent in-flight DEK provisionings per profile in this isolate */\nconst inFlightDekPromises = new Map<string, Promise<ActiveDek>>();\n\n/**\n * Key Provider / Consumer Interface:\n * Retrieves the currently published DEK for a profile with ZERO expiration checks.\n * The encryption pipeline consumes this directly without blocking or validating timestamps.\n *\n * @param profileId Profile identifier\n * @returns ActiveDek or null if not yet initialized in this worker isolate\n */\nexport function getCurrentDek(profileId: string): ActiveDek | null {\n return activeDekMap.get(profileId) || null;\n}\n\n/**\n * Key Producer Interface:\n * Ensures an active DEK is available in this isolate for the profile.\n * If none exists or the existing key has exceeded its rotation window,\n * generates a fresh post-quantum hybrid KEM key, writes kem_ct to D1,\n * and publishes the new key to the active in-memory pool.\n *\n * Uses inFlightDekPromises mutex to ensure parallel concurrent requests\n * do not generate duplicate keys simultaneously.\n *\n * @param db D1 Database instance\n * @param profileId Profile identifier\n * @param pqcPublicKeyBase64 1249-byte hybrid public key in Base64\n * @returns The active DEK\n */\nexport async function ensureActiveDek(\n db: D1Database,\n profileId: string,\n pqcPublicKeyBase64: string\n): Promise<ActiveDek> {\n const now = Date.now();\n const existing = activeDekMap.get(profileId);\n\n // If existing key is still within its rotation window, continue using it\n if (existing && existing.expiresAt > now) {\n return existing;\n }\n\n // Deduplicate concurrent in-flight requests for the same profile\n const inFlight = inFlightDekPromises.get(profileId);\n if (inFlight) {\n return await inFlight;\n }\n\n const promise = (async () => {\n try {\n return await rotateDek(db, profileId, pqcPublicKeyBase64);\n } finally {\n inFlightDekPromises.delete(profileId);\n }\n })();\n\n inFlightDekPromises.set(profileId, promise);\n return await promise;\n}\n\n/**\n * Key Producer Interface:\n * Generates a new DEK via P256-MLKEM768 encapsulation, persists kem_ct to D1 kem_keys,\n * and atomically updates the in-memory active pool.\n *\n * @param db D1 Database instance\n * @param profileId Profile identifier\n * @param pqcPublicKeyBase64 1249-byte hybrid public key in Base64\n * @returns Newly minted ActiveDek\n */\nexport async function rotateDek(\n db: D1Database,\n profileId: string,\n pqcPublicKeyBase64: string\n): Promise<ActiveDek> {\n const now = Date.now();\n const nowSec = Math.floor(now / 1000);\n const expiresAtSec = nowSec + 3600;\n\n // 1. Encapsulate with recipient's hybrid public key (1,249 bytes)\n const pkBytes = fromBase64(pqcPublicKeyBase64);\n const { kemCtBase64, sharedSecret } = encapsulatePqcDek(pkBytes);\n\n // 2. Derive 256-bit AES-GCM DEK via HKDF-SHA256\n const dek = await deriveDekFromSharedSecret(sharedSecret);\n\n // 3. Generate unique kem_key_id\n const kemKeyId = `kem_${generateLogId()}`;\n\n // 4. Persist KEM ciphertext to kem_keys table in D1\n try {\n await db\n .prepare(\n \"INSERT INTO kem_keys (id, profile_id, kem_ct, created_at, expires_at) VALUES (?, ?, ?, ?, ?)\"\n )\n .bind(kemKeyId, profileId, kemCtBase64, nowSec, expiresAtSec)\n .run();\n } catch (err) {\n console.error(`[DekManager] Failed to persist kem_key ${kemKeyId} to D1:`, err);\n }\n\n // 5. Publish to in-memory active pool\n const activeDek: ActiveDek = {\n kemKeyId,\n dek,\n profileId,\n createdAt: now,\n expiresAt: now + DEK_LIFETIME_MS,\n };\n\n activeDekMap.set(profileId, activeDek);\n return activeDek;\n}\n\n/**\n * Invalidates the active DEK for a profile (e.g. when user updates or disables E2EE).\n */\nexport function invalidateActiveDek(profileId: string): void {\n activeDekMap.delete(profileId);\n inFlightDekPromises.delete(profileId);\n}\n", "import { Context, DNSQuery, ProfileSettings, ResolutionResult } from \"../../types\";\nimport {\n buildResponse,\n buildResponseMulti,\n buildDNSQuery,\n parseDNSAnswer,\n DNSRecord\n} from \"../../utils/dns\";\nimport { enqueueLog } from \"../logBatcher\";\n\n/**\n * Resolver function type used for resolving CNAME targets during redirection.\n */\nexport type RecursiveResolverFn = (\n request: Request,\n query: DNSQuery,\n context: Context,\n settings: ProfileSettings,\n action: \"PASS\"\n) => Promise<ResolutionResult>;\n\n/**\n * Synthesizes DNS responses for blocked or redirected queries.\n *\n * For REDIRECT:\n * - If user queries A or AAAA and rule targets a CNAME domain, recursively resolves\n * the target domain's IP addresses and constructs a multi-record response.\n * - Otherwise synthesizes a single record answer.\n *\n * For BLOCK:\n * - Supports NXDOMAIN (RCODE 3), NODATA (RCODE 0), CUSTOM_IP, or NULL_IP (0.0.0.0 / ::).\n *\n * Persists the block/redirect event asynchronously to the log batcher.\n *\n * @param request - Inbound HTTP Request.\n * @param query - The incoming DNS query.\n * @param context - Request context.\n * @param settings - Current profile settings.\n * @param action - Either 'BLOCK' or 'REDIRECT'.\n * @param reason - Explanation for the action (e.g., rule pattern or category).\n * @param customAnswer - Custom IP or CNAME target domain if redirecting.\n * @param responseType - Target DNS record type (e.g. 'A', 'AAAA', 'CNAME').\n * @param recursiveResolver - Resolver callback for recursive target domain lookups.\n * @returns Standard ResolutionResult.\n */\nexport async function handleBlockOrRedirect(\n request: Request,\n query: DNSQuery,\n context: Context,\n settings: ProfileSettings,\n action: \"BLOCK\" | \"REDIRECT\",\n reason: string,\n customAnswer?: string,\n responseType?: string,\n recursiveResolver?: RecursiveResolverFn\n): Promise<ResolutionResult> {\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n let answer: Uint8Array;\n let displayAnswer = customAnswer || \"\";\n\n if (action === \"REDIRECT\" && customAnswer) {\n if (\n responseType === \"CNAME\" &&\n (query.type === \"A\" || query.type === \"AAAA\") &&\n recursiveResolver\n ) {\n try {\n // If client requested A/AAAA but rule returns CNAME, resolve the target domain's A/AAAA\n const targetQueryRaw = buildDNSQuery(customAnswer, query.type);\n const targetQuery: DNSQuery = {\n name: customAnswer,\n type: query.type,\n raw: targetQueryRaw\n };\n const upstreamRes = await recursiveResolver(\n request,\n targetQuery,\n context,\n settings,\n \"PASS\"\n );\n\n if (upstreamRes.answer && upstreamRes.answer.length > 0) {\n const parsedTargetAnswers = parseDNSAnswer(upstreamRes.answer);\n const records: DNSRecord[] = [\n { type: \"CNAME\", value: customAnswer, ttl: 60 }\n ];\n\n for (const a of parsedTargetAnswers) {\n if (a.type === query.type || a.type === \"CNAME\") {\n records.push({\n name: a.name,\n type: a.type,\n value: a.data,\n ttl: a.ttl\n });\n }\n }\n answer = buildResponseMulti(query.raw, records, 0);\n } else {\n answer = buildResponse(query.raw, responseType, customAnswer);\n }\n } catch (e) {\n console.error(\"CNAME resolution failed:\", e);\n answer = buildResponse(query.raw, responseType, customAnswer);\n }\n } else {\n answer = buildResponse(\n query.raw,\n responseType || query.type,\n customAnswer\n );\n }\n } else {\n // Handling BLOCK modes\n const mode = settings.block_mode || \"NULL_IP\";\n\n if (mode === \"NXDOMAIN\") {\n answer = buildResponse(query.raw, query.type, \"\", 3600, 3);\n displayAnswer = \"NXDOMAIN\";\n } else if (mode === \"NODATA\") {\n answer = buildResponse(query.raw, query.type, \"\", 3600, 0);\n displayAnswer = \"NODATA\";\n } else if (mode === \"CUSTOM_IP\") {\n const customIp =\n query.type === \"AAAA\"\n ? settings.custom_block_ipv6 || \"::\"\n : settings.custom_block_ipv4 || \"0.0.0.0\";\n answer = buildResponse(query.raw, query.type, customIp);\n displayAnswer = customIp;\n } else {\n const nullIp = query.type === \"AAAA\" ? \"::\" : \"0.0.0.0\";\n answer = buildResponse(query.raw, query.type, nullIp);\n displayAnswer = nullIp;\n }\n }\n\n const latency = Date.now() - context.startTime;\n const cfCountry =\n (request as unknown as { cf?: { country?: string } }).cf?.country ||\n request.headers.get(\"CF-IPCountry\") ||\n \"UN\";\n\n context.ctx.waitUntil(\n (async () => {\n try {\n enqueueLog(\n {\n profile_id: context.profileId,\n access_point_id: context.accessPointId,\n timestamp: Math.floor(Date.now() / 1000),\n client_ip: clientIp,\n geo_country: cfCountry,\n domain: query.name,\n record_type: query.type,\n action,\n reason,\n answer: displayAnswer,\n latency\n },\n settings,\n context.env,\n context.ctx\n );\n } catch {\n // Ignore non-critical background logging errors\n }\n })()\n );\n\n return { answer, ttl: 3600, action, reason, latency };\n}\n", "import { cacheUtils } from \"./cache\";\r\nimport { isPublicInternetIP } from \"./validator\";\r\n\r\nexport interface GeoIP {\r\n country: string;\r\n country_code: string;\r\n city?: string;\r\n isp?: string;\r\n org?: string;\r\n region?: string;\r\n as?: string;\r\n}\r\n\r\n// In-memory cache fallback (expires after 14 days)\r\nconst memoryCache = new Map<string, { data: GeoIP; expiresAt: number }>();\r\nconst MEMORY_CACHE_TTL = 14 * 24 * 60 * 60 * 1000; // 14 days\r\n\r\nexport async function fetchGeoIP(ip: string): Promise<GeoIP | null> {\r\n if (!isPublicInternetIP(ip)) {\r\n return null;\r\n }\r\n\r\n const cacheKey = `geoip:${ip}`;\r\n const now = Date.now();\r\n\r\n // Try In-memory Cache first (fastest, works in all environments)\r\n const memCached = memoryCache.get(cacheKey);\r\n if (memCached) {\r\n if (now < memCached.expiresAt) {\r\n return memCached.data;\r\n } else {\r\n memoryCache.delete(cacheKey); // Expired\r\n }\r\n }\r\n\r\n // Try Cloudflare Cache API (if available and enabled on custom domains)\r\n let cache: any = null;\r\n if (typeof caches !== \"undefined\" && (caches as any).default) {\r\n cache = (caches as any).default;\r\n }\r\n\r\n if (cache) {\r\n try {\r\n const cached = await cacheUtils.get<GeoIP>(cache, cacheKey);\r\n if (cached) {\r\n // Cache in memory for subsequent super-fast lookups\r\n memoryCache.set(cacheKey, { data: cached, expiresAt: now + MEMORY_CACHE_TTL });\r\n return cached;\r\n }\r\n } catch (err) {\r\n console.warn(\"Cloudflare Cache API read error, falling back to memory/network:\", err);\r\n }\r\n }\r\n\r\n const storeInCache = async (geo: GeoIP) => {\r\n if (memoryCache.size >= 100000) {\r\n const firstKey = memoryCache.keys().next().value;\r\n if (firstKey !== undefined) {\r\n memoryCache.delete(firstKey);\r\n }\r\n }\r\n memoryCache.set(cacheKey, { data: geo, expiresAt: now + MEMORY_CACHE_TTL });\r\n\r\n if (cache) {\r\n try {\r\n await cacheUtils.set(cache, cacheKey, geo, 86400 * 14);\r\n } catch (err) {\r\n console.warn(\"Cloudflare Cache API write error:\", err);\r\n }\r\n }\r\n };\r\n\r\n // 1. Primary provider: ip-api.com\r\n try {\r\n const response = await fetch(\r\n `http://ip-api.com/json/${ip}?fields=status,country,countryCode,regionName,city,isp,org,as`,\r\n {\r\n cf: {\r\n cacheTtlByStatus: {\r\n \"200-299\": 86400 * 14, // 14 days\r\n \"400-499\": 5,\r\n \"500-599\": 0,\r\n },\r\n cacheEverything: true,\r\n },\r\n }\r\n );\r\n if (response.ok) {\r\n const data = (await response.json()) as any;\r\n if (data.status === \"success\") {\r\n const geo: GeoIP = {\r\n country: data.country,\r\n country_code: data.countryCode,\r\n region: data.regionName,\r\n city: data.city,\r\n isp: data.isp,\r\n org: data.org,\r\n as: data.as,\r\n };\r\n await storeInCache(geo);\r\n return geo;\r\n }\r\n }\r\n } catch {\r\n // Fall through to fallback provider\r\n }\r\n\r\n // 2. Secondary fallback provider: ipwho.is (HTTPS, no auth required)\r\n try {\r\n const response2 = await fetch(`https://ipwho.is/${ip}`);\r\n if (response2.ok) {\r\n const data2 = (await response2.json()) as any;\r\n if (data2.success) {\r\n const geo: GeoIP = {\r\n country: data2.country,\r\n country_code: data2.country_code,\r\n region: data2.region,\r\n city: data2.city,\r\n isp: data2.connection?.isp,\r\n org: data2.connection?.org,\r\n as: data2.connection?.asn\r\n ? `AS${data2.connection.asn} ${data2.connection?.org || \"\"}`.trim()\r\n : undefined,\r\n };\r\n await storeInCache(geo);\r\n return geo;\r\n }\r\n }\r\n } catch (e2) {\r\n console.error(\"GeoIP Fetch Error:\", e2);\r\n }\r\n\r\n return null;\r\n}\r\n", "import { Context, DNSQuery, ProfileSettings, ResolutionResult } from \"../../types\";\nimport { fetchGeoIP } from \"../../utils/geoip\";\nimport { dnsCache } from \"../cache\";\nimport { enqueueLog } from \"../logBatcher\";\nimport { UpstreamHttpError, ParsedDNSAnswerRecord } from \"./types\";\n\n/**\n * Asynchronously enriches successful DNS resolution logs with GeoIP,\n * flushes the log entry into the log batcher, and caches the result in memory.\n *\n * @param request - Inbound HTTP Request.\n * @param query - The incoming DNS query.\n * @param context - Request context.\n * @param settings - Current profile settings.\n * @param action - Action status ('PASS').\n * @param effectiveReason - Resolution reason note.\n * @param parsedAnswers - Decoded DNS answer records.\n * @param answer - Raw wire-format DNS answer.\n * @param minTTL - Minimum TTL across answers (bounded between 10 and 3600 seconds).\n * @param rawUpstreamUrl - Upstream URL or stamp string.\n * @param ecs - Normalized ECS string if applied.\n */\nexport function recordSuccessAndCache(\n request: Request,\n query: DNSQuery,\n context: Context,\n settings: ProfileSettings,\n action: \"PASS\",\n effectiveReason: string | undefined,\n parsedAnswers: ParsedDNSAnswerRecord[],\n answer: Uint8Array,\n minTTL: number,\n rawUpstreamUrl: string,\n ecs?: string\n): void {\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const cfCountry =\n (request as unknown as { cf?: { country?: string } }).cf?.country ||\n request.headers.get(\"CF-IPCountry\") ||\n \"UN\";\n\n context.ctx.waitUntil(\n (async () => {\n try {\n const firstIp = parsedAnswers.find(\n (a) => a.type === \"A\" || a.type === \"AAAA\"\n )?.data;\n let destGeoJson = \"\";\n let destCountryCode: string | null = null;\n let destCountry: string | null = null;\n let destIsp: string | null = null;\n\n if (firstIp) {\n const geo = await fetchGeoIP(firstIp);\n if (geo) {\n destGeoJson = JSON.stringify(geo);\n destCountryCode = geo.country_code ? geo.country_code.toUpperCase() : null;\n destCountry = geo.country || null;\n destIsp = geo.isp || null;\n }\n }\n\n const latency = Date.now() - context.startTime;\n enqueueLog(\n {\n profile_id: context.profileId,\n access_point_id: context.accessPointId,\n timestamp: Math.floor(Date.now() / 1000),\n client_ip: clientIp,\n geo_country: cfCountry,\n domain: query.name,\n record_type: query.type,\n action,\n reason: effectiveReason,\n answer: parsedAnswers.map((a) => a.data).join(\", \"),\n dest_geoip: destGeoJson,\n dest_country_code: destCountryCode,\n dest_country: destCountry,\n dest_isp: destIsp,\n upstream: rawUpstreamUrl,\n latency,\n ecs\n },\n settings,\n context.env,\n context.ctx\n );\n\n if (answer.length > 0) {\n dnsCache.set(`${context.profileId}:${query.name}:${query.type}`, {\n answer,\n ttl: minTTL,\n action,\n reason: effectiveReason,\n expiresAt: Date.now() + minTTL * 1000\n });\n }\n } catch {\n // Ignore non-critical background caching errors\n }\n })()\n );\n}\n\n/**\n * Handles upstream resolution failures, records diagnostic failure logs,\n * and formats the failure ResolutionResult.\n *\n * @param request - Inbound HTTP Request.\n * @param query - The incoming DNS query.\n * @param context - Request context.\n * @param settings - Current profile settings.\n * @param rawUpstreamUrl - Configured upstream URL or stamp.\n * @param error - The error caught during resolution.\n * @param ecs - Normalized ECS string if applied.\n * @param diagTarget - Diagnostic target address.\n * @param diagMethod - Diagnostic protocol method.\n * @returns ResolutionResult populated with FAIL status and diagnostic metadata.\n */\nexport function recordFailure(\n request: Request,\n query: DNSQuery,\n context: Context,\n settings: ProfileSettings,\n rawUpstreamUrl: string,\n error: unknown,\n ecs?: string,\n diagTarget?: string,\n diagMethod?: string\n): ResolutionResult {\n let status = 0;\n let statusText: string | undefined;\n let errorDetail =\n error instanceof Error ? error.message : String(error);\n let responseBodySnippet: string | undefined;\n let cfRay: string | undefined;\n\n if (error instanceof UpstreamHttpError) {\n status = error.status;\n statusText = error.statusText || undefined;\n responseBodySnippet = error.responseBodySnippet || undefined;\n cfRay = error.cfRay;\n } else if (\n error &&\n typeof error === \"object\" &&\n \"cause\" in error &&\n error.cause\n ) {\n const cause = error.cause;\n const causeMsg =\n typeof cause === \"object\" && cause && \"message\" in cause\n ? String((cause as { message: unknown }).message)\n : String(cause);\n errorDetail += ` (Cause: ${causeMsg})`;\n }\n\n const failReason =\n status > 0\n ? `Upstream HTTP ${status}${statusText ? ` ${statusText}` : \"\"}`\n : `Upstream Error: ${errorDetail}`;\n\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const cfCountry =\n (request as unknown as { cf?: { country?: string } }).cf?.country ||\n request.headers.get(\"CF-IPCountry\") ||\n \"UN\";\n\n // Asynchronously persist failure log for upstream troubleshooting\n context.ctx.waitUntil(\n (async () => {\n try {\n enqueueLog(\n {\n profile_id: context.profileId,\n access_point_id: context.accessPointId,\n timestamp: Math.floor(Date.now() / 1000),\n client_ip: clientIp,\n geo_country: cfCountry,\n domain: query.name,\n record_type: query.type,\n action: \"FAIL\",\n reason: failReason,\n answer: \"\",\n dest_geoip: \"\",\n dest_country_code: null,\n dest_country: null,\n dest_isp: null,\n upstream: rawUpstreamUrl,\n latency: Date.now() - context.startTime,\n ecs\n },\n settings,\n context.env,\n context.ctx\n );\n } catch {\n // Ignore background logging failures\n }\n })()\n );\n\n const effectiveDiagTarget = diagTarget || rawUpstreamUrl;\n const effectiveDiagMethod = diagMethod || \"POST\";\n\n return {\n answer: new Uint8Array(),\n ttl: 0,\n action: \"FAIL\",\n reason: failReason,\n latency: Date.now() - context.startTime,\n diagnostics: {\n upstream_url: rawUpstreamUrl.startsWith(\"sdns://\")\n ? `${rawUpstreamUrl} (${effectiveDiagTarget})`\n : effectiveDiagTarget,\n method: effectiveDiagMethod,\n status,\n status_text: statusText,\n error_detail: errorDetail,\n response_body: responseBodySnippet,\n cf_ray: cfRay\n }\n };\n}\n", "import { Context, DNSQuery, ProfileSettings, ResolutionResult } from \"../../types\";\nimport { parseDNSAnswer } from \"../../utils/dns\";\nimport { isSafeUrl } from \"../../utils/validator\";\nimport {\n fetchFromUpstream,\n resolveUpstreamEndpoint,\n UpstreamEndpointInfo\n} from \"./transport\";\nimport { applyEcs } from \"./ecs\";\nimport { processBestEffortEch } from \"./ech\";\nimport { handleBlockOrRedirect } from \"./block\";\nimport { recordSuccessAndCache, recordFailure } from \"./observability\";\n\nexport * from \"./types\";\nexport * from \"./transport\";\nexport * from \"./ecs\";\nexport * from \"./ech\";\nexport * from \"./block\";\nexport * from \"./observability\";\n\n/**\n * High-level DNS resolution coordinator for the ObexDNS pipeline.\n *\n * Orchestrates:\n * - Upstream URL safety validation\n * - RFC 7871 EDNS Client Subnet (ECS) application\n * - Multi-protocol upstream transmission (DoH, DoT, TCP, DNS Stamps)\n * - Best-effort Cloudflare ECH injection\n * - Success/Failure asynchronous observability and memory caching\n * - Synthesis of Block and Redirect responses\n */\nexport const pipelineResolver = {\n /**\n * Resolves a DNS query against the configured upstream server.\n *\n * @param request - Inbound HTTP Request.\n * @param query - Parsed incoming DNS query.\n * @param context - Request context containing DB, timings, and credentials.\n * @param settings - Current profile settings.\n * @param action - Action status ('PASS').\n * @param reason - Optional rule explanation.\n * @returns ResolutionResult containing answer wire bytes, TTL, timings, and diagnostics.\n */\n async resolve(\n request: Request,\n query: DNSQuery,\n context: Context,\n settings: ProfileSettings,\n action: \"PASS\",\n reason?: string\n ): Promise<ResolutionResult> {\n const rawUpstreamUrl =\n settings.upstream[0] || \"https://security.cloudflare-dns.com/dns-query\";\n\n if (!isSafeUrl(rawUpstreamUrl)) {\n return {\n answer: new Uint8Array(),\n ttl: 0,\n action: \"FAIL\",\n reason: \"Unsafe upstream URL\",\n diagnostics: {\n upstream_url: rawUpstreamUrl,\n method: \"BLOCKED\",\n status: 0\n },\n latency: Date.now() - context.startTime\n };\n }\n\n const { queryRaw, ecs } = applyEcs(request, query, settings);\n\n let endpoint: UpstreamEndpointInfo | undefined;\n try {\n endpoint = resolveUpstreamEndpoint(rawUpstreamUrl);\n const transportRes = await fetchFromUpstream(endpoint, queryRaw);\n\n const initialParsedAnswers = parseDNSAnswer(transportRes.answer);\n\n const echRes = await processBestEffortEch(\n query,\n initialParsedAnswers,\n transportRes.answer,\n context,\n settings,\n request,\n pipelineResolver.resolve,\n reason\n );\n\n const answer = echRes.answer;\n const parsedAnswers = echRes.parsedAnswers;\n const effectiveReason = echRes.effectiveReason;\n\n const minTTL =\n parsedAnswers.length > 0\n ? Math.max(10, Math.min(...parsedAnswers.map((a) => a.ttl)))\n : 60;\n\n recordSuccessAndCache(\n request,\n query,\n context,\n settings,\n action,\n effectiveReason,\n parsedAnswers,\n answer,\n minTTL,\n rawUpstreamUrl,\n ecs\n );\n\n return {\n answer,\n ttl: minTTL,\n action,\n reason: effectiveReason,\n latency: Date.now() - context.startTime,\n timings: { upstream_fetch: transportRes.latency },\n diagnostics: {\n upstream_url: rawUpstreamUrl.startsWith(\"sdns://\")\n ? `${rawUpstreamUrl} (${transportRes.diagTarget})`\n : transportRes.diagTarget,\n method: transportRes.diagMethod,\n status: 200,\n status_text: \"OK\"\n }\n };\n } catch (e: unknown) {\n return recordFailure(\n request,\n query,\n context,\n settings,\n rawUpstreamUrl,\n e,\n ecs,\n endpoint?.diagTarget,\n endpoint?.diagMethod\n );\n }\n },\n\n /**\n * Synthesizes DNS responses for blocked or redirected queries.\n *\n * @param request - Inbound HTTP Request.\n * @param query - The incoming DNS query.\n * @param context - Request context.\n * @param settings - Current profile settings.\n * @param action - Action status ('BLOCK' | 'REDIRECT').\n * @param reason - Explanation for the action.\n * @param customAnswer - Optional custom answer payload (IP or CNAME domain).\n * @param responseType - Optional DNS record type for the answer.\n * @returns ResolutionResult.\n */\n async block(\n request: Request,\n query: DNSQuery,\n context: Context,\n settings: ProfileSettings,\n action: \"BLOCK\" | \"REDIRECT\",\n reason: string,\n customAnswer?: string,\n responseType?: string\n ): Promise<ResolutionResult> {\n return handleBlockOrRedirect(\n request,\n query,\n context,\n settings,\n action,\n reason,\n customAnswer,\n responseType,\n pipelineResolver.resolve\n );\n }\n};\n", "import { Rule } from \"../types\";\r\n\r\nexport class DNSFilter {\r\n /**\r\n * \u68C0\u67E5\u57DF\u540D\u662F\u5426\u5339\u914D\u7279\u5B9A\u6A21\u5F0F (\u652F\u6301\u5B50\u57DF\u540D\u5339\u914D)\r\n * \u6A21\u5F0F example.com \u5C06\u5339\u914D example.com \u548C *.example.com\r\n */\r\n static isMatch(domain: string, pattern: string): boolean {\r\n const d = domain.toLowerCase();\r\n const p = pattern.toLowerCase();\r\n return d === p || d.endsWith(`.${p}`);\r\n }\r\n\r\n /**\r\n * \u5728\u7ED9\u5B9A\u89C4\u5219\u96C6\u4E2D\u5BFB\u627E\u5339\u914D\u9879\r\n * \u8FD4\u56DE\u5339\u914D\u7684\u89C4\u5219\uFF0C\u5982\u679C\u672A\u627E\u5230\u5219\u8FD4\u56DE null\r\n */\r\n static findMatch(domain: string, rules: Rule[]): Rule | null {\r\n // \u89C4\u5219\u5DF2\u5728\u6570\u636E\u5E93\u67E5\u8BE2\u4E2D\u6309 priority \u6392\u5E8F\r\n for (const rule of rules) {\r\n if (this.isMatch(domain, rule.pattern)) {\r\n return rule;\r\n }\r\n }\r\n return null;\r\n }\r\n}\r\n", "import { Context, DNSQuery, ResolutionResult, ProfileSettings, Rule } from \"../types\";\r\nimport { DNSFilter } from \"../lib/filtering\";\r\nimport { BloomFilter } from \"../utils/bloom\";\r\nimport { cacheUtils } from \"../utils/cache\";\r\nimport { buildResponse } from \"../utils/dns\";\r\nimport { pipelineResolver } from \"./resolver\";\r\n\r\nexport const pipelineFilter = {\r\n async match(\r\n request: Request,\r\n query: DNSQuery,\r\n context: Context,\r\n settings: ProfileSettings,\r\n rules: Rule[],\r\n bloom: BloomFilter | undefined,\r\n track: (name: string) => void\r\n ): Promise<ResolutionResult | null> {\r\n const domainLower = query.name.toLowerCase();\r\n\r\n // \u672C\u5730\u767D\u540D\u5355\r\n const whitelist = rules.filter(r => r.type === 'ALLOW');\r\n if (DNSFilter.findMatch(query.name, whitelist)) {\r\n track('local_rules');\r\n return pipelineResolver.resolve(request, query, context, settings, \"PASS\", \"Whitelist\");\r\n }\r\n\r\n // \u672C\u5730\u91CD\u5B9A\u5411\r\n const redirections = rules.filter(r => r.type === 'REDIRECT');\r\n const redirectRule = DNSFilter.findMatch(query.name, redirections);\r\n if (redirectRule) {\r\n let val: string | undefined;\r\n let respType = query.type;\r\n\r\n if (query.type === 'A') {\r\n val = redirectRule.v_a;\r\n if (!val && redirectRule.v_cname) {\r\n val = redirectRule.v_cname;\r\n respType = 'CNAME';\r\n }\r\n } else if (query.type === 'AAAA') {\r\n val = redirectRule.v_aaaa;\r\n if (!val && redirectRule.v_cname) {\r\n val = redirectRule.v_cname;\r\n respType = 'CNAME';\r\n }\r\n } else if (query.type === 'TXT') {\r\n val = redirectRule.v_txt;\r\n } else if (query.type === 'CNAME') {\r\n val = redirectRule.v_cname;\r\n }\r\n\r\n if (val) {\r\n track('local_rules');\r\n const result = await pipelineResolver.block(request, query, context, settings, \"REDIRECT\", `Rule: ${redirectRule.pattern}`, val, respType);\r\n return result;\r\n }\r\n }\r\n\r\n // \u672C\u5730\u9ED1\u540D\u5355\r\n const blacklist = rules.filter(r => r.type === 'BLOCK');\r\n const blockRule = DNSFilter.findMatch(query.name, blacklist);\r\n if (blockRule) {\r\n track('local_rules');\r\n return pipelineResolver.block(request, query, context, settings, \"BLOCK\", `Blacklist: ${blockRule.pattern}`);\r\n }\r\n track('local_rules');\r\n\r\n // \u5916\u90E8\u5217\u8868\u8FC7\u6EE4 (\u7CBE\u786E\u5339\u914D + \u5B50\u57DF\u540D\u5339\u914D)\r\n if (bloom) {\r\n let currentDomain = domainLower;\r\n while (currentDomain) {\r\n if (bloom.test(currentDomain)) {\r\n track('bloom_check');\r\n return pipelineResolver.block(request, query, context, settings, \"BLOCK\", `External List: ${currentDomain}`);\r\n }\r\n const firstDot = currentDomain.indexOf('.');\r\n if (firstDot === -1) break;\r\n currentDomain = currentDomain.substring(firstDot + 1);\r\n }\r\n track('bloom_check');\r\n }\r\n\r\n return null;\r\n }\r\n};\r\n", "import { Context, DNSQuery, ResolutionResult } from \"../types\";\r\nimport { buildResponse } from \"../utils/dns\";\r\nimport { dnsCache, pipelineCache } from \"./cache\";\r\nimport { pipelineConfig } from \"./config\";\r\nimport { pipelineResolver } from \"./resolver\";\r\nimport { pipelineFilter } from \"./filter\";\r\n\r\nexport const pipeline = {\r\n clearCache: pipelineCache.clear,\r\n\r\n async process(request: Request, query: DNSQuery, context: Context): Promise<ResolutionResult> {\r\n const timings: Record<string, number> = {};\r\n let mark = context.startTime;\r\n const track = (name: string) => {\r\n const now = Date.now();\r\n timings[name] = (timings[name] || 0) + (now - mark);\r\n mark = now;\r\n };\r\n\r\n // \u7279\u6B8A\u57DF\u540D & \u7F13\u5B58\u68C0\u67E5 (L1)\r\n if (query.name.toLowerCase() === 'obex' && query.type === 'TXT') {\r\n const answer = buildResponse(query.raw, 'TXT', context.profileId || 'obex');\r\n return { answer, ttl: 60, action: \"PASS\", reason: \"Internal Verification\", latency: Date.now() - context.startTime, timings: { total: Date.now() - context.startTime } };\r\n }\r\n\r\n const cached = dnsCache.get(`${context.profileId}:${query.name}:${query.type}`);\r\n if (cached && Date.now() < cached.expiresAt && cached.answer instanceof Uint8Array) {\r\n // \u8FD9\u91CC\u7684 cached.answer \u5DF2\u7ECF\u662F\u5B8C\u6574\u7684 DNS \u62A5\u6587\r\n // \u5173\u952E\uFF1ADNS ID \u5FC5\u987B\u5339\u914D\u539F\u59CB\u67E5\u8BE2\u3002ID \u4F4D\u4E8E\u62A5\u6587\u7684\u524D 2 \u5B57\u8282\u3002\r\n const response = new Uint8Array(cached.answer);\r\n if (response.length >= 2 && query.raw.length >= 2) {\r\n response[0] = query.raw[0];\r\n response[1] = query.raw[1];\r\n }\r\n\r\n const latency = Date.now() - context.startTime;\r\n return {\r\n answer: response,\r\n ttl: Math.ceil((cached.expiresAt - Date.now()) / 1000),\r\n action: cached.action,\r\n reason: cached.reason,\r\n latency,\r\n timings: { dns_cache_mem: latency }\r\n };\r\n }\r\n\r\n // \u52A0\u8F7D\u914D\u7F6E\r\n const config = await pipelineConfig.load(context, track);\r\n if (!config) return { answer: new Uint8Array(), ttl: 0, action: \"FAIL\", reason: \"Profile Not Found\" };\r\n\r\n // \u5339\u914D\u89C4\u5219 (\u672C\u5730 + \u5916\u90E8)\r\n const matchResult = await pipelineFilter.match(request, query, context, config.settings, config.rules, config.bloom, track);\r\n if (matchResult) return { ...matchResult, timings: { ...timings, ...matchResult.timings } };\r\n\r\n // \u9ED8\u8BA4\u7B56\u7565 & \u6700\u7EC8\u89E3\u6790\r\n if (config.settings.default_policy === 'BLOCK') {\r\n const block = await pipelineResolver.block(request, query, context, config.settings, \"BLOCK\", \"Default Policy\");\r\n return { ...block, timings: { ...timings, ...block.timings } };\r\n }\r\n\r\n const final = await pipelineResolver.resolve(request, query, context, config.settings, \"PASS\");\r\n return { ...final, timings: { ...timings, ...final.timings } };\r\n }\r\n};\r\n", "/**\n * Resolves the highest configured KEK version (e.g. \"v1\", \"v2\", \"v101\") from environment variables.\n * Scans all environment keys matching KEK_v<N> or KEK_V<N> dynamically without hardcoded version caps.\n * Returns null if no KEK is configured, disabling envelope encryption.\n *\n * @param env - Cloudflare Workers environment bindings or Node.js process.env object\n * @returns Active KEK version string (e.g. \"v1\") or null if no KEK configured\n */\nexport function getActiveKekVersion(env: any): string | null {\n if (!env || typeof env !== \"object\") return null;\n let maxVer = 0;\n for (const key of Object.keys(env)) {\n const match = key.match(/^KEK_[vV](\\d+)$/);\n if (match) {\n const val = env[key];\n if (val !== undefined && val !== null && val !== \"\") {\n const ver = parseInt(match[1], 10);\n if (!isNaN(ver) && ver > maxVer) {\n maxVer = ver;\n }\n }\n }\n }\n if (maxVer > 0) return `v${maxVer}`;\n return null;\n}\n\n/**\n * Retrieves the raw KEK secret string for a given version.\n * Note: If version is \"v0\" or \"default\" and no explicit KEK_v0 is set, falls back to env.JWT_SECRET\n * for backward compatibility with legacy deployments that encrypted credentials using JWT_SECRET.\n *\n * @param version - KEK version string (e.g. \"v1\", \"v0\")\n * @param env - Cloudflare Workers environment bindings or Node.js process.env object\n * @returns Raw KEK secret string or null if not found\n */\nexport function getKekSecret(version: string, env: any): string | null {\n if (!env) return null;\n const val = env[`KEK_${version}`] || env[`KEK_${version.toUpperCase()}`];\n if (val && typeof val === \"string\" && val.trim() !== \"\") {\n return val;\n }\n // Backward compatibility: fallback to JWT_SECRET for legacy v0/default data\n if ((version === \"v0\" || version === \"default\") && env.JWT_SECRET && typeof env.JWT_SECRET === \"string\" && env.JWT_SECRET.trim() !== \"\") {\n return env.JWT_SECRET;\n }\n return null;\n}\n\nfunction toBase64(bytes: Uint8Array): string {\n let binary = \"\";\n const len = bytes.byteLength;\n for (let i = 0; i < len; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\nfunction fromBase64(base64: string): Uint8Array {\n const binaryString = atob(base64);\n const len = binaryString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\n\nasync function importKek(secret: string): Promise<CryptoKey> {\n const encoder = new TextEncoder();\n const secretData = encoder.encode(secret.trim());\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", secretData);\n return await crypto.subtle.importKey(\n \"raw\",\n hashBuffer,\n { name: \"AES-GCM\" },\n false,\n [\"encrypt\", \"decrypt\"]\n );\n}\n\nasync function importDek(dekBytes: Uint8Array): Promise<CryptoKey> {\n return await crypto.subtle.importKey(\n \"raw\",\n dekBytes,\n { name: \"AES-GCM\" },\n false,\n [\"encrypt\", \"decrypt\"]\n );\n}\n\nexport interface EnvelopeEncryptedData {\n ciphertext: string;\n iv: string;\n}\n\nexport interface EnvelopeEncryptedDek {\n ciphertext: string;\n iv: string;\n kek_version: string;\n}\n\n/**\n * Encrypts a plaintext string using Envelope Encryption.\n * @returns Encrypted data and encrypted DEK strings (JSON serialized)\n */\nexport async function encryptEnvelope(\n plainText: string,\n env: any\n): Promise<{ dataEncrypted: string; dekEncrypted: string } | null> {\n const activeVersion = getActiveKekVersion(env);\n if (!activeVersion) {\n // If no KEK is present in env, skip encryption\n return null;\n }\n\n const kekSecret = getKekSecret(activeVersion, env);\n if (!kekSecret || typeof kekSecret !== \"string\" || kekSecret.trim() === \"\") {\n throw new Error(`KEK version ${activeVersion} is missing or invalid in environment variables`);\n }\n\n const kekKey = await importKek(kekSecret);\n\n // 1. Generate plain Data Encryption Key (DEK) - 256 bits (32 bytes)\n const dekBytes = new Uint8Array(32);\n crypto.getRandomValues(dekBytes);\n const dekKey = await importDek(dekBytes);\n\n // 2. Encrypt plaintext data using the DEK via AES-256-GCM\n const dataIv = new Uint8Array(12);\n crypto.getRandomValues(dataIv);\n const encoder = new TextEncoder();\n const dataBuffer = encoder.encode(plainText);\n const encryptedDataBuffer = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv: dataIv },\n dekKey,\n dataBuffer\n );\n\n const dataEncrypted: EnvelopeEncryptedData = {\n ciphertext: toBase64(new Uint8Array(encryptedDataBuffer)),\n iv: toBase64(dataIv)\n };\n\n // 3. Encrypt the DEK using the KEK via AES-256-GCM\n const dekIv = new Uint8Array(12);\n crypto.getRandomValues(dekIv);\n const encryptedDekBuffer = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv: dekIv },\n kekKey,\n dekBytes\n );\n\n const dekEncrypted: EnvelopeEncryptedDek = {\n ciphertext: toBase64(new Uint8Array(encryptedDekBuffer)),\n iv: toBase64(dekIv),\n kek_version: activeVersion\n };\n\n return {\n dataEncrypted: JSON.stringify(dataEncrypted),\n dekEncrypted: JSON.stringify(dekEncrypted)\n };\n}\n\n/**\n * Decrypts envelope encrypted data using the stored DEK and KEK.\n * If decryption fails using the specified KEK version (e.g. missing KEK or rotated environment),\n * it falls back to trying KEK \"v0\" (derived from JWT_SECRET) to preserve backward compatibility for legacy deployments.\n */\nexport async function decryptEnvelope(\n dataEncryptedStr: string,\n dekEncryptedStr: string,\n env: any\n): Promise<string> {\n let dataEncrypted: EnvelopeEncryptedData;\n let dekEncrypted: EnvelopeEncryptedDek;\n try {\n dataEncrypted = JSON.parse(dataEncryptedStr);\n dekEncrypted = JSON.parse(dekEncryptedStr);\n } catch (parseErr) {\n throw new Error(`Failed to parse envelope payload: ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`);\n }\n\n const attemptDecrypt = async (kekVersion: string): Promise<string> => {\n const kekSecret = getKekSecret(kekVersion, env);\n if (!kekSecret || typeof kekSecret !== \"string\" || kekSecret.trim() === \"\") {\n throw new Error(`Required KEK version ${kekVersion} is missing or invalid in environment variables`);\n }\n\n const kekKey = await importKek(kekSecret);\n\n // 1. Decrypt the DEK using the KEK\n const encryptedDekBytes = fromBase64(dekEncrypted.ciphertext);\n const dekIv = fromBase64(dekEncrypted.iv);\n const decryptedDekBuffer = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: dekIv },\n kekKey,\n encryptedDekBytes\n );\n\n const dekBytes = new Uint8Array(decryptedDekBuffer);\n const dekKey = await importDek(dekBytes);\n\n // 2. Decrypt the data using the decrypted DEK\n const encryptedDataBytes = fromBase64(dataEncrypted.ciphertext);\n const dataIv = fromBase64(dataEncrypted.iv);\n const decryptedDataBuffer = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: dataIv },\n dekKey,\n encryptedDataBytes\n );\n\n const decoder = new TextDecoder();\n return decoder.decode(decryptedDataBuffer);\n };\n\n try {\n return await attemptDecrypt(dekEncrypted.kek_version);\n } catch (primaryErr) {\n // If decryption failed or KEK was missing, and the failed version wasn't already \"v0\",\n // fallback to \"v0\" (JWT_SECRET) for legacy compatibility.\n if (dekEncrypted.kek_version !== \"v0\" && env?.JWT_SECRET && typeof env.JWT_SECRET === \"string\" && env.JWT_SECRET.trim() !== \"\") {\n try {\n return await attemptDecrypt(\"v0\");\n } catch (fallbackErr) {\n // Both primary and fallback failed, throw primary error\n throw primaryErr;\n }\n }\n throw primaryErr;\n }\n}\n\n/**\n * Performs on-the-fly key rotation of the DEK if a newer version of KEK is available.\n * Rotation logic is strictly sequential: N -> N+1.\n * @returns The new encrypted DEK string (JSON serialized), or null if no rotation occurred.\n */\nexport async function rotateEnvelopeDek(\n dekEncryptedStr: string,\n env: any\n): Promise<string | null> {\n if (!env) return null;\n\n let dekEncrypted: EnvelopeEncryptedDek;\n try {\n dekEncrypted = JSON.parse(dekEncryptedStr);\n } catch {\n return null;\n }\n\n const currentKekVersion = dekEncrypted.kek_version; // e.g. \"v0\" or \"v1\"\n const currentVersionNumber = parseInt(currentKekVersion.replace(/^v/, \"\"), 10);\n if (isNaN(currentVersionNumber)) {\n return null;\n }\n\n const nextVersionNumber = currentVersionNumber + 1;\n const nextKekVersion = `v${nextVersionNumber}`;\n const nextKekSecret = getKekSecret(nextKekVersion, env);\n\n if (!nextKekSecret || typeof nextKekSecret !== \"string\" || nextKekSecret.trim() === \"\") {\n // Next version not configured/available yet\n return null;\n }\n\n // 1. Decrypt the DEK with current KEK\n const currentKekSecret = getKekSecret(currentKekVersion, env);\n if (!currentKekSecret || typeof currentKekSecret !== \"string\" || currentKekSecret.trim() === \"\") {\n throw new Error(`Current KEK version ${currentKekVersion} required for rotation is missing or invalid in environment`);\n }\n\n const currentKekKey = await importKek(currentKekSecret);\n const encryptedDekBytes = fromBase64(dekEncrypted.ciphertext);\n const dekIv = fromBase64(dekEncrypted.iv);\n const decryptedDekBuffer = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: dekIv },\n currentKekKey,\n encryptedDekBytes\n );\n\n const dekBytes = new Uint8Array(decryptedDekBuffer);\n\n // 2. Encrypt the DEK with the next KEK (KEK_v(N+1))\n const nextKekKey = await importKek(nextKekSecret);\n const newDekIv = new Uint8Array(12);\n crypto.getRandomValues(newDekIv);\n const newEncryptedDekBuffer = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv: newDekIv },\n nextKekKey,\n dekBytes\n );\n\n const updatedDekEncrypted: EnvelopeEncryptedDek = {\n ciphertext: toBase64(new Uint8Array(newEncryptedDekBuffer)),\n iv: toBase64(newDekIv),\n kek_version: nextKekVersion\n };\n\n return JSON.stringify(updatedDekEncrypted);\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { UserRecord } from \"./types\";\nimport { encryptEnvelope, decryptEnvelope, rotateEnvelopeDek } from \"../../utils/envelope\";\n\n/**\n * Handles envelope encryption, decryption, transparent migration from legacy plaintext,\n * and automatic DEK rotation for sensitive user secrets (TOTP secret & recovery keys).\n *\n * Single Responsibility: Cryptographic envelope vault management for user credentials.\n */\nexport class UserSecretProcessor {\n constructor(private db: D1Database, private env?: unknown) {}\n\n /**\n * Transparently processes user secret fields:\n * 1. Decrypts envelope-encrypted TOTP secret and recovery keys.\n * 2. Automatically rotates DEK if a newer KEK version is active in the environment.\n * 3. Migrates legacy plaintext secrets to envelope encryption on read.\n * 4. Persists any rotated or migrated secrets back to D1.\n *\n * @param user Raw user record fetched from D1.\n * @returns Processed user record with decrypted secret fields, or null if input was null.\n */\n async processUserSecrets(user: UserRecord | null): Promise<UserRecord | null> {\n if (!user) return null;\n\n const updatedFields: Record<string, unknown> = {};\n let shouldUpdateDb = false;\n\n // 1. Process totp_secret\n let totpSecret = user.totp_secret;\n if (user.totp_secret_encrypted && user.totp_secret_dek) {\n try {\n totpSecret = await decryptEnvelope(user.totp_secret_encrypted, user.totp_secret_dek, this.env);\n\n // Check for DEK rotation (vN -> vN+1)\n const rotatedDek = await rotateEnvelopeDek(user.totp_secret_dek, this.env);\n if (rotatedDek) {\n updatedFields.totp_secret_dek = rotatedDek;\n shouldUpdateDb = true;\n }\n } catch (e: unknown) {\n const err = e instanceof Error ? e.stack || e.message : String(e);\n console.error(`[Envelope Encryption] Failed to decrypt/rotate totp_secret for user ${user.id}:`, err);\n }\n } else if (user.totp_secret) {\n // Legacy plain-text secret found. If KEK is active, migrate to envelope encryption!\n try {\n const encrypted = await encryptEnvelope(user.totp_secret, this.env);\n if (encrypted) {\n updatedFields.totp_secret_encrypted = encrypted.dataEncrypted;\n updatedFields.totp_secret_dek = encrypted.dekEncrypted;\n updatedFields.totp_secret = null; // Clear plain-text column\n shouldUpdateDb = true;\n }\n } catch (e: unknown) {\n console.error(`[Envelope Encryption] Migration failed for totp_secret of user ${user.id}:`, e);\n }\n }\n\n // 2. Process totp_recovery_keys\n let totpRecoveryKeys = user.totp_recovery_keys;\n if (user.totp_recovery_keys_encrypted && user.totp_recovery_keys_dek) {\n try {\n totpRecoveryKeys = await decryptEnvelope(user.totp_recovery_keys_encrypted, user.totp_recovery_keys_dek, this.env);\n\n // Check for DEK rotation (vN -> vN+1)\n const rotatedDek = await rotateEnvelopeDek(user.totp_recovery_keys_dek, this.env);\n if (rotatedDek) {\n updatedFields.totp_recovery_keys_dek = rotatedDek;\n shouldUpdateDb = true;\n }\n } catch (e: unknown) {\n const err = e instanceof Error ? e.stack || e.message : String(e);\n console.error(`[Envelope Encryption] Failed to decrypt/rotate totp_recovery_keys for user ${user.id}:`, err);\n }\n } else if (user.totp_recovery_keys) {\n // Legacy plain-text recovery keys found. If KEK is active, migrate to envelope encryption!\n try {\n const encrypted = await encryptEnvelope(user.totp_recovery_keys, this.env);\n if (encrypted) {\n updatedFields.totp_recovery_keys_encrypted = encrypted.dataEncrypted;\n updatedFields.totp_recovery_keys_dek = encrypted.dekEncrypted;\n updatedFields.totp_recovery_keys = null; // Clear plain-text column\n shouldUpdateDb = true;\n }\n } catch (e: unknown) {\n console.error(`[Envelope Encryption] Migration failed for totp_recovery_keys of user ${user.id}:`, e);\n }\n }\n\n // Update D1 database if migration or rotation occurred\n if (shouldUpdateDb) {\n try {\n const updateParts: string[] = [];\n const bindings: unknown[] = [];\n for (const [key, val] of Object.entries(updatedFields)) {\n updateParts.push(`${key} = ?`);\n bindings.push(val);\n }\n bindings.push(user.id);\n\n await this.db\n .prepare(`UPDATE users SET ${updateParts.join(\", \")} WHERE id = ?`)\n .bind(...bindings)\n .run();\n\n // Merge the newly updated/encrypted values to the returned user object\n Object.assign(user, updatedFields);\n } catch (e: unknown) {\n console.error(`[Envelope Encryption] Failed to save rotated/migrated secrets for user ${user.id}:`, e);\n }\n }\n\n // Return the user object with decrypted totp_secret and totp_recovery_keys\n return {\n ...user,\n totp_secret: totpSecret,\n totp_recovery_keys: totpRecoveryKeys\n };\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { User } from \"../../types\";\nimport { UserRecord, CreateUserOptions } from \"./types\";\nimport { UserSecretProcessor } from \"./secrets\";\n\n/**\n * Handles core user entity lifecycle, querying, profile attributes, and CRUD operations.\n *\n * Single Responsibility: User identity entity persistence and profile metadata management.\n */\nexport class UserCoreModel {\n constructor(\n private db: D1Database,\n private secretProcessor: UserSecretProcessor\n ) {}\n\n /**\n * Retrieves a user by their unique identifier.\n * Transparently decrypts/migrates envelope-encrypted secrets if present.\n *\n * @param id The user ID.\n * @returns The user record with decrypted secrets, or null if not found.\n */\n async getById(id: string): Promise<UserRecord | null> {\n const user = await this.db.prepare(\"SELECT * FROM users WHERE id = ?\").bind(id).first<UserRecord>();\n return await this.secretProcessor.processUserSecrets(user);\n }\n\n /**\n * Retrieves a user by their username.\n * Transparently decrypts/migrates envelope-encrypted secrets if present.\n *\n * @param username The username.\n * @returns The user record with decrypted secrets, or null if not found or table doesn't exist.\n */\n async getByUsername(username: string): Promise<UserRecord | null> {\n try {\n const user = await this.db.prepare(\"SELECT * FROM users WHERE username = ?\").bind(username).first<UserRecord>();\n return await this.secretProcessor.processUserSecrets(user);\n } catch (e: unknown) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n if (errorMsg.includes(\"no such table\")) {\n return null;\n }\n throw e;\n }\n }\n\n /**\n * Lists all users in the system, ordered by creation date descending.\n *\n * @returns Array of user summary entities.\n */\n async listAll(): Promise<User[]> {\n const { results } = await this.db.prepare(`\n SELECT \n u.id, \n u.username, \n u.role, \n u.created_at, \n u.totp_enabled,\n (SELECT COUNT(1) FROM passkeys p WHERE p.user_id = u.id) as passkeys_count,\n u.timezone,\n u.locale,\n u.last_active_at as last_resolve_at\n FROM users u\n ORDER BY u.created_at DESC\n `).all<User>();\n return results;\n }\n\n /**\n * Registers a new user in the database.\n *\n * @param user Options containing ID, username, password hash, role, timezone, etc.\n * @returns True if insertion succeeded.\n */\n async create(user: CreateUserOptions): Promise<boolean> {\n const now = Math.floor(Date.now() / 1000);\n const result = await this.db.prepare(\n \"INSERT INTO users (id, username, hashed_password, role, created_at, timezone, locale, password_version) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\"\n ).bind(\n user.id,\n user.username,\n user.passwordHash,\n user.role,\n now,\n user.timezone || null,\n user.locale || 'en-US',\n user.passwordVersion ?? 2\n ).run();\n return result.success;\n }\n\n /**\n * Updates the username of a specified user.\n *\n * @param id The user ID.\n * @param username The new username.\n * @returns True if update succeeded.\n */\n async updateUsername(id: string, username: string): Promise<boolean> {\n const result = await this.db.prepare(\"UPDATE users SET username = ? WHERE id = ?\").bind(username, id).run();\n return result.success;\n }\n\n /**\n * Updates the preferred timezone of a user.\n *\n * @param id The user ID.\n * @param timezone The timezone string (e.g. 'Asia/Shanghai') or null.\n * @returns True if update succeeded.\n */\n async updateTimezone(id: string, timezone: string | null): Promise<boolean> {\n const result = await this.db.prepare(\"UPDATE users SET timezone = ? WHERE id = ?\").bind(timezone, id).run();\n return result.success;\n }\n\n /**\n * Updates the preferred UI locale of a user.\n *\n * @param id The user ID.\n * @param locale The locale code (e.g. 'zh-CN', 'en-US') or null.\n * @returns True if update succeeded.\n */\n async updateLocale(id: string, locale: string | null): Promise<boolean> {\n const result = await this.db.prepare(\"UPDATE users SET locale = ? WHERE id = ?\").bind(locale || 'en-US', id).run();\n return result.success;\n }\n\n /**\n * Deletes a user account by their ID.\n *\n * @param id The user ID.\n * @returns True if deletion succeeded.\n */\n async delete(id: string): Promise<boolean> {\n const result = await this.db.prepare(\"DELETE FROM users WHERE id = ?\").bind(id).run();\n return result.success;\n }\n\n /**\n * Checks whether the users table is completely empty (no users registered).\n *\n * Security consideration (CWE-636 Fail-Closed):\n * When the query fails, we must NEVER assume the database is empty, as that\n * could lead to privilege escalation (e.g. assigning 'admin' role to new signups).\n *\n * @returns True if there are confirmed 0 users in the database, false otherwise.\n */\n async isEmpty(): Promise<boolean> {\n try {\n const count = await this.db.prepare(\"SELECT COUNT(*) as count FROM users\").first<number>('count');\n return (count ?? 0) === 0;\n } catch (err) {\n console.error(\"[UserModel] isEmpty: query failed, failing closed (assuming users exist):\", err);\n return false;\n }\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\n\n/**\n * Handles primary password credentials and client session-lock security configurations.\n *\n * Single Responsibility: Primary authentication credentials and session-lock policy management.\n */\nexport class UserSecurityModel {\n constructor(private db: D1Database) {}\n\n /**\n * Updates the hashed password and associated password version for a user.\n *\n * @param id The user ID.\n * @param passwordHash The new hashed password string.\n * @param passwordVersion The credential version (default: 2).\n * @returns True if the update succeeded.\n */\n async updatePassword(id: string, passwordHash: string, passwordVersion: number = 2): Promise<boolean> {\n const result = await this.db\n .prepare(\"UPDATE users SET hashed_password = ?, password_version = ? WHERE id = ?\")\n .bind(passwordHash, passwordVersion, id)\n .run();\n return result.success;\n }\n\n /**\n * Updates or clears the quick-unlock PIN hash for a user.\n *\n * @param id The user ID.\n * @param pinHash The new PIN hash, or null to remove the PIN.\n * @returns True if the update succeeded.\n */\n async updatePinHash(id: string, pinHash: string | null): Promise<boolean> {\n const result = await this.db\n .prepare(\"UPDATE users SET pin_hash = ? WHERE id = ?\")\n .bind(pinHash, id)\n .run();\n return result.success;\n }\n\n /**\n * Updates the client session lock timeout duration (in minutes or seconds).\n *\n * @param id The user ID.\n * @param timeout The timeout duration value.\n * @returns True if the update succeeded.\n */\n async updateSessionLockTimeout(id: string, timeout: number): Promise<boolean> {\n const result = await this.db\n .prepare(\"UPDATE users SET session_lock_timeout = ? WHERE id = ?\")\n .bind(timeout, id)\n .run();\n return result.success;\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { StoredRecoveryKey } from \"./types\";\nimport { encryptEnvelope } from \"../../utils/envelope\";\n\n/**\n * Handles Multi-Factor Authentication (MFA) factors, including TOTP and emergency recovery keys.\n *\n * Single Responsibility: Second-factor credentials lifecycle, storage, and recovery key consumption.\n */\nexport class UserMfaModel {\n constructor(private db: D1Database, private env?: unknown) {}\n\n /**\n * Activates TOTP for a user by storing the envelope-encrypted secret and recovery keys.\n * If envelope encryption (KEK) is not configured in the environment, falls back to plaintext.\n *\n * @param id The user ID.\n * @param secret Plaintext base32 TOTP secret.\n * @param recoveryKeys Array of recovery key hashes or objects.\n * @returns True if the update succeeded.\n */\n async updateTOTP(id: string, secret: string, recoveryKeys: StoredRecoveryKey[]): Promise<boolean> {\n const recoveryKeysStr = JSON.stringify(recoveryKeys);\n const encryptedSecret = await encryptEnvelope(secret, this.env);\n const encryptedKeys = await encryptEnvelope(recoveryKeysStr, this.env);\n\n if (encryptedSecret && encryptedKeys) {\n const result = await this.db\n .prepare(\n 'UPDATE users SET totp_secret = NULL, totp_secret_encrypted = ?, totp_secret_dek = ?, totp_enabled = 1, totp_skip_password = 0, totp_recovery_keys = NULL, totp_recovery_keys_encrypted = ?, totp_recovery_keys_dek = ? WHERE id = ?'\n )\n .bind(\n encryptedSecret.dataEncrypted,\n encryptedSecret.dekEncrypted,\n encryptedKeys.dataEncrypted,\n encryptedKeys.dekEncrypted,\n id\n )\n .run();\n return result.success;\n } else {\n // Fallback: plaintext storage if KEK is not configured\n const result = await this.db\n .prepare(\n 'UPDATE users SET totp_secret = ?, totp_secret_encrypted = NULL, totp_secret_dek = NULL, totp_enabled = 1, totp_skip_password = 0, totp_recovery_keys = ?, totp_recovery_keys_encrypted = NULL, totp_recovery_keys_dek = NULL WHERE id = ?'\n )\n .bind(secret, recoveryKeysStr, id)\n .run();\n return result.success;\n }\n }\n\n /**\n * Disables TOTP. If `keepMfaState` is true (e.g. user still has registered Passkeys),\n * recovery keys and passwordless settings are retained.\n *\n * @param id The user ID.\n * @param keepMfaState Whether to keep recovery keys and skip-password status intact.\n * @returns True if the update succeeded.\n */\n async removeTOTP(id: string, keepMfaState: boolean = false): Promise<boolean> {\n if (keepMfaState) {\n const result = await this.db\n .prepare('UPDATE users SET totp_secret = NULL, totp_secret_encrypted = NULL, totp_secret_dek = NULL, totp_enabled = 0 WHERE id = ?')\n .bind(id)\n .run();\n return result.success;\n }\n const result = await this.db\n .prepare('UPDATE users SET totp_secret = NULL, totp_secret_encrypted = NULL, totp_secret_dek = NULL, totp_enabled = 0, totp_skip_password = 0, totp_recovery_keys = NULL, totp_recovery_keys_encrypted = NULL, totp_recovery_keys_dek = NULL WHERE id = ?')\n .bind(id)\n .run();\n return result.success;\n }\n\n /**\n * Saves or updates recovery keys for a user (used by Passkeys or TOTP).\n *\n * @param id The user ID.\n * @param recoveryKeys Array of recovery key items.\n * @returns True if the update succeeded.\n */\n async updateRecoveryKeys(id: string, recoveryKeys: StoredRecoveryKey[]): Promise<boolean> {\n const recoveryKeysStr = JSON.stringify(recoveryKeys);\n const encryptedKeys = await encryptEnvelope(recoveryKeysStr, this.env);\n\n if (encryptedKeys) {\n const result = await this.db\n .prepare(\n 'UPDATE users SET totp_recovery_keys = NULL, totp_recovery_keys_encrypted = ?, totp_recovery_keys_dek = ? WHERE id = ?'\n )\n .bind(\n encryptedKeys.dataEncrypted,\n encryptedKeys.dekEncrypted,\n id\n )\n .run();\n return result.success;\n } else {\n const result = await this.db\n .prepare(\n 'UPDATE users SET totp_recovery_keys = ?, totp_recovery_keys_encrypted = NULL, totp_recovery_keys_dek = NULL WHERE id = ?'\n )\n .bind(recoveryKeysStr, id)\n .run();\n return result.success;\n }\n }\n\n /**\n * Updates the skip-password setting for a user's MFA configuration.\n *\n * @param id The user ID.\n * @param skipPassword Whether password verification can be bypassed in favor of MFA.\n * @returns True if the update succeeded.\n */\n async updateTOTPSettings(id: string, skipPassword: boolean): Promise<boolean> {\n const result = await this.db\n .prepare('UPDATE users SET totp_skip_password = ? WHERE id = ?')\n .bind(skipPassword ? 1 : 0, id)\n .run();\n return result.success;\n }\n\n /**\n * Consumes and burns a single used recovery key by filtering it out from the stored array.\n *\n * @param id The user ID.\n * @param usedIndex The index of the consumed recovery key.\n * @param currentHashes The existing array of recovery key hashes.\n * @returns True if the update succeeded.\n */\n async consumeRecoveryKey(id: string, usedIndex: number, currentHashes: StoredRecoveryKey[]): Promise<boolean> {\n const updated = currentHashes.filter((_, i) => i !== usedIndex);\n const updatedStr = JSON.stringify(updated);\n\n const encryptedKeys = await encryptEnvelope(updatedStr, this.env);\n if (encryptedKeys) {\n const result = await this.db\n .prepare('UPDATE users SET totp_recovery_keys = NULL, totp_recovery_keys_encrypted = ?, totp_recovery_keys_dek = ? WHERE id = ?')\n .bind(encryptedKeys.dataEncrypted, encryptedKeys.dekEncrypted, id)\n .run();\n return result.success;\n } else {\n const result = await this.db\n .prepare('UPDATE users SET totp_recovery_keys = ?, totp_recovery_keys_encrypted = NULL, totp_recovery_keys_dek = NULL WHERE id = ?')\n .bind(updatedStr, id)\n .run();\n return result.success;\n }\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { InactivityPolicyResult } from \"./types\";\n\n/**\n * Handles account activity timestamp tracking and automated data retention / inactivity cleanup policies.\n *\n * Single Responsibility: Account resolution activity tracking and periodic inactivity retention enforcement.\n */\nexport class UserLifecycleModel {\n constructor(private db: D1Database) {}\n\n /**\n * Updates the `last_active_at` timestamp of the user who owns the specified profile.\n * Called on incoming DNS queries to keep the user active.\n *\n * @param profileId Profile identifier.\n * @param now Current timestamp in seconds.\n * @returns True if the update succeeded.\n */\n async updateLastActiveByProfile(profileId: string, now: number): Promise<boolean> {\n const result = await this.db\n .prepare(\"UPDATE users SET last_active_at = ? WHERE id = (SELECT owner_id FROM profiles WHERE id = ?)\")\n .bind(now, profileId)\n .run();\n return result.success;\n }\n\n /**\n * Applies the inactivity retention policies:\n * 1. 30 days of no DNS resolution activity: cleans up all query logs, pre-aggregated hourly rollups, and DNS profiles.\n * 2. 90 days of no login activity: deletes the zombie user account and all remaining metadata.\n *\n * @param now Current timestamp in seconds.\n * @returns Statistics on cleared profiles and deleted user accounts.\n */\n async applyInactivityPolicy(now: number): Promise<InactivityPolicyResult> {\n const thirtyDaysAgo = now - 30 * 24 * 3600;\n const ninetyDaysAgo = now - 90 * 24 * 3600;\n\n // 1. \u8D85\u8FC7 30 \u5929\u65E0\u89E3\u6790\u8BB0\u5F55\u7684\u8D26\u6237\uFF0C\u5176\u6240\u6709\u5173\u8054\u67E5\u8BE2\u65E5\u5FD7\u53CA DNS \u914D\u7F6E\u5C06\u88AB\u81EA\u52A8\u6E05\u7406\n const deleteLogsStmt = this.db.prepare(`\n DELETE FROM logs\n WHERE profile_id IN (\n SELECT id FROM profiles\n WHERE owner_id IN (\n SELECT id FROM users\n WHERE role = 'user'\n AND (last_active_at < ? OR (last_active_at IS NULL AND created_at < ?))\n )\n )\n `).bind(thirtyDaysAgo, thirtyDaysAgo);\n\n const deleteProfilesStmt = this.db.prepare(`\n DELETE FROM profiles \n WHERE owner_id IN (\n SELECT id FROM users \n WHERE role = 'user' \n AND (last_active_at < ? OR (last_active_at IS NULL AND created_at < ?))\n )\n `).bind(thirtyDaysAgo, thirtyDaysAgo);\n\n // 2. \u8D26\u6237\u672C\u8EAB\u5728 90 \u5929\u65E0\u767B\u5F55\u540E\u5C06\u4E88\u79FB\u9664\n const deleteUsersStmt = this.db.prepare(`\n DELETE FROM users\n WHERE role = 'user'\n AND (\n SELECT COALESCE(MAX(timestamp), created_at) \n FROM user_activity_log \n WHERE user_id = users.id\n ) < ?\n `).bind(ninetyDaysAgo);\n\n const results = await this.db.batch([\n deleteLogsStmt,\n deleteProfilesStmt,\n deleteUsersStmt\n ]);\n\n return {\n clearedProfiles: results[1].meta.changes || 0,\n deletedUsers: results[2].meta.changes || 0\n };\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { User } from \"../../types\";\nimport { UserRecord, CreateUserOptions, InactivityPolicyResult, StoredRecoveryKey } from \"./types\";\nimport { UserSecretProcessor } from \"./secrets\";\nimport { UserCoreModel } from \"./core\";\nimport { UserSecurityModel } from \"./security\";\nimport { UserMfaModel } from \"./mfa\";\nimport { UserLifecycleModel } from \"./lifecycle\";\n\nexport * from \"./types\";\nexport * from \"./secrets\";\nexport * from \"./core\";\nexport * from \"./security\";\nexport * from \"./mfa\";\nexport * from \"./lifecycle\";\n\n/**\n * Unified UserModel Facade.\n *\n * Single Responsibility: Assembles and delegates to specialized sub-models\n * (Core, Security, MFA, Lifecycle, and Secrets), providing full backward compatibility.\n */\nexport class UserModel {\n public readonly secrets: UserSecretProcessor;\n public readonly core: UserCoreModel;\n public readonly security: UserSecurityModel;\n public readonly mfa: UserMfaModel;\n public readonly lifecycle: UserLifecycleModel;\n\n constructor(db: D1Database, env?: unknown) {\n this.secrets = new UserSecretProcessor(db, env);\n this.core = new UserCoreModel(db, this.secrets);\n this.security = new UserSecurityModel(db);\n this.mfa = new UserMfaModel(db, env);\n this.lifecycle = new UserLifecycleModel(db);\n }\n\n /**\n * Retrieves a user by their unique identifier.\n */\n async getById(id: string): Promise<UserRecord | null> {\n return this.core.getById(id);\n }\n\n /**\n * Retrieves a user by their username.\n */\n async getByUsername(username: string): Promise<UserRecord | null> {\n return this.core.getByUsername(username);\n }\n\n /**\n * Lists all users in the database.\n */\n async listAll(): Promise<User[]> {\n return this.core.listAll();\n }\n\n /**\n * Creates a new user record.\n */\n async create(user: CreateUserOptions): Promise<boolean> {\n return this.core.create(user);\n }\n\n /**\n * Updates a user's username.\n */\n async updateUsername(id: string, username: string): Promise<boolean> {\n return this.core.updateUsername(id, username);\n }\n\n /**\n * Updates a user's timezone preference.\n */\n async updateTimezone(id: string, timezone: string | null): Promise<boolean> {\n return this.core.updateTimezone(id, timezone);\n }\n\n /**\n * Updates a user's UI locale preference.\n */\n async updateLocale(id: string, locale: string | null): Promise<boolean> {\n return this.core.updateLocale(id, locale);\n }\n\n /**\n * Deletes a user by their ID.\n */\n async delete(id: string): Promise<boolean> {\n return this.core.delete(id);\n }\n\n /**\n * Checks if the user database is empty.\n */\n async isEmpty(): Promise<boolean> {\n return this.core.isEmpty();\n }\n\n /**\n * Updates a user's hashed password and password version.\n */\n async updatePassword(id: string, passwordHash: string, passwordVersion: number = 2): Promise<boolean> {\n return this.security.updatePassword(id, passwordHash, passwordVersion);\n }\n\n /**\n * Updates or clears a user's PIN hash.\n */\n async updatePinHash(id: string, pinHash: string | null): Promise<boolean> {\n return this.security.updatePinHash(id, pinHash);\n }\n\n /**\n * Updates a user's session lock timeout.\n */\n async updateSessionLockTimeout(id: string, timeout: number): Promise<boolean> {\n return this.security.updateSessionLockTimeout(id, timeout);\n }\n\n /**\n * Activates TOTP for a user.\n */\n async updateTOTP(id: string, secret: string, recoveryKeys: StoredRecoveryKey[]): Promise<boolean> {\n return this.mfa.updateTOTP(id, secret, recoveryKeys);\n }\n\n /**\n * Disables TOTP for a user.\n */\n async removeTOTP(id: string, keepMfaState: boolean = false): Promise<boolean> {\n return this.mfa.removeTOTP(id, keepMfaState);\n }\n\n /**\n * Saves or updates recovery keys for a user.\n */\n async updateRecoveryKeys(id: string, recoveryKeys: StoredRecoveryKey[]): Promise<boolean> {\n return this.mfa.updateRecoveryKeys(id, recoveryKeys);\n }\n\n /**\n * Updates skip-password setting for TOTP.\n */\n async updateTOTPSettings(id: string, skipPassword: boolean): Promise<boolean> {\n return this.mfa.updateTOTPSettings(id, skipPassword);\n }\n\n /**\n * Consumes a single used recovery key.\n */\n async consumeRecoveryKey(id: string, usedIndex: number, currentHashes: StoredRecoveryKey[]): Promise<boolean> {\n return this.mfa.consumeRecoveryKey(id, usedIndex, currentHashes);\n }\n\n /**\n * Updates last active timestamp by profile ID.\n */\n async updateLastActiveByProfile(profileId: string, now: number): Promise<boolean> {\n return this.lifecycle.updateLastActiveByProfile(profileId, now);\n }\n\n /**\n * Executes the periodic inactivity cleanup policy.\n */\n async applyInactivityPolicy(now: number): Promise<InactivityPolicyResult> {\n return this.lifecycle.applyInactivityPolicy(now);\n }\n}\n", "import { Context, Env, ExecutionContext } from '../types';\nimport { parseDNSQuery } from '../utils/dns';\nimport { pipeline } from '../pipeline';\nimport { ProfileModel, ProfileWithBloom } from '../models/profile';\nimport { UserModel } from '../models/user';\nimport { cacheUtils } from '../utils/cache';\nimport { profileKeyMemoryMap } from '../pipeline/cache';\nimport { resolveUpstreamEndpoint, fetchFromUpstream } from '../pipeline/resolver/transport';\n\n/**\n * Safely extracts raw wire-format DNS query bytes from incoming HTTP GET or POST request.\n */\nasync function extractRawQuery(request: Request): Promise<Uint8Array | null> {\n try {\n if (request.method === \"GET\") {\n const url = new URL(request.url);\n const dnsParam = url.searchParams.get(\"dns\");\n if (!dnsParam) return null;\n let base64 = dnsParam.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4) base64 += \"=\";\n const binary = atob(base64);\n const raw = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n raw[i] = binary.charCodeAt(i);\n }\n return raw;\n } else if (request.method === \"POST\") {\n const buffer = await request.clone().arrayBuffer();\n return new Uint8Array(buffer);\n }\n } catch {\n return null;\n }\n return null;\n}\n\n/**\n * Resolves profile and access point metadata with multi-tier caching (L1 Memory -> L2 Cache API -> D1 DB).\n */\nexport async function resolveProfileByKey(\n profileKey: string,\n env: Env,\n ctx: ExecutionContext\n): Promise<(ProfileWithBloom & { access_point_id?: string; access_point_name?: string }) | null> {\n const cache = (caches as any).default;\n const now = Date.now();\n\n // 1. Check L1 Memory Cache (Isolate Global) - 5 minutes\n const inMem = profileKeyMemoryMap.get(profileKey);\n if (inMem && now - inMem.ts < 300_000) {\n return inMem.data;\n }\n\n // 2. Check L2 Cloudflare Cache API - 1 hour\n const cacheKey = `doh_key_v1:${profileKey}`;\n try {\n const cached = await cacheUtils.get<any>(cache, cacheKey);\n if (cached) {\n profileKeyMemoryMap.set(profileKey, { data: cached, ts: now });\n return cached;\n }\n } catch {\n /* ignore Cache API match error */\n }\n\n // 3. Fallback to D1 (only executed on cache miss)\n try {\n const profileModel = new ProfileModel(env.DB);\n const profile = await profileModel.findByKey(profileKey);\n\n if (profile) {\n profileKeyMemoryMap.set(profileKey, { data: profile, ts: now });\n ctx.waitUntil(cacheUtils.set(cache, cacheKey, profile, 3600));\n return profile;\n }\n } catch (e: any) {\n console.warn(`[DoH] D1 profile lookup failed for key ${profileKey}:`, e.message || e);\n // If D1 is exhausted or erroring, attempt to return stale in-memory data if available\n if (inMem?.data) {\n return inMem.data;\n }\n\n // Emergency fail-open fallback if D1 has exceeded its read quota or is unavailable:\n // Generate a temporary fallback profile so DNS resolution doesn't return 404 or 500\n const failOpenUpstream = env.FAIL_OPEN_UPSTREAM || \"https://freedns.controld.com/no-ads-malware-typo\";\n console.warn(`[DoH] D1 unavailable. Providing emergency fallback profile for key ${profileKey} -> ${failOpenUpstream}`);\n return {\n id: profileKey,\n name: \"Emergency Fallback\",\n settings: JSON.stringify({\n upstream: [failOpenUpstream],\n default_policy: \"ALLOW\",\n log_retention_days: 0,\n ecs: { enabled: true, use_client_ip: true }\n }),\n owner_id: \"system\",\n created_at: Math.floor(now / 1000),\n updated_at: Math.floor(now / 1000)\n } as any;\n }\n\n return null;\n}\n\n/**\n * Resolves default profile when no profile key is specified in connection or query.\n */\nexport async function resolveDefaultProfile(\n env: Env,\n ctx: ExecutionContext\n): Promise<(ProfileWithBloom & { access_point_id?: string; access_point_name?: string }) | null> {\n const defaultKey = env.SERVERFULL_DEFAULT_PROFILE_KEY || env.DEFAULT_PROFILE_KEY;\n if (defaultKey) {\n const profile = await resolveProfileByKey(defaultKey, env, ctx);\n if (profile) return profile;\n }\n\n try {\n const profileModel = new ProfileModel(env.DB);\n const profiles = await profileModel.list(\"ORDER BY created_at ASC LIMIT 1\", []);\n if (profiles && profiles.length > 0) {\n const fullProfile = await profileModel.getById(profiles[0].id);\n if (fullProfile) return fullProfile;\n }\n } catch (e: any) {\n console.warn(\"[Profile] Failed to query fallback default profile:\", e.message || e);\n }\n\n return {\n id: \"default\",\n name: \"Default Profile\",\n settings: JSON.stringify({\n upstream: [env.FAIL_OPEN_UPSTREAM || \"https://security.cloudflare-dns.com/dns-query\"],\n default_policy: \"ALLOW\",\n log_retention_days: 0,\n ecs: { enabled: true, use_client_ip: true }\n }),\n owner_id: \"system\",\n created_at: Math.floor(Date.now() / 1000),\n updated_at: Math.floor(Date.now() / 1000)\n } as any;\n}\n\n/**\n * Handles DNS-over-HTTPS (DoH) requests, coordinates parsing, pipeline resolution, \n * active connection cache registration, and database active tracking.\n */\nexport async function handleDoHRequest(\n request: Request,\n env: Env,\n ctx: ExecutionContext,\n profileKey: string\n): Promise<Response> {\n const cache = (caches as any).default;\n let queryRaw: Uint8Array | null = null;\n\n try {\n const profile = await resolveProfileByKey(profileKey, env, ctx);\n if (!profile) {\n return new Response('Invalid Profile Key', { status: 404 });\n }\n const profileId = profile.id;\n const query = await parseDNSQuery(request);\n if (!query) {\n return new Response('Invalid DNS Query', { status: 400 });\n }\n queryRaw = query.raw;\n\n const context: Context = { \n profileId, \n accessPointId: profile.access_point_id, \n accessPointName: profile.access_point_name,\n startTime: Date.now(), \n env, \n ctx \n };\n const result = await pipeline.process(request, query, context);\n\n // Async task: record active connections and update active timestamps with throttling\n ctx.waitUntil((async () => {\n try {\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n \n // Record active connection (used by Debug API)\n const activeDnsTtl = Number(env.ACTIVE_DNS_CACHE_TTL) || 60;\n await cacheUtils.set(cache, `active_dns:${clientIp}`, profileId, activeDnsTtl);\n\n // Update profile activity timestamp (throttled hourly)\n const nowSec = Math.floor(Date.now() / 1000);\n const lastActiveKey = `active_throttle:${profileId}`;\n const lastActiveThrottled = await cacheUtils.get<number>(cache, lastActiveKey);\n\n const throttleSec = Number(env.THROTTLE_ACTIVE_SEC) || 3600;\n if (!lastActiveThrottled || nowSec - lastActiveThrottled > throttleSec) {\n try {\n const profileModel = new ProfileModel(env.DB);\n await profileModel.updateLastActive(profileId, nowSec);\n \n const userModel = new UserModel(env.DB, env);\n await userModel.updateLastActiveByProfile(profileId, nowSec);\n } catch {\n /* ignore D1 write quota errors */\n }\n \n await cacheUtils.set(cache, lastActiveKey, nowSec, throttleSec);\n }\n } catch (e) {\n console.error(`[Background Task] Error for ${profileId}:`, e);\n }\n })());\n\n return new Response(result.answer as any, {\n headers: {\n 'Content-Type': 'application/dns-message',\n 'Cache-Control': `max-age=${result.ttl}`\n }\n });\n } catch (e: any) {\n console.error(`[DoH Pipeline] Internal Error:`, e);\n try {\n // Emergency fail-open: dispatch DNS wire query directly to FAIL_OPEN_UPSTREAM\n // Supports HTTPS (DoH), TLS/DoT (RFC 7858), TCP (RFC 7766), and DNS Stamps (sdns://)\n const failOpenUpstream = env.FAIL_OPEN_UPSTREAM || \"https://freedns.controld.com/no-ads-malware-typo\";\n const rawBytes = queryRaw || (await extractRawQuery(request));\n\n if (rawBytes && rawBytes.length > 0) {\n const endpoint = resolveUpstreamEndpoint(failOpenUpstream);\n const transportRes = await fetchFromUpstream(endpoint, rawBytes);\n return new Response(transportRes.answer as any, {\n headers: {\n \"Content-Type\": \"application/dns-message\",\n \"Cache-Control\": \"max-age=60\"\n }\n });\n }\n\n // If wire query bytes couldn't be decoded and upstream is HTTP(S), proxy request directly\n if (failOpenUpstream.startsWith(\"http://\") || failOpenUpstream.startsWith(\"https://\")) {\n const fallbackUrl = new URL(failOpenUpstream);\n const reqUrl = new URL(request.url);\n fallbackUrl.search = reqUrl.search;\n const fallbackRes = await fetch(fallbackUrl.toString(), {\n method: request.method,\n headers: {\n \"Accept\": request.headers.get(\"Accept\") || \"application/dns-message\",\n \"Content-Type\": request.headers.get(\"Content-Type\") || \"application/dns-message\"\n },\n body: request.method === \"POST\" ? request.body : undefined\n });\n return fallbackRes;\n }\n\n return new Response(\"Invalid DNS Query\", { status: 400 });\n } catch (proxyErr) {\n console.error(`[DoH Pipeline] Emergency fallback failed:`, proxyErr);\n return new Response(`Internal Server Error`, { status: 500 });\n }\n }\n}\n", "/**\n * @file format.ts\n * @description Declarative CLI formatters for menus, banners, diagnostic notices, and service summaries.\n */\n\n/**\n * Item representing a CLI command, option, or setting in the help menu.\n */\nexport interface HelpItem {\n label: string;\n desc: string;\n}\n\n/**\n * Configuration options for rendering a formatted CLI help menu.\n */\nexport interface HelpMenuConfig {\n name: string;\n description: string;\n usage: string | string[];\n commands?: HelpItem[];\n options?: HelpItem[];\n services?: HelpItem[];\n envVars?: HelpItem[];\n tip?: string | string[];\n}\n\n/**\n * Formats a declarative tip block with indentation.\n *\n * @param tip - Tip string or array of tip strings.\n * @param indent - Left indentation string (default: ' ').\n * @returns Formatted tip string.\n */\nexport function formatTip(tip: string | string[], indent: string = ' '): string {\n if (!tip) return '';\n const lines = Array.isArray(tip)\n ? tip.map((t) => `${indent}${t}`).join('\\n')\n : `${indent}${tip}`;\n return `\\nTip:\\n${lines}\\n`;\n}\n\n/**\n * Formats a structured CLI help menu with dynamic column alignment.\n *\n * @param config - The help menu configuration structure.\n * @returns Formatted help menu string ready for console output.\n */\nexport function formatHelpMenu(config: HelpMenuConfig): string {\n const { name, description, usage, commands, options, services, envVars, tip } = config;\n const indent = ' ';\n const gap = 4; // Space between label column and description column\n\n // Shared alignment width for commands and options if both exist\n const cmdAndOptItems = [...(commands || []), ...(options || [])];\n const cmdAndOptWidth = Math.max(...cmdAndOptItems.map((item) => item.label.length), 0);\n\n const renderSection = (title: string, items?: HelpItem[], customWidth?: number): string => {\n if (!items || items.length === 0) return '';\n const width = customWidth ?? Math.max(...items.map((item) => item.label.length), 0);\n const lines = items.map(\n (item) => `${indent}${item.label.padEnd(width + gap)}${item.desc}`\n );\n return `\\n${title}:\\n${lines.join('\\n')}`;\n };\n\n const usageText = Array.isArray(usage)\n ? usage.map((u) => `${indent}${u}`).join('\\n')\n : `${indent}${usage}`;\n\n const tipText = tip ? formatTip(tip, indent).trimEnd() : '';\n\n const sections = [\n `${name} - ${description}`,\n `\\nUsage:\\n${usageText}`,\n renderSection('Commands', commands, cmdAndOptWidth),\n renderSection('Options', options, cmdAndOptWidth),\n renderSection('Current Service Ports', services),\n renderSection('Environment Variables', envVars),\n tipText\n ];\n\n return sections.filter(Boolean).join('\\n') + '\\n';\n}\n\n/**\n * Item representing a CLI command and its description.\n */\nexport interface CommandItem {\n command: string;\n desc: string;\n}\n\n/**\n * Configuration options for rendering a declarative command list.\n */\nexport interface CommandListConfig {\n title?: string;\n items: CommandItem[];\n indent?: string;\n gap?: number;\n}\n\n/**\n * Formats a list of commands with dynamic column alignment.\n *\n * @param config - The command list configuration.\n * @returns Formatted multi-line string with aligned commands and descriptions.\n */\nexport function formatCommandList(config: CommandListConfig): string {\n const { title, items, indent = ' ', gap = 4 } = config;\n if (!items || items.length === 0) return '';\n\n const maxCmdWidth = Math.max(...items.map((i) => i.command.length), 0);\n const lines = items.map(\n (item) => `${indent}${item.command.padEnd(maxCmdWidth + gap)}# ${item.desc}`\n );\n\n if (title) {\n return `${title}\\n${lines.join('\\n')}\\n`;\n }\n return lines.join('\\n') + '\\n';\n}\n\n/**\n * Configuration options for rendering a bordered content or log preview box.\n */\nexport interface LogBoxConfig {\n title: string;\n content: string;\n borderChar?: '-' | '=' | '#' | '*';\n minWidth?: number;\n tip?: string;\n}\n\n/**\n * Formats a declarative bordered content or log viewer block with title and optional tip.\n *\n * @param config - The log box configuration.\n * @returns Formatted log box string.\n */\nexport function formatLogBox(config: LogBoxConfig): string {\n const { title, content, borderChar = '-', minWidth = 60, tip } = config;\n const headerText = `[${title}]`;\n const borderLength = Math.max(minWidth, headerText.length + 8);\n const border = borderChar.repeat(borderLength);\n\n const leftPad = 3;\n const rightPad = Math.max(3, borderLength - headerText.length - leftPad - 2);\n const header = `${borderChar.repeat(leftPad)} ${headerText} ${borderChar.repeat(rightPad)}`;\n\n const lines: string[] = [\n header,\n content,\n border\n ];\n\n if (tip) {\n lines.push(`Tip: ${tip}`);\n }\n\n return '\\n' + lines.join('\\n') + '\\n';\n}\n\n/**\n * Key-value item for structured output sections.\n */\nexport interface KeyValueItem {\n label: string;\n value: string;\n}\n\n/**\n * Configuration options for rendering a formatted key-value section.\n */\nexport interface KeyValueSectionConfig {\n title?: string;\n bullet?: string;\n items: KeyValueItem[];\n indent?: string;\n gap?: number;\n}\n\n/**\n * Formats a key-value list with dynamic alignment between labels and values.\n *\n * @param config - The key-value section configuration.\n * @returns Formatted multi-line string with aligned columns.\n */\nexport function formatKeyValueSection(config: KeyValueSectionConfig): string {\n const { title, bullet = '\u2022 ', items, indent = ' ', gap = 2 } = config;\n if (!items || items.length === 0) return '';\n\n const maxLabelWidth = Math.max(...items.map((item) => item.label.length), 0);\n const lines = items.map(\n (item) => `${indent}${bullet}${item.label.padEnd(maxLabelWidth + gap)}: ${item.value}`\n );\n\n if (title) {\n return `${title}\\n${lines.join('\\n')}`;\n }\n return lines.join('\\n');\n}\n\n/**\n * Item displayed inside a banner card.\n */\nexport interface BannerItem {\n label: string;\n value: string;\n}\n\n/**\n * Configuration options for rendering an ASCII banner or card.\n */\nexport interface BannerConfig {\n title: string;\n subtitle?: string;\n borderChar?: '-' | '=' | '#' | '*';\n bullet?: string;\n minWidth?: number;\n items?: BannerItem[];\n}\n\n/**\n * Formats a declarative bordered banner with optional centered title and aligned key-values.\n *\n * @param config - The banner configuration.\n * @returns Formatted banner string ready for console display.\n */\nexport function formatBanner(config: BannerConfig): string {\n const { title, subtitle, borderChar = '-', bullet = '\u2022 ', minWidth = 54, items } = config;\n\n // Determine ideal banner width\n const contentLengths = [\n title.length + 6,\n subtitle ? subtitle.length + 6 : 0,\n ...(items || []).map((i) => i.label.length + i.value.length + 10)\n ];\n const calculatedWidth = Math.max(minWidth, ...contentLengths);\n const border = borderChar.repeat(calculatedWidth);\n\n const centerText = (text: string): string => {\n const pad = Math.max(0, Math.floor((calculatedWidth - text.length) / 2));\n return ' '.repeat(pad) + text;\n };\n\n const lines: string[] = [border, centerText(title)];\n\n if (subtitle) {\n lines.push(centerText(subtitle));\n }\n\n lines.push(border);\n\n if (items && items.length > 0) {\n const maxLabelWidth = Math.max(...items.map((item) => item.label.length), 0);\n const itemLines = items.map(\n (item) => ` ${bullet}${item.label.padEnd(maxLabelWidth + 2)}: ${item.value}`\n );\n lines.push(...itemLines);\n lines.push(border);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Severity level for diagnostic messages.\n */\nexport type DiagnosticLevel = 'error' | 'warning' | 'info' | 'success';\n\n/**\n * Configuration options for rendering a structured diagnostic or alert message.\n */\nexport interface DiagnosticConfig {\n level?: DiagnosticLevel;\n title: string;\n message: string;\n details?: string[];\n causes?: string[];\n solutions?: string[];\n}\n\n/**\n * Formats a structured diagnostic notice with causes, solutions, and details.\n *\n * @param config - Diagnostic configuration options.\n * @returns Formatted diagnostic message string.\n */\nexport function formatDiagnostic(config: DiagnosticConfig): string {\n const { title, message, details, causes, solutions } = config;\n const indent = ' ';\n const subIndent = ' ';\n\n const sections: string[] = [\n `[${title}] ${message}`\n ];\n\n if (details && details.length > 0) {\n sections.push(details.map((d) => `${indent}${d}`).join('\\n'));\n }\n\n if (causes && causes.length > 0) {\n const causesHeader = `${indent}Possible causes:`;\n const causeLines = causes.map((c) => `${subIndent}- ${c}`).join('\\n');\n sections.push(`${causesHeader}\\n${causeLines}`);\n }\n\n if (solutions && solutions.length > 0) {\n const solHeader = solutions.length === 1 ? `${indent}Solution:` : `${indent}Solutions:`;\n const solLines = solutions.map((s) => `${subIndent}- ${s}`).join('\\n');\n sections.push(`${solHeader}\\n${solLines}`);\n }\n\n return '\\n' + sections.filter(Boolean).join('\\n') + '\\n';\n}\n\n/**\n * Configuration options for rendering a network socket or port startup error.\n */\nexport interface PortErrorConfig {\n serviceName: string;\n protocol: 'UDP' | 'DoT' | 'HTTP' | string;\n port: number;\n err: unknown;\n alternateOption: string;\n disableOption?: string;\n}\n\n/**\n * Formats a declarative diagnostic message for port conflicts (EADDRINUSE) and permissions (EACCES).\n *\n * @param config - Port error configuration.\n * @returns Formatted diagnostic string.\n */\nexport function formatPortError(config: PortErrorConfig): string {\n const { serviceName, protocol, port, err, alternateOption, disableOption } = config;\n const errCode = (err as { code?: string })?.code;\n\n if (errCode === 'EADDRINUSE') {\n const causes: string[] = [];\n if (protocol === 'UDP' && port === 53) {\n causes.push('Linux: systemd-resolved is listening on port 53 (stop it or configure DNSStubListener=no).');\n causes.push('Another DNS server (bind9, dnsmasq, AdGuard Home) is running.');\n } else {\n causes.push(`Another service or application is already listening on ${protocol} port ${port}.`);\n }\n\n const solutions = [\n `Use ${alternateOption} to specify an alternate port.`,\n disableOption ? `Or use ${disableOption} to disable this transport.` : null\n ].filter(Boolean) as string[];\n\n return formatDiagnostic({\n level: 'error',\n title: 'Port Conflict',\n message: `${serviceName} port ${port} is already in use.`,\n causes,\n solutions\n });\n }\n\n if (errCode === 'EACCES') {\n const causes = [\n 'Port numbers below 1024 require elevated privileges on Linux/macOS.',\n 'On Windows, the port may fall within a reserved NAT/Hyper-V port exclusion range.'\n ];\n\n const solutions = [\n 'Run with elevated privileges (e.g. sudo npx dns-worker).',\n `Or use ${alternateOption} to bind to an unprivileged or available port.`\n ];\n\n return formatDiagnostic({\n level: 'error',\n title: 'Permission Denied',\n message: `Permission denied binding to ${serviceName} port ${port}.`,\n causes,\n solutions\n });\n }\n\n const message = err instanceof Error ? err.message : String(err);\n return `\\n[${serviceName}] Startup Error: ${message}\\n`;\n}\n", "/**\n * @file dot.ts\n * @description DNS over TLS (DoT - RFC 7858) server with TLS SNI Profile routing for Serverfull mode.\n */\n\nimport fs from 'node:fs';\nimport tls from 'node:tls';\nimport { Buffer } from 'node:buffer';\nimport { Env, Context, ExecutionContext } from '../types';\nimport { parseDNSQueryFromRaw } from '../utils/dns';\nimport { pipeline } from '../pipeline';\nimport { resolveDefaultProfile, resolveProfileByKey } from '../api/doh';\nimport { resolveUpstreamEndpoint, fetchFromUpstream } from '../pipeline/resolver/transport';\nimport { ACCESS_KEY_REGEX } from '../utils/validator';\nimport { formatDiagnostic, formatPortError } from './format';\n\nexport interface DotServerOptions {\n port: number;\n host: string;\n tlsKeyPath: string;\n tlsCertPath: string;\n defaultProfileKey?: string;\n env: Env;\n}\n\nexport class DotDnsServer {\n private server: tls.Server | null = null;\n private isRunning: boolean = false;\n\n constructor(private options: DotServerOptions) {}\n\n start(): Promise<void> {\n return new Promise((resolve, reject) => {\n const { tlsKeyPath, tlsCertPath, port, host } = this.options;\n\n if (!tlsKeyPath || !tlsCertPath) {\n console.warn(formatDiagnostic({\n level: 'warning',\n title: 'DoT - TLS Not Configured',\n message: 'TLS key or certificate path not provided. Skipping DoT server startup.',\n solutions: [\n 'Set SERVERFULL_TLS_KEY_PATH and SERVERFULL_TLS_CERT_PATH to enable DNS over TLS (DoT).'\n ]\n }));\n resolve();\n return;\n }\n\n if (!fs.existsSync(tlsKeyPath) || !fs.existsSync(tlsCertPath)) {\n console.warn(formatDiagnostic({\n level: 'warning',\n title: 'DoT - TLS Files Missing',\n message: 'Configured TLS key or certificate files were not found on disk. Skipping DoT server startup.',\n details: [\n `Key path : ${tlsKeyPath}`,\n `Cert path : ${tlsCertPath}`\n ],\n solutions: [\n 'Verify the file paths or generate TLS certificates before enabling DoT.'\n ]\n }));\n resolve();\n return;\n }\n\n try {\n const tlsOptions: tls.TlsOptions = {\n key: fs.readFileSync(tlsKeyPath),\n cert: fs.readFileSync(tlsCertPath),\n ALPNProtocols: ['dot'],\n sessionIdContext: 'dns-worker-dot'\n };\n\n this.server = tls.createServer(tlsOptions, (socket: tls.TLSSocket) => {\n this.handleConnection(socket);\n });\n\n const startupErrorHandler = (err: any) => {\n this.server = null;\n\n console.error(formatPortError({\n serviceName: 'DNS over TLS (DoT)',\n protocol: 'DoT',\n port,\n err,\n alternateOption: '--dot-port <port> (e.g. --dot-port 8853)',\n disableOption: '--disable-dot'\n }));\n\n reject(err);\n };\n\n this.server.once('error', startupErrorHandler);\n\n this.server.listen(port, host, () => {\n this.server?.removeListener('error', startupErrorHandler);\n this.server?.on('error', (err: Error) => {\n console.error('[DoT] Runtime error:', err);\n });\n this.isRunning = true;\n console.log(`[DoT] DNS over TLS listening on tls://${host}:${port}`);\n resolve();\n });\n } catch (err) {\n console.error('[DoT] Failed to initialize TLS server:', err);\n reject(err);\n }\n });\n }\n\n private handleConnection(socket: tls.TLSSocket): void {\n // Disable Nagle's algorithm (RFC 7858) and enable TCP keepalive\n socket.setNoDelay(true);\n socket.setKeepAlive(true, 60000);\n\n const remoteIp = socket.remoteAddress?.replace(/^::ffff:/, '') || '127.0.0.1';\n let buffer = Buffer.alloc(0);\n\n // Extract profile key from TLS SNI (e.g. \"k7d9w2.dns.example.com\" -> \"k7d9w2\")\n const serverName = socket.servername || '';\n let candidateKey = '';\n if (serverName) {\n const firstPart = serverName.split('.')[0];\n if (ACCESS_KEY_REGEX.test(firstPart)) {\n candidateKey = firstPart;\n }\n }\n\n socket.setTimeout(30000, () => {\n socket.destroy();\n });\n\n socket.on('error', (err: Error) => {\n // Common client disconnection / reset\n if ((err as any).code !== 'ECONNRESET') {\n console.warn(`[DoT] Socket error from ${remoteIp}:`, err.message);\n }\n });\n\n const MAX_DOT_BUFFER = 65537; // 2-byte length prefix + 65535 max DNS packet (RFC 7858)\n socket.on('data', async (chunk: Buffer) => {\n if (buffer.length + chunk.length > MAX_DOT_BUFFER) {\n socket.destroy();\n return;\n }\n buffer = Buffer.concat([buffer, chunk]);\n\n while (buffer.length >= 2) {\n const msgLen = buffer.readUInt16BE(0);\n if (buffer.length < 2 + msgLen) {\n // Incomplete message frame, wait for more data\n break;\n }\n\n const msgPayload = buffer.subarray(2, 2 + msgLen);\n buffer = buffer.subarray(2 + msgLen);\n\n await this.processQuery(socket, msgPayload, remoteIp, candidateKey);\n }\n });\n }\n\n private async processQuery(\n socket: tls.TLSSocket,\n rawMsg: Uint8Array,\n remoteIp: string,\n sniKey: string\n ): Promise<void> {\n try {\n const query = parseDNSQueryFromRaw(rawMsg);\n if (!query) return;\n\n const env = this.options.env;\n const ctx: ExecutionContext = {\n waitUntil(promise: Promise<any>) {\n promise.catch((err) => console.error('[DoT Background Task Error]:', err));\n },\n passThroughOnException() {}\n } as any;\n\n // Profile selection:\n // 1. Matched SNI Profile Key (Android Private DNS standard)\n // 2. Default Profile Key from config\n // 3. Fallback default profile from database\n let profile = null;\n if (sniKey) {\n profile = await resolveProfileByKey(sniKey, env, ctx);\n }\n if (!profile && this.options.defaultProfileKey) {\n profile = await resolveProfileByKey(this.options.defaultProfileKey, env, ctx);\n }\n if (!profile) {\n profile = await resolveDefaultProfile(env, ctx);\n }\n if (!profile) return;\n\n const context: Context = {\n profileId: profile.id,\n accessPointId: profile.access_point_id,\n accessPointName: profile.access_point_name,\n startTime: Date.now(),\n env,\n ctx\n };\n\n const host = remoteIp.includes(':') ? `[${remoteIp}]` : remoteIp;\n const request = new Request(`http://${host}/dns-query`, {\n headers: {\n 'CF-Connecting-IP': remoteIp,\n 'Accept': 'application/dns-message',\n 'Content-Type': 'application/dns-message'\n }\n });\n\n const result = await pipeline.process(request, query, context);\n if (!result || !result.answer || result.answer.length === 0) return;\n\n if (!socket.destroyed && socket.writable) {\n const answer = result.answer;\n const responseBuf = Buffer.allocUnsafe(2 + answer.length);\n responseBuf.writeUInt16BE(answer.length, 0);\n responseBuf.set(answer, 2);\n socket.write(responseBuf);\n }\n } catch (err) {\n console.error('[DoT] Query processing exception:', err);\n try {\n const failOpenUpstream = this.options.env.FAIL_OPEN_UPSTREAM || 'https://freedns.controld.com/no-ads-malware-typo';\n const endpoint = resolveUpstreamEndpoint(failOpenUpstream);\n const transportRes = await fetchFromUpstream(endpoint, rawMsg);\n if (!socket.destroyed && socket.writable) {\n const answer = transportRes.answer;\n const responseBuf = Buffer.allocUnsafe(2 + answer.length);\n responseBuf.writeUInt16BE(answer.length, 0);\n responseBuf.set(answer, 2);\n socket.write(responseBuf);\n }\n } catch (fallbackErr) {\n console.error('[DoT] Fail-open upstream fallback failed:', fallbackErr);\n }\n }\n }\n\n stop(): Promise<void> {\n return new Promise((resolve) => {\n if (this.server && this.isRunning) {\n this.server.close(() => {\n this.isRunning = false;\n console.log('[DoT] Service stopped.');\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n}\n", "/**\n * @file http.ts\n * @description Node.js HTTP server hosting Web UI, REST API, and DoH endpoints for Serverfull mode.\n */\n\nimport http from 'node:http';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { Buffer } from 'node:buffer';\nimport worker from '../index';\nimport { Env, ExecutionContext } from '../types';\nimport { getPackageRoot } from './config';\nimport { formatPortError } from './format';\n\nexport interface HttpServerOptions {\n port: number;\n host: string;\n staticDir?: string;\n env: Env;\n}\n\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.mjs': 'application/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.webp': 'image/webp',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.webmanifest': 'application/manifest+json',\n '.wasm': 'application/wasm'\n};\n\nexport class HttpServer {\n private server: http.Server | null = null;\n private isRunning: boolean = false;\n\n constructor(private options: HttpServerOptions) {\n const staticDir = options.staticDir || path.join(getPackageRoot(), 'static');\n\n // Attach static asset provider to env.ASSETS\n this.options.env.ASSETS = {\n fetch: async (req: Request): Promise<Response> => {\n try {\n const url = new URL(req.url);\n let pathname = decodeURIComponent(url.pathname);\n if (pathname === '/' || pathname === '') {\n pathname = '/index.html';\n }\n\n const safePath = path.normalize(pathname).replace(/^(\\.\\.[\\/\\\\])+/, '');\n const filePath = path.join(staticDir, safePath);\n\n if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {\n const ext = path.extname(filePath).toLowerCase();\n const contentType = MIME_TYPES[ext] || 'application/octet-stream';\n const fileContent = fs.readFileSync(filePath);\n\n return new Response(fileContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': ext === '.html' ? 'no-cache' : 'public, max-age=31536000, immutable',\n 'Cross-Origin-Opener-Policy': 'same-origin',\n 'Cross-Origin-Embedder-Policy': 'credentialless',\n 'Cross-Origin-Resource-Policy': 'same-origin'\n }\n });\n }\n\n return new Response('Not Found', { status: 404 });\n } catch (err) {\n return new Response('Asset Error', { status: 500 });\n }\n }\n };\n }\n\n start(): Promise<void> {\n return new Promise((resolve, reject) => {\n const { port, host } = this.options;\n\n this.server = http.createServer(async (req: http.IncomingMessage, res: http.ServerResponse) => {\n await this.handleHttpRequest(req, res);\n });\n\n // Optimize HTTP keepalive timeouts and disable Nagle's algorithm for low-latency DoH\n this.server.keepAliveTimeout = 65000;\n this.server.headersTimeout = 66000;\n this.server.on('connection', (socket) => {\n socket.setNoDelay(true);\n });\n\n const startupErrorHandler = (err: any) => {\n this.server = null;\n\n console.error(formatPortError({\n serviceName: 'Web Dashboard & DoH (HTTP)',\n protocol: 'HTTP',\n port,\n err,\n alternateOption: `-p, --port <port> (e.g. --port ${port + 1})`\n }));\n\n reject(err);\n };\n\n this.server.once('error', startupErrorHandler);\n\n this.server.listen(port, host, () => {\n this.server?.removeListener('error', startupErrorHandler);\n this.server?.on('error', (err: Error) => {\n console.error('[HTTP Server] Runtime error:', err);\n });\n this.isRunning = true;\n console.log(`[HTTP Server] Web Dashboard & DoH listening on http://${host}:${port}`);\n resolve();\n });\n });\n }\n\n private async handleHttpRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\n try {\n const proto = (req.headers['x-forwarded-proto'] as string) || 'http';\n const host = req.headers.host || `${this.options.host}:${this.options.port}`;\n const fullUrl = new URL(req.url || '/', `${proto}://${host}`);\n\n const headers = new Headers();\n for (const [k, v] of Object.entries(req.headers)) {\n if (!v) continue;\n if (Array.isArray(v)) {\n v.forEach((val) => headers.append(k, val));\n } else {\n headers.set(k, String(v));\n }\n }\n\n // Populate remote IP if not set\n if (!headers.has('CF-Connecting-IP')) {\n const remoteIp = req.socket.remoteAddress?.replace(/^::ffff:/, '') || '127.0.0.1';\n headers.set('CF-Connecting-IP', remoteIp);\n }\n\n // Populate fallback geolocation coordinates for serverfull environments without Cloudflare edge proxy\n if (!headers.has('CF-IPLatitude')) {\n headers.set('CF-IPLatitude', '0.0');\n }\n if (!headers.has('CF-IPLongitude')) {\n headers.set('CF-IPLongitude', '0.0');\n }\n\n let body: any = null;\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);\n }\n body = Buffer.concat(chunks);\n }\n\n const request = new Request(fullUrl.toString(), {\n method: req.method,\n headers,\n body,\n // @ts-ignore\n duplex: 'half'\n });\n\n const ctx: ExecutionContext = {\n waitUntil(promise: Promise<any>) {\n promise.catch((err) => console.error('[HTTP Background Task Error]:', err));\n },\n passThroughOnException() {}\n } as any;\n\n const response = await worker.fetch(request, this.options.env, ctx);\n\n res.statusCode = response.status;\n res.statusMessage = response.statusText;\n\n response.headers.forEach((val, key) => {\n res.setHeader(key, val);\n });\n\n if (response.body) {\n const reader = response.body.getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n res.write(value);\n }\n }\n res.end();\n } catch (err: any) {\n console.error('[HTTP Server] Request processing failed:', err);\n if (!res.headersSent) {\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Internal Server Error', message: err.message }));\n }\n }\n }\n\n stop(): Promise<void> {\n return new Promise((resolve) => {\n if (this.server && this.isRunning) {\n this.server.close(() => {\n this.isRunning = false;\n console.log('[HTTP Server] Service stopped.');\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n}\n", "import { Env, User, ExecutionContext } from './types';\nimport { ScheduledEvent } from '@cloudflare/workers-types';\nimport { readCsrfCookie, createCsrfCookie, generateId } from './lib/auth';\nimport { generateLinuxSetupScript } from './utils/linuxSetup';\nimport { ACCESS_KEY_REGEX } from './utils/validator';\n\n// Middleware imports\nimport { applySecurityHeaders, getCurrentUser, validateCsrf } from './lib/middleware';\n\n// Route handlers\nimport { handleAuthRequest } from './api/auth';\nimport { handleProfilesRequest } from './api/profiles';\nimport { handleAccountRequest } from './api/account';\nimport { handleSystemRequest } from './api/system';\nimport { handleDoHRequest } from './api/doh';\nimport { handleScheduled } from './cron';\nimport { handleMapDataRequest } from './api/mapData';\nimport { isUsableJwtSecret } from './lib/jwt';\n\nexport default {\n async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {\n const nonceBytes = new Uint8Array(16);\n crypto.getRandomValues(nonceBytes);\n const nonce = Array.from(nonceBytes).map(b => b.toString(16).padStart(2, '0')).join('');\n\n let currentUser: User | null = null;\n\n const handleRequest = async (): Promise<Response> => {\n const url = new URL(request.url);\n\n // Map Data API Proxy\n if (url.pathname === '/world-110m.json') {\n return handleMapDataRequest(request, env, ctx);\n }\n\n // Business API Router\n if (url.pathname.startsWith('/api/')) {\n let isDbMissing = false;\n try {\n isDbMissing = !env.DB;\n } catch (e) {\n isDbMissing = true;\n }\n const isJwtSecretMissing = !isUsableJwtSecret(env.JWT_SECRET);\n\n if (isDbMissing || isJwtSecretMissing) {\n return new Response(JSON.stringify({\n error: \"configuration_error\",\n isDbMissing,\n isJwtSecretMissing,\n message: \"DNS Worker is not fully configured. Please configure the D1 database binding and JWT_SECRET secret variable.\"\n }), {\n status: 503,\n headers: {\n \"Content-Type\": \"application/json\",\n \"Access-Control-Allow-Origin\": \"*\"\n }\n });\n }\n\n // Authenticate request\n currentUser = await getCurrentUser(request, env, ctx);\n\n const isPublicRoute = [\n '/api/auth/login', \n '/api/auth/signup', \n '/api/auth/prelogin', \n '/api/auth/check-username',\n '/api/auth/config',\n '/api/auth/refresh',\n '/api/auth/logout',\n '/api/auth/unlock-session',\n '/api/clientinfo',\n '/api/regions'\n ].includes(url.pathname) || \n url.pathname.startsWith('/api/icon/') || \n url.pathname.startsWith('/api/presets/') ||\n url.pathname.startsWith('/api/auth/forgot-password');\n\n // Check authentication boundary\n if (!currentUser && !isPublicRoute) {\n return new Response(\"Unauthorized\", { status: 401 });\n }\n\n // Handle paused sessions: block all routes except unlock, logout, and refresh\n if (currentUser && currentUser.isPaused) {\n const isAllowedWhilePaused = [\n '/api/auth/unlock-session',\n '/api/auth/logout',\n '/api/auth/refresh'\n ].includes(url.pathname);\n if (!isAllowedWhilePaused) {\n return new Response(\"session_paused\", { status: 403 });\n }\n }\n\n // Validate CSRF for mutating requests\n if (currentUser && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(request.method) && !isPublicRoute) {\n if (!validateCsrf(request)) {\n return new Response(\"CSRF validation failed\", { status: 403 });\n }\n }\n\n // Route requests to handlers\n if (url.pathname.startsWith('/api/auth/')) {\n return handleAuthRequest(request, env);\n }\n if (\n url.pathname === '/api/clientinfo' ||\n url.pathname === '/api/regions' ||\n url.pathname === '/api/substitute' ||\n url.pathname.startsWith('/api/presets/') ||\n url.pathname.startsWith('/api/icon/')\n ) {\n return handleSystemRequest(request, env);\n }\n if (url.pathname.startsWith('/api/profiles')) {\n return handleProfilesRequest(request, env, currentUser, ctx);\n }\n if (url.pathname.startsWith('/api/account') || url.pathname.startsWith('/api/admin')) {\n return handleAccountRequest(request, env, currentUser!, ctx);\n }\n return new Response(\"API Not Found\", { status: 404 });\n }\n\n // Linux Setup Script Route\n if (url.pathname === '/setup.sh') {\n const key = url.searchParams.get('key');\n if (!key || !ACCESS_KEY_REGEX.test(key)) {\n return new Response('Missing or invalid key parameter', { status: 400 });\n }\n const script = generateLinuxSetupScript(url.origin, key);\n return new Response(script, {\n headers: {\n 'Content-Type': 'text/plain; charset=utf-8',\n 'Cache-Control': 'no-cache'\n }\n });\n }\n\n // DNS-over-HTTPS (DoH) Route: /<5-12 char profile key or access point token>\n const rawKey = url.pathname.slice(1); \n const isKeyValid = ACCESS_KEY_REGEX.test(rawKey);\n const isDoHRequest = request.method === 'POST' || \n url.searchParams.has('dns') || \n request.headers.get('accept')?.includes('dns-message');\n \n if (isKeyValid && (isDoHRequest || request.method === 'OPTIONS')) {\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n status: 204,\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Accept',\n 'Access-Control-Max-Age': '86400'\n }\n });\n }\n return handleDoHRequest(request, env, ctx, rawKey);\n }\n\n // Static Assets Hosting with Single Page App (SPA) fallback\n try {\n let response = await (env as any).ASSETS.fetch(request);\n if (response.status === 404 && !url.pathname.startsWith('/assets/') && (request.method === 'GET' || request.method === 'HEAD')) {\n response = await (env as any).ASSETS.fetch(new Request(url.origin + '/', {\n method: request.method,\n headers: request.headers\n }));\n }\n\n // Ensure WASM binary assets are served with the standard MIME type\n if (url.pathname.endsWith('.wasm')) {\n const wasmHeaders = new Headers(response.headers);\n wasmHeaders.set('Content-Type', 'application/wasm');\n wasmHeaders.set('Cross-Origin-Resource-Policy', 'same-origin');\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: wasmHeaders\n });\n }\n\n const contentType = response.headers.get('Content-Type') || '';\n if (contentType.includes('text/html')) {\n let isDbMissing = false;\n try {\n isDbMissing = !env.DB;\n } catch (e) {\n isDbMissing = true;\n }\n const isJwtSecretMissing = !isUsableJwtSecret(env.JWT_SECRET);\n\n let configStr = \"{}\";\n try {\n configStr = JSON.stringify({\n nonce,\n isDbMissing,\n isJwtSecretMissing\n });\n } catch (e) { }\n \n return new HTMLRewriter()\n .on('head', {\n element(element) {\n element.prepend(`<script nonce=\"${nonce}\">window.DNS_WORKER_CONFIG = ${configStr};</script>`, { html: true });\n }\n })\n .transform(response);\n }\n\n return response;\n } catch (e) {\n return new Response(\"Asset Fetch Error\", { status: 500 });\n }\n };\n\n let response = await handleRequest();\n \n // Ensure CSRF token is set in cookies if authenticated but cookie is missing from the request\n const finalUser = currentUser as User | null;\n if (finalUser && !readCsrfCookie(request.headers.get(\"Cookie\"))) {\n const isKeepLoggedIn = finalUser.sessionId?.startsWith(\"k_\");\n try {\n const csrfToken = generateId(32);\n response.headers.append(\"Set-Cookie\", createCsrfCookie(csrfToken, env, isKeepLoggedIn));\n } catch (e) {\n // If headers are immutable (e.g. from static asset fetch), clone the response and set the header\n const newHeaders = new Headers(response.headers);\n const csrfToken = generateId(32);\n newHeaders.append(\"Set-Cookie\", createCsrfCookie(csrfToken, env, isKeepLoggedIn));\n response = new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders\n });\n }\n }\n\n // DoH binary responses must not be constrained by HTML CSP/COOP/COEP\n if (response.headers.get('Content-Type')?.includes('application/dns-message')) {\n const dohHeaders = new Headers(response.headers);\n dohHeaders.set('Access-Control-Allow-Origin', '*');\n dohHeaders.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n dohHeaders.set('Access-Control-Allow-Headers', 'Content-Type, Accept');\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: dohHeaders\n });\n }\n\n return applySecurityHeaders(response, nonce);\n },\n\n async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {\n await handleScheduled(event, env, ctx);\n }\n};\n", "export function generateLinuxSetupScript(origin: string, key: string): string {\n return `#!/bin/bash\nsudo curl -L 'https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64' -o /usr/local/bin/cloudflared\nsudo chmod +x /usr/local/bin/cloudflared\nsudo tee /etc/systemd/system/cloudflared-dns.service > /dev/null <<EOF\n[Unit]\nDescription=DNS Worker DoH Proxy\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=/usr/local/bin/cloudflared proxy-dns --upstream ${origin}/${key} --port 53\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\nEOF\nsudo systemctl daemon-reload\nsudo systemctl enable --now cloudflared-dns\necho \"DNS Worker DoH Proxy installed successfully.\"\necho \"Please change your system DNS to 127.0.0.1\"\n`;\n}\n", "import { Env } from \"../../types\";\nimport { UserModel } from \"../../models/user\";\nimport { SystemSettingsModel } from \"../../models/systemSettings\";\nimport { USERNAME_REGEX } from \"../../utils/validator\";\n\n/**\n * Handle public authentication configuration and checks\n */\nexport async function handleAuthConfigRequest(request: Request, env: Env): Promise<Response> {\n const url = new URL(request.url);\n const userModel = new UserModel(env.DB, env);\n\n // \u516C\u5F00\u914D\u7F6E\u63A5\u53E3\n if (url.pathname === '/api/auth/config' && request.method === 'GET') {\n const settingsModel = new SystemSettingsModel(env.DB);\n const [siteKey, signupEnabled, loginEnabled, hasUsers, registrationEnabled] = await Promise.all([\n settingsModel.get('turnstile_site_key'),\n settingsModel.get('turnstile_enabled_signup'),\n settingsModel.get('turnstile_enabled_login'),\n userModel.isEmpty().then((empty) => !empty).catch(() => false),\n settingsModel.get('registration_enabled')\n ]);\n return new Response(JSON.stringify({\n turnstile_site_key: siteKey,\n turnstile_enabled_signup: signupEnabled === 'true',\n turnstile_enabled_login: loginEnabled === 'true',\n optional_session_expiration_days: Number(env.OPTIONAL_SESSION_EXPIRATION_DAYS) || 7,\n has_users: hasUsers,\n registration_enabled: registrationEnabled !== 'false'\n }), {\n headers: {\n 'Content-Type': 'application/json',\n 'Cache-Control': hasUsers ? 'public, max-age=300, s-maxage=300' : 'no-store, no-cache'\n }\n });\n }\n\n // \u68C0\u67E5\u7528\u6237\u540D\u662F\u5426\u5B58\u5728\u63A5\u53E3\n if (url.pathname === '/api/auth/check-username' && request.method === 'GET') {\n const username = url.searchParams.get('username') || '';\n if (!USERNAME_REGEX.test(username)) {\n return new Response(JSON.stringify({ error: \"Invalid username\" }), { status: 400, headers: { 'Content-Type': 'application/json' } });\n }\n const exists = await userModel.getByUsername(username);\n return new Response(JSON.stringify({ exists: !!exists }), {\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env } from \"../../types\";\nimport {\n generateId,\n createSession, createRefreshTokenCookie,\n getRequestCoordinates,\n createCsrfCookie,\n getJwtSecretStatus,\n generateSessionHash\n} from \"../../lib/auth\";\nimport { importJwtSecret, signJWT } from \"../../lib/jwt\";\nimport { hashPassword } from \"../../utils/crypto\";\nimport { UserModel } from \"../../models/user\";\nimport { ProfileModel } from \"../../models/profile\";\nimport { ActivityLogModel } from \"../../models/activityLog\";\nimport { SystemSettingsModel } from \"../../models/systemSettings\";\nimport { generateRecoveryKey, hashRecoveryKey } from \"../../lib/totp\";\nimport { cacheUtils } from \"../../utils/cache\";\nimport { PASSWORD_REGEX, USERNAME_REGEX } from \"../../utils/validator\";\nimport { verifyTurnstile } from \"./utils\";\n\n/**\n * Handle user registration requests (signup)\n */\nexport async function handleAuthRegisterRequest(request: Request, env: Env): Promise<Response> {\n const userModel = new UserModel(env.DB, env);\n const activityLog = new ActivityLogModel(env.DB);\n const cache = typeof caches !== \"undefined\" ? (caches as any).default : null;\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const userAgent = request.headers.get(\"User-Agent\");\n\n if (request.headers.get(\"X-Password-Leaked\") === \"true\" || request.headers.get(\"Exposed-Credential-Check\") === \"true\") {\n return new Response(\"password_leaked\", { status: 400 });\n }\n\n if (await cacheUtils.isRateLimited(cache, `signup:${clientIp}`, 10, 60)) {\n return new Response(\"Too many attempts\", { status: 429 });\n }\n\n const { username, password, turnstileToken } = await request.json() as any;\n const settingsModel = new SystemSettingsModel(env.DB);\n const [secretKey, enabled, registrationEnabled, isDbEmpty] = await Promise.all([\n settingsModel.get('turnstile_secret_key'),\n settingsModel.get('turnstile_enabled_signup'),\n settingsModel.get('registration_enabled'),\n userModel.isEmpty()\n ]);\n\n // \u505C\u7528\u6CE8\u518C\u62E6\u622A\uFF1A\u5982\u679C\u5E93\u4E2D\u5DF2\u6709\u7528\u6237\u4E14\u7BA1\u7406\u5458\u505C\u7528\u4E86\u81EA\u4E3B\u6CE8\u518C\uFF0C\u7981\u6B62\u6CE8\u518C\n if (!isDbEmpty && registrationEnabled === 'false') {\n return new Response(\"registration_disabled\", { status: 403 });\n }\n\n if (enabled === 'true' && secretKey) {\n if (!await verifyTurnstile(turnstileToken, secretKey, clientIp)) {\n return new Response(\"Verification failed\", { status: 400 });\n }\n }\n\n if (!USERNAME_REGEX.test(username)) return new Response(\"Invalid username\", { status: 400 });\n if (!password || !PASSWORD_REGEX.test(password)) {\n return new Response(\"Password format error\", { status: 400 });\n }\n\n if (await userModel.getByUsername(username)) {\n return new Response(\"username_exists\", { status: 400 });\n }\n\n // 1. \u9A8C\u8BC1\u5730\u7406\u4FE1\u606F\uFF08\u5199\u5165\u6570\u636E\u5E93\u524D\u4E25\u683C\u68C0\u9A8C\uFF0C\u82E5\u7F3A\u5931\u7ACB\u5373\u8FD4\u56DE\uFF0C\u907F\u514D\u5199\u5165\u672A\u5B8C\u6210\u6CE8\u518C\u7684\u810F\u6570\u636E\uFF09\n const { latitude, longitude } = getRequestCoordinates(request);\n if (latitude === null || longitude === null) {\n return new Response(\"geolocation_missing\", { status: 400 });\n }\n\n // 2. \u9A8C\u8BC1 JWT_SECRET\uFF08\u5199\u5165\u6570\u636E\u5E93\u524D\u4E25\u683C\u68C0\u9A8C\uFF0C\u533A\u5206\u7F3A\u5C11\u914D\u7F6E\u4E0E\u4ECD\u4E3A\u9884\u8BBE\u503C\uFF09\n const jwtStatus = getJwtSecretStatus(env.JWT_SECRET);\n if (jwtStatus === \"missing\") {\n return new Response(\"jwt_secret_missing\", { status: 400 });\n }\n if (jwtStatus === \"preset\") {\n return new Response(\"jwt_secret_preset\", { status: 400 });\n }\n\n // 3. \u9884\u5148\u6D3E\u751F JWT CryptoKey\uFF0C\u786E\u4FDD\u52A0\u5BC6\u6A21\u5757\u53EF\u7528\n let jwtKey: CryptoKey;\n try {\n jwtKey = await importJwtSecret(env.JWT_SECRET!);\n } catch {\n return new Response(\"jwt_secret_invalid\", { status: 400 });\n }\n\n const hashedPassword = await hashPassword(password, 2);\n const userId = generateId(15);\n const cf = (request as any).cf;\n const timezone = cf?.timezone || request.headers.get(\"CF-Timezone\") || null;\n const role = isDbEmpty ? 'admin' : 'user';\n\n let userCreated = false;\n try {\n await userModel.create({ id: userId, username, passwordHash: hashedPassword, role, timezone, passwordVersion: 2 });\n userCreated = true;\n\n // Create default profile for the user\n const profileModel = new ProfileModel(env.DB);\n const defaultProfileId = generateId(6);\n const defaultRetentionDays = role === 'admin' ? 7 : 1;\n await profileModel.create({\n id: defaultProfileId,\n owner_id: userId,\n name: \"Default\",\n settings: {\n upstream: [\"https://security.cloudflare-dns.com/dns-query\"],\n ecs: { enabled: true, use_client_ip: true },\n log_retention_days: defaultRetentionDays,\n skip_log_on_pass: false,\n default_policy: \"ALLOW\",\n best_effort_ech: { enabled: false, fronting_domain: \"cloudflare-ech.com\" }\n }\n });\n\n // Generate zero-knowledge Recovery Key; store ONLY irreversible hash in DB\n const recoveryKey = generateRecoveryKey();\n const recoveryKeyHash = await hashRecoveryKey(recoveryKey);\n await userModel.updateRecoveryKeys(userId, [{ hash: recoveryKeyHash }] as any);\n\n const { session, refreshToken } = await createSession(env, userId, clientIp, userAgent, latitude, longitude, false);\n const sessionHash = await generateSessionHash(session.id, userId);\n await activityLog.record(userId, 'signup', clientIp, userAgent, undefined, sessionHash);\n const refreshCookie = createRefreshTokenCookie(refreshToken, env, false);\n const csrfToken = generateId(32);\n const csrfCookie = createCsrfCookie(csrfToken, env, false);\n\n const expMinutes = Number(env.ACCESS_TOKEN_EXPIRATION_MINUTES) || 10;\n const accessToken = await signJWT({ \n userId: userId, \n role: role, \n sessionId: session.id,\n exp: Math.floor(Date.now() / 1000) + expMinutes * 60\n }, jwtKey);\n\n const headers = new Headers();\n headers.append(\"Set-Cookie\", refreshCookie);\n headers.append(\"Set-Cookie\", csrfCookie);\n headers.append(\"Content-Type\", \"application/json\");\n return new Response(JSON.stringify({ success: true, accessToken, recoveryKey }), { headers });\n } catch (e: any) {\n if (userCreated) {\n // \u9632\u5FA1\u6027\u56DE\u6EDA\uFF1A\u6E05\u9664\u7531\u4E8E\u540E\u7EED\u6B65\u9AA4\u5931\u8D25\u9057\u7559\u7684\u672A\u5B8C\u6210\u6CE8\u518C\u810F\u6570\u636E\uFF0C\u9632\u6B62\u7528\u6237\u540D\u88AB\u9501\u6B7B\n try {\n await env.DB.prepare(\"DELETE FROM profiles WHERE owner_id = ?\").bind(userId).run();\n await env.DB.prepare(\"DELETE FROM users WHERE id = ?\").bind(userId).run();\n } catch (cleanupErr) {\n console.error(\"Failed to clean up incomplete user registration:\", cleanupErr);\n }\n }\n return new Response(e.message, { status: 400 });\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\n\nexport type ActivityAction =\n | 'signup'\n | 'login_success'\n | 'login_fail'\n | 'logout'\n | 'password_change_success'\n | 'password_change_fail'\n | 'totp_verify_success'\n | 'totp_verify_fail'\n | 'totp_setup'\n | 'totp_removed'\n | 'recovery_key_used'\n | 'recovery_key_rotated'\n | 'session_revoked'\n | 'pin_verify_success'\n | 'pin_verify_fail'\n | 'passkey_registered'\n | 'passkey_deleted'\n | 'passkey_verify_success'\n | 'passkey_verify_fail';\n\nexport interface UserActivityEntry {\n id: number;\n user_id: string;\n action: ActivityAction;\n ip_address: string | null;\n user_agent: string | null;\n timestamp: number;\n extra: string | null;\n session_id_hash: string | null;\n}\n\nexport class ActivityLogModel {\n constructor(private db: D1Database) {}\n\n /**\n * Records a user security event to the activity log.\n * @param userId - The user's ID\n * @param action - The type of security event\n * @param ip - Client IP address from CF-Connecting-IP header\n * @param userAgent - Optional User-Agent string\n * @param extra - Optional JSON-serializable extra metadata\n */\n async record(\n userId: string,\n action: ActivityAction,\n ip: string,\n userAgent?: string | null,\n extra?: Record<string, unknown>,\n sessionIdHash?: string | null\n ): Promise<void> {\n try {\n const now = Math.floor(Date.now() / 1000);\n await this.db\n .prepare(\n 'INSERT INTO user_activity_log (user_id, action, ip_address, user_agent, timestamp, extra, session_id_hash) VALUES (?, ?, ?, ?, ?, ?, ?)'\n )\n .bind(\n userId,\n action,\n ip,\n userAgent ?? null,\n now,\n extra ? JSON.stringify(extra) : null,\n sessionIdHash ?? null\n )\n .run();\n } catch (err: any) {\n console.error(`[ActivityLog] Failed to record '${action}' event for user ${userId}:`, err?.message || err);\n }\n }\n\n /**\n * Returns paginated activity log entries for a specific user.\n * @param userId - The user's ID\n * @param limit - Max records to return (default 20, max 50)\n * @param before - Cursor: only return entries with timestamp < before\n */\n async listByUser(\n userId: string,\n limit: number = 20,\n before?: number\n ): Promise<UserActivityEntry[]> {\n const clampedLimit = Math.min(limit, 50);\n\n if (before) {\n const { results } = await this.db\n .prepare(\n 'SELECT * FROM user_activity_log WHERE user_id = ? AND timestamp < ? ORDER BY timestamp DESC LIMIT ?'\n )\n .bind(userId, before, clampedLimit)\n .all<UserActivityEntry>();\n return results;\n }\n\n const { results } = await this.db\n .prepare(\n 'SELECT * FROM user_activity_log WHERE user_id = ? ORDER BY timestamp DESC LIMIT ?'\n )\n .bind(userId, clampedLimit)\n .all<UserActivityEntry>();\n return results;\n }\n}\n", "/**\n * TOTP (Time-based One-Time Password) implementation for Cloudflare Workers\n * RFC 6238 / RFC 4226 compliant, using Web Crypto API only (no Node.js dependencies)\n */\n\nimport { TOTP_TOKEN_REGEX } from \"../utils/validator\";\n\nconst BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';\n\n/**\n * Encodes a Uint8Array to a Base32 string.\n */\nfunction base32Encode(bytes: Uint8Array): string {\n let result = '';\n let buffer = 0;\n let bitsLeft = 0;\n\n for (const byte of bytes) {\n buffer = (buffer << 8) | byte;\n bitsLeft += 8;\n while (bitsLeft >= 5) {\n bitsLeft -= 5;\n result += BASE32_ALPHABET[(buffer >> bitsLeft) & 0x1f];\n }\n }\n\n if (bitsLeft > 0) {\n result += BASE32_ALPHABET[(buffer << (5 - bitsLeft)) & 0x1f];\n }\n\n return result;\n}\n\n/**\n * Decodes a Base32 string to a Uint8Array.\n */\nfunction base32Decode(input: string): Uint8Array {\n let end = input.length;\n while (end > 0 && input[end - 1] === '=') {\n end--;\n }\n const normalized = input.slice(0, end).toUpperCase();\n const bytes: number[] = [];\n let buffer = 0;\n let bitsLeft = 0;\n\n for (const char of normalized) {\n const val = BASE32_ALPHABET.indexOf(char);\n if (val === -1) continue; // skip invalid chars\n buffer = (buffer << 5) | val;\n bitsLeft += 5;\n if (bitsLeft >= 8) {\n bitsLeft -= 8;\n bytes.push((buffer >> bitsLeft) & 0xff);\n }\n }\n\n return new Uint8Array(bytes);\n}\n\n/**\n * Generates a cryptographically secure 20-byte TOTP secret encoded as Base32.\n * @returns Base32-encoded secret string (compatible with Google Authenticator)\n */\nexport function generateTOTPSecret(): string {\n const bytes = new Uint8Array(20);\n crypto.getRandomValues(bytes);\n return base32Encode(bytes);\n}\n\n/**\n * Computes an HOTP value for a given counter using HMAC-SHA1.\n * @param secret - Base32-encoded shared secret\n * @param counter - 8-byte counter value\n * @returns 6-digit OTP string\n */\nasync function computeHOTP(secret: string, counter: number): Promise<string> {\n const keyBytes = base32Decode(secret);\n const counterBytes = new Uint8Array(8);\n // Write counter as big-endian 64-bit integer\n const view = new DataView(counterBytes.buffer);\n view.setUint32(4, counter >>> 0, false);\n view.setUint32(0, Math.floor(counter / 0x100000000) >>> 0, false);\n\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n keyBytes,\n { name: 'HMAC', hash: 'SHA-1' },\n false,\n ['sign']\n );\n\n const signature = await crypto.subtle.sign('HMAC', cryptoKey, counterBytes);\n const hmac = new Uint8Array(signature);\n\n // Dynamic truncation\n const offset = hmac[hmac.length - 1] & 0x0f;\n const code =\n ((hmac[offset] & 0x7f) << 24) |\n ((hmac[offset + 1] & 0xff) << 16) |\n ((hmac[offset + 2] & 0xff) << 8) |\n (hmac[offset + 3] & 0xff);\n\n return (code % 1_000_000).toString().padStart(6, '0');\n}\n\n/**\n * Verifies a TOTP token against a secret, allowing \u00B11 time step (30s window).\n * @param secret - Base32-encoded TOTP secret\n * @param token - 6-digit OTP string from the user\n * @returns true if the token is valid within the time window\n */\nexport interface TotpVerificationResult {\n valid: boolean;\n timeStep?: number;\n}\n\n/**\n * Verifies a TOTP token against a secret and returns the matched time step for anti-replay tracking.\n *\n * @param secret - Base32-encoded TOTP secret\n * @param token - 6-digit OTP string or hashed token from the user\n * @param salt - Optional salt if token was pre-hashed by client\n * @returns TotpVerificationResult with valid status and matched timeStep\n */\nexport async function verifyTOTPWithStep(\n secret: string,\n token: string,\n salt?: string\n): Promise<TotpVerificationResult> {\n if (!secret || !token) return { valid: false };\n if (!salt && !TOTP_TOKEN_REGEX.test(token)) return { valid: false };\n\n try {\n const timeStep = Math.floor(Date.now() / 1000 / 30);\n\n // Check current step and \u00B11 adjacent steps to tolerate clock skew\n for (const delta of [-1, 0, 1]) {\n const step = timeStep + delta;\n const expected = await computeHOTP(secret, step);\n\n if (salt) {\n const data = new TextEncoder().encode(expected + salt);\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n const hashHex = Array.from(new Uint8Array(hashBuffer))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n if (hashHex === token) return { valid: true, timeStep: step };\n } else {\n if (expected === token) return { valid: true, timeStep: step };\n }\n }\n } catch (e) {\n console.error(\"[TOTP Verification Error]:\", e);\n return { valid: false };\n }\n\n return { valid: false };\n}\n\n/**\n * Verifies a TOTP token against a secret, allowing \u00B11 time step (30s window).\n * @param secret - Base32-encoded TOTP secret\n * @param token - 6-digit OTP string from the user\n * @param salt - Optional client salt for pre-hashed token\n * @returns true if the token is valid within the time window\n */\nexport async function verifyTOTP(secret: string, token: string, salt?: string): Promise<boolean> {\n const result = await verifyTOTPWithStep(secret, token, salt);\n return result.valid;\n}\n\n/**\n * Generates an otpauth:// URI for QR code generation.\n * @param secret - Base32-encoded TOTP secret\n * @param username - Account username label\n * @param issuer - Service name displayed in the authenticator app\n * @returns otpauth:// URI string\n */\nexport function getTOTPUri(secret: string, username: string, issuer: string): string {\n const label = encodeURIComponent(`${issuer}:${username}`);\n const params = new URLSearchParams({\n secret,\n issuer,\n algorithm: 'SHA1',\n digits: '6',\n period: '30',\n });\n return `otpauth://totp/${label}?${params.toString()}`;\n}\n\n/**\n * Generates a single 6-digit group where the first 5 digits are randomly generated\n * and the 6th digit is a Modulo 11 check digit ensuring the 6-digit integer % 11 === 0.\n *\n * @returns 6-digit numeric string\n */\nexport function generateRecoveryGroup(): string {\n while (true) {\n const randomBytes = new Uint32Array(1);\n crypto.getRandomValues(randomBytes);\n const val = randomBytes[0] % 100000;\n const rem = val % 11;\n if (rem < 10) {\n const first5 = val.toString().padStart(5, '0');\n const checkDigit = rem.toString();\n return `${first5}${checkDigit}`;\n }\n }\n}\n\n/**\n * Generates a 30-digit recovery key consisting of 5 groups of 6 digits,\n * separated by hyphens (e.g., \"123453-678908-246802-135798-987659\").\n *\n * @returns 30-digit formatted recovery key string\n */\nexport function generateRecoveryKey(): string {\n const groups: string[] = [];\n for (let i = 0; i < 5; i++) {\n groups.push(generateRecoveryGroup());\n }\n return groups.join('-');\n}\n\n/**\n * Generates recovery keys as plaintext.\n * Returns an array containing the 30-digit recovery key.\n *\n * @returns Array of plaintext recovery keys\n */\nexport function generateRecoveryKeys(): string[] {\n return [generateRecoveryKey()];\n}\n\n/**\n * Hashes a recovery key using SHA-256 for secure storage.\n * Strips hyphens and whitespace before hashing for robust comparison.\n *\n * @param key - Plaintext recovery key\n * @returns Hex-encoded SHA-256 hash\n */\nexport async function hashRecoveryKey(key: string): Promise<string> {\n const normalized = key.replace(/[-\\s]/g, '').toUpperCase().trim();\n const data = new TextEncoder().encode(normalized);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return Array.from(new Uint8Array(hash))\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Item representing a stored recovery key in the database.\n */\nexport interface StoredRecoveryKeyItem {\n key: string;\n hash: string;\n}\n\n/**\n * Verifies a plaintext recovery key against an array of stored recovery items or hashes.\n * Returns the index of the matching key, or -1 if no match.\n *\n * @param key - Plaintext key entered by the user\n * @param storedItems - Array of recovery items ({ key, hash }) or legacy SHA-256 hex strings\n * @returns Index of matching item, or -1\n */\nexport async function findMatchingRecoveryKey(key: string, storedItems: any[]): Promise<number> {\n const inputHash = await hashRecoveryKey(key);\n const normalizedInput = key.replace(/[-\\s]/g, '').toUpperCase().trim();\n\n return storedItems.findIndex(item => {\n if (!item) return false;\n if (typeof item === 'string') {\n if (item === inputHash) return true;\n const normalizedItem = item.replace(/[-\\s]/g, '').toUpperCase().trim();\n return normalizedItem === normalizedInput;\n } else if (typeof item === 'object') {\n if (item.hash && item.hash === inputHash) return true;\n if (item.key) {\n const normalizedItem = String(item.key).replace(/[-\\s]/g, '').toUpperCase().trim();\n return normalizedItem === normalizedInput;\n }\n }\n return false;\n });\n}\n\n\n", "/**\n * Verify Cloudflare Turnstile token\n */\nexport async function verifyTurnstile(token: string, secret: string, ip: string): Promise<boolean> {\n if (!token || !secret) return false;\n try {\n const params = new URLSearchParams();\n params.append('secret', secret);\n params.append('response', token);\n params.append('remoteip', ip);\n const result = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {\n method: 'POST',\n body: params,\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded'\n },\n signal: AbortSignal.timeout(5000)\n });\n const outcome = await result.json() as { success?: boolean };\n return !!outcome.success;\n } catch (e) {\n return false;\n }\n}\n", "import { Env } from \"../../types\";\nimport {\n generateId,\n createSession, createRefreshTokenCookie,\n createPreauthSession, createPreauthCookie,\n validatePreauthSession, invalidatePreauthSession, clearPreauthCookie,\n readPreauthCookie,\n recordFailedPreauthAttempt,\n getRequestCoordinates,\n createCsrfCookie,\n getOrCreateJwtSecret,\n extractSaltHex, hmacSha256,\n generateSessionHash\n} from \"../../lib/auth\";\nimport { importJwtSecret, signJWT } from \"../../lib/jwt\";\nimport { verifyPassword } from \"../../utils/crypto\";\nimport { verifyTOTPWithStep, findMatchingRecoveryKey, generateRecoveryKey, hashRecoveryKey } from \"../../lib/totp\";\nimport { UserModel } from \"../../models/user\";\nimport { PasskeyModel } from \"../../models/passkey\";\nimport { ActivityLogModel } from \"../../models/activityLog\";\nimport { SystemSettingsModel } from \"../../models/systemSettings\";\nimport { cacheUtils } from \"../../utils/cache\";\nimport { verifyTurnstile } from \"./utils\";\nimport { generateWebAuthnChallenge, verifyAuthenticationResponse } from \"../../lib/webauthn\";\n\n/**\n * Handle prelogin and login requests\n */\nexport async function handleLoginRequest(request: Request, env: Env): Promise<Response> {\n const url = new URL(request.url);\n const userModel = new UserModel(env.DB, env);\n const activityLog = new ActivityLogModel(env.DB);\n const cache = (caches as any).default;\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const userAgent = request.headers.get(\"User-Agent\");\n\n // \u7B2C\u4E00\u6B65\uFF1A\u9884\u767B\u5F55 (\u53EA\u9A8C\u8BC1\u7528\u6237\u540D\u548C Turnstile)\n if (url.pathname === '/api/auth/prelogin' && request.method === 'POST') {\n if (await cacheUtils.isRateLimited(cache, `login_fail:${clientIp}`, 10, 900)) {\n return new Response(\"Too many attempts\", { status: 429 });\n }\n const { username, turnstileToken } = await request.json() as any;\n\n const settingsModel = new SystemSettingsModel(env.DB);\n const [secretKey, enabled] = await Promise.all([\n settingsModel.get('turnstile_secret_key'),\n settingsModel.get('turnstile_enabled_login')\n ]);\n if (enabled === 'true' && secretKey) {\n if (!await verifyTurnstile(turnstileToken, secretKey, clientIp)) {\n return new Response(\"Verification failed\", { status: 400 });\n }\n }\n\n const user = await userModel.getByUsername(username);\n if (!user) {\n const isEmpty = await userModel.isEmpty().catch(() => false);\n if (isEmpty) {\n return new Response(\"no_users_registered\", { status: 404 });\n }\n await cacheUtils.isRateLimited(cache, `login_fail:${clientIp}`, 100, 900); // penalty\n return new Response(\"User not found\", { status: 404 });\n }\n\n // \u7B7E\u53D1 preauth session\n const preauthToken = await createPreauthSession(env, user.id);\n const preauthCookie = createPreauthCookie(preauthToken, env);\n\n const requires_password = !user.totp_skip_password;\n const requires_totp = !!user.totp_enabled;\n const password_version = user.password_version ?? 1;\n\n // Check if user has registered passkeys for MFA\n const passkeyModel = new PasskeyModel(env.DB);\n const passkeys = await passkeyModel.listByUser(user.id);\n const has_passkey = passkeys.length > 0;\n let passkey_options: any = null;\n let passkey_challenge: string | null = null;\n\n if (has_passkey) {\n passkey_challenge = generateWebAuthnChallenge();\n passkey_options = {\n challenge: passkey_challenge,\n rpId: url.hostname,\n allowCredentials: passkeys.map(p => ({\n id: p.credential_id,\n type: \"public-key\",\n transports: p.transports ? JSON.parse(p.transports) : undefined\n })),\n timeout: 60000,\n userVerification: \"preferred\"\n };\n }\n\n // Generate nonce for Step 2 challenge-response verification\n const nonce = generateId(32);\n let serverSalt: string | null = null;\n if (password_version === 2 && user.hashed_password) {\n serverSalt = extractSaltHex(user.hashed_password);\n }\n\n const preauthTtl = Number(env.PREAUTH_TTL_SECONDS) || 300;\n await cacheUtils.set(cache, `preauth_state:${preauthToken}`, {\n nonce,\n failedAttempts: 0,\n passkeyChallenge: passkey_challenge,\n rpId: url.hostname\n }, preauthTtl);\n\n return new Response(JSON.stringify({\n requires_password,\n requires_totp,\n has_passkey,\n passkey_options,\n password_version,\n nonce,\n serverSalt\n }), {\n headers: {\n \"Set-Cookie\": preauthCookie,\n \"Content-Type\": \"application/json\"\n }\n });\n }\n\n // \u7B2C\u4E8C\u6B65\uFF1A\u6B63\u5F0F\u767B\u5F55 (\u63D0\u4EA4\u5BC6\u7801\u548C/\u6216 TOTP)\n if (url.pathname === '/api/auth/login' && request.method === 'POST') {\n if (request.headers.get(\"X-Password-Leaked\") === \"true\" || request.headers.get(\"Exposed-Credential-Check\") === \"true\") {\n return new Response(\"password_leaked\", { status: 400 });\n }\n if (await cacheUtils.isRateLimited(cache, `login_fail:${clientIp}`, 10, 900)) {\n return new Response(\"Too many login attempts\", { status: 429 });\n }\n\n const preauthToken = readPreauthCookie(request.headers.get(\"Cookie\"));\n if (!preauthToken) return new Response(\"Pre-auth session missing or expired\", { status: 401 });\n\n const userId = await validatePreauthSession(env, preauthToken);\n if (!userId) return new Response(\"Session expired, please start over\", { status: 401 });\n\n const preauthState = await cacheUtils.get<{\n nonce: string;\n failedAttempts: number;\n passkeyChallenge?: string | null;\n rpId?: string;\n }>(cache, `preauth_state:${preauthToken}`);\n if (!preauthState) {\n await invalidatePreauthSession(env, preauthToken);\n return new Response(\"Session expired, please start over\", { status: 401 });\n }\n const { nonce } = preauthState;\n\n const user = await userModel.getById(userId);\n if (!user) return new Response(\"User not found\", { status: 404 });\n\n const { password, totpTokenHash, totpSalt, recoveryKey, passkeyAssertion, keepLoggedIn } = await request.json() as any;\n\n let needsMigration = false;\n\n // Check MFA configuration & whether user chose \"Other options\" to authenticate via MFA directly\n const passkeyModel = new PasskeyModel(env.DB);\n const userPasskeys = await passkeyModel.listByUser(userId);\n const hasPasskeys = userPasskeys.length > 0;\n const hasTotp = !!user.totp_enabled;\n const hasRecoveryKeys = !!user.totp_recovery_keys;\n const requiresMfa = hasTotp || hasPasskeys;\n\n // Hardware-backed Passkeys with WebAuthn user verification can serve as an independent passwordless factor.\n // In contrast, TOTP codes and emergency recovery keys are strictly second factors and must never bypass\n // password verification unless the user explicitly configured passwordless mode (user.totp_skip_password === 1).\n const isPasswordlessPasskey = hasPasskeys && !!passkeyAssertion;\n const skipPasswordAllowed = !!user.totp_skip_password || isPasswordlessPasskey;\n\n // \u9A8C\u8BC1\u5BC6\u7801 (\u82E5\u7528\u6237\u672A\u542F\u7528\u514D\u5BC6\u4E14\u672A\u63D0\u4F9B\u6709\u6548 Passkey \u51ED\u636E\uFF0C\u5F3A\u5236\u6821\u9A8C\u4E3B\u5BC6\u7801)\n if (!skipPasswordAllowed) {\n if (!password) {\n return new Response(\"Password is required\", { status: 400 });\n }\n if ((user.password_version ?? 1) === 2) {\n // Nonce challenge-response validation\n const expectedResponse = await hmacSha256(user.hashed_password, nonce);\n if (password !== expectedResponse) {\n await cacheUtils.isRateLimited(cache, `login_fail:${clientIp}`, 100, 900);\n await activityLog.record(userId, 'login_fail', clientIp, userAgent, { reason: 'wrong_password' });\n const remaining = await recordFailedPreauthAttempt(cache, preauthToken, env);\n if (remaining <= 0) {\n return new Response(\"Invalid password\", { status: 400 });\n } else {\n return new Response(`Invalid password. ${remaining} attempt${remaining > 1 ? 's' : ''} remaining.`, { status: 400 });\n }\n }\n } else {\n // Plaintext validation (v1)\n const passwordValid = await verifyPassword(password, user.hashed_password, 1);\n if (!passwordValid) {\n await cacheUtils.isRateLimited(cache, `login_fail:${clientIp}`, 100, 900);\n await activityLog.record(userId, 'login_fail', clientIp, userAgent, { reason: 'wrong_password' });\n const remaining = await recordFailedPreauthAttempt(cache, preauthToken, env);\n if (remaining <= 0) {\n return new Response(\"Invalid password\", { status: 400 });\n } else {\n return new Response(`Invalid password. ${remaining} attempt${remaining > 1 ? 's' : ''} remaining.`, { status: 400 });\n }\n }\n needsMigration = true;\n }\n }\n\n let isTotpSuccess = false;\n let isRecoverySuccess = false;\n let isPasskeySuccess = false;\n let recoveryRemaining = 0;\n let rotatedRecoveryKey: string | undefined = undefined;\n\n if (requiresMfa) {\n if (passkeyAssertion && preauthState.passkeyChallenge) {\n // \u5C1D\u8BD5\u901A\u8FC7\u901A\u884C\u5BC6\u94A5\u9A8C\u8BC1\n const credentialId = passkeyAssertion.id;\n const passkey = userPasskeys.find(p => p.credential_id === credentialId);\n if (!passkey) {\n await activityLog.record(userId, 'passkey_verify_fail', clientIp, userAgent, { reason: 'credential_not_found' });\n const remaining = await recordFailedPreauthAttempt(cache, preauthToken, env);\n if (remaining <= 0) {\n return new Response(\"Invalid Passkey\", { status: 400 });\n } else {\n return new Response(`Invalid Passkey. ${remaining} attempt${remaining > 1 ? 's' : ''} remaining.`, { status: 400 });\n }\n }\n\n try {\n const { signCount } = await verifyAuthenticationResponse({\n clientDataJSON: passkeyAssertion.response.clientDataJSON,\n authenticatorData: passkeyAssertion.response.authenticatorData,\n signature: passkeyAssertion.response.signature,\n publicKeySpki: passkey.public_key,\n algorithm: passkey.algorithm,\n expectedChallenge: preauthState.passkeyChallenge,\n expectedOrigin: `${url.protocol}//${url.host}`,\n expectedRpId: preauthState.rpId || url.hostname,\n previousSignCount: passkey.sign_count\n });\n\n await passkeyModel.updateUsage(passkey.id, signCount);\n isPasskeySuccess = true;\n } catch (err: any) {\n console.warn(\"[Passkey Login] Verification failed:\", err.message || err);\n await activityLog.record(userId, 'passkey_verify_fail', clientIp, userAgent, { reason: err.message });\n const remaining = await recordFailedPreauthAttempt(cache, preauthToken, env);\n if (remaining <= 0) {\n return new Response(\"Invalid Passkey signature\", { status: 400 });\n } else {\n return new Response(`Invalid Passkey signature. ${remaining} attempt${remaining > 1 ? 's' : ''} remaining.`, { status: 400 });\n }\n }\n } else if ((hasTotp || hasPasskeys || hasRecoveryKeys) && recoveryKey) {\n let storedHashes: any[] = [];\n try {\n const raw = user.totp_recovery_keys;\n storedHashes = typeof raw === 'string' ? JSON.parse(raw || '[]') : (raw || []);\n if (!Array.isArray(storedHashes)) storedHashes = [storedHashes];\n } catch { }\n const matchIndex = await findMatchingRecoveryKey(recoveryKey, storedHashes);\n if (matchIndex === -1) {\n await activityLog.record(userId, 'totp_verify_fail', clientIp, userAgent, { method: 'recovery_key' });\n const remaining = await recordFailedPreauthAttempt(cache, preauthToken, env);\n if (remaining <= 0) {\n return new Response(\"Invalid recovery key\", { status: 400 });\n } else {\n return new Response(`Invalid recovery key. ${remaining} attempt${remaining > 1 ? 's' : ''} remaining.`, { status: 400 });\n }\n }\n // \u6062\u590D\u5BC6\u94A5\u5355\u6B21\u4F7F\u7528\u540E\u7ACB\u5373\u8F6E\u6362\uFF0C\u65E7\u5BC6\u94A5\u5931\u6548\n const rotated = generateRecoveryKey();\n const rotatedHash = await hashRecoveryKey(rotated);\n await userModel.updateRecoveryKeys(userId, [{ hash: rotatedHash }] as any);\n rotatedRecoveryKey = rotated;\n isRecoverySuccess = true;\n recoveryRemaining = 1;\n } else if (hasTotp && totpTokenHash) {\n const totpResult = await verifyTOTPWithStep(user.totp_secret || '', totpTokenHash, totpSalt);\n if (!totpResult.valid) {\n await activityLog.record(userId, 'totp_verify_fail', clientIp, userAgent);\n const remaining = await recordFailedPreauthAttempt(cache, preauthToken, env);\n if (remaining <= 0) {\n return new Response(\"Invalid TOTP code\", { status: 400 });\n } else {\n return new Response(`Invalid TOTP code. ${remaining} attempt${remaining > 1 ? 's' : ''} remaining.`, { status: 400 });\n }\n }\n\n // Anti-Replay: Reject token if this time step has already been consumed for this user\n if (totpResult.timeStep !== undefined) {\n const replayKey = `totp_used:${userId}:${totpResult.timeStep}`;\n if (await cacheUtils.get(cache, replayKey)) {\n await activityLog.record(userId, 'totp_verify_fail', clientIp, userAgent, { reason: 'replay_blocked' });\n return new Response(\"TOTP code has already been used. Please wait for the next 30-second code.\", { status: 400 });\n }\n await cacheUtils.set(cache, replayKey, 1, 90);\n }\n\n isTotpSuccess = true;\n } else {\n const remaining = await recordFailedPreauthAttempt(cache, preauthToken, env);\n if (remaining <= 0) {\n return new Response(\"Missing MFA verification\", { status: 400 });\n } else {\n return new Response(`Missing MFA verification (Passkey, TOTP code, or recovery key). ${remaining} attempt${remaining > 1 ? 's' : ''} remaining.`, { status: 400 });\n }\n }\n }\n\n // \u6240\u6709\u9A8C\u8BC1\u901A\u8FC7\uFF0C\u5DF2\u6D88\u8017 preauthToken \u9881\u53D1\u6B63\u5F0F Session\n await invalidatePreauthSession(env, preauthToken);\n await cacheUtils.delete(cache, `preauth_state:${preauthToken}`);\n await cacheUtils.delete(cache, `ratelimit:login_fail:${clientIp}`);\n\n const { latitude, longitude } = getRequestCoordinates(request);\n if (latitude === null || longitude === null) {\n return new Response(\"geolocation_missing\", { status: 400 });\n }\n const { session, refreshToken } = await createSession(env, userId, clientIp, userAgent, latitude, longitude, !!keepLoggedIn);\n const sessionHash = await generateSessionHash(session.id, userId);\n\n if (isPasskeySuccess) {\n await activityLog.record(userId, 'passkey_verify_success', clientIp, userAgent, undefined, sessionHash);\n } else if (isTotpSuccess) {\n await activityLog.record(userId, 'totp_verify_success', clientIp, userAgent, undefined, sessionHash);\n } else if (isRecoverySuccess) {\n await activityLog.record(userId, 'recovery_key_used', clientIp, userAgent, { remaining: recoveryRemaining }, sessionHash);\n }\n await activityLog.record(userId, 'login_success', clientIp, userAgent, undefined, sessionHash);\n\n const csrfToken = generateId(32);\n const csrfCookie = createCsrfCookie(csrfToken, env, !!keepLoggedIn);\n\n const secret = await getOrCreateJwtSecret(env);\n const jwtKey = await importJwtSecret(secret);\n const expMinutes = Number(env.ACCESS_TOKEN_EXPIRATION_MINUTES) || 10;\n const accessToken = await signJWT({ \n userId: userId, \n role: user.role, \n sessionId: session.id,\n exp: Math.floor(Date.now() / 1000) + expMinutes * 60\n }, jwtKey);\n\n const headers = new Headers({ \"Content-Type\": \"application/json\" });\n headers.append(\"Set-Cookie\", createRefreshTokenCookie(refreshToken, env, !!keepLoggedIn));\n headers.append(\"Set-Cookie\", csrfCookie);\n headers.append(\"Set-Cookie\", clearPreauthCookie());\n \n return new Response(JSON.stringify({ success: true, accessToken, needsMigration, rotatedRecoveryKey }), { headers });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { Passkey } from \"../types\";\nimport { generateId } from \"../lib/auth\";\n\nexport class PasskeyModel {\n constructor(private db: D1Database) {}\n\n /**\n * Lists all passkeys registered for a user, ordered by creation date descending.\n */\n async listByUser(userId: string): Promise<Passkey[]> {\n const { results } = await this.db.prepare(\n \"SELECT * FROM passkeys WHERE user_id = ? ORDER BY created_at DESC\"\n ).bind(userId).all<Passkey>();\n return results;\n }\n\n /**\n * Gets a specific passkey by its internal ID and user ID.\n */\n async getById(id: string, userId: string): Promise<Passkey | null> {\n return await this.db.prepare(\n \"SELECT * FROM passkeys WHERE id = ? AND user_id = ?\"\n ).bind(id, userId).first<Passkey>();\n }\n\n /**\n * Finds a passkey by its WebAuthn credential ID across all users.\n */\n async getByCredentialId(credentialId: string): Promise<Passkey | null> {\n return await this.db.prepare(\n \"SELECT * FROM passkeys WHERE credential_id = ?\"\n ).bind(credentialId).first<Passkey>();\n }\n\n /**\n * Finds a passkey belonging to a specific user by its internal ID or WebAuthn credential ID.\n * Handles base64url padding differences defensively.\n */\n async getByIdOrCredentialId(idOrCredId: string, userId: string): Promise<Passkey | null> {\n const direct = await this.db.prepare(\n \"SELECT * FROM passkeys WHERE (id = ? OR credential_id = ?) AND user_id = ?\"\n ).bind(idOrCredId, idOrCredId, userId).first<Passkey>();\n if (direct) return direct;\n\n // Fallback: match by normalized base64url credential ID\n const userPasskeys = await this.listByUser(userId);\n const cleanId = idOrCredId.replace(/=/g, \"\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\");\n return userPasskeys.find((p) => {\n const pClean = p.credential_id.replace(/=/g, \"\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\");\n return p.id === idOrCredId || pClean === cleanId;\n }) || null;\n }\n\n /**\n * Registers a new passkey credential.\n */\n async create(data: {\n userId: string;\n name: string;\n credentialId: string;\n publicKey: string;\n algorithm: number;\n signCount: number;\n transports?: string[] | null;\n aaguid?: string | null;\n }): Promise<Passkey> {\n const id = generateId(12);\n const now = Math.floor(Date.now() / 1000);\n const transportsJson = data.transports ? JSON.stringify(data.transports) : null;\n\n await this.db.prepare(\n `INSERT INTO passkeys (id, user_id, name, credential_id, public_key, algorithm, sign_count, transports, aaguid, created_at, last_used_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n ).bind(\n id,\n data.userId,\n data.name,\n data.credentialId,\n data.publicKey,\n data.algorithm,\n data.signCount,\n transportsJson,\n data.aaguid || null,\n now,\n null\n ).run();\n\n return {\n id,\n user_id: data.userId,\n name: data.name,\n credential_id: data.credentialId,\n public_key: data.publicKey,\n algorithm: data.algorithm,\n sign_count: data.signCount,\n transports: transportsJson,\n aaguid: data.aaguid || null,\n created_at: now,\n last_used_at: null\n };\n }\n\n /**\n * Updates the friendly label/name of a passkey.\n */\n async updateName(id: string, userId: string, name: string): Promise<boolean> {\n const res = await this.db.prepare(\n \"UPDATE passkeys SET name = ? WHERE id = ? AND user_id = ?\"\n ).bind(name, id, userId).run();\n return res.success;\n }\n\n /**\n * Updates the sign counter and last used timestamp after successful authentication.\n */\n async updateUsage(id: string, signCount: number): Promise<boolean> {\n const now = Math.floor(Date.now() / 1000);\n const res = await this.db.prepare(\n \"UPDATE passkeys SET sign_count = ?, last_used_at = ? WHERE id = ?\"\n ).bind(signCount, now, id).run();\n return res.success;\n }\n\n /**\n * Deletes a passkey by ID.\n */\n async delete(id: string, userId: string): Promise<boolean> {\n const res = await this.db.prepare(\n \"DELETE FROM passkeys WHERE id = ? AND user_id = ?\"\n ).bind(id, userId).run();\n return res.success;\n }\n\n /**\n * Returns the count of passkeys registered by a user.\n */\n async countByUser(userId: string): Promise<number> {\n const res = await this.db.prepare(\n \"SELECT COUNT(*) as count FROM passkeys WHERE user_id = ?\"\n ).bind(userId).first<{ count: number }>();\n return res?.count || 0;\n }\n}\n", "/**\n * Encodes a buffer or byte array into a Base64URL string (RFC 4648 \u00A75).\n *\n * @param buffer - Raw bytes or ArrayBuffer to encode.\n * @returns Base64URL encoded string.\n */\nexport function base64UrlEncode(buffer: Uint8Array | ArrayBuffer): string {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\n/**\n * Decodes a Base64URL string into a Uint8Array.\n *\n * @param str - Base64URL string to decode.\n * @returns Decoded byte array.\n */\nexport function base64UrlDecode(str: string): Uint8Array {\n let base64 = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4 !== 0) {\n base64 += \"=\";\n }\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n/**\n * Generates a high-entropy random challenge for WebAuthn ceremony.\n *\n * @returns 32-byte Base64URL challenge string.\n */\nexport function generateWebAuthnChallenge(): string {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n\n/**\n * Converts an ASN.1 DER-encoded ECDSA signature to raw IEEE P1363 (r || s) format\n * expected by Web Crypto API `subtle.verify`.\n *\n * @param der - ASN.1 DER-encoded signature bytes.\n * @returns 64-byte raw IEEE P1363 signature (32 bytes r + 32 bytes s).\n */\nexport function derToP1363(der: Uint8Array): Uint8Array {\n if (der[0] !== 0x30) {\n if (der.length === 64) return der;\n throw new Error(\"Invalid ECDSA signature: expected ASN.1 SEQUENCE\");\n }\n\n let offset = 2;\n if (der[1] & 0x80) {\n offset = 2 + (der[1] & 0x7f);\n }\n\n // Read r\n if (der[offset++] !== 0x02) throw new Error(\"Expected INTEGER for r in signature\");\n const rLen = der[offset++];\n let r = der.subarray(offset, offset + rLen);\n offset += rLen;\n\n // Read s\n if (der[offset++] !== 0x02) throw new Error(\"Expected INTEGER for s in signature\");\n const sLen = der[offset++];\n let s = der.subarray(offset, offset + sLen);\n\n // Strip leading zero padding if added to keep value positive in two's complement\n while (r.length > 32 && r[0] === 0x00) r = r.subarray(1);\n while (s.length > 32 && s[0] === 0x00) s = s.subarray(1);\n\n const p1363 = new Uint8Array(64);\n p1363.set(r, 32 - r.length);\n p1363.set(s, 64 - s.length);\n return p1363;\n}\n", "/**\n * Represents any valid decoded CBOR value type.\n */\nexport type CborValue =\n | number\n | bigint\n | string\n | boolean\n | null\n | undefined\n | Uint8Array\n | CborValue[]\n | { [key: string | number]: CborValue };\n\n/**\n * Result structure returned by decodeCbor.\n */\nexport interface CborDecodeResult {\n value: any;\n bytesRead: number;\n}\n\n/**\n * Minimal, robust CBOR decoder tailored for WebAuthn attestationObject and COSE key structures (RFC 8949).\n *\n * @param data - Raw byte array containing CBOR data.\n * @returns Object with parsed value and total bytes consumed.\n */\nexport function decodeCbor(data: Uint8Array): CborDecodeResult {\n let offset = 0;\n\n function readUint(info: number): number | bigint {\n if (info < 24) return info;\n if (info === 24) return data[offset++];\n if (info === 25) {\n const val = (data[offset] << 8) | data[offset + 1];\n offset += 2;\n return val;\n }\n if (info === 26) {\n const val =\n ((data[offset] << 24) >>> 0) +\n (data[offset + 1] << 16) +\n (data[offset + 2] << 8) +\n data[offset + 3];\n offset += 4;\n return val;\n }\n if (info === 27) {\n const hi = BigInt(\n ((data[offset] << 24) >>> 0) +\n (data[offset + 1] << 16) +\n (data[offset + 2] << 8) +\n data[offset + 3]\n );\n const lo = BigInt(\n ((data[offset + 4] << 24) >>> 0) +\n (data[offset + 5] << 16) +\n (data[offset + 6] << 8) +\n data[offset + 7]\n );\n offset += 8;\n return (hi << 32n) | lo;\n }\n throw new Error(`Unsupported CBOR uint info: ${info}`);\n }\n\n function parseItem(): any {\n if (offset >= data.length) throw new Error(\"Unexpected end of CBOR data\");\n const initialByte = data[offset++];\n const majorType = initialByte >> 5;\n const info = initialByte & 0x1f;\n\n switch (majorType) {\n case 0: {\n // unsigned integer\n const val = readUint(info);\n return typeof val === \"bigint\" ? Number(val) : val;\n }\n case 1: {\n // negative integer (-1 - n)\n const val = readUint(info);\n return typeof val === \"bigint\" ? Number(-1n - val) : -1 - Number(val);\n }\n case 2: {\n // byte string\n const len = Number(readUint(info));\n const slice = data.subarray(offset, offset + len);\n offset += len;\n return slice;\n }\n case 3: {\n // text string\n const len = Number(readUint(info));\n const slice = data.subarray(offset, offset + len);\n offset += len;\n return new TextDecoder().decode(slice);\n }\n case 4: {\n // array\n const len = Number(readUint(info));\n const arr: any[] = [];\n for (let i = 0; i < len; i++) {\n arr.push(parseItem());\n }\n return arr;\n }\n case 5: {\n // map\n const len = Number(readUint(info));\n const map: Record<string | number, any> = {};\n for (let i = 0; i < len; i++) {\n const key = parseItem();\n const val = parseItem();\n map[key] = val;\n }\n return map;\n }\n case 7: {\n // simple values\n if (info === 20) return false;\n if (info === 21) return true;\n if (info === 22) return null;\n return undefined;\n }\n default:\n throw new Error(`Unsupported CBOR major type: ${majorType}`);\n }\n }\n\n const value = parseItem();\n return { value, bytesRead: offset };\n}\n", "import { base64UrlEncode, base64UrlDecode } from \"./encoding\";\nimport { decodeCbor } from \"./cbor\";\nimport { ParsedAttestation, VerifyRegistrationOptions } from \"./types\";\n\n/**\n * Parses the WebAuthn authenticatorData structure from an attestationObject.\n * Extracts flags, AAGUID, credential ID, and exports the public key as SPKI Base64URL.\n *\n * @param authData - Raw bytes of authenticatorData.\n * @returns Parsed attestation details with SPKI public key.\n */\nexport async function parseAttestationAuthData(\n authData: Uint8Array\n): Promise<ParsedAttestation> {\n if (authData.length < 37) {\n throw new Error(\"Invalid authenticatorData length\");\n }\n\n const rpIdHash = authData.subarray(0, 32);\n const flags = authData[32];\n const signCount =\n ((authData[33] << 24) >>> 0) +\n (authData[34] << 16) +\n (authData[35] << 8) +\n authData[36];\n\n // Bit 6 must be set for Attested Credential Data\n const hasAttestedCredData = Boolean(flags & 0x40);\n if (!hasAttestedCredData) {\n throw new Error(\"Attestation does not contain attested credential data\");\n }\n\n let offset = 37;\n const aaguidBytes = authData.subarray(offset, offset + 16);\n offset += 16;\n const aaguid = Array.from(aaguidBytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n const credIdLen = (authData[offset] << 8) | authData[offset + 1];\n offset += 2;\n\n const credIdBytes = authData.subarray(offset, offset + credIdLen);\n offset += credIdLen;\n const credentialId = base64UrlEncode(credIdBytes);\n\n // Remaining bytes contain the COSE Key encoded in CBOR\n const coseCborData = authData.subarray(offset);\n const { value: coseKey } = decodeCbor(coseCborData);\n\n // Key 3 is algorithm: -7 = ES256, -257 = RS256\n const alg = coseKey[3] ?? -7;\n let publicKeySpki = \"\";\n\n if (alg === -7) {\n // ES256: Key 1=2 (EC2), Key -1=1 (P-256), Key -2=x (32 bytes), Key -3=y (32 bytes)\n const x = coseKey[-2];\n const y = coseKey[-3];\n if (!x || !y || x.length !== 32 || y.length !== 32) {\n throw new Error(\"Invalid EC2 COSE public key components\");\n }\n\n const uncompressedPoint = new Uint8Array(65);\n uncompressedPoint[0] = 0x04;\n uncompressedPoint.set(x, 1);\n uncompressedPoint.set(y, 33);\n\n const cryptoKey = await crypto.subtle.importKey(\n \"raw\",\n uncompressedPoint,\n { name: \"ECDSA\", namedCurve: \"P-256\" },\n true,\n [\"verify\"]\n );\n\n const spkiBuffer = (await crypto.subtle.exportKey(\"spki\", cryptoKey)) as ArrayBuffer;\n publicKeySpki = base64UrlEncode(spkiBuffer);\n } else if (alg === -257) {\n // RS256: Key 1=3 (RSA), Key -1=n, Key -2=e\n const n = coseKey[-1];\n const e = coseKey[-2];\n if (!n || !e) {\n throw new Error(\"Invalid RSA COSE public key components\");\n }\n\n const jwk = {\n kty: \"RSA\",\n n: base64UrlEncode(n),\n e: base64UrlEncode(e),\n alg: \"RS256\",\n ext: true\n };\n\n const cryptoKey = await crypto.subtle.importKey(\n \"jwk\",\n jwk,\n { name: \"RSASSA-PKCS1-v1_5\", hash: \"SHA-256\" },\n true,\n [\"verify\"]\n );\n\n const spkiBuffer = (await crypto.subtle.exportKey(\"spki\", cryptoKey)) as ArrayBuffer;\n publicKeySpki = base64UrlEncode(spkiBuffer);\n } else {\n throw new Error(`Unsupported COSE algorithm: ${alg}`);\n }\n\n return {\n rpIdHash,\n flags,\n signCount,\n aaguid,\n credentialId,\n publicKeySpki,\n algorithm: alg\n };\n}\n\n/**\n * Validates a client's WebAuthn registration response (Ceremony 1: Attestation).\n *\n * @param options - Verification options including client data, attestation object, and expected values.\n * @returns Parsed attestation containing verified credential ID and public key.\n */\nexport async function verifyRegistrationResponse(\n options: VerifyRegistrationOptions\n): Promise<ParsedAttestation> {\n const {\n clientDataJSON,\n attestationObject,\n expectedChallenge,\n expectedOrigin,\n expectedRpId\n } = options;\n\n // 1. Verify clientDataJSON\n const clientDataBytes = base64UrlDecode(clientDataJSON);\n const clientData = JSON.parse(new TextDecoder().decode(clientDataBytes));\n\n if (clientData.type !== \"webauthn.create\") {\n throw new Error(`Invalid clientData type: ${clientData.type}`);\n }\n\n if (clientData.challenge !== expectedChallenge) {\n throw new Error(\"WebAuthn challenge mismatch\");\n }\n\n const clientOriginUrl = new URL(clientData.origin);\n const expectedOriginUrl = expectedOrigin.includes(\"://\")\n ? new URL(expectedOrigin)\n : new URL(`https://${expectedOrigin}`);\n\n if (clientOriginUrl.origin.toLowerCase() !== expectedOriginUrl.origin.toLowerCase()) {\n throw new Error(\n `WebAuthn origin mismatch: expected ${expectedOriginUrl.origin}, received ${clientOriginUrl.origin}`\n );\n }\n\n const isLocalhost = clientOriginUrl.hostname === \"localhost\" || clientOriginUrl.hostname === \"127.0.0.1\";\n if (!isLocalhost && clientOriginUrl.protocol !== \"https:\") {\n throw new Error(`WebAuthn requires HTTPS origin, received: ${clientOriginUrl.protocol}`);\n }\n\n // 2. Decode attestationObject\n const attestationBytes = base64UrlDecode(attestationObject);\n const { value: attestation } = decodeCbor(attestationBytes);\n\n if (!attestation.authData) {\n throw new Error(\"Missing authData in attestationObject\");\n }\n\n const parsed = await parseAttestationAuthData(attestation.authData);\n\n // 3. Verify RP ID hash\n const expectedRpIdHash = new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(expectedRpId))\n );\n for (let i = 0; i < 32; i++) {\n if (parsed.rpIdHash[i] !== expectedRpIdHash[i]) {\n throw new Error(\"WebAuthn rpIdHash mismatch\");\n }\n }\n\n // 4. Verify User Presence (UP) flag\n if (!(parsed.flags & 0x01)) {\n throw new Error(\"User Presence (UP) flag not set\");\n }\n\n return parsed;\n}\n", "import { base64UrlDecode, derToP1363 } from \"./encoding\";\nimport { VerifyAuthenticationOptions } from \"./types\";\n\n/**\n * Validates a client's WebAuthn assertion (Ceremony 2: Authentication).\n *\n * Verifies:\n * 1. clientDataJSON (type 'webauthn.get', challenge, and origin).\n * 2. authenticatorData (RP ID hash, UP flag, monotonic sign count check).\n * 3. Cryptographic signature over `authenticatorData || SHA-256(clientDataJSON)`\n * using Web Crypto API (ES256 via IEEE P1363 conversion, or RS256).\n *\n * @param options - Verification options including signatures, public key, and challenges.\n * @returns Object containing the verified new signature count.\n */\nexport async function verifyAuthenticationResponse(\n options: VerifyAuthenticationOptions\n): Promise<{ signCount: number }> {\n const {\n clientDataJSON,\n authenticatorData,\n signature,\n publicKeySpki,\n algorithm,\n expectedChallenge,\n expectedOrigin,\n expectedRpId,\n previousSignCount = 0\n } = options;\n\n // 1. Verify clientDataJSON\n const clientDataBytes = base64UrlDecode(clientDataJSON);\n const clientData = JSON.parse(new TextDecoder().decode(clientDataBytes));\n\n if (clientData.type !== \"webauthn.get\") {\n throw new Error(`Invalid clientData type: ${clientData.type}`);\n }\n\n if (clientData.challenge !== expectedChallenge) {\n throw new Error(\"WebAuthn challenge mismatch\");\n }\n\n const clientOriginUrl = new URL(clientData.origin);\n const expectedOriginUrl = expectedOrigin.includes(\"://\")\n ? new URL(expectedOrigin)\n : new URL(`https://${expectedOrigin}`);\n\n if (clientOriginUrl.origin.toLowerCase() !== expectedOriginUrl.origin.toLowerCase()) {\n throw new Error(\n `WebAuthn origin mismatch: expected ${expectedOriginUrl.origin}, received ${clientOriginUrl.origin}`\n );\n }\n\n const isLocalhost = clientOriginUrl.hostname === \"localhost\" || clientOriginUrl.hostname === \"127.0.0.1\";\n if (!isLocalhost && clientOriginUrl.protocol !== \"https:\") {\n throw new Error(`WebAuthn requires HTTPS origin, received: ${clientOriginUrl.protocol}`);\n }\n\n // 2. Verify authenticatorData\n const authDataBytes = base64UrlDecode(authenticatorData);\n if (authDataBytes.length < 37) {\n throw new Error(\"Invalid authenticatorData length\");\n }\n\n const rpIdHash = authDataBytes.subarray(0, 32);\n const flags = authDataBytes[32];\n const signCount =\n ((authDataBytes[33] << 24) >>> 0) +\n (authDataBytes[34] << 16) +\n (authDataBytes[35] << 8) +\n authDataBytes[36];\n\n const expectedRpIdHash = new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(expectedRpId))\n );\n for (let i = 0; i < 32; i++) {\n if (rpIdHash[i] !== expectedRpIdHash[i]) {\n throw new Error(\"WebAuthn rpIdHash mismatch\");\n }\n }\n\n // User Present flag\n if (!(flags & 0x01)) {\n throw new Error(\"User Presence flag (UP) was not set by authenticator\");\n }\n\n // Monotonic sign count check (detect cloned authenticators)\n if (signCount > 0 && previousSignCount > 0 && signCount <= previousSignCount) {\n throw new Error(\n \"Authenticator sign count is not strictly monotonic. Possible cloned authenticator detected.\"\n );\n }\n\n // 3. Cryptographic Signature Verification\n // Signed data = authenticatorData || SHA-256(clientDataJSON)\n const clientDataHash = new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", clientDataBytes)\n );\n const signedData = new Uint8Array(authDataBytes.length + clientDataHash.length);\n signedData.set(authDataBytes, 0);\n signedData.set(clientDataHash, authDataBytes.length);\n\n const sigBytes = base64UrlDecode(signature);\n const spkiBytes = base64UrlDecode(publicKeySpki);\n\n if (algorithm === -7) {\n // ES256 (ECDSA P-256)\n const cryptoKey = await crypto.subtle.importKey(\n \"spki\",\n spkiBytes,\n { name: \"ECDSA\", namedCurve: \"P-256\" },\n false,\n [\"verify\"]\n );\n\n const rawSig = derToP1363(sigBytes);\n const valid = await crypto.subtle.verify(\n { name: \"ECDSA\", hash: \"SHA-256\" },\n cryptoKey,\n rawSig,\n signedData\n );\n\n if (!valid) {\n throw new Error(\"WebAuthn signature verification failed\");\n }\n } else if (algorithm === -257) {\n // RS256\n const cryptoKey = await crypto.subtle.importKey(\n \"spki\",\n spkiBytes,\n { name: \"RSASSA-PKCS1-v1_5\", hash: \"SHA-256\" },\n false,\n [\"verify\"]\n );\n\n const valid = await crypto.subtle.verify(\n { name: \"RSASSA-PKCS1-v1_5\" },\n cryptoKey,\n sigBytes,\n signedData\n );\n\n if (!valid) {\n throw new Error(\"WebAuthn signature verification failed\");\n }\n } else {\n throw new Error(`Unsupported public key algorithm: ${algorithm}`);\n }\n\n return { signCount };\n}\n", "import { Env } from \"../../types\";\nimport {\n readRefreshTokenCookie, createBlankRefreshTokenCookie,\n createRefreshTokenCookie, getOrCreateJwtSecret, rotateSession,\n parseRefreshTokenString, generateSessionHash\n} from \"../../lib/auth\";\nimport { importJwtSecret, signJWT } from \"../../lib/jwt\";\nimport { ActivityLogModel } from \"../../models/activityLog\";\nimport { cacheUtils } from \"../../utils/cache\";\n\n/**\n * Handle session token refresh requests\n */\nexport async function handleRefreshRequest(request: Request, env: Env): Promise<Response> {\n const cache = (caches as any).default;\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const userAgent = request.headers.get(\"User-Agent\");\n const activityLog = new ActivityLogModel(env.DB);\n\n if (await cacheUtils.isRateLimited(cache, `refresh_fail:${clientIp}`, 20, 60)) {\n return new Response(\"Too many attempts\", { status: 429 });\n }\n\n const refreshToken = readRefreshTokenCookie(request.headers.get(\"Cookie\"));\n if (!refreshToken) return new Response(\"Refresh token missing\", { status: 401 });\n\n const { getRequestCoordinates } = await import(\"../../lib/auth\");\n const { latitude, longitude } = getRequestCoordinates(request);\n const { session, user, newRefreshToken, reason } = await rotateSession(env, refreshToken, latitude, longitude);\n\n if (!session || !user || !newRefreshToken) {\n if (user) {\n const parsed = refreshToken ? parseRefreshTokenString(refreshToken) : null;\n const sessionHash = parsed ? await generateSessionHash(parsed.sid, user.id) : null;\n await activityLog.record(user.id, 'logout', clientIp, userAgent, { reason: reason || 'unknown' }, sessionHash);\n }\n await cacheUtils.isRateLimited(cache, `refresh_fail:${clientIp}`, 100, 60);\n return new Response(JSON.stringify({ error: \"Invalid refresh token\", reason: reason || \"unknown\" }), { \n status: 401,\n headers: {\n \"Set-Cookie\": createBlankRefreshTokenCookie(),\n \"Content-Type\": \"application/json\"\n }\n });\n }\n\n const secret = await getOrCreateJwtSecret(env);\n const jwtKey = await importJwtSecret(secret);\n const expMinutes = Number(env.ACCESS_TOKEN_EXPIRATION_MINUTES) || 10;\n const accessToken = await signJWT({ \n userId: user.id, \n role: user.role, \n sessionId: session.id,\n exp: Math.floor(Date.now() / 1000) + expMinutes * 60\n }, jwtKey);\n\n const headers = new Headers({ \"Content-Type\": \"application/json\" });\n const isKeepLoggedIn = session.id.startsWith(\"k_\");\n headers.append(\"Set-Cookie\", createRefreshTokenCookie(newRefreshToken, env, isKeepLoggedIn));\n\n return new Response(JSON.stringify({ success: true, accessToken }), { headers });\n}\n", "import { Env } from \"../../types\";\nimport {\n readRefreshTokenCookie, parseRefreshTokenString, invalidateSession, createBlankRefreshTokenCookie,\n generateSessionHash\n} from \"../../lib/auth\";\nimport { SessionModel } from \"../../models/session\";\nimport { ActivityLogModel } from \"../../models/activityLog\";\nimport { invalidateAuthUserCache } from \"../../lib/middleware\";\n\n/**\n * Handle session logout requests\n */\nexport async function handleLogoutRequest(request: Request, env: Env): Promise<Response> {\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const userAgent = request.headers.get(\"User-Agent\");\n const activityLog = new ActivityLogModel(env.DB);\n\n const refreshToken = readRefreshTokenCookie(request.headers.get(\"Cookie\"));\n if (refreshToken) {\n const parsed = parseRefreshTokenString(refreshToken);\n if (parsed) {\n const sessionModel = new SessionModel(env.DB);\n const userId = await sessionModel.getSessionUserId(parsed.sid);\n await invalidateSession(env, parsed.sid);\n invalidateAuthUserCache(parsed.sid);\n if (userId) {\n const sessionHash = await generateSessionHash(parsed.sid, userId);\n await activityLog.record(userId, 'logout', clientIp, userAgent, { reason: 'user_active' }, sessionHash);\n }\n }\n }\n const responseHeaders = new Headers({ \"Content-Type\": \"application/json\" });\n responseHeaders.append(\"Set-Cookie\", createBlankRefreshTokenCookie());\n responseHeaders.append(\"Set-Cookie\", \"csrf_token=; SameSite=Lax; Path=/; Max-Age=0; Secure\");\n return new Response(JSON.stringify({ success: true }), {\n headers: responseHeaders\n });\n}\n", "import { Env } from \"../../types\";\nimport { getOrCreateJwtSecret, generateSessionHash } from \"../../lib/auth\";\nimport { importJwtSecret, verifyJWT } from \"../../lib/jwt\";\nimport { generateId, verifyPassword, hashChallenge } from \"../../utils/crypto\";\nimport { UserModel } from \"../../models/user\";\nimport { ActivityLogModel } from \"../../models/activityLog\";\nimport { SessionModel } from \"../../models/session\";\nimport { cacheUtils } from \"../../utils/cache\";\nimport { invalidateAuthUserCache } from \"../../lib/middleware\";\n\n/**\n * Verifies client PIN hash against stored legacy base64 PIN hash using pre-challenge methods.\n */\nasync function verifyLegacyPin(clientPinHash: string, storedPinHash: string): Promise<boolean> {\n // Method A: PBKDF2 (stored as standard password hash)\n try {\n if (await verifyPassword(clientPinHash, storedPinHash)) {\n return true;\n }\n } catch (e) {}\n\n // Method B: Custom SHA-256 with Salt\n try {\n const binaryString = atob(storedPinHash);\n const combined = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n combined[i] = binaryString.charCodeAt(i);\n }\n if (combined.length === 48) {\n const salt = combined.slice(0, 16);\n const storedHash = combined.slice(16);\n \n const saltHex = Array.from(salt).map(b => b.toString(16).padStart(2, '0')).join('');\n const msgWithSalt = new TextEncoder().encode(clientPinHash + saltHex);\n \n const computedHashBuffer = await crypto.subtle.digest('SHA-256', msgWithSalt);\n const computedHash = new Uint8Array(computedHashBuffer);\n \n if (computedHash.length === storedHash.length) {\n let result = 0;\n for (let i = 0; i < computedHash.length; i++) {\n result |= computedHash[i] ^ storedHash[i];\n }\n if (result === 0) return true;\n }\n }\n } catch (e) {}\n\n return false;\n}\n\n/**\n * Handle session locking and unlocking requests\n */\nexport async function handleSessionLockRequest(request: Request, env: Env): Promise<Response> {\n const url = new URL(request.url);\n const userModel = new UserModel(env.DB, env);\n const activityLog = new ActivityLogModel(env.DB);\n const cache = (caches as any).default;\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const userAgent = request.headers.get(\"User-Agent\");\n\n // \u83B7\u53D6\u89E3\u9501\u4F1A\u8BDD\u7684 Nonce \u6311\u6218\n if (url.pathname === '/api/auth/unlock-session' && request.method === 'GET') {\n const authHeader = request.headers.get(\"Authorization\") || \"\";\n let accessToken = \"\";\n if (authHeader.startsWith(\"Bearer \")) {\n accessToken = authHeader.slice(7);\n }\n if (!accessToken) return new Response(\"Unauthorized\", { status: 401 });\n\n try {\n const secret = await getOrCreateJwtSecret(env);\n const jwtKey = await importJwtSecret(secret);\n const payload = await verifyJWT<{ userId: string; role: string; sessionId: string; exp: number }>(\n accessToken,\n jwtKey\n );\n if (!payload) return new Response(\"Unauthorized\", { status: 401 });\n\n const dbUser = await userModel.getById(payload.userId);\n /**\n * PIN \u517C\u5BB9\u6027\u8BF4\u660E\uFF1A\n * \u65E7\u7248 PIN \u662F\u4EE5 base64 \u7F16\u7801\u7684 SHA-256 \u54C8\u5E0C\u503C\u5B58\u50A8\u5728\u6570\u636E\u5E93\u4E2D\u3002\n * \u65B0\u7248 PIN \u662F\u4EE5 PBKDF2 \u54C8\u5E0C\u503C\u5B58\u50A8\u5728\u6570\u636E\u5E93\u4E2D\u3002\n * \u4E3A\u4E86\u517C\u5BB9\u65E7\u7248\u7528\u6237\uFF0C\u6211\u4EEC\u9700\u8981\u68C0\u67E5\u7528\u6237\u7684 pin_hash \u662F\u5426\u7B26\u5408\u65E7\u7248\u683C\u5F0F\uFF08base64 \u7F16\u7801\u7684 SHA-256 \u54C8\u5E0C\uFF09\u3002\n * \u5982\u679C\u662F\u65E7\u7248\u683C\u5F0F\uFF0C\u6211\u4EEC\u5C06\u4F7F\u7528 verifyLegacyPin \u65B9\u6CD5\u8FDB\u884C\u9A8C\u8BC1\uFF0C\u5E76\u5728\u9A8C\u8BC1\u6210\u529F\u540E\u81EA\u52A8\u8FC1\u79FB\u5230\u65B0\u683C\u5F0F\u3002\n */\n const isLegacy = !!(dbUser?.pin_hash && !/^[a-f0-9]{64}$/.test(dbUser.pin_hash));\n\n const nonce = generateId(32);\n const cacheKey = `unlock_nonce:${payload.sessionId}`;\n await cacheUtils.set(cache, cacheKey, { nonce, isLegacy }, 300); // 5 minutes TTL\n\n return new Response(JSON.stringify({ nonce, legacy: isLegacy }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n } catch (e) {\n return new Response(\"Unauthorized\", { status: 401 });\n }\n }\n\n // \u89E3\u9501\u4F1A\u8BDD\n if (url.pathname === '/api/auth/unlock-session' && request.method === 'POST') {\n const authHeader = request.headers.get(\"Authorization\") || \"\";\n let accessToken = \"\";\n if (authHeader.startsWith(\"Bearer \")) {\n accessToken = authHeader.slice(7);\n }\n if (!accessToken) return new Response(\"Unauthorized\", { status: 401 });\n\n try {\n const secret = await getOrCreateJwtSecret(env);\n const jwtKey = await importJwtSecret(secret);\n const payload = await verifyJWT<{ userId: string; role: string; sessionId: string; exp: number }>(\n accessToken,\n jwtKey\n );\n if (!payload) return new Response(\"Unauthorized\", { status: 401 });\n\n const { pinHash, nonce } = await request.json() as any;\n if (!pinHash) return new Response(\"Missing pinHash\", { status: 400 });\n if (!nonce) return new Response(\"Missing nonce\", { status: 400 });\n\n // \u9A8C\u8BC1 Session \u5B58\u5728\n const sessionModel = new SessionModel(env.DB);\n const session = await sessionModel.getSession(payload.sessionId);\n if (!session) return new Response(\"Session not found\", { status: 401 });\n\n // \u9A8C\u8BC1\u7528\u6237\u7684 PIN\n const dbUser = await userModel.getById(payload.userId);\n if (!dbUser || !dbUser.pin_hash) return new Response(\"PIN not configured\", { status: 400 });\n\n // \u83B7\u53D6\u7F13\u5B58\u7684 Nonce \u6311\u6218\n const cacheKeyNonce = `unlock_nonce:${payload.sessionId}`;\n const cachedNonceState = await cacheUtils.get<{ nonce: string; isLegacy?: boolean }>(cache, cacheKeyNonce);\n if (!cachedNonceState?.nonce) {\n return new Response(\"Challenge expired, please start over\", { status: 400 });\n }\n const cachedNonce = cachedNonceState.nonce;\n const isLegacy = !!cachedNonceState.isLegacy;\n\n // \u6D88\u8D39\u6389 nonce \u9632\u6B62\u91CD\u653E\n await cacheUtils.delete(cache, cacheKeyNonce);\n\n // \u9A8C\u8BC1\u5BA2\u6237\u7AEF\u63D0\u4EA4\u7684 nonce \u662F\u5426\u4E0E\u7F13\u5B58\u7684 nonce \u4E00\u81F4\n if (nonce !== cachedNonce) {\n return new Response(\"Invalid nonce\", { status: 400 });\n }\n\n const cacheKey = `unlock_fail:${session.id}`;\n const failedAttemptsState = await cacheUtils.get<{ count: number }>(cache, cacheKey);\n const failedAttempts = failedAttemptsState?.count || 0;\n\n const sessionHash = await generateSessionHash(session.id, payload.userId);\n\n let isPinValid = false;\n if (isLegacy) {\n // \u517C\u5BB9\u539F\u6709\u7684 PIN \u903B\u8F91\n isPinValid = await verifyLegacyPin(pinHash, dbUser.pin_hash);\n if (isPinValid) {\n // \u6210\u529F\u9A8C\u8BC1\u540E\uFF0C\u81EA\u52A8\u5C06\u539F\u6709\u8D26\u53F7\u7684 PIN \u5347\u7EA7\u8FC1\u79FB\u4E3A\u76F4\u63A5\u5B58\u50A8 client-side pinHash \u7684\u65B0\u683C\u5F0F\n await userModel.updatePinHash(payload.userId, pinHash);\n }\n } else {\n // \u9A8C\u8BC1\u6311\u6218\u54CD\u5E94\uFF1AhashChallenge(dbUser.pin_hash, cachedNonce)\n const expectedChallengedHash = await hashChallenge(dbUser.pin_hash, cachedNonce);\n \n // Constant-time comparison\n if (pinHash.length === expectedChallengedHash.length) {\n let result = 0;\n for (let i = 0; i < pinHash.length; i++) {\n result |= pinHash.charCodeAt(i) ^ expectedChallengedHash.charCodeAt(i);\n }\n isPinValid = result === 0;\n }\n }\n\n if (!isPinValid) {\n const nextFailedCount = failedAttempts + 1;\n if (nextFailedCount >= 3) {\n // \u8D85\u8FC7 3 \u6B21\u5931\u8D25\uFF0C\u9500\u6BC1 Session \u5F3A\u5236\u767B\u51FA\n await sessionModel.deleteSession(session.id);\n await cacheUtils.delete(cache, cacheKey);\n await activityLog.record(payload.userId, 'pin_verify_fail', clientIp, userAgent, { reason: 'too_many_attempts' }, sessionHash);\n return new Response(JSON.stringify({ success: false, error: \"too_many_attempts\" }), {\n status: 401,\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n await cacheUtils.set(cache, cacheKey, { count: nextFailedCount }, 900); // 15\u5206\u949F\u9501\u5B9A\u8FFD\u8E2A\n await activityLog.record(payload.userId, 'pin_verify_fail', clientIp, userAgent, { reason: 'incorrect_pin', attempt: nextFailedCount }, sessionHash);\n return new Response(JSON.stringify({\n success: false,\n error: \"incorrect_pin\",\n attemptsRemaining: 3 - nextFailedCount\n }), {\n status: 400,\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // \u9A8C\u8BC1\u6210\u529F\uFF1A\u6062\u590D\u4F1A\u8BDD\u72B6\u6001\n const now = Math.floor(Date.now() / 1000);\n await sessionModel.resumeSession(session.id, now);\n invalidateAuthUserCache(session.id);\n await cacheUtils.delete(cache, cacheKey);\n await activityLog.record(payload.userId, 'pin_verify_success', clientIp, userAgent, undefined, sessionHash);\n\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n } catch (e: any) {\n console.error(\"Unlock session error:\", e);\n return new Response(\"Unauthorized\", { status: 401 });\n }\n }\n\n // \u9501\u5B9A\u4F1A\u8BDD\n if (url.pathname === '/api/auth/lock-session' && request.method === 'POST') {\n const authHeader = request.headers.get(\"Authorization\") || \"\";\n let accessToken = \"\";\n if (authHeader.startsWith(\"Bearer \")) {\n accessToken = authHeader.slice(7);\n }\n if (!accessToken) return new Response(\"Unauthorized\", { status: 401 });\n\n try {\n const secret = await getOrCreateJwtSecret(env);\n const jwtKey = await importJwtSecret(secret);\n const payload = await verifyJWT<{ userId: string; role: string; sessionId: string; exp: number }>(\n accessToken,\n jwtKey\n );\n if (!payload) return new Response(\"Unauthorized\", { status: 401 });\n\n const sessionModel = new SessionModel(env.DB);\n const session = await sessionModel.getSession(payload.sessionId);\n if (!session) return new Response(\"Session not found\", { status: 401 });\n\n await sessionModel.pauseSession(session.id);\n invalidateAuthUserCache(session.id);\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n } catch (e: any) {\n return new Response(\"Unauthorized\", { status: 401 });\n }\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env } from \"../../types\";\nimport { UserModel } from \"../../models/user\";\nimport { ActivityLogModel } from \"../../models/activityLog\";\nimport { PasskeyModel } from \"../../models/passkey\";\nimport { SessionModel } from \"../../models/session\";\nimport { SystemSettingsModel } from \"../../models/systemSettings\";\nimport { cacheUtils } from \"../../utils/cache\";\nimport { generateId, hashPassword } from \"../../utils/crypto\";\nimport { PASSWORD_REGEX } from \"../../utils/validator\";\nimport { verifyTurnstile } from \"./utils\";\nimport { generateWebAuthnChallenge, verifyAuthenticationResponse } from \"../../lib/webauthn\";\nimport { verifyTOTPWithStep, findMatchingRecoveryKey } from \"../../lib/totp\";\nimport { StoredRecoveryKey } from \"../../models/user\";\n\ninterface RecoveryState {\n userId: string;\n username: string;\n passkeyChallenge?: string | null;\n rpId: string;\n failedAttempts: number;\n}\n\ninterface ResetState {\n userId: string;\n username: string;\n}\n\n/**\n * Handles the 3-step MFA-based Forgot Password flow:\n * 1. POST /api/auth/forgot-password/init \u2014 identify user, return available MFA options & challenge\n * 2. POST /api/auth/forgot-password/verify \u2014 verify Passkey, TOTP, or Recovery Key, issue resetToken\n * 3. POST /api/auth/forgot-password/reset \u2014 consume resetToken, set new password, revoke sessions\n */\nexport async function handleForgotPasswordRequest(request: Request, env: Env): Promise<Response> {\n const url = new URL(request.url);\n const path = url.pathname;\n const userModel = new UserModel(env.DB, env);\n const activityLog = new ActivityLogModel(env.DB);\n const cache = (caches as any).default;\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const userAgent = request.headers.get(\"User-Agent\");\n\n // Step 1: Init recovery session\n if (path === '/api/auth/forgot-password/init' && request.method === 'POST') {\n if (await cacheUtils.isRateLimited(cache, `forgot_pw_ip:${clientIp}`, 10, 600)) {\n return new Response(\"Too many attempts\", { status: 429 });\n }\n\n const { username, turnstileToken } = await request.json() as any;\n if (!username) {\n return new Response(\"Username is required\", { status: 400 });\n }\n\n // Verify Turnstile if enabled\n const settingsModel = new SystemSettingsModel(env.DB);\n const [secretKey, enabled] = await Promise.all([\n settingsModel.get('turnstile_secret_key'),\n settingsModel.get('turnstile_enabled_login')\n ]);\n if (enabled === 'true' && secretKey) {\n if (!await verifyTurnstile(turnstileToken, secretKey, clientIp)) {\n return new Response(\"Verification failed\", { status: 400 });\n }\n }\n\n const user = await userModel.getByUsername(username);\n if (!user) {\n await cacheUtils.isRateLimited(cache, `forgot_pw_ip:${clientIp}`, 100, 600); // penalty\n return new Response(\"User not found\", { status: 404 });\n }\n\n // Check available MFA credentials\n const passkeyModel = new PasskeyModel(env.DB);\n const passkeys = await passkeyModel.listByUser(user.id);\n const has_passkey = passkeys.length > 0;\n const has_totp = !!user.totp_enabled && (!!user.totp_secret || !!user.totp_secret_encrypted);\n\n let has_recovery_keys = false;\n const rawRecoveryKeys = user.totp_recovery_keys;\n if (rawRecoveryKeys) {\n try {\n const parsed = typeof rawRecoveryKeys === \"string\" ? JSON.parse(rawRecoveryKeys) : rawRecoveryKeys;\n has_recovery_keys = Array.isArray(parsed) && parsed.length > 0;\n } catch {\n has_recovery_keys = typeof rawRecoveryKeys === \"string\" && rawRecoveryKeys.length > 0;\n }\n } else if (user.totp_recovery_keys_encrypted) {\n has_recovery_keys = true;\n }\n\n // If no MFA configured at all, user cannot self-reset\n if (!has_passkey && !has_totp && !has_recovery_keys) {\n return new Response(JSON.stringify({\n can_reset: false,\n reason: \"no_mfa_configured\"\n }), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // Prepare WebAuthn options if user has Passkeys\n let passkey_challenge: string | null = null;\n let passkey_options: any = null;\n if (has_passkey) {\n passkey_challenge = generateWebAuthnChallenge();\n passkey_options = {\n challenge: passkey_challenge,\n rpId: url.hostname,\n allowCredentials: passkeys.map(p => ({\n id: p.credential_id,\n type: \"public-key\",\n transports: p.transports ? JSON.parse(p.transports) : undefined\n })),\n timeout: 60000,\n userVerification: \"preferred\"\n };\n }\n\n const recoveryToken = generateId(32);\n const recoveryState: RecoveryState = {\n userId: user.id,\n username: user.username,\n passkeyChallenge: passkey_challenge,\n rpId: url.hostname,\n failedAttempts: 0\n };\n\n // Cache recovery state for 5 minutes (300s)\n await cacheUtils.set(cache, `forgot_pw_state:${recoveryToken}`, recoveryState, 300);\n\n return new Response(JSON.stringify({\n can_reset: true,\n recoveryToken,\n has_passkey,\n passkey_options,\n has_totp,\n has_recovery_keys\n }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // Step 2: Verify MFA identity\n if (path === '/api/auth/forgot-password/verify' && request.method === 'POST') {\n const { recoveryToken, passkeyAssertion, totpTokenHash, totpSalt, recoveryKey } = await request.json() as any;\n if (!recoveryToken) {\n return new Response(\"Recovery token is required\", { status: 400 });\n }\n\n const cacheKey = `forgot_pw_state:${recoveryToken}`;\n const state = await cacheUtils.get<RecoveryState>(cache, cacheKey);\n if (!state) {\n return new Response(\"Recovery session expired or invalid\", { status: 400 });\n }\n\n if (state.failedAttempts >= 3) {\n await cacheUtils.delete(cache, cacheKey);\n return new Response(\"Too many failed attempts. Please start over.\", { status: 429 });\n }\n\n const user = await userModel.getById(state.userId);\n if (!user) {\n await cacheUtils.delete(cache, cacheKey);\n return new Response(\"User not found\", { status: 404 });\n }\n\n // Verify Passkey\n if (passkeyAssertion && state.passkeyChallenge) {\n const passkeyModel = new PasskeyModel(env.DB);\n const passkeys = await passkeyModel.listByUser(user.id);\n const passkey = passkeys.find(p => p.credential_id === passkeyAssertion.id);\n\n if (!passkey) {\n state.failedAttempts++;\n await cacheUtils.set(cache, cacheKey, state, 300);\n return new Response(\"Passkey not recognized\", { status: 400 });\n }\n\n try {\n const { signCount } = await verifyAuthenticationResponse({\n clientDataJSON: passkeyAssertion.response.clientDataJSON,\n authenticatorData: passkeyAssertion.response.authenticatorData,\n signature: passkeyAssertion.response.signature,\n publicKeySpki: passkey.public_key,\n algorithm: passkey.algorithm,\n expectedChallenge: state.passkeyChallenge,\n expectedOrigin: `${url.protocol}//${url.host}`,\n expectedRpId: state.rpId,\n previousSignCount: passkey.sign_count\n });\n\n await passkeyModel.updateUsage(passkey.id, signCount);\n await activityLog.record(user.id, 'passkey_verify_success', clientIp, userAgent, { flow: 'forgot_password' });\n } catch (err: any) {\n state.failedAttempts++;\n await cacheUtils.set(cache, cacheKey, state, 300);\n return new Response(\"Passkey verification failed\", { status: 400 });\n }\n }\n // Verify Recovery Key\n else if (recoveryKey) {\n let storedHashes: StoredRecoveryKey[] = [];\n try {\n const raw = user.totp_recovery_keys;\n storedHashes = typeof raw === 'string' ? JSON.parse(raw || '[]') : (raw || []);\n if (!Array.isArray(storedHashes)) storedHashes = [storedHashes];\n } catch {}\n\n const matchIndex = await findMatchingRecoveryKey(recoveryKey, storedHashes);\n if (matchIndex === -1) {\n state.failedAttempts++;\n await cacheUtils.set(cache, cacheKey, state, 300);\n return new Response(\"Invalid recovery key\", { status: 400 });\n }\n\n await userModel.consumeRecoveryKey(user.id, matchIndex, storedHashes);\n await activityLog.record(user.id, 'recovery_key_used', clientIp, userAgent, { flow: 'forgot_password', remaining: storedHashes.length - 1 });\n }\n // Verify TOTP\n else if (totpTokenHash && user.totp_secret) {\n const totpResult = await verifyTOTPWithStep(user.totp_secret, totpTokenHash, totpSalt);\n if (!totpResult.valid) {\n state.failedAttempts++;\n await cacheUtils.set(cache, cacheKey, state, 300);\n return new Response(\"Invalid TOTP code\", { status: 400 });\n }\n\n // Anti-replay protection: prevent reusing the same TOTP time-step\n if (totpResult.timeStep !== undefined) {\n const stepKey = `totp_step:${user.id}:${totpResult.timeStep}`;\n const replayed = await cacheUtils.get(cache, stepKey);\n if (replayed) {\n state.failedAttempts++;\n await cacheUtils.set(cache, cacheKey, state, 300);\n return new Response(\"TOTP code already used, please wait for next code\", { status: 400 });\n }\n await cacheUtils.set(cache, stepKey, true, 180);\n }\n\n await activityLog.record(user.id, 'totp_verify_success', clientIp, userAgent, { flow: 'forgot_password' });\n }\n else {\n return new Response(\"No valid MFA credential provided\", { status: 400 });\n }\n\n // MFA verified! Invalidate recovery session and issue single-use resetToken\n await cacheUtils.delete(cache, cacheKey);\n\n const resetToken = generateId(32);\n const resetState: ResetState = {\n userId: user.id,\n username: user.username\n };\n await cacheUtils.set(cache, `forgot_pw_reset:${resetToken}`, resetState, 300);\n\n return new Response(JSON.stringify({ success: true, resetToken }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // Step 3: Reset password\n if (path === '/api/auth/forgot-password/reset' && request.method === 'POST') {\n const { resetToken, newPassword } = await request.json() as any;\n if (!resetToken || !newPassword) {\n return new Response(\"Reset token and new password are required\", { status: 400 });\n }\n\n if (!PASSWORD_REGEX.test(newPassword)) {\n return new Response(\"Password format error\", { status: 400 });\n }\n\n const resetKey = `forgot_pw_reset:${resetToken}`;\n const resetState = await cacheUtils.get<ResetState>(cache, resetKey);\n if (!resetState) {\n return new Response(\"Reset token expired or invalid\", { status: 400 });\n }\n\n // Immediately consume reset token\n await cacheUtils.delete(cache, resetKey);\n\n const user = await userModel.getById(resetState.userId);\n if (!user) {\n return new Response(\"User not found\", { status: 404 });\n }\n\n // Hash and update password (v2)\n const hashedPassword = await hashPassword(newPassword, 2);\n await userModel.updatePassword(user.id, hashedPassword, 2);\n\n // Revoke all existing sessions for security\n const sessionModel = new SessionModel(env.DB);\n await sessionModel.deleteAllByUserId(user.id);\n\n await activityLog.record(user.id, 'password_change_success', clientIp, userAgent, { method: 'forgot_password_reset' });\n\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env } from \"../types\";\nimport { handleAuthConfigRequest } from \"./auth/config\";\nimport { handleAuthRegisterRequest } from \"./auth/register\";\nimport { handleLoginRequest } from \"./auth/login\";\nimport { handleRefreshRequest } from \"./auth/refresh\";\nimport { handleLogoutRequest } from \"./auth/logout\";\nimport { handleSessionLockRequest } from \"./auth/lock\";\nimport { handleForgotPasswordRequest } from \"./auth/forgotPassword\";\n\n/**\n * Handle authentication related requests by delegating to specialized handlers.\n */\nexport async function handleAuthRequest(request: Request, env: Env): Promise<Response> {\n try {\n const url = new URL(request.url);\n const path = url.pathname;\n\n // \u516C\u5F00\u914D\u7F6E\u4E0E\u8F85\u52A9\u63A5\u53E3\n if (path === '/api/auth/config' || path === '/api/auth/check-username') {\n return await handleAuthConfigRequest(request, env);\n }\n\n // \u6CE8\u518C\u63A5\u53E3\n if (path === '/api/auth/signup') {\n return await handleAuthRegisterRequest(request, env);\n }\n\n // \u767B\u5F55\u751F\u547D\u5468\u671F\u63A5\u53E3\n if (path === '/api/auth/prelogin' || path === '/api/auth/login') {\n return await handleLoginRequest(request, env);\n }\n\n // Token \u5237\u65B0\u63A5\u53E3\n if (path === '/api/auth/refresh') {\n return await handleRefreshRequest(request, env);\n }\n\n // \u767B\u51FA\u63A5\u53E3\n if (path === '/api/auth/logout') {\n return await handleLogoutRequest(request, env);\n }\n\n // \u5FD8\u8BB0\u5BC6\u7801\u91CD\u7F6E\u63A5\u53E3\n if (path.startsWith('/api/auth/forgot-password')) {\n return await handleForgotPasswordRequest(request, env);\n }\n\n // \u4F1A\u8BDD\u9501\u5B9A\u4E0E\u89E3\u9501\u63A5\u53E3\n if (path === '/api/auth/unlock-session' || path === '/api/auth/lock-session') {\n return await handleSessionLockRequest(request, env);\n }\n\n return new Response(\"Not Found\", { status: 404 });\n } catch (e: any) {\n console.error(\"[Auth API] Unhandled Error:\", e.message || e);\n return new Response(e.message || \"Internal Server Error\", {\n status: 500,\n headers: { \"Content-Type\": \"text/plain; charset=utf-8\" }\n });\n }\n}\n", "import { User, Profile } from \"../types\";\n\nexport class RBAC {\n /**\n * \u68C0\u67E5\u7528\u6237\u662F\u5426\u4E3A\u7BA1\u7406\u5458\n */\n static isAdmin(user: User): boolean {\n return user.role === 'admin';\n }\n\n /**\n * \u68C0\u67E5\u7528\u6237\u662F\u5426\u6709\u6743\u8BBF\u95EE\u7279\u5B9A Profile\n */\n static canAccessProfile(user: User, profile: Profile): boolean {\n // \u4E25\u683C\u9650\u5236\u6240\u6709\u6743\uFF0C\u5373\u4FBF\u662F admin \u4E5F\u4E0D\u5E94\u76F4\u63A5\u67E5\u770B\u5176\u4ED6\u7528\u6237\u7684\u4E1A\u52A1\u914D\u7F6E/\u65E5\u5FD7\n return profile.owner_id === user.id;\n }\n}\n", "import { Env, User, Profile, ProfileSettings, ExecutionContext } from \"../../types\";\nimport { ProfileModel } from \"../../models/profile\";\nimport { LogModel } from \"../../models/log\";\nimport { generateId } from \"../../lib/auth\";\nimport { isSafeUrl } from \"../../utils/validator\";\nimport { buildDNSQuery, parseDNSAnswer } from \"../../utils/dns\";\nimport { pipeline } from \"../../pipeline\";\nimport { PROFILE_NAME_REGEX } from \"../../utils/validator\";\n\n/**\n * Handle requests to /api/profiles (without specific profile ID)\n */\nexport async function handleProfilesCoreCollectionRequest(\n request: Request,\n env: Env,\n user: User,\n pathParts: string[]\n): Promise<Response> {\n const profileModel = new ProfileModel(env.DB);\n\n // GET /api/profiles\n if (request.method === 'GET') {\n const results = await profileModel.listByOwner(user.id);\n return new Response(JSON.stringify(results), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // POST /api/profiles\n if (request.method === 'POST') {\n const body = await request.json() as { name: string };\n \n if (!body.name || !PROFILE_NAME_REGEX.test(body.name)) {\n return new Response(\"Invalid Profile Name format\", { status: 400 });\n }\n\n const existingProfiles = await profileModel.listByOwner(user.id);\n const maxProfiles = Number(env.MAX_PROFILES_PER_USER) || 10;\n if (existingProfiles.length >= maxProfiles) return new Response(`Profile limit exceeded (max ${maxProfiles})`, { status: 400 });\n\n const existing = existingProfiles.find(p => p.name === body.name);\n if (existing) return new Response(\"The profile name already exists\", { status: 400 });\n\n const newId = generateId(6);\n const globalMaxRetention = env.MAX_LOG_RETENTION_DAYS !== undefined && env.MAX_LOG_RETENTION_DAYS !== ''\n ? Number(env.MAX_LOG_RETENTION_DAYS)\n : 30;\n const adminDefaultRetention = env.DEFAULT_LOG_RETENTION_DAYS !== undefined && env.DEFAULT_LOG_RETENTION_DAYS !== ''\n ? Math.min(Number(env.DEFAULT_LOG_RETENTION_DAYS), globalMaxRetention)\n : Math.min(7, globalMaxRetention);\n const normalUserMaxRetention = env.NORMAL_USER_MAX_LOG_RETENTION_DAYS !== undefined && env.NORMAL_USER_MAX_LOG_RETENTION_DAYS !== ''\n ? Math.min(Number(env.NORMAL_USER_MAX_LOG_RETENTION_DAYS), globalMaxRetention)\n : Math.min(7, globalMaxRetention);\n const normalUserDefaultRetention = env.NORMAL_USER_DEFAULT_LOG_RETENTION_DAYS !== undefined && env.NORMAL_USER_DEFAULT_LOG_RETENTION_DAYS !== ''\n ? Math.min(Number(env.NORMAL_USER_DEFAULT_LOG_RETENTION_DAYS), normalUserMaxRetention)\n : Math.min(1, normalUserMaxRetention);\n\n const defaultRetentionDays = user?.role === 'admin' ? adminDefaultRetention : normalUserDefaultRetention;\n const defaultSettings: ProfileSettings = {\n upstream: [\"https://security.cloudflare-dns.com/dns-query\"],\n ecs: { enabled: true, use_client_ip: true },\n log_retention_days: defaultRetentionDays,\n skip_log_on_pass: false,\n default_policy: 'ALLOW',\n best_effort_ech: { enabled: false, fronting_domain: \"cloudflare-ech.com\" }\n };\n await profileModel.create({ id: newId, owner_id: user.id, name: body.name || \"Unnamed Profile\", settings: defaultSettings });\n\n // Automatically inherit active account-level E2EE keys if configured\n try {\n const existingLogKey = await env.DB.prepare(\n \"SELECT public_key, is_active FROM user_log_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?) LIMIT 1\"\n )\n .bind(user.id)\n .first<{ public_key: string; is_active?: number }>();\n\n if (existingLogKey?.public_key) {\n const now = Math.floor(Date.now() / 1000);\n await env.DB.prepare(\n \"INSERT INTO user_log_keys (profile_id, public_key, created_at, is_active) VALUES (?, ?, ?, ?)\"\n )\n .bind(newId, existingLogKey.public_key, now, existingLogKey.is_active ?? 1)\n .run();\n\n const existingRecKey = await env.DB.prepare(\n \"SELECT encrypted_sk, iv, salt FROM user_recovery_wrapped_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?) LIMIT 1\"\n )\n .bind(user.id)\n .first<{ encrypted_sk: string; iv: string; salt: string }>();\n\n if (existingRecKey) {\n await env.DB.prepare(\n \"INSERT INTO user_recovery_wrapped_keys (profile_id, encrypted_sk, iv, salt, created_at) VALUES (?, ?, ?, ?, ?)\"\n )\n .bind(newId, existingRecKey.encrypted_sk, existingRecKey.iv, existingRecKey.salt, now)\n .run();\n }\n }\n } catch (e) {\n console.error(\"[Profiles] Failed to inherit E2EE log key for new profile:\", e);\n }\n\n return new Response(JSON.stringify({ id: newId }), { status: 201 });\n }\n\n return new Response(\"Method Not Allowed\", { status: 405 });\n}\n\n/**\n * Handle core requests to /api/profiles/:id\n */\nexport async function handleProfilesCoreRequest(\n request: Request,\n env: Env,\n user: User | null,\n profile: Profile,\n pathParts: string[],\n ctx: ExecutionContext\n): Promise<Response> {\n const profileModel = new ProfileModel(env.DB);\n const logModel = new LogModel(env.DB);\n const profileId = profile.id;\n\n // Handles:\n // - DELETE /api/profiles/:id\n // - PATCH /api/profiles/:id (updateName)\n // - GET /api/profiles/:id\n if (pathParts.length === 3) {\n if (request.method === 'DELETE') {\n await profileModel.delete(profileId);\n return new Response(null, { status: 204 });\n }\n\n if (request.method === 'PATCH') {\n const { name } = await request.json() as { name: string };\n if (!name || !PROFILE_NAME_REGEX.test(name)) return new Response(\"Invalid Profile Name format\", { status: 400 });\n await profileModel.updateName(profileId, name);\n ctx.waitUntil(pipeline.clearCache(profileId, true, env));\n return new Response(JSON.stringify({ success: true }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (request.method === 'GET') {\n return new Response(JSON.stringify(profile), {\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n return new Response(\"Method Not Allowed\", { status: 405 });\n }\n\n // POST /api/profiles/:id/rotate_key\n if (pathParts[3] === 'rotate_key' && request.method === 'POST') {\n const newKey = generateId(12);\n await profileModel.rotateKey(profileId, newKey);\n ctx.waitUntil(pipeline.clearCache(profileId, false, env));\n return new Response(JSON.stringify({ profile_key: newKey }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // PATCH /api/profiles/:id/settings\n if (pathParts[3] === 'settings' && request.method === 'PATCH') {\n const newSettings = await request.json() as ProfileSettings;\n \n // Enforce log retention limit\n const globalMaxRetention = env.MAX_LOG_RETENTION_DAYS !== undefined && env.MAX_LOG_RETENTION_DAYS !== ''\n ? Number(env.MAX_LOG_RETENTION_DAYS)\n : 30;\n const adminMaxRetention = env.ADMIN_USER_MAX_LOG_RETENTION_DAYS !== undefined && env.ADMIN_USER_MAX_LOG_RETENTION_DAYS !== ''\n ? Math.min(Number(env.ADMIN_USER_MAX_LOG_RETENTION_DAYS), globalMaxRetention)\n : globalMaxRetention;\n const normalUserMax = env.NORMAL_USER_MAX_LOG_RETENTION_DAYS !== undefined && env.NORMAL_USER_MAX_LOG_RETENTION_DAYS !== ''\n ? Math.min(Number(env.NORMAL_USER_MAX_LOG_RETENTION_DAYS), globalMaxRetention)\n : Math.min(7, globalMaxRetention);\n const userMaxRetention = user?.role === 'admin' ? adminMaxRetention : normalUserMax;\n\n if (newSettings.log_retention_days != null) {\n newSettings.log_retention_days = Math.min(Number(newSettings.log_retention_days), userMaxRetention);\n }\n \n if (newSettings.upstream && Array.isArray(newSettings.upstream)) {\n for (const url of newSettings.upstream) {\n // \u5141\u8BB8\u4E09\u79CD\u683C\u5F0F\uFF1Ahttps://...\u3001http://...\u3001tcp://...\u3001\u88F8 IP\uFF088.8.8.8\uFF09\u6216 IP:port\uFF088.8.8.8:5353\uFF09\n // resolver.ts \u4E2D\u88F8 IP \u5DF2\u88AB\u89C6\u4E3A\u7ECF\u5178 TCP DNS\uFF0CAPI \u9A8C\u8BC1\u5C42\u5BF9\u9F50\u6B64\u884C\u4E3A\u3002\n const isHttps = url.startsWith('https://');\n const isHttp = url.startsWith('http://');\n const isTcp = url.startsWith('tcp://');\n const isTls = url.startsWith('tls://');\n const isDot = url.startsWith('dot://');\n const isUdp = url.startsWith('udp://');\n const isSdns = url.startsWith('sdns://');\n // \u88F8 host[:port]\uFF1A\u4E0D\u542B / \u4E14\u4E0D\u542B scheme\n const isBareHost = !url.includes('//') && !url.startsWith('/');\n\n if (!isHttps && !isHttp && !isTcp && !isTls && !isDot && !isUdp && !isSdns && !isBareHost) {\n return new Response(\"Invalid upstream URL format. Only HTTP(S), TCP, TLS (DoT), DNS Stamp (sdns://), or bare host[:port] are allowed.\", { status: 400 });\n }\n // \u7EDF\u4E00\u89C4\u8303\u5316\u540E\u505A\u5B89\u5168\u68C0\u67E5\uFF08\u9632 SSRF\uFF09\n const normalized = isBareHost ? `tcp://${url}` : url;\n if (!isSafeUrl(normalized)) {\n return new Response(\"Invalid upstream URL. Private networks and localhosts are not allowed.\", { status: 400 });\n }\n }\n }\n\n // \u68C0\u67E5\u65E7\u914D\u7F6E\u4E2D\u7684 log_retention_days\n const oldProfile = await profileModel.getById(profileId);\n let oldDays = 30;\n try {\n const oldSettings = JSON.parse(oldProfile?.settings || \"{}\");\n if (oldSettings?.log_retention_days != null) {\n oldDays = Number(oldSettings.log_retention_days);\n }\n } catch {}\n\n await profileModel.updateSettings(profileId, newSettings);\n\n // \u4EC5\u5728\u663E\u5F0F\u7F29\u77ED\u65E5\u5FD7\u7559\u5B58\u671F\u65F6\u89E6\u53D1\u4E3B\u52A8\u6E05\u7406\uFF0C\u907F\u514D\u6BCF\u6B21\u4FDD\u5B58\u8BBE\u7F6E\u65E0\u8C13\u6267\u884C DELETE\n const newDays = newSettings.log_retention_days;\n if (newDays != null && Number(newDays) < oldDays) {\n if (Number(newDays) === 0) {\n // \u5173\u95ED\u65E5\u5FD7\u65F6\u5F7B\u5E95\u6E05\u7A7A\u5F53\u524D\u914D\u7F6E\u7684\u5386\u53F2\u65E5\u5FD7\n ctx.waitUntil((async () => {\n try {\n await env.DB.prepare(\"DELETE FROM logs WHERE profile_id = ?\").bind(profileId).run();\n await logModel.kemKeys.cleanupProfileOrphans(profileId);\n } catch (e: any) {\n console.error(\"[Profile] Failed to purge logs on retention disable:\", e?.message || e);\n }\n })());\n } else {\n const threshold = Math.floor(Date.now() / 1000 - (Number(newDays) * 24 * 3600));\n ctx.waitUntil(logModel.cleanup(profileId, threshold));\n }\n }\n\n // \u8BBE\u7F6E\u53D8\u66F4\u4EC5\u6E05\u9664\u914D\u7F6E\u7F13\u5B58\uFF0C\u4FDD\u7559 2.5MB \u5E03\u9686\u8FC7\u6EE4\u5668\u7F13\u5B58\n ctx.waitUntil(pipeline.clearCache(profileId, false, env));\n return new Response(JSON.stringify({ success: true }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // POST /api/profiles/:id/test\n if (pathParts[3] === 'test' && request.method === 'POST') {\n const { domain, type } = await request.json() as { domain: string, type: string };\n const rawQuery = buildDNSQuery(domain, type);\n const result = await pipeline.process(request, { name: domain, type, raw: rawQuery }, { profileId, startTime: Date.now(), env, ctx });\n return new Response(JSON.stringify({\n action: result.action,\n reason: result.reason,\n answers: result.answer.length > 0 ? parseDNSAnswer(result.answer) : [],\n diagnostics: result.diagnostics,\n latency: result.latency,\n timings: result.timings,\n client_ip: request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\",\n geo_country: (request as any).cf?.country || request.headers.get(\"CF-IPCountry\") || \"UNKNOWN\",\n success: true \n }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { AccessPoint } from \"../types\";\nimport { generateId, generateZBase32Token } from \"../lib/auth\";\n\nexport class AccessPointModel {\n constructor(private db: D1Database) {}\n\n async getAccessPoints(profileId: string): Promise<AccessPoint[]> {\n const { results } = await this.db.prepare(\"SELECT * FROM access_points WHERE profile_id = ? ORDER BY created_at ASC\")\n .bind(profileId).all<AccessPoint>();\n return results;\n }\n\n async addAccessPoint(profileId: string, name: string): Promise<AccessPoint> {\n const now = Math.floor(Date.now() / 1000);\n const id = generateId(12);\n const MAX_RETRIES = 5;\n\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n const token = generateZBase32Token(5);\n try {\n await this.db.prepare(\n \"INSERT INTO access_points (id, profile_id, name, token, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)\"\n ).bind(id, profileId, name, token, now, now).run();\n return { id, profile_id: profileId, name, token, created_at: now, updated_at: now };\n } catch (err: any) {\n if (attempt < MAX_RETRIES - 1 && (String(err).includes(\"UNIQUE constraint failed\") || String(err).includes(\"SQLITE_CONSTRAINT\"))) {\n continue;\n }\n throw err;\n }\n }\n throw new Error(\"Failed to generate a unique access point token\");\n }\n\n async updateAccessPointName(id: string, profileId: string, name: string): Promise<boolean> {\n const now = Math.floor(Date.now() / 1000);\n const result = await this.db.prepare(\"UPDATE access_points SET name = ?, updated_at = ? WHERE id = ? AND profile_id = ?\")\n .bind(name, now, id, profileId).run();\n return result.success;\n }\n\n async rotateAccessPointToken(id: string, profileId: string): Promise<string> {\n const now = Math.floor(Date.now() / 1000);\n const MAX_RETRIES = 5;\n\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n const newToken = generateZBase32Token(5);\n try {\n const result = await this.db.prepare(\"UPDATE access_points SET token = ?, updated_at = ? WHERE id = ? AND profile_id = ?\")\n .bind(newToken, now, id, profileId).run();\n if (!result.success) {\n throw new Error(\"Failed to update access point token\");\n }\n return newToken;\n } catch (err: any) {\n if (attempt < MAX_RETRIES - 1 && (String(err).includes(\"UNIQUE constraint failed\") || String(err).includes(\"SQLITE_CONSTRAINT\"))) {\n continue;\n }\n throw err;\n }\n }\n throw new Error(\"Failed to generate a unique access point token\");\n }\n\n async deleteAccessPoint(id: string, profileId: string): Promise<boolean> {\n const result = await this.db.prepare(\"DELETE FROM access_points WHERE id = ? AND profile_id = ?\")\n .bind(id, profileId).run();\n return result.success;\n }\n}\n", "import { Env, User, ExecutionContext } from \"../../types\";\nimport { AccessPointModel } from \"../../models/accessPoint\";\nimport { AP_NAME_REGEX } from \"../../utils/validator\";\n\n/**\n * Handle access points requests to /api/profiles/:id/access_points\n */\nexport async function handleProfileAccessPointsRequest(\n request: Request,\n env: Env,\n user: User,\n profileId: string,\n pathParts: string[],\n ctx: ExecutionContext\n): Promise<Response> {\n const apModel = new AccessPointModel(env.DB);\n\n if (request.method === 'GET') {\n const results = await apModel.getAccessPoints(profileId);\n return new Response(JSON.stringify(results), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (request.method === 'POST') {\n if (pathParts.length === 6 && pathParts[5] === 'rotate_token') {\n const apId = pathParts[4];\n const newToken = await apModel.rotateAccessPointToken(apId, profileId);\n return new Response(JSON.stringify({ token: newToken }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n const body = await request.json() as { name: string };\n if (!body.name) return new Response(\"Name is required\", { status: 400 });\n if (!AP_NAME_REGEX.test(body.name)) return new Response(\"Invalid Access Point name format\", { status: 400 });\n \n const currentAps = await apModel.getAccessPoints(profileId);\n if (currentAps.some(ap => ap.name.toLowerCase() === body.name.toLowerCase())) {\n return new Response(\"Access Point name already exists\", { status: 400 });\n }\n const maxAps = Number(env.MAX_ACCESS_POINTS_PER_PROFILE) || 100;\n if (currentAps.length >= maxAps) return new Response(`Access point limit exceeded (max ${maxAps})`, { status: 400 });\n \n const result = await apModel.addAccessPoint(profileId, body.name);\n return new Response(JSON.stringify(result), { status: 201, headers: { 'Content-Type': 'application/json' } });\n }\n\n if (request.method === 'PATCH' && pathParts.length === 5) {\n const apId = pathParts[4];\n const body = await request.json() as { name: string };\n if (!body.name) return new Response(\"Name is required\", { status: 400 });\n if (!AP_NAME_REGEX.test(body.name)) return new Response(\"Invalid Access Point name format\", { status: 400 });\n\n const currentAps = await apModel.getAccessPoints(profileId);\n if (currentAps.some(ap => ap.id !== apId && ap.name.toLowerCase() === body.name.toLowerCase())) {\n return new Response(\"Access Point name already exists\", { status: 400 });\n }\n\n await apModel.updateAccessPointName(apId, profileId, body.name);\n return new Response(JSON.stringify({ success: true }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (request.method === 'DELETE' && pathParts.length === 5) {\n const apId = pathParts[4];\n await apModel.deleteAccessPoint(apId, profileId);\n return new Response(null, { status: 204 });\n }\n\n return new Response(\"Method Not Allowed\", { status: 405 });\n}\n", "import { Env, User, ExecutionContext, Rule } from \"../../types\";\nimport { RuleModel } from \"../../models/rule\";\nimport { pipeline } from \"../../pipeline\";\n\n/**\n * Handle custom rules requests to /api/profiles/:id/rules\n */\nexport async function handleProfileRulesRequest(\n request: Request,\n env: Env,\n user: User,\n profileId: string,\n pathParts: string[],\n ctx: ExecutionContext\n): Promise<Response> {\n const ruleModel = new RuleModel(env.DB);\n\n if (request.method === 'GET') {\n const results = await ruleModel.getRules(profileId);\n return new Response(JSON.stringify(results), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (request.method === 'POST') {\n const body = await request.json() as any;\n\n // Support bulk insertion when body is an array or contains rules array\n const isBulk = Array.isArray(body) || (body && Array.isArray(body.rules));\n if (isBulk) {\n const rawList: any[] = Array.isArray(body) ? body : body.rules;\n const seen = new Set<string>();\n const validRules: Partial<Rule>[] = [];\n\n for (const r of rawList) {\n if (!r || typeof r.pattern !== 'string') continue;\n const pattern = r.pattern.trim().toLowerCase();\n if (!pattern || seen.has(pattern)) continue;\n seen.add(pattern);\n\n const type = String(r.type || 'BLOCK').toUpperCase();\n if (type !== 'ALLOW' && type !== 'BLOCK' && type !== 'REDIRECT') continue;\n\n validRules.push({\n type: type as 'ALLOW' | 'BLOCK' | 'REDIRECT',\n pattern,\n v_a: r.v_a || null,\n v_aaaa: r.v_aaaa || null,\n v_txt: r.v_txt || null,\n v_cname: r.v_cname || null,\n created_at: typeof r.created_at === 'number' ? r.created_at : undefined,\n });\n }\n\n if (validRules.length === 0) {\n return new Response(JSON.stringify({ count: 0, message: \"No valid rules provided\" }), {\n status: 200,\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n const existingRules = await ruleModel.getRules(profileId);\n const existingPatterns = new Set(existingRules.map(r => r.pattern.trim().toLowerCase()));\n const rulesToInsert = validRules.filter(r => !existingPatterns.has(r.pattern!));\n\n let insertedCount = 0;\n if (rulesToInsert.length > 0) {\n insertedCount = await ruleModel.addRulesBulk(profileId, rulesToInsert);\n ctx.waitUntil(pipeline.clearCache(profileId, false, env));\n }\n\n return new Response(JSON.stringify({ success: true, count: insertedCount }), {\n status: 201,\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n const rule = body;\n const pattern = rule.pattern ? rule.pattern.trim() : \"\";\n if (!pattern) {\n return new Response(\"Domain pattern cannot be empty\", { status: 400 });\n }\n const existing = await ruleModel.getRuleByPattern(profileId, pattern);\n if (existing) {\n return new Response(\"Rule for this domain already exists\", { status: 400 });\n }\n await ruleModel.addRule(profileId, rule);\n ctx.waitUntil(pipeline.clearCache(profileId, false, env));\n return new Response(null, { status: 201 });\n }\n\n if (request.method === 'PUT') {\n const rule = await request.json() as any;\n const pattern = rule.pattern ? rule.pattern.trim() : \"\";\n if (!pattern) {\n return new Response(\"Domain pattern cannot be empty\", { status: 400 });\n }\n const existing = await ruleModel.getRuleByPatternExcludeId(profileId, pattern, rule.id);\n if (existing) {\n return new Response(\"Rule for this domain already exists\", { status: 400 });\n }\n await ruleModel.updateRule(rule.id, profileId, rule);\n ctx.waitUntil(pipeline.clearCache(profileId, false, env));\n return new Response(null, { status: 200 });\n }\n\n if (request.method === 'DELETE') {\n const { id } = await request.json() as any;\n await ruleModel.deleteRule(id, profileId);\n ctx.waitUntil(pipeline.clearCache(profileId, false, env));\n return new Response(null, { status: 204 });\n }\n\n return new Response(\"Method Not Allowed\", { status: 405 });\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { List } from \"../types\";\n\nexport class ListModel {\n constructor(private db: D1Database) {}\n\n async getLists(profileId: string): Promise<List[]> {\n const { results } = await this.db.prepare(\"SELECT * FROM lists WHERE profile_id = ?\").bind(profileId).all<List>();\n return results;\n }\n\n async addList(profileId: string, url: string): Promise<boolean> {\n const result = await this.db.prepare(\"INSERT INTO lists (profile_id, url) VALUES (?, ?)\").bind(profileId, url).run();\n return result.success;\n }\n\n /**\n * Batch inserts multiple filter list URLs for a profile using D1 batch operations.\n * Chunks execution into batches of up to 100 statements to respect D1 limits.\n *\n * @param profileId - Profile identifier.\n * @param items - Array of filter list URLs or objects with url & enabled status.\n * @returns Total number of lists successfully inserted.\n */\n async addListsBulk(profileId: string, items: (string | { url: string; enabled?: number | boolean })[]): Promise<number> {\n if (!items || items.length === 0) return 0;\n const statements = items.map(item => {\n const url = typeof item === 'string' ? item.trim() : item.url.trim();\n const enabled = (typeof item === 'object' && item.enabled !== undefined)\n ? (item.enabled ? 1 : 0)\n : 1;\n return this.db.prepare(\"INSERT INTO lists (profile_id, url, enabled) VALUES (?, ?, ?)\").bind(profileId, url, enabled);\n });\n\n let inserted = 0;\n for (let i = 0; i < statements.length; i += 100) {\n const chunk = statements.slice(i, i + 100);\n const results = await this.db.batch(chunk);\n for (const res of results) {\n if (res.success) inserted += res.meta.changes || 1;\n }\n }\n return inserted;\n }\n\n /**\n * Enables or disables a list without deleting it. Disabled lists are skipped\n * by the sync orchestrator and excluded when the merged profile bloom is\n * rebuilt, so toggling one off removes its domains from filtering while\n * keeping the subscription and its sync history.\n */\n async setEnabled(id: number, profileId: string, enabled: boolean): Promise<boolean> {\n const result = await this.db\n .prepare(\"UPDATE lists SET enabled = ? WHERE id = ? AND profile_id = ?\")\n .bind(enabled ? 1 : 0, id, profileId)\n .run();\n return result.success;\n }\n\n /** Fetches a single list belonging to a profile, or null when absent. */\n async getListById(id: number, profileId: string): Promise<List | null> {\n return await this.db\n .prepare(\"SELECT * FROM lists WHERE id = ? AND profile_id = ?\")\n .bind(id, profileId)\n .first<List | null>();\n }\n\n async deleteList(id: number, profileId: string): Promise<boolean> {\n const result = await this.db.prepare(\"DELETE FROM lists WHERE id = ? AND profile_id = ?\").bind(id, profileId).run();\n return result.success;\n }\n\n /**\n * Updates list sync status. If enabled is null or undefined,\n * the current enabled status in the database is preserved.\n */\n async updateListSyncStatus(id: number, now: number | null, enabled?: number | null, syncError: string | null = null): Promise<boolean> {\n if (enabled !== undefined && enabled !== null) {\n const result = await this.db.prepare(\n \"UPDATE lists SET last_synced_at = ?, enabled = ?, sync_error = ? WHERE id = ?\"\n )\n .bind(now, enabled, syncError, id)\n .run();\n return result.success;\n } else {\n const result = await this.db.prepare(\n \"UPDATE lists SET last_synced_at = ?, sync_error = ? WHERE id = ?\"\n )\n .bind(now, syncError, id)\n .run();\n return result.success;\n }\n }\n\n async resetListSyncStatus(profileId: string): Promise<boolean> {\n const result = await this.db.prepare(\n \"UPDATE lists SET last_synced_at = 0 WHERE profile_id = ?\"\n )\n .bind(profileId)\n .run();\n return result.success;\n }\n}\n", "import { D1Database } from \"@cloudflare/workers-types\";\nimport { BLOOM_CHUNK_SIZE } from \"../utils/bloom\";\n\nexport class ListBloomModel {\n constructor(private db: D1Database) {}\n\n async upsertListBloom(listId: number, bloomFilter: ArrayBuffer, now: number): Promise<boolean> {\n const uint8Array = new Uint8Array(bloomFilter);\n const statements = [];\n\n // Delete existing chunks for this list\n statements.push(\n this.db.prepare(\"DELETE FROM list_blooms WHERE list_id = ?\").bind(listId)\n );\n\n // Insert new chunks (1.5MB per chunk, <= 2MB D1 limit)\n let chunkIndex = 0;\n for (let offset = 0; offset < uint8Array.length; offset += BLOOM_CHUNK_SIZE) {\n const chunkData = uint8Array.slice(offset, offset + BLOOM_CHUNK_SIZE).buffer as ArrayBuffer;\n statements.push(\n this.db.prepare(\n \"INSERT INTO list_blooms (list_id, chunk_index, bloom_filter_chunk, updated_at) VALUES (?, ?, ?, ?)\"\n ).bind(listId, chunkIndex, chunkData, now)\n );\n chunkIndex++;\n }\n\n const results = await this.db.batch(statements);\n return results.every(r => r.success);\n }\n\n /**\n * Whether any bloom chunk is stored for this list.\n *\n * getListBloom concatenates every chunk (~2.4MB for a 1M-domain list), which\n * is far too much work to answer a yes/no question - and doing it for several\n * lists concurrently would blow past the memory ceiling combineAndPromote\n * maintains by loading one list at a time.\n */\n async hasListBloom(listId: number): Promise<boolean> {\n const row = await this.db.prepare(\n \"SELECT 1 AS present FROM list_blooms WHERE list_id = ? LIMIT 1\"\n ).bind(listId).first<{ present: number }>();\n return !!row;\n }\n\n async getListBloom(listId: number): Promise<ArrayBuffer | null> {\n const { results } = await this.db.prepare(\n \"SELECT bloom_filter_chunk FROM list_blooms WHERE list_id = ? ORDER BY chunk_index ASC\"\n )\n .bind(listId)\n .all<{ bloom_filter_chunk: ArrayBuffer }>();\n\n if (!results || results.length === 0) return null;\n\n const totalLength = results.reduce((acc, row) => {\n const len = row.bloom_filter_chunk.byteLength ?? (row.bloom_filter_chunk as any).length;\n return acc + len;\n }, 0);\n const combined = new Uint8Array(totalLength);\n\n let offset = 0;\n for (const row of results) {\n const chunk = new Uint8Array(row.bloom_filter_chunk);\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n\n return combined.buffer as ArrayBuffer;\n }\n\n async deleteListBloom(listId: number): Promise<boolean> {\n const result = await this.db.prepare(\n \"DELETE FROM list_blooms WHERE list_id = ?\"\n )\n .bind(listId)\n .run();\n return result.success;\n }\n\n async getActiveListBloomsForProfile(profileId: string): Promise<ArrayBuffer[]> {\n const { results } = await this.db.prepare(`\n SELECT lb.list_id, lb.bloom_filter_chunk\n FROM list_blooms lb\n JOIN lists l ON lb.list_id = l.id\n WHERE l.profile_id = ? AND l.enabled = 1\n ORDER BY lb.list_id ASC, lb.chunk_index ASC\n `)\n .bind(profileId)\n .all<{ list_id: number; bloom_filter_chunk: ArrayBuffer }>();\n\n if (!results || results.length === 0) return [];\n\n // Group chunks by list_id\n const groups = new Map<number, ArrayBuffer[]>();\n for (const row of results) {\n if (!groups.has(row.list_id)) {\n groups.set(row.list_id, []);\n }\n groups.get(row.list_id)!.push(row.bloom_filter_chunk);\n }\n\n // Combine chunks for each list_id\n const blooms: ArrayBuffer[] = [];\n for (const [, chunks] of groups.entries()) {\n const totalLength = chunks.reduce((acc, chunk) => {\n const len = chunk.byteLength ?? (chunk as any).length;\n return acc + len;\n }, 0);\n const combined = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n const chunkArr = new Uint8Array(chunk);\n combined.set(chunkArr, offset);\n offset += chunkArr.length;\n }\n blooms.push(combined.buffer as ArrayBuffer);\n }\n\n return blooms;\n }\n}\n", "import { Env, ExecutionContext } from \"../../types\";\nimport { BloomFilter } from \"../bloom\";\nimport { pipelineCache } from \"../../pipeline/cache\";\nimport { ProfileModel } from \"../../models/profile\";\nimport { ListModel } from \"../../models/list\";\nimport { ProfileBloomModel } from \"../../models/profileBloom\";\nimport { ListBloomModel } from \"../../models/listBloom\";\n\n/**\n * \u6C47\u603B\u5E76\u5408\u5E76\u6240\u6709\u5DF2\u542F\u7528\u5217\u8868\u7684 Bloom Filter\uFF0C\u5E76\u5C06\u5176\u539F\u5B50\u66F4\u65B0\u4E3A Profile \u7684 active Bloom\u3002\n * \n * \u6027\u80FD\u4F18\u5316\uFF1A\u4E0D\u518D\u4E00\u6B21\u6027\u901A\u8FC7 JOIN \u4ECE D1 \u52A0\u8F7D\u6240\u6709\u5217\u8868\u7684\u6240\u6709 Chunks\uFF0C\n * \u800C\u662F\u4F9D\u6B21\uFF08\u5206\u6B65\uFF09\u5355\u72EC\u52A0\u8F7D\u6BCF\u4E2A\u5217\u8868\u7684\u5B8C\u6574 Bloom Filter\uFF0C\u5E76\u5728\u5185\u5B58\u4E2D\u8FDB\u884C\u6309\u4F4D\u6216\u5408\u5E76\u3002\n */\nexport async function combineAndPromote(\n profileId: string,\n listModel: ListModel,\n listBloomModel: ListBloomModel,\n profileBloomModel: ProfileBloomModel,\n profileModel: ProfileModel,\n ctx: ExecutionContext,\n now: number,\n maxDomains: number,\n falsePositiveRate: number,\n env?: Env\n): Promise<void> {\n const lists = await listModel.getLists(profileId);\n const activeLists = lists.filter((l) => !!l.enabled);\n\n if (activeLists.length === 0) {\n // \u82E5\u6CA1\u6709\u5DF2\u540C\u6B65\u7684\u5217\u8868\uFF0C\u6E05\u7A7A profile_blooms\n await profileBloomModel.clearProfileBlooms(profileId);\n } else {\n // \u91CD\u5EFA\u5E76\u6309\u4F4D\u6216 (OR) \u5408\u5E76\u6240\u6709\u5217\u8868\u7EA7\u5E03\u9686\u8FC7\u6EE4\u5668\n const merged = BloomFilter.create(maxDomains, falsePositiveRate);\n let hasMergedAny = false;\n\n for (const list of activeLists) {\n try {\n // \u5355\u4E2A\u5217\u8868\u7684 Bloom Filter \u53CD\u5E8F\u5217\u5316\uFF0C\u5185\u5B58\u5F00\u9500\u4E0A\u9650\u6052\u5B9A\u4E3A ~2.4MB\n const buf = await listBloomModel.getListBloom(list.id);\n if (buf) {\n const filter = BloomFilter.fromUint8Array(new Uint8Array(buf));\n merged.merge(filter);\n hasMergedAny = true;\n }\n } catch (err) {\n console.error(`[Sync] Failed to load/merge list bloom for list #${list.id}:`, err);\n }\n }\n\n if (hasMergedAny) {\n const binary = merged.toUint8Array();\n await profileBloomModel.upsertProfileBloom(profileId, binary.buffer as ArrayBuffer, now);\n } else {\n await profileBloomModel.clearProfileBlooms(profileId);\n }\n }\n\n // \u4E8C\u6B21\u786E\u8BA4\u66F4\u65B0 Profile \u4E3B\u8868\u7684\u65F6\u95F4\u6233\uFF08\u5373\u4F7F\u524D\u7F6E\u6B65\u9AA4\u5DF2\u9632\u8303\u6027\u5730\u5199\u5165\u8FC7\uFF09\n await profileModel.updateListUpdatedAt(profileId, now);\n\n if (ctx && typeof ctx.waitUntil === \"function\") {\n ctx.waitUntil(pipelineCache.clear(profileId, true, env));\n }\n}\n", "function isIpLike(str: string): boolean {\n if (str.includes(':')) return true; // IPv6\n\n let dotCount = 0;\n const len = str.length;\n if (len < 7 || len > 15) return false;\n for (let i = 0; i < len; i++) {\n const code = str.charCodeAt(i);\n if (code === 46) { // '.'\n dotCount++;\n } else if (code < 48 || code > 57) { // not '0'-'9'\n return false;\n }\n }\n return dotCount === 3;\n}\n\n/**\n * \u89E3\u6790\u5355\u884C\u8BA2\u9605\u89C4\u5219\uFF0C\u63D0\u53D6\u57DF\u540D\u3002\n * \u652F\u6301\uFF1AHosts\u683C\u5F0F\u3001AdGuard\u5E7F\u544A\u8FC7\u6EE4\u89C4\u5219\u683C\u5F0F\u3002\n */\nexport function parseLine(line: string): string | null {\n // \u624B\u52A8\u53BB\u9664\u4E24\u7AEF\u7A7A\u767D\u5B57\u7B26\uFF0C\u63D0\u5347\u6027\u80FD\u5E76\u51CF\u5C11\u4E34\u65F6\u5B57\u7B26\u4E32\u5206\u914D\n let start = 0;\n const len = line.length;\n while (start < len) {\n const code = line.charCodeAt(start);\n if (code === 32 || code === 9 || code === 13 || code === 10) {\n start++;\n } else {\n break;\n }\n }\n\n let end = len;\n while (end > start) {\n const code = line.charCodeAt(end - 1);\n if (code === 32 || code === 9 || code === 13 || code === 10) {\n end--;\n } else {\n break;\n }\n }\n\n if (start >= end) {\n return null;\n }\n\n // \u68C0\u67E5\u662F\u5426\u4E3A\u6CE8\u91CA\u884C\uFF08\u9996\u4E2A\u975E\u7A7A\u767D\u5B57\u7B26\u4E3A '!' \u6216 '#' \u6216 '@@'\uFF09\n const firstChar = line.charCodeAt(start);\n if (firstChar === 33 || firstChar === 35) { // '!' \u6216 '#'\n return null;\n }\n if (firstChar === 64 && start + 1 < end && line.charCodeAt(start + 1) === 64) { // '@@'\n return null;\n }\n\n const cleanLine = line.substring(start, end);\n\n // 1. AdGuard \u5E7F\u544A\u8FC7\u6EE4\u89C4\u5219\u683C\u5F0F (\u5982: ||example.com^$all)\n if (cleanLine.startsWith('||')) {\n let domain = cleanLine.substring(2);\n const caretIdx = domain.indexOf('^');\n if (caretIdx !== -1) {\n domain = domain.substring(0, caretIdx);\n }\n const dollarIdx = domain.indexOf('$');\n if (dollarIdx !== -1) {\n domain = domain.substring(0, dollarIdx);\n }\n domain = domain.trim().toLowerCase();\n return domain || null;\n }\n\n // 2. Hosts \u89C4\u5219\u6587\u4EF6\u683C\u5F0F (\u5982: 127.0.0.1 example.com)\n let firstSpace = -1;\n const lineLen = cleanLine.length;\n for (let i = 0; i < lineLen; i++) {\n const code = cleanLine.charCodeAt(i);\n if (code === 32 || code === 9) { // \u9047\u5230\u7A7A\u683C\u6216 Tab\n firstSpace = i;\n break;\n }\n }\n\n if (firstSpace === -1) {\n return cleanLine.toLowerCase();\n } else {\n const part0 = cleanLine.substring(0, firstSpace);\n // \u8DE8\u8FC7\u8FDE\u7EED\u7684\u7A7A\u683C\u6216 Tab\n let secondStart = firstSpace;\n while (secondStart < lineLen) {\n const code = cleanLine.charCodeAt(secondStart);\n if (code === 32 || code === 9) {\n secondStart++;\n } else {\n break;\n }\n }\n\n let part1 = \"\";\n if (secondStart < lineLen) {\n let secondEnd = secondStart;\n while (secondEnd < lineLen) {\n const code = cleanLine.charCodeAt(secondEnd);\n if (code === 32 || code === 9) {\n break;\n }\n secondEnd++;\n }\n part1 = cleanLine.substring(secondStart, secondEnd);\n }\n\n if (part1 !== \"\") {\n if (isIpLike(part0)) {\n const domain = part1.toLowerCase();\n if (domain !== 'localhost' && domain !== '0.0.0.0' && domain !== '127.0.0.1') {\n return domain;\n }\n return null;\n } else {\n return part0.toLowerCase();\n }\n } else {\n return part0.toLowerCase();\n }\n }\n}\n\n/**\n * \u5B8C\u6574\u6587\u672C\u89E3\u6790\uFF08\u56DE\u9000/\u6D4B\u8BD5\u7528\uFF09\n */\nexport function parseList(content: string): string[] {\n const domains = new Set<string>();\n const lines = content.split('\\n');\n for (const line of lines) {\n const domain = parseLine(line);\n if (domain) {\n domains.add(domain);\n }\n }\n return Array.from(domains);\n}\n", "import { isSafeUrl } from \"./validator\";\nimport { parseLine } from \"./parser\";\n\n/** \u5355\u4E2A\u5217\u8868\u7684\u62C9\u53D6\u7ED3\u679C */\nexport interface FetchListResult {\n /** \u6210\u529F\u89E3\u6790\u5E76\u7531\u56DE\u8C03\u63A5\u6536\u7684\u57DF\u540D\u603B\u6570 */\n count: number;\n /** \u9519\u8BEF\u4FE1\u606F\uFF1B\u6210\u529F\u65F6\u4E3A null */\n error: string | null;\n}\n\n/**\n * \u6D41\u5F0F\u62C9\u53D6\u5355\u4E2A\u8BA2\u9605\u5217\u8868\u5E76\u9010\u884C\u89E3\u6790\u57DF\u540D\u3002\n *\n * \u6027\u80FD\u4F18\u5316\uFF1A\u4E0D\u518D\u5C06\u5B8C\u6574\u7684\u7F51\u7EDC\u6D41\u5B58\u5165\u5927\u4E8C\u8FDB\u5236\u7F13\u51B2\u533A\u3001\u89E3\u7801\u4E3A\u5927\u5B57\u7B26\u4E32\u5E76\u62C6\u5206\u5927\u6570\u7EC4\u3002\n * \u800C\u662F\u6D41\u5F0F\uFF08chunk-by-chunk\uFF09\u8FDB\u884C UTF-8 \u6587\u672C\u89E3\u7801\u4E0E\u884C\u5207\u5206\uFF0C\u5BF9\u6BCF\u4E2A\u57DF\u540D\u89E6\u53D1\u5F02\u6B65\u56DE\u8C03\u3002\n * \u8FD9\u5C06\u5185\u5B58\u5F00\u9500\u7531 40MB+ \u964D\u4E3A <1MB\uFF0C\u5E76\u5C06\u5927\u5217\u8868 CPU \u8FD0\u884C\u65F6\u95F4\u5B8C\u5168\u6253\u6563\uFF0C\u4ECE\u6839\u672C\u4E0A\u9632\u6B62 exceeded CPU limit\u3002\n *\n * @param url - \u8BA2\u9605\u5217\u8868\u7684 HTTP(S) URL\n * @param maxBytes - \u5355\u4E2A\u5217\u8868\u7684\u5B57\u8282\u6570\u4E0A\u9650\uFF08\u8F6F\u622A\u65AD\uFF0C\u8D85\u51FA\u540E\u505C\u6B62\u8BFB\u53D6\uFF09\n * @param timeoutMs - \u8BF7\u6C42\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\n * @param onDomain - \u5F53\u63D0\u53D6\u51FA\u6709\u6548\u57DF\u540D\u65F6\u7684\u5F02\u6B65\u56DE\u8C03\u51FD\u6570\n * @returns \u89E3\u6790\u7684\u57DF\u540D\u6570\u91CF\u4E0E\u9519\u8BEF\u4FE1\u606F\n */\nexport async function fetchListContent(\n url: string,\n maxBytes: number,\n timeoutMs: number,\n onDomain: (domain: string) => Promise<void>\n): Promise<FetchListResult> {\n const deadline = Date.now() + timeoutMs;\n let currentUrl = url;\n let redirectCount = 0;\n const MAX_REDIRECTS = 5;\n let response: Response;\n\n try {\n while (true) {\n if (!isSafeUrl(currentUrl)) {\n return {\n count: 0,\n error: \"Invalid list URL. Private networks, localhosts, and internal domains are not allowed.\",\n };\n }\n\n const remainingTimeout = deadline - Date.now();\n if (remainingTimeout <= 0) {\n return { count: 0, error: \"Request timed out\" };\n }\n\n response = await fetch(currentUrl, {\n redirect: \"manual\",\n signal: AbortSignal.timeout(remainingTimeout),\n });\n\n // Handle HTTP 3xx redirects securely (RFC 7231 / RFC 7538)\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get(\"location\");\n if (!location) {\n return {\n count: 0,\n error: `Redirect status ${response.status} without Location header`,\n };\n }\n\n redirectCount++;\n if (redirectCount > MAX_REDIRECTS) {\n return {\n count: 0,\n error: `Too many redirects (max ${MAX_REDIRECTS})`,\n };\n }\n\n try {\n const nextUrl = new URL(location, currentUrl).toString();\n const nextProto = new URL(nextUrl).protocol;\n if (nextProto !== \"http:\" && nextProto !== \"https:\") {\n return {\n count: 0,\n error: `Invalid redirect protocol: ${nextProto}`,\n };\n }\n currentUrl = nextUrl;\n continue;\n } catch {\n return {\n count: 0,\n error: `Invalid redirect location: ${location}`,\n };\n }\n }\n\n break;\n }\n\n if (!response.ok) {\n return {\n count: 0,\n error: `HTTP error! Status: ${response.status} ${response.statusText}`,\n };\n }\n\n // \u901A\u8FC7 Content-Length \u5FEB\u901F\u62D2\u7EDD\u660E\u663E\u8D85\u9650\u7684\u5217\u8868\n const contentLength = response.headers.get(\"content-length\");\n if (contentLength && parseInt(contentLength, 10) > maxBytes) {\n return {\n count: 0,\n error: `List too large (${(parseInt(contentLength, 10) / 1024 / 1024).toFixed(2)} MB), limit is ${(maxBytes / 1024 / 1024).toFixed(0)} MB`,\n };\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n return { count: 0, error: \"Failed to get stream reader from response\" };\n }\n\n const decoder = new TextDecoder(\"utf-8\", { fatal: false, ignoreBOM: true });\n let buffer = \"\";\n let count = 0;\n let totalBytes = 0;\n let truncated = false;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n totalBytes += value.length;\n if (totalBytes > maxBytes) {\n await reader.cancel();\n truncated = true;\n break;\n }\n\n // \u6D41\u5F0F\u89E3\u7801 UTF-8\uFF0C\u5904\u7406\u8FB9\u754C\u5B57\u7B26\n const chunkText = decoder.decode(value, { stream: true });\n buffer += chunkText;\n\n // \u5FEB\u901F\u5207\u5206\u51FA\u5B8C\u6574\u884C\n const lines = buffer.split(\"\\n\");\n // \u6700\u540E\u4E00\u4E2A\u5143\u7D20\u82E5\u6CA1\u6709\u6362\u884C\u7B26\u5219\u4E3A\u672A\u5B8C\u7ED3\u7684\u884C\u788E\u7247\uFF0C\u7559\u5230\u4E0B\u4E00\u6B21\u8FED\u4EE3\u62FC\u63A5\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n const domain = parseLine(line);\n if (domain) {\n await onDomain(domain);\n count++;\n }\n }\n }\n }\n\n // \u5904\u7406\u6D41\u5B8C\u7ED3\u540E\u6B8B\u7559\u7684\u5C3E\u884C\u6570\u636E\n if (buffer) {\n const domain = parseLine(buffer);\n if (domain) {\n await onDomain(domain);\n count++;\n }\n }\n\n if (truncated) {\n console.warn(`[Fetcher] List truncated at ${(maxBytes / 1024 / 1024).toFixed(0)} MB: ${url}`);\n }\n\n return { count, error: null };\n } catch (e: any) {\n return { count: 0, error: e.message || String(e) };\n }\n}\n", "import { Env } from \"../../types\";\nimport { BloomFilter } from \"../bloom\";\nimport { fetchListContent } from \"../listFetcher\";\nimport { ListModel } from \"../../models/list\";\nimport { ListBloomModel } from \"../../models/listBloom\";\n\nconst MAX_LIST_BYTES = 5 * 1024 * 1024;\n\n/**\n * \u540C\u6B65\u5355\u4E2A\u8BA2\u9605\u5217\u8868\u7684\u901A\u7528\u4E1A\u52A1\u903B\u8F91\u3002\n * \n * \u6027\u80FD\u4F18\u5316\uFF1A\u4F7F\u7528\u6D41\u5F0F\u4E0B\u8F7D\u4E0E\u884C\u89E3\u6790\uFF0C\u76F4\u63A5\u5728\u4E0B\u8F7D\u8FC7\u7A0B\u4E2D\u52A8\u6001\u5C06\u57DF\u540D\u586B\u5165\u5E03\u9686\u8FC7\u6EE4\u5668\u3002\n * \u6781\u5927\u51CF\u5C11\u5185\u5B58\u5206\u914D\uFF08\u4E0D\u518D\u9700\u8981\u4FDD\u5B58\u5E9E\u5927\u7684 domains \u6570\u7EC4\uFF09\uFF0C\u5E76\u6BCF 50,000 \u4E2A\u57DF\u540D\u4E3B\u52A8\u51FA\u8BA9 CPU \u5598\u606F\uFF0C\u89C4\u907F CPU \u8D85\u8F7D\u3002\n * \n * @returns \u6700\u7EC8\u7684\u540C\u6B65\u9519\u8BEF\u4FE1\u606F (\u82E5\u6210\u529F\u5219\u4E3A null)\n */\nexport async function syncSingleList(\n profileId: string,\n list: { id: number; url: string },\n env: Env,\n listModel: ListModel,\n listBloomModel: ListBloomModel,\n now: number\n): Promise<string | null> {\n const timeoutMs = Number(env.SYNC_TIMEOUT_MS) || 30000;\n const maxListDomains = Number(env.MAX_LIST_DOMAINS) || 500000;\n const maxDomains = Number(env.MAX_SYNC_DOMAINS) || 1000000;\n const falsePositiveRate = Number(env.BLOOM_FALSE_POSITIVE_RATE) || 0.0001;\n\n // \u521B\u5EFA\u5BF9\u5E94\u7684\u5217\u8868\u5E03\u9686\u8FC7\u6EE4\u5668\n const listBloom = BloomFilter.create(maxDomains, falsePositiveRate);\n let count = 0;\n\n // \u6D41\u5F0F\u4E0B\u8F7D\u5E76\u6309\u884C\u89E3\u6790\u57DF\u540D\uFF0C\u76F4\u63A5\u5F80\u5E03\u9686\u8FC7\u6EE4\u5668\u91CC\u585E\n const { error: fetchError } = await fetchListContent(\n list.url,\n MAX_LIST_BYTES,\n timeoutMs,\n async (domain) => {\n if (count < maxListDomains) {\n listBloom.add(domain);\n count++;\n\n // \u6BCF\u5904\u7406 50k \u4E2A\u57DF\u540D\u8BA9\u51FA\u4E00\u6B21 CPU \u5598\u606F\u65F6\u95F4\uFF0C\u5E73\u644A\u9AD8\u80FD CPU \u8D1F\u8F7D\n if (count % 50000 === 0) {\n await new Promise((resolve) => setTimeout(resolve, 0));\n }\n }\n }\n );\n\n let syncError: string | null = fetchError;\n\n if (!fetchError) {\n // \u5199\u5165\u5217\u8868\u7EA7\u5E03\u9686\u8FC7\u6EE4\u5668\u5230\u6570\u636E\u5E93\n await listBloomModel.upsertListBloom(list.id, listBloom.toUint8Array().buffer as ArrayBuffer, now);\n console.log(\n `[Sync] Profile ${profileId}: successfully updated list #${list.id} with ${count} domains.`\n );\n } else {\n // \u62C9\u53D6\u5931\u8D25\uFF1A\u8DF3\u8FC7\uFF0C\u5E76\u6CBF\u7528\u539F\u6709\u5217\u8868\u7F13\u5B58\uFF0C\u8BB0\u5F55\u9519\u8BEF\u539F\u56E0\uFF0C\u4FDD\u6301\u542F\u7528\u72B6\u6001\n console.warn(\n `[Sync] Profile ${profileId}: failed to fetch/parse list #${list.id}. ` +\n `Skipping and keeping old bloom. Error: ${fetchError}`\n );\n }\n\n // \u65E0\u8BBA\u6210\u529F\u8FD8\u662F\u5931\u8D25\uFF0C\u90FD\u66F4\u65B0 last_synced_at \u5E76\u8BB0\u5F55 syncError\uFF0C\u4FDD\u6301\u5217\u8868\u539F\u6709\u7684 enabled \u72B6\u6001\uFF08\u907F\u514D\u5E76\u53D1\u5207\u6362\u4E3A\u7981\u7528\u540E\u88AB\u5F3A\u5236\u8986\u76D6\uFF09\n await listModel.updateListSyncStatus(list.id, now, null, syncError);\n return syncError;\n}\n", "import { Env, ExecutionContext } from \"../../types\";\nimport { ProfileModel } from \"../../models/profile\";\nimport { ListModel } from \"../../models/list\";\nimport { ProfileBloomModel } from \"../../models/profileBloom\";\nimport { ListBloomModel } from \"../../models/listBloom\";\nimport { combineAndPromote } from \"./combine\";\nimport { syncSingleList } from \"./singleListSync\";\n\n/**\n * \u3010Cron \u4E13\u7528\u3011\u6BCF\u6B21\u89E6\u53D1\u65F6\u5904\u7406**\u5355\u4E2A**\u6700\u65E7\u7684\u8BA2\u9605\u5217\u8868\u3002\n */\nexport async function syncNextListForProfile(\n profileId: string,\n env: Env,\n ctx: ExecutionContext\n): Promise<void> {\n const profileModel = new ProfileModel(env.DB);\n const listModel = new ListModel(env.DB);\n const profileBloomModel = new ProfileBloomModel(env.DB);\n const listBloomModel = new ListBloomModel(env.DB);\n const now = Math.floor(Date.now() / 1000);\n\n try {\n const profile = await profileModel.getById(profileId);\n if (!profile) {\n console.error(`[Sync] Profile ${profileId} not found.`);\n return;\n }\n\n const lastFullSync: number = profile.list_updated_at ?? 0;\n const lists = await listModel.getLists(profileId);\n const activeLists = lists.filter((l) => !!l.enabled);\n\n if (activeLists.length === 0) {\n await profileBloomModel.clearProfileBlooms(profileId);\n await profileModel.updateListUpdatedAt(profileId, now);\n return;\n }\n\n // \u2500\u2500 \u65B0\u5468\u671F\u68C0\u6D4B \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const isNewCycle = !activeLists.some((l) => (l.last_synced_at ?? 0) > lastFullSync);\n\n if (isNewCycle) {\n console.log(`[Sync] Profile ${profileId}: starting a new sync cycle.`);\n }\n\n // \u2500\u2500 \u9009\u53D6\u4E0B\u4E00\u4E2A\u5F85\u540C\u6B65\u5217\u8868 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const pendingLists = activeLists\n .filter((l) => (l.last_synced_at ?? 0) <= lastFullSync)\n .sort((a, b) => (a.last_synced_at ?? 0) - (b.last_synced_at ?? 0));\n\n if (pendingLists.length === 0) {\n // \u903B\u8F91\u91CD\u5165\u8FB9\u9645\u60C5\u51B5\uFF1A\u6240\u6709\u5217\u8868\u5DF2\u5B8C\u6210\uFF0C\u8FDB\u884C\u5408\u5E76\n const maxDomains = Number(env.MAX_SYNC_DOMAINS) || 1000000;\n const falsePositiveRate = Number(env.BLOOM_FALSE_POSITIVE_RATE) || 0.0001;\n\n // \u3010\u7194\u65AD\u5668\u3011\u5728\u8FDB\u5165\u9AD8\u80FD CPU \u8FD0\u7B97\u524D\uFF0C\u62A2\u5148\u5C06\u65F6\u95F4\u6233\u6807\u8BB0\u4E3A\u5F53\u524D\u65F6\u95F4\u3002\n // \u907F\u514D\u4E07\u4E00\u5408\u5E76\u9636\u6BB5\u8D85\u65F6\u5D29\u6389\uFF0C\u5BFC\u81F4\u8BE5 Profile \u4E0B\u6B21\u4ECD\u5904\u4E8E stale \u6001\uFF0C\u4ECE\u800C\u9677\u5165\u6BCF\u5206\u949F\u65E0\u9650\u91CD\u8BD5\u8D85\u65F6\u7684\u6B7B\u5FAA\u73AF\u3002\n await profileModel.updateListUpdatedAt(profileId, now);\n\n await combineAndPromote(\n profileId,\n listModel,\n listBloomModel,\n profileBloomModel,\n profileModel,\n ctx,\n now,\n maxDomains,\n falsePositiveRate,\n env\n );\n return;\n }\n\n // \u2500\u2500 \u5904\u7406\u5355\u4E2A\u5217\u8868 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const list = pendingLists[0];\n await syncSingleList(profileId, list, env, listModel, listBloomModel, now);\n\n // \u2500\u2500 \u68C0\u67E5\u672C\u5468\u671F\u662F\u5426\u5168\u90E8\u5B8C\u6210 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const updatedLists = await listModel.getLists(profileId);\n const activeUpdatedLists = updatedLists.filter((l) => !!l.enabled);\n const allDone = activeUpdatedLists.every((l) => (l.last_synced_at ?? 0) > lastFullSync);\n\n if (allDone) {\n const maxDomains = Number(env.MAX_SYNC_DOMAINS) || 1000000;\n const falsePositiveRate = Number(env.BLOOM_FALSE_POSITIVE_RATE) || 0.0001;\n\n // \u3010\u7194\u65AD\u5668\u3011\u5148\u6807\u8BB0\u8BE5 Profile \u4E3A Fresh \u6001\uFF0C\u4EE5\u9632 combineAndPromote \u8D85\u65F6 crash \u9020\u6210\u6B7B\u5FAA\u73AF\u91CD\u8BD5\n await profileModel.updateListUpdatedAt(profileId, now);\n\n await combineAndPromote(\n profileId,\n listModel,\n listBloomModel,\n profileBloomModel,\n profileModel,\n ctx,\n now,\n maxDomains,\n falsePositiveRate,\n env\n );\n console.log(\n `[Sync] Profile ${profileId}: all ${activeUpdatedLists.length} list(s) processed \u2014 ` +\n `promoted combined active bloom.`\n );\n } else {\n const remaining = activeUpdatedLists.filter(\n (l) => (l.last_synced_at ?? 0) <= lastFullSync\n ).length;\n console.log(\n `[Sync] Profile ${profileId}: ${remaining} active list(s) remaining in this cycle.`\n );\n }\n } catch (e) {\n console.error(`[Sync] Critical failure for Profile ${profileId}:`, e);\n // \u9632\u6B62\u5355\u70B9\u6545\u969C\u6C38\u4E45\u963B\u585E\n await profileModel.updateListUpdatedAt(profileId, now);\n }\n}\n\n/**\n * \u3010\u624B\u52A8/\u5168\u91CF\u540C\u6B65\u4E13\u7528\u3011\u540C\u6B65\u8BE5 Profile \u4E0B\u7684\u6240\u6709\u5DF2\u542F\u7528\u8BA2\u9605\u5217\u8868\u3002\n */\nexport async function syncAllListsForProfile(\n profileId: string,\n env: Env,\n ctx: ExecutionContext\n): Promise<void> {\n const profileModel = new ProfileModel(env.DB);\n const listModel = new ListModel(env.DB);\n const profileBloomModel = new ProfileBloomModel(env.DB);\n const listBloomModel = new ListBloomModel(env.DB);\n const now = Math.floor(Date.now() / 1000);\n\n try {\n const profile = await profileModel.getById(profileId);\n if (!profile) {\n console.error(`[Sync] Profile ${profileId} not found.`);\n return;\n }\n\n const lists = await listModel.getLists(profileId);\n const activeLists = lists.filter((l) => !!l.enabled);\n\n if (activeLists.length === 0) {\n await profileBloomModel.clearProfileBlooms(profileId);\n await profileModel.updateListUpdatedAt(profileId, now);\n return;\n }\n\n // \u4F9D\u6B21\u540C\u6B65\u6240\u6709\u5217\u8868\n for (const list of activeLists) {\n await syncSingleList(profileId, list, env, listModel, listBloomModel, now);\n }\n\n // \u3010\u7194\u65AD\u5668\u3011\u5148\u66F4\u65B0\u4E3B\u8868\u65F6\u95F4\uFF0C\u4EE5\u9632\u5408\u5E76\u8FC7\u7A0B\u4E2D\u5D29\u6E83\u5BFC\u81F4\u65E0\u9650\u91CD\u8BD5\n await profileModel.updateListUpdatedAt(profileId, now);\n\n const maxDomains = Number(env.MAX_SYNC_DOMAINS) || 1000000;\n const falsePositiveRate = Number(env.BLOOM_FALSE_POSITIVE_RATE) || 0.0001;\n\n // \u5168\u90E8\u540C\u6B65\u5B8C\u6BD5\u540E\uFF0C\u6267\u884C\u5408\u5E76\u548C\u664B\u5347\u64CD\u4F5C\n await combineAndPromote(\n profileId,\n listModel,\n listBloomModel,\n profileBloomModel,\n profileModel,\n ctx,\n now,\n maxDomains,\n falsePositiveRate,\n env\n );\n console.log(`[Sync] Profile ${profileId}: manual sync cycle complete.`);\n } catch (e) {\n console.error(`[Sync] Critical failure in manual sync for Profile ${profileId}:`, e);\n // \u9632\u6B62\u5355\u70B9\u6545\u969C\u6C38\u4E45\u963B\u585E\n await profileModel.updateListUpdatedAt(profileId, now);\n }\n}\n\n/**\n * \u5FEB\u901F\u91CD\u6784\u5E76\u5408\u5E76 Profile \u7684\u5E03\u9686\u8FC7\u6EE4\u5668\uFF08\u7EAF\u5185\u5B58\u5408\u5E76\uFF0C\u4E0D\u89E6\u53D1\u5916\u90E8\u7F51\u7EDC\u4E0B\u8F7D\uFF09\u3002\n * \u9002\u7528\u573A\u666F\uFF1A\u5220\u9664\u89C4\u5219\u5217\u8868\u3001\u5207\u6362\u5217\u8868\u542F\u7528/\u7981\u7528\u72B6\u6001\u7B49\u4EC5\u6539\u53D8\u53C2\u4E0E\u5408\u5E76\u7684\u96C6\u5408\u3001\u800C\u4E0D\u6539\u53D8\u5217\u8868\u672C\u8EAB\u5185\u5BB9\u7684\u573A\u666F\u3002\n */\nexport async function rebuildProfileBloom(\n profileId: string,\n env: Env,\n ctx: ExecutionContext\n): Promise<void> {\n const listModel = new ListModel(env.DB);\n const listBloomModel = new ListBloomModel(env.DB);\n const profileBloomModel = new ProfileBloomModel(env.DB);\n const profileModel = new ProfileModel(env.DB);\n\n const profile = await profileModel.getById(profileId);\n const priorListUpdatedAt = profile?.list_updated_at ?? 0;\n\n // A list can be enabled while it has no bloom yet - added disabled in bulk, or\n // its last sync failed. combineAndPromote would skip it silently, so fetch it\n // first. hasListBloom is an existence probe: getListBloom would concatenate\n // every chunk of every list just to answer a boolean.\n const activeLists = (await listModel.getLists(profileId)).filter((l) => !!l.enabled);\n const presence = await Promise.all(\n activeLists.map((l) =>\n listBloomModel\n .hasListBloom(l.id)\n // Treat an unreadable row as present rather than downloading on a\n // transient D1 error; cron will catch it on its next pass.\n .catch(() => true)\n )\n );\n\n // Sync the list that is actually missing, rather than delegating to\n // syncNextListForProfile: a list whose sync errored carries a recent\n // last_synced_at, so that picker would fetch a healthy list ahead of it.\n // One per call keeps this bounded; cron handles any others.\n const missingIndex = presence.findIndex((present) => !present);\n if (missingIndex !== -1) {\n const missing = activeLists[missingIndex];\n try {\n await syncSingleList(\n profileId,\n missing,\n env,\n listModel,\n listBloomModel,\n Math.floor(Date.now() / 1000)\n );\n } catch (e) {\n console.error(`[Sync] fetch of list ${missing.id} during rebuild failed:`, e);\n }\n }\n\n await combineAndPromote(\n profileId,\n listModel,\n listBloomModel,\n profileBloomModel,\n profileModel,\n ctx,\n Math.floor(Date.now() / 1000),\n Number(env.MAX_SYNC_DOMAINS) || 1000000,\n Number(env.BLOOM_FALSE_POSITIVE_RATE) || 0.0001,\n env\n );\n\n // combineAndPromote \u4F1A\u5C06 list_updated_at \u6807\u8BB0\u4E3A\u5F53\u524D\u65F6\u95F4\u6233\uFF08\u7C7B\u4F3C\u4E8E\u5B8C\u6210\u4E86\u4E00\u4E2A\u5168\u91CF\u540C\u6B65\u5468\u671F\uFF09\u3002\n // \u7531\u4E8E\u672C\u6B21\u4EC5\u4E3A\u5217\u8868\u5220\u9664\u540E\u7684\u672C\u5730\u91CD\u6784\u5408\u5E76\uFF0C\u5E76\u672A\u771F\u6B63\u91CD\u65B0\u4E0B\u8F7D\u66F4\u65B0\u6240\u6709\u5217\u8868\uFF0C\u56E0\u6B64\u9700\u6062\u590D\u5148\u524D\u7684 list_updated_at\uFF1B\n // \u5426\u5219 Cron \u5B9A\u65F6\u4EFB\u52A1\u4F1A\u8BEF\u8BA4\u4E3A\u521A\u521A\u5B8C\u6210\u4E86\u5168\u91CF\u66F4\u65B0\uFF0C\u4ECE\u800C\u63A8\u8FDF\u5E38\u89C4\u7684\u5468\u671F\u6027\u5237\u65B0\u3002\n await profileModel.updateListUpdatedAt(profileId, priorListUpdatedAt);\n}\n", "import { Env, User, ExecutionContext } from \"../../types\";\nimport { ListModel } from \"../../models/list\";\nimport { ProfileModel } from \"../../models/profile\";\nimport { syncNextListForProfile, syncAllListsForProfile, rebuildProfileBloom } from \"../../utils/sync\";\nimport { isSafeUrl } from \"../../utils/validator\";\nimport { pipeline } from \"../../pipeline\";\nimport { ListBloomModel } from \"../../models/listBloom\";\nimport { BloomFilter } from \"../../utils/bloom\";\n\n/**\n * Handle filter lists requests to /api/profiles/:id/lists\n */\nexport async function handleProfileListsRequest(\n request: Request,\n env: Env,\n user: User,\n profileId: string,\n pathParts: string[],\n ctx: ExecutionContext\n): Promise<Response> {\n const listModel = new ListModel(env.DB);\n const profileModel = new ProfileModel(env.DB);\n\n if (request.method === 'GET') {\n // GET /api/profiles/:id/lists/:listId/check?domain=example.com\n //\n // Answers \"does this list block this domain\" straight from the list's own\n // bloom filter, walking parent domains exactly as pipeline/filter.ts does.\n // External lists are stored only as bloom filters, so their entries cannot\n // be enumerated or substring-searched - but membership is answerable in\n // microseconds, which is the question the UI actually asks.\n if (pathParts[5] === 'check') {\n const listId = Number(pathParts[4]);\n if (!Number.isInteger(listId) || listId <= 0) {\n return new Response(\"Invalid list id\", { status: 400 });\n }\n\n const rawDomain = (new URL(request.url).searchParams.get('domain') || '').trim().toLowerCase();\n // Strip trailing dots (e.g. FQDN \"example.com.\") to match DNS labels correctly\n const domain = rawDomain.replace(/\\.+$/, '');\n if (!domain || domain.length > 253 || !/^[a-z0-9._-]+$/.test(domain)) {\n return new Response(\"Invalid or missing domain parameter\", { status: 400 });\n }\n\n const list = await listModel.getListById(listId, profileId);\n if (!list) return new Response(\"List Not Found\", { status: 404 });\n\n const buffer = await new ListBloomModel(env.DB).getListBloom(listId);\n if (!buffer) {\n return new Response(JSON.stringify({ domain, blocked: false, synced: false }), {\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n let bloom: BloomFilter;\n try {\n bloom = BloomFilter.fromUint8Array(new Uint8Array(buffer));\n } catch (err) {\n console.error(`[Lists] Failed to deserialize bloom filter for list #${listId}:`, err);\n return new Response(JSON.stringify({ domain, blocked: false, synced: false, error: \"Corrupted filter\" }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n let candidate: string = domain;\n let matched: string | null = null;\n while (candidate) {\n if (bloom.test(candidate)) { matched = candidate; break; }\n const dot = candidate.indexOf('.');\n if (dot === -1) break;\n candidate = candidate.slice(dot + 1);\n }\n\n return new Response(JSON.stringify({\n domain,\n blocked: matched !== null,\n // The entry that matched: equal to `domain` on an exact hit, or the\n // parent domain when the block comes from a wildcard higher up.\n matchedEntry: matched,\n // A positive is a bloom hit, not proof of membership. The parent walk\n // tests one suffix per label, so the effective false-positive rate is\n // roughly the configured rate times the number of labels tested.\n falsePositiveRate: Number(env.BLOOM_FALSE_POSITIVE_RATE) || 0.0001,\n // A disabled list still has a stored bloom, so say so rather than\n // implying the domain is currently being filtered.\n enabled: !!list.enabled,\n synced: true\n }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n const results = await listModel.getLists(profileId);\n return new Response(JSON.stringify(results), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (request.method === 'PATCH') {\n const listId = Number(pathParts[4]);\n if (!Number.isInteger(listId) || listId <= 0) {\n return new Response(\"Invalid list id\", { status: 400 });\n }\n\n let body: { enabled?: unknown };\n try {\n body = await request.json() as { enabled?: unknown };\n } catch {\n return new Response(\"Body must be { enabled: boolean }\", { status: 400 });\n }\n if (typeof body.enabled !== 'boolean') {\n return new Response(\"Body must be { enabled: boolean }\", { status: 400 });\n }\n\n const list = await listModel.getListById(listId, profileId);\n if (!list) return new Response(\"List Not Found\", { status: 404 });\n\n const updated = await listModel.setEnabled(listId, profileId, body.enabled);\n if (!updated) {\n return new Response(\"Failed to update list\", { status: 500 });\n }\n\n // Rebuild the merged profile bloom from the per-list blooms already stored.\n // rebuildProfileBloom skips the incremental orchestrator deliberately: that\n // path would re-download every remaining list, and with 2+ active lists it\n // would not recombine at all. clearCache is chained so it cannot run before\n // the new bloom is promoted.\n ctx.waitUntil(\n rebuildProfileBloom(profileId, env, ctx)\n .then(() => pipeline.clearCache(profileId, true, env))\n .catch((e) => console.error(`[Lists] bloom rebuild failed for ${profileId}:`, e))\n );\n\n return new Response(JSON.stringify({ id: listId, enabled: body.enabled }), {\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n if (request.method === 'POST') {\n if (pathParts[4] === 'sync') {\n // \u89E6\u53D1\u6240\u6709\u5217\u8868\u7684\u540C\u6B65\n ctx.waitUntil(syncAllListsForProfile(profileId, env, ctx));\n return new Response(JSON.stringify({ message: \"Sync started\" }), { status: 202 });\n }\n\n const body = await request.json() as any;\n\n // Support bulk list addition when body is an array or contains lists/urls/filters/blocklists array\n const candidateList = Array.isArray(body)\n ? body\n : (body?.lists || body?.list || body?.urls || body?.url || body?.filters || body?.filter || body?.blocklists || body?.blocklist);\n\n const isBulk = Array.isArray(candidateList);\n if (isBulk) {\n const rawList: any[] = candidateList;\n const seen = new Set<string>();\n const validItems: { url: string; enabled: number }[] = [];\n\n for (const item of rawList) {\n let urlStr = typeof item === 'string'\n ? item.trim()\n : (item?.url ?? item?.link ?? item?.uri ?? item?.address ?? item?.source ?? item?.target ?? item?.download_url ?? '');\n if (typeof urlStr !== 'string') continue;\n urlStr = urlStr.replace(/^[\"']|[\"']$/g, '').trim();\n if (urlStr.startsWith('//')) {\n urlStr = `https:${urlStr}`;\n } else if (!urlStr.startsWith('http://') && !urlStr.startsWith('https://')) {\n if (urlStr.includes('.') && !urlStr.includes(' ') && urlStr.length > 3) {\n urlStr = `https://${urlStr}`;\n } else {\n continue;\n }\n }\n if (!isSafeUrl(urlStr)) continue;\n const norm = urlStr.toLowerCase();\n if (seen.has(norm)) continue;\n seen.add(norm);\n\n const enabled = (item && typeof item === 'object' && item.enabled !== undefined)\n ? (item.enabled ? 1 : 0)\n : 1;\n validItems.push({ url: urlStr, enabled });\n }\n\n if (validItems.length === 0) {\n return new Response(JSON.stringify({ count: 0, message: \"No valid URLs provided\" }), {\n status: 200,\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n const existingLists = await listModel.getLists(profileId);\n const existingUrls = new Set(existingLists.map(l => l.url.trim().toLowerCase()));\n const itemsToInsert = validItems.filter(item => !existingUrls.has(item.url.toLowerCase()));\n\n let insertedCount = 0;\n if (itemsToInsert.length > 0) {\n insertedCount = await listModel.addListsBulk(profileId, itemsToInsert);\n ctx.waitUntil(syncNextListForProfile(profileId, env, ctx));\n ctx.waitUntil(pipeline.clearCache(profileId, true, env));\n }\n\n return new Response(JSON.stringify({ success: true, count: insertedCount }), {\n status: 201,\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n const { url: listUrl } = body as { url: string };\n if (!listUrl || (!listUrl.startsWith('http://') && !listUrl.startsWith('https://'))) {\n return new Response(\"Invalid list URL format\", { status: 400 });\n }\n if (!isSafeUrl(listUrl)) {\n return new Response(\"Invalid list URL. Private networks and localhosts are not allowed.\", { status: 400 });\n }\n \n await listModel.addList(profileId, listUrl);\n // \u53EA\u89E6\u53D1\u65B0\u6DFB\u52A0\u5217\u8868\u7684\u540C\u6B65 (syncNextListForProfile \u4F1A\u6311\u9009\u672A\u540C\u6B65\u7684\u6700\u65E7\u5217\u8868\uFF0C\u5373\u6B64\u65B0\u5217\u8868)\n ctx.waitUntil(syncNextListForProfile(profileId, env, ctx));\n ctx.waitUntil(pipeline.clearCache(profileId, true, env));\n return new Response(null, { status: 201 });\n }\n\n if (request.method === 'DELETE') {\n const { id } = await request.json() as { id: number };\n await listModel.deleteList(id, profileId);\n // \u89E6\u53D1\u7EAF\u5185\u5B58\u91CD\u6784\u5408\u5E76\uFF0C\u5E76\u5728\u843D\u5E93\u540E\u6E05\u9664\u7F13\u5B58\uFF0C\u5F7B\u5E95\u6D88\u9664\u7ADE\u6001\u6761\u4EF6\u4E0E pending \u963B\u585E\n ctx.waitUntil(\n rebuildProfileBloom(profileId, env, ctx)\n .then(() => pipeline.clearCache(profileId, true, env))\n .catch((e) => console.error(`[Lists] Bloom rebuild after delete failed for ${profileId}:`, e))\n );\n return new Response(null, { status: 204 });\n }\n\n return new Response(\"Method Not Allowed\", { status: 405 });\n}\n", "import { Env, User, Profile, ProfileSettings, ExecutionContext } from \"../../types\";\nimport { LogModel } from \"../../models/log\";\nimport { cacheUtils } from \"../../utils/cache\";\nimport { handleProfileLogsWs } from \"./logsWs\";\n\n/**\n * Handle logs and analytics requests to /api/profiles/:id/logs and /api/profiles/:id/analytics\n */\nexport async function handleProfileLogsAndAnalyticsRequest(\n request: Request,\n env: Env,\n user: User,\n profile: Profile,\n pathParts: string[],\n ctx: ExecutionContext\n): Promise<Response> {\n const logModel = new LogModel(env.DB);\n const profileId = profile.id;\n\n // Handle Logs endpoint: /api/profiles/:id/logs\n if (pathParts[3] === 'logs') {\n // Handle WebSocket real-time streaming endpoint: /api/profiles/:id/logs/ws or Upgrade: websocket\n if (pathParts[4] === 'ws' || request.headers.get('Upgrade')?.toLowerCase() === 'websocket') {\n return handleProfileLogsWs(request, env, user, profile, ctx);\n }\n\n if (request.method !== 'GET') {\n return new Response(\"Method Not Allowed\", { status: 405 });\n }\n\n const urlParams = new URL(request.url).searchParams;\n const range = urlParams.get('range') || '24h';\n const before = urlParams.get('before');\n const status = urlParams.get('status');\n const search = urlParams.get('search');\n const accessPointId = urlParams.get('access_point_id');\n const destCountry = urlParams.get('dest_country');\n const isp = urlParams.get('isp');\n const domain = urlParams.get('domain') || undefined;\n const recordType = urlParams.get('record_type') || undefined;\n const geoCountry = urlParams.get('geo_country') || undefined;\n const reason = urlParams.get('reason') || undefined;\n const startParam = urlParams.get('start');\n const endParam = urlParams.get('end');\n \n let since: number;\n let until = Math.floor(Date.now() / 1000);\n const globalMaxRetention = env.MAX_LOG_RETENTION_DAYS !== undefined && env.MAX_LOG_RETENTION_DAYS !== ''\n ? Number(env.MAX_LOG_RETENTION_DAYS)\n : 30;\n const settings: ProfileSettings = JSON.parse(profile.settings);\n const logRetentionDays = settings.log_retention_days !== undefined\n ? Math.min(Number(settings.log_retention_days), globalMaxRetention)\n : globalMaxRetention;\n\n if (logRetentionDays === 0) {\n if (pathParts.length > 4 && pathParts[4] !== 'export') {\n return new Response(\"Log Not Found\", { status: 404 });\n }\n return new Response(JSON.stringify([]), { headers: { 'Content-Type': 'application/json' } });\n }\n\n const retentionThreshold = Math.floor(until - (logRetentionDays * 24 * 3600));\n\n if (startParam && endParam) {\n since = Math.max(parseInt(startParam), retentionThreshold);\n until = parseInt(endParam);\n } else {\n since = until;\n switch (range) {\n case '10m': since -= 600; break;\n case '1h': since -= 3600; break;\n case '24h': since -= 86400; break;\n case '7d': since -= 604800; break;\n case '30d': since -= 2592000; break;\n default: since -= 86400; break;\n }\n since = Math.max(since, retentionThreshold);\n }\n\n if (pathParts.length > 4) {\n if (pathParts[4] === 'export') {\n const results = await logModel.getLogs(profileId, {\n since,\n until,\n status: status || undefined,\n search: search || undefined,\n access_point_id: accessPointId || undefined,\n dest_country: destCountry || undefined,\n isp: isp || undefined,\n domain,\n record_type: recordType,\n geo_country: geoCountry,\n reason,\n export: true\n });\n return new Response(JSON.stringify(results), { headers: { 'Content-Type': 'application/json' } });\n }\n\n const logId = parseInt(pathParts[4], 10);\n if (isNaN(logId)) {\n return new Response(\"Invalid Log ID\", { status: 400 });\n }\n\n const timestampParam = urlParams.get('timestamp');\n const timestamp = timestampParam ? parseInt(timestampParam, 10) : undefined;\n\n const logDetail = await logModel.getLog(profileId, logId, timestamp);\n if (!logDetail) {\n return new Response(\"Log Not Found\", { status: 404 });\n }\n return new Response(JSON.stringify(logDetail), { headers: { 'Content-Type': 'application/json' } });\n }\n\n let limit = parseInt(urlParams.get('limit') || '50', 10);\n if (isNaN(limit) || limit <= 0) {\n limit = 50;\n } else if (limit > 100) {\n limit = 100;\n }\n\n const results = await logModel.getLogs(profileId, {\n since,\n until,\n status: status || undefined,\n search: search || undefined,\n before: before ? parseInt(before) : undefined,\n limit,\n access_point_id: accessPointId || undefined,\n dest_country: destCountry || undefined,\n isp: isp || undefined,\n domain,\n record_type: recordType,\n geo_country: geoCountry,\n reason\n });\n return new Response(JSON.stringify(results), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // Handle Analytics endpoint: /api/profiles/:id/analytics\n if (pathParts[3] === 'analytics') {\n if (request.method !== 'GET') {\n return new Response(\"Method Not Allowed\", { status: 405 });\n }\n\n const urlParams = new URL(request.url).searchParams;\n const range = urlParams.get('range');\n const startParam = urlParams.get('start');\n const endParam = urlParams.get('end');\n const accessPointId = urlParams.get('access_point_id') || undefined;\n let since: number; let until = Math.floor(Date.now() / 1000); let interval: string;\n\n if (startParam && endParam) { since = parseInt(startParam); until = parseInt(endParam); interval = \"(timestamp/3600)*3600\"; }\n else {\n switch (range) {\n case '10m': since = until - 600; interval = \"(timestamp/60)*60\"; break;\n case '1h': since = until - 3600; interval = \"(timestamp/60)*60\"; break;\n case '24h': since = until - 86400; interval = \"(timestamp/3600)*3600\"; break;\n case '7d': since = until - 604800; interval = \"(timestamp/86400)*86400\"; break;\n case '30d': since = until - 2592000; interval = \"(timestamp/86400)*86400\"; break;\n default: since = until - 86400; interval = \"(timestamp/3600)*3600\"; break;\n }\n }\n\n const subResource = pathParts[4];\n if (subResource) {\n const cache = (caches as any).default;\n const subCacheKey = `analytics_sub:${profileId}:${subResource}:${urlParams.toString()}`;\n const cached = await cacheUtils.get<any>(cache, subCacheKey);\n if (cached) {\n return new Response(JSON.stringify(cached), {\n headers: {\n 'Content-Type': 'application/json',\n 'X-Cache': 'HIT',\n 'Cache-Control': 'private, no-cache'\n }\n });\n }\n\n let data: any = null;\n if (subResource === 'summary') {\n const search = urlParams.get('search') || undefined;\n data = await logModel.getSummary(profileId, since, until, search, accessPointId);\n } else if (subResource === 'trend') {\n data = await logModel.getTrend(profileId, since, until, interval, accessPointId);\n } else if (subResource === 'top_allowed') {\n data = await logModel.getTopAllowed(profileId, since, until, accessPointId);\n } else if (subResource === 'top_blocked') {\n data = await logModel.getTopBlocked(profileId, since, until, accessPointId);\n } else if (subResource === 'clients') {\n data = await logModel.getClients(profileId, since, until, accessPointId);\n } else if (subResource === 'destinations') {\n const limitParam = urlParams.get('limit');\n const limit = limitParam ? parseInt(limitParam, 10) : undefined;\n data = await logModel.getDestinations(profileId, since, until, accessPointId, limit !== undefined && !isNaN(limit) ? limit : undefined);\n } else if (subResource === 'isps') {\n const countryCode = urlParams.get('country_code') || undefined;\n const limitParam = urlParams.get('limit');\n const limit = limitParam ? parseInt(limitParam, 10) : undefined;\n data = await logModel.getISPByCountry(profileId, countryCode, since, until, accessPointId, limit !== undefined && !isNaN(limit) ? limit : undefined);\n } else {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n await cacheUtils.set(cache, subCacheKey, data, 300);\n return new Response(JSON.stringify(data), {\n headers: {\n 'Content-Type': 'application/json',\n 'X-Cache': 'MISS',\n 'Cache-Control': 'private, no-cache'\n }\n });\n }\n \n const cache = (caches as any).default;\n const cacheKey = `analytics:${profileId}:${since}:${until}:${interval}:${accessPointId || 'all'}`;\n const cached = await cacheUtils.get<any>(cache, cacheKey);\n if (cached) {\n return new Response(JSON.stringify(cached), {\n headers: {\n 'Content-Type': 'application/json',\n 'X-Cache': 'HIT',\n 'Cache-Control': 'private, no-cache'\n }\n });\n }\n\n const analytics = await logModel.getAnalytics(profileId, since, until, interval, accessPointId);\n await cacheUtils.set(cache, cacheKey, analytics, 60);\n\n return new Response(JSON.stringify(analytics), {\n headers: {\n 'Content-Type': 'application/json',\n 'X-Cache': 'MISS',\n 'Cache-Control': 'private, no-cache'\n }\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "/**\n * @file logsWs.ts\n * @description WebSocket endpoint for real-time DNS log streaming and bidirectional gap sync.\n * Handles client subscriptions, low-overhead server-side change probing, and push notifications.\n */\n\nimport { Env, Profile, User, ExecutionContext, ResolutionLog } from '../../types';\n\nexport type LogWsClientMessage =\n | {\n type: 'SUBSCRIBE';\n sinceTimestamp: number;\n lastId?: number;\n }\n | {\n type: 'CHECK_NEW';\n sinceTimestamp?: number;\n lastId?: number;\n }\n | {\n type: 'SYNC_GAP';\n since: number;\n until: number;\n limit?: number;\n }\n | {\n type: 'PING';\n };\n\nexport type LogWsServerMessage =\n | {\n type: 'NEW_LOGS';\n logs: ResolutionLog[];\n latestTimestamp: number;\n latestId: number;\n }\n | {\n type: 'SYNC_DATA';\n logs: ResolutionLog[];\n hasMore: boolean;\n }\n | {\n type: 'PONG';\n }\n | {\n type: 'ERROR';\n message: string;\n };\n\n/**\n * Handles incoming WebSocket upgrade requests for /api/profiles/:id/logs/ws.\n *\n * @param request - HTTP Upgrade Request\n * @param env - Worker environment bindings\n * @param user - Authenticated user\n * @param profile - Target Profile entity\n * @param ctx - Execution context\n * @returns Response with 101 Switching Protocols or error status\n */\nexport async function handleProfileLogsWs(\n request: Request,\n env: Env,\n user: User | null,\n profile: Profile,\n ctx: ExecutionContext\n): Promise<Response> {\n const upgradeHeader = request.headers.get('Upgrade');\n if (!upgradeHeader || upgradeHeader.toLowerCase() !== 'websocket') {\n return new Response('Expected Upgrade: websocket', { status: 426 });\n }\n\n // Ensure WebSocketPair is supported in runtime\n const globalObj = globalThis as unknown as {\n WebSocketPair?: new () => { 0: WebSocket; 1: WebSocket };\n };\n\n if (typeof globalObj.WebSocketPair === 'undefined') {\n return new Response('WebSocket is not supported on this runtime environment', { status: 501 });\n }\n\n const pair = new globalObj.WebSocketPair();\n const [clientWs, serverWs] = Object.values(pair);\n\n // Accept server-side WebSocket\n (serverWs as unknown as { accept: () => void }).accept();\n\n const profileId = profile.id;\n let isClosed = false;\n let lastTimestamp = 0;\n let lastId = 0;\n let pollInterval: ReturnType<typeof setInterval> | null = null;\n let isChecking = false;\n\n // Keep Cloudflare Workers isolate alive for the entire lifespan of this WebSocket\n let socketCloseResolver: (() => void) | null = null;\n const socketLifetimePromise = new Promise<void>((resolve) => {\n socketCloseResolver = resolve;\n });\n\n if (ctx && typeof ctx.waitUntil === 'function') {\n ctx.waitUntil(socketLifetimePromise);\n }\n\n /**\n * Probes for new logs since (lastTimestamp, lastId).\n * Runs a lightweight LIMIT 1 index probe first before running the full query.\n */\n const checkForNewLogs = async (): Promise<void> => {\n if (isClosed || isChecking || lastTimestamp <= 0) return;\n isChecking = true;\n\n try {\n // Step 1: Low-overhead index probe\n const probe = await env.DB.prepare(`\n SELECT id, timestamp \n FROM logs \n WHERE profile_id = ? AND (timestamp > ? OR (timestamp = ? AND id > ?))\n ORDER BY timestamp DESC, id DESC \n LIMIT 1;\n `)\n .bind(profileId, lastTimestamp, lastTimestamp, lastId)\n .first<{ id: number; timestamp: number }>();\n\n if (!probe) {\n // No newer logs found, skip detailed extraction\n return;\n }\n\n // Step 2: Since new logs are confirmed, fetch details with access point name\n const { results } = await env.DB.prepare(`\n SELECT l.id, l.timestamp, l.client_ip, l.domain, l.action, l.record_type, \n l.latency, l.answer, l.geo_country, l.reason, l.access_point_id, \n l.dest_country_code, l.dest_country, l.dest_isp, l.encrypt_version, \n l.kem_key_id, l.encrypted_payload, k.kem_ct, ap.name as access_point_name \n FROM logs l \n LEFT JOIN access_points ap ON l.access_point_id = ap.id \n LEFT JOIN kem_keys k ON l.kem_key_id = k.id\n WHERE l.profile_id = ? AND (l.timestamp > ? OR (l.timestamp = ? AND l.id > ?))\n ORDER BY l.timestamp DESC, l.id DESC \n LIMIT 50;\n `)\n .bind(profileId, lastTimestamp, lastTimestamp, lastId)\n .all<ResolutionLog>();\n\n if (results && results.length > 0) {\n const newest = results[0];\n lastTimestamp = newest.timestamp;\n lastId = Number(newest.id);\n\n if (!isClosed) {\n const payload: LogWsServerMessage = {\n type: 'NEW_LOGS',\n logs: results,\n latestTimestamp: lastTimestamp,\n latestId: lastId,\n };\n serverWs.send(JSON.stringify(payload));\n }\n }\n } catch (err: unknown) {\n console.error('[Logs WS] Error probing new logs:', err);\n } finally {\n isChecking = false;\n }\n };\n\n // Start periodic 2-second background check\n pollInterval = setInterval(() => {\n void checkForNewLogs();\n }, 2000);\n\n // Listen to client messages\n serverWs.addEventListener('message', async (event: MessageEvent) => {\n try {\n const dataStr = typeof event.data === 'string' ? event.data : new TextDecoder().decode(event.data as ArrayBuffer);\n const msg = JSON.parse(dataStr) as LogWsClientMessage;\n\n switch (msg.type) {\n case 'SUBSCRIBE': {\n lastTimestamp = msg.sinceTimestamp;\n lastId = msg.lastId || 0;\n await checkForNewLogs();\n break;\n }\n case 'CHECK_NEW': {\n if (msg.sinceTimestamp !== undefined) lastTimestamp = msg.sinceTimestamp;\n if (msg.lastId !== undefined) lastId = msg.lastId;\n await checkForNewLogs();\n break;\n }\n case 'SYNC_GAP': {\n const limit = Math.min(msg.limit || 100, 100);\n const { results } = await env.DB.prepare(`\n SELECT l.id, l.timestamp, l.client_ip, l.domain, l.action, l.record_type, \n l.latency, l.answer, l.geo_country, l.reason, l.access_point_id, \n l.dest_country_code, l.dest_country, l.dest_isp, l.encrypt_version, \n l.kem_key_id, l.encrypted_payload, k.kem_ct, ap.name as access_point_name \n FROM logs l \n LEFT JOIN access_points ap ON l.access_point_id = ap.id \n LEFT JOIN kem_keys k ON l.kem_key_id = k.id\n WHERE l.profile_id = ? AND l.timestamp >= ? AND l.timestamp <= ?\n ORDER BY l.timestamp DESC, l.id DESC \n LIMIT ?;\n `)\n .bind(profileId, msg.since, msg.until, limit)\n .all<ResolutionLog>();\n\n if (!isClosed) {\n const payload: LogWsServerMessage = {\n type: 'SYNC_DATA',\n logs: results || [],\n hasMore: (results?.length || 0) >= limit,\n };\n serverWs.send(JSON.stringify(payload));\n }\n break;\n }\n case 'PING': {\n if (!isClosed) {\n const payload: LogWsServerMessage = { type: 'PONG' };\n serverWs.send(JSON.stringify(payload));\n void checkForNewLogs();\n }\n break;\n }\n default:\n break;\n }\n } catch (err: unknown) {\n console.error('[Logs WS] Message processing error:', err);\n }\n });\n\n const cleanup = (): void => {\n if (isClosed) return;\n isClosed = true;\n if (pollInterval) {\n clearInterval(pollInterval);\n pollInterval = null;\n }\n try {\n serverWs.close();\n } catch {\n // Ignore if socket already closed\n }\n if (socketCloseResolver) {\n socketCloseResolver();\n socketCloseResolver = null;\n }\n };\n\n serverWs.addEventListener('close', cleanup);\n serverWs.addEventListener('error', cleanup);\n\n return new Response(null, {\n status: 101,\n webSocket: clientWs,\n });\n}\n", "/**\n * @file e2ee.ts\n * @description API router for Profile Log End-to-End Encryption (E2EE) key management.\n * Handles public key publishing, Passkey envelope wrapped key storage, and key rotation.\n */\n\nimport { Env, User, Profile, ExecutionContext } from \"../../types\";\nimport { PasskeyModel } from \"../../models/passkey\";\nimport { invalidateProfileLogKeyCache } from \"../../pipeline/logBatcher\";\n\n/**\n * Routes /api/profiles/:id/e2ee/* endpoints.\n */\nexport async function handleProfileE2eeRequest(\n request: Request,\n env: Env,\n user: User,\n profile: Profile,\n pathParts: string[],\n _ctx: ExecutionContext\n): Promise<Response> {\n const passkeyModel = new PasskeyModel(env.DB);\n const subResource = pathParts[4]; // e.g. status, wrapped-key, init, wrap\n\n // 1. GET /api/profiles/:id/e2ee/status\n if (subResource === \"status\" && request.method === \"GET\") {\n const [logKey, wrappedKeys, recoveryKey, passkeys] = await Promise.all([\n env.DB.prepare(\"SELECT public_key, created_at, is_active FROM user_log_keys WHERE profile_id = ?\")\n .bind(profile.id)\n .first<{ public_key: string; created_at: number; is_active?: number }>(),\n env.DB.prepare(`\n SELECT upwk.passkey_id \n FROM user_passkey_wrapped_keys upwk\n JOIN passkeys pk ON upwk.passkey_id = pk.id\n WHERE pk.user_id = ?\n `)\n .bind(user.id)\n .all<{ passkey_id: string }>(),\n env.DB.prepare(\"SELECT profile_id FROM user_recovery_wrapped_keys WHERE profile_id = ?\")\n .bind(profile.id)\n .first<{ profile_id: string }>(),\n passkeyModel.listByUser(user.id),\n ]);\n\n return Response.json({\n hasKeys: Boolean(logKey),\n enabled: Boolean(logKey && (logKey.is_active === undefined || logKey.is_active === 1)),\n publicKey: logKey?.public_key || null,\n wrappedPasskeys: (wrappedKeys.results || []).map((w) => w.passkey_id),\n hasRecoveryKey: Boolean(recoveryKey),\n userPasskeyCount: passkeys.length,\n createdAt: logKey?.created_at || null,\n });\n }\n\n // 2. GET /api/profiles/:id/e2ee/wrapped-key?passkey_id=...\n if (subResource === \"wrapped-key\" && request.method === \"GET\") {\n const url = new URL(request.url);\n const passkeyId = url.searchParams.get(\"passkey_id\");\n\n let wrapped: { encrypted_sk: string; iv: string } | null = null;\n\n if (passkeyId) {\n const passkey = await passkeyModel.getByIdOrCredentialId(passkeyId, user.id);\n if (passkey) {\n wrapped = await env.DB.prepare(\n \"SELECT encrypted_sk, iv FROM user_passkey_wrapped_keys WHERE passkey_id = ?\"\n )\n .bind(passkey.id)\n .first<{ encrypted_sk: string; iv: string }>();\n }\n }\n\n if (!wrapped) {\n // Fallback: If specific passkey_id is not matched or omitted, check for any wrapped key for this user\n wrapped = await env.DB.prepare(`\n SELECT upwk.encrypted_sk, upwk.iv \n FROM user_passkey_wrapped_keys upwk\n JOIN passkeys pk ON upwk.passkey_id = pk.id\n WHERE pk.user_id = ?\n LIMIT 1\n `)\n .bind(user.id)\n .first<{ encrypted_sk: string; iv: string }>();\n }\n\n if (!wrapped) {\n return new Response(\"Wrapped key not found for this passkey\", { status: 404 });\n }\n\n return Response.json({\n encryptedSk: wrapped.encrypted_sk,\n iv: wrapped.iv,\n });\n }\n\n // 3. POST /api/profiles/:id/e2ee/init (Enable E2EE)\n if (subResource === \"init\" && request.method === \"POST\") {\n let body: any;\n try {\n body = await request.json();\n } catch {\n return new Response(\"Invalid JSON body\", { status: 400 });\n }\n\n const { publicKey, passkeyId, encryptedSk, iv, recovery } = body;\n if (!publicKey || !passkeyId || !encryptedSk || !iv) {\n return new Response(\"Missing required fields (publicKey, passkeyId, encryptedSk, iv)\", {\n status: 400,\n });\n }\n\n // Verify the passkey belongs to this user\n let passkey = await passkeyModel.getByIdOrCredentialId(passkeyId, user.id);\n if (!passkey) {\n const userPasskeys = await passkeyModel.listByUser(user.id);\n if (userPasskeys.length > 0) {\n passkey = userPasskeys[0];\n }\n }\n if (!passkey) {\n return new Response(\"Passkey not found or unauthorized\", { status: 403 });\n }\n\n const now = Math.floor(Date.now() / 1000);\n const publicKeyStr = typeof publicKey === \"string\" ? publicKey : JSON.stringify(publicKey);\n\n const stmts = [\n env.DB.prepare(\n \"INSERT INTO user_log_keys (profile_id, public_key, created_at) VALUES (?, ?, ?) ON CONFLICT(profile_id) DO UPDATE SET public_key = excluded.public_key, created_at = excluded.created_at\"\n ).bind(profile.id, publicKeyStr, now),\n env.DB.prepare(\n \"INSERT INTO user_passkey_wrapped_keys (passkey_id, profile_id, encrypted_sk, iv, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(passkey_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, created_at = excluded.created_at\"\n ).bind(passkey.id, profile.id, encryptedSk, iv, now),\n ];\n\n if (recovery && recovery.encryptedSk && recovery.iv && recovery.salt) {\n stmts.push(\n env.DB.prepare(\n \"INSERT INTO user_recovery_wrapped_keys (profile_id, encrypted_sk, iv, salt, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(profile_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, salt = excluded.salt, created_at = excluded.created_at\"\n ).bind(profile.id, recovery.encryptedSk, recovery.iv, recovery.salt, now)\n );\n }\n\n await env.DB.batch(stmts);\n invalidateProfileLogKeyCache(profile.id);\n\n return Response.json({ success: true });\n }\n\n // 4. POST /api/profiles/:id/e2ee/wrap (Wrap existing SK for a newly registered passkey)\n if (subResource === \"wrap\" && request.method === \"POST\") {\n let body: any;\n try {\n body = await request.json();\n } catch {\n return new Response(\"Invalid JSON body\", { status: 400 });\n }\n\n const { passkeyId, encryptedSk, iv } = body;\n if (!passkeyId || !encryptedSk || !iv) {\n return new Response(\"Missing required fields (passkeyId, encryptedSk, iv)\", { status: 400 });\n }\n\n let passkey = await passkeyModel.getByIdOrCredentialId(passkeyId, user.id);\n if (!passkey) {\n const userPasskeys = await passkeyModel.listByUser(user.id);\n if (userPasskeys.length > 0) {\n passkey = userPasskeys[0];\n }\n }\n if (!passkey) {\n return new Response(\"Passkey not found or unauthorized\", { status: 403 });\n }\n\n const now = Math.floor(Date.now() / 1000);\n await env.DB.prepare(\n \"INSERT INTO user_passkey_wrapped_keys (passkey_id, profile_id, encrypted_sk, iv, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(passkey_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, created_at = excluded.created_at\"\n )\n .bind(passkey.id, profile.id, encryptedSk, iv, now)\n .run();\n\n return Response.json({ success: true });\n }\n\n // 5. DELETE /api/profiles/:id/e2ee (Disable E2EE for this profile)\n if (request.method === \"DELETE\" && (!subResource || subResource === \"keys\")) {\n await env.DB.batch([\n env.DB.prepare(\"DELETE FROM user_log_keys WHERE profile_id = ?\").bind(profile.id),\n env.DB.prepare(\"DELETE FROM user_passkey_wrapped_keys WHERE profile_id = ?\").bind(profile.id),\n env.DB.prepare(\"DELETE FROM user_recovery_wrapped_keys WHERE profile_id = ?\").bind(profile.id),\n ]);\n\n invalidateProfileLogKeyCache(profile.id);\n return Response.json({ success: true });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env, User, ExecutionContext } from \"../types\";\nimport { RBAC } from \"../lib/rbac\";\nimport { ProfileModel } from \"../models/profile\";\nimport { handleProfilesCoreCollectionRequest, handleProfilesCoreRequest } from \"./profiles/core\";\nimport { handleProfileAccessPointsRequest } from \"./profiles/accessPoints\";\nimport { handleProfileRulesRequest } from \"./profiles/rules\";\nimport { handleProfileListsRequest } from \"./profiles/lists\";\nimport { handleProfileLogsAndAnalyticsRequest } from \"./profiles/logs\";\nimport { handleProfileE2eeRequest } from \"./profiles/e2ee\";\n\nexport async function handleProfilesRequest(request: Request, env: Env, user: User | null, ctx: ExecutionContext): Promise<Response> {\n const url = new URL(request.url);\n const pathParts = url.pathname.split('/').filter(Boolean); // ['api', 'profiles', ':id', ...]\n const profileModel = new ProfileModel(env.DB);\n\n // \u5904\u7406\u5217\u8868\u548C\u521B\u5EFA (/api/profiles)\n if (pathParts.length === 2) {\n if (!user) return new Response(\"Unauthorized\", { status: 401 });\n return handleProfilesCoreCollectionRequest(request, env, user, pathParts);\n }\n\n // \u5904\u7406\u7279\u5B9A Profile (/api/profiles/:id)\n if (pathParts.length >= 3) {\n const profileId = pathParts[2];\n const profile = await profileModel.getById(profileId);\n \n if (!profile) return new Response(\"Profile Not Found\", { status: 404 });\n\n if (!user) return new Response(\"Unauthorized\", { status: 401 });\n if (!RBAC.canAccessProfile(user, profile)) return new Response(\"Profile Not Found\", { status: 404 });\n\n const subResource = pathParts[3];\n\n // Delegate to sub-router based on the path\n if (!subResource || ['rotate_key', 'settings', 'test'].includes(subResource)) {\n return handleProfilesCoreRequest(request, env, user, profile, pathParts, ctx);\n }\n\n switch (subResource) {\n case 'access_points':\n return handleProfileAccessPointsRequest(request, env, user, profileId, pathParts, ctx);\n case 'rules':\n return handleProfileRulesRequest(request, env, user, profileId, pathParts, ctx);\n case 'lists':\n return handleProfileListsRequest(request, env, user, profileId, pathParts, ctx);\n case 'logs':\n case 'analytics':\n return handleProfileLogsAndAnalyticsRequest(request, env, user, profile, pathParts, ctx);\n case 'e2ee':\n return handleProfileE2eeRequest(request, env, user, profile, pathParts, ctx);\n default:\n return new Response(\"Not Found\", { status: 404 });\n }\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env, User, ExecutionContext } from \"../../types\";\nimport { createBlankRefreshTokenCookie, readRefreshTokenCookie, parseRefreshTokenString, isUsableJwtSecret, isStrongJwtSecret } from \"../../lib/auth\";\nimport { UserModel } from \"../../models/user\";\nimport { LogModel } from \"../../models/log\";\nimport { PasskeyModel } from \"../../models/passkey\";\nimport { USERNAME_REGEX } from \"../../utils/validator\";\n\n/**\n * Handle requests to /api/account/me, /api/account/logs, /api/account/delete\n */\nexport async function handleMeRequest(\n request: Request,\n env: Env,\n user: User,\n pathParts: string[],\n ctx: ExecutionContext\n): Promise<Response> {\n const userModel = new UserModel(env.DB, env);\n const logModel = new LogModel(env.DB);\n const action = pathParts[2];\n\n // GET /api/account/me & PATCH /api/account/me\n if (action === 'me') {\n if (request.method === 'GET') {\n const dbUser = await userModel.getById(user.id);\n const globalMaxRetention = env.MAX_LOG_RETENTION_DAYS !== undefined && env.MAX_LOG_RETENTION_DAYS !== ''\n ? Number(env.MAX_LOG_RETENTION_DAYS)\n : 30;\n const adminMaxRetention = env.ADMIN_USER_MAX_LOG_RETENTION_DAYS !== undefined && env.ADMIN_USER_MAX_LOG_RETENTION_DAYS !== ''\n ? Math.min(Number(env.ADMIN_USER_MAX_LOG_RETENTION_DAYS), globalMaxRetention)\n : globalMaxRetention;\n const normalUserMax = env.NORMAL_USER_MAX_LOG_RETENTION_DAYS !== undefined && env.NORMAL_USER_MAX_LOG_RETENTION_DAYS !== ''\n ? Math.min(Number(env.NORMAL_USER_MAX_LOG_RETENTION_DAYS), globalMaxRetention)\n : Math.min(7, globalMaxRetention);\n\n const passkeyModel = new PasskeyModel(env.DB);\n const passkeysCount = await passkeyModel.countByUser(user.id);\n const mfaEnabled = !!(dbUser?.totp_enabled) || passkeysCount > 0;\n\n let hasRecoveryKeys = false;\n if (dbUser?.totp_recovery_keys) {\n try {\n const parsed = typeof dbUser.totp_recovery_keys === 'string' ? JSON.parse(dbUser.totp_recovery_keys) : dbUser.totp_recovery_keys;\n if (Array.isArray(parsed) && parsed.length > 0) {\n hasRecoveryKeys = true;\n }\n } catch {\n hasRecoveryKeys = true;\n }\n } else if (dbUser?.totp_recovery_keys_encrypted) {\n hasRecoveryKeys = true;\n }\n\n const jwtSecretWarning = user.role === 'admin'\n ? isUsableJwtSecret(env.JWT_SECRET) && !isStrongJwtSecret(env.JWT_SECRET)\n : false;\n\n return new Response(JSON.stringify({\n id: user.id,\n username: dbUser?.username || \"\",\n role: user.role,\n totp_enabled: !!(dbUser?.totp_enabled),\n totp_skip_password: !!(dbUser?.totp_skip_password),\n passkeys_count: passkeysCount,\n mfa_enabled: mfaEnabled,\n has_recovery_keys: hasRecoveryKeys,\n recovery_keys_encrypted: !!(dbUser?.totp_recovery_keys_encrypted),\n timezone: dbUser?.timezone || null,\n locale: dbUser?.locale || \"en-US\",\n password_version: dbUser?.password_version ?? 1,\n pin_enabled: !!(dbUser?.pin_hash),\n session_lock_timeout: dbUser?.session_lock_timeout ?? 15,\n max_log_retention_days: user.role === 'admin' ? adminMaxRetention : normalUserMax,\n jwt_secret_warning: jwtSecretWarning,\n }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (request.method === 'PATCH') {\n const body = await request.json() as any;\n try {\n if (body.session_lock_timeout !== undefined) {\n const timeout = Number(body.session_lock_timeout);\n await userModel.updateSessionLockTimeout(user.id, timeout);\n }\n\n if (body.username !== undefined) {\n const newUsername = body.username;\n if (!newUsername || !USERNAME_REGEX.test(newUsername)) {\n return new Response(\"Username format error\", { status: 400 });\n }\n await userModel.updateUsername(user.id, newUsername);\n }\n\n if (body.timezone !== undefined) {\n const newTimezone = body.timezone;\n if (newTimezone !== null && typeof newTimezone === 'string' && newTimezone !== '') {\n try {\n Intl.DateTimeFormat(undefined, { timeZone: newTimezone });\n } catch (e) {\n return new Response(\"Invalid timezone format\", { status: 400 });\n }\n }\n await userModel.updateTimezone(user.id, newTimezone || null);\n }\n\n if (body.locale !== undefined) {\n const newLocale = body.locale;\n if (newLocale !== null && typeof newLocale === 'string' && newLocale !== '') {\n try {\n Intl.DateTimeFormat(newLocale);\n } catch (e) {\n return new Response(\"Invalid locale format\", { status: 400 });\n }\n }\n await userModel.updateLocale(user.id, newLocale || \"en-US\");\n }\n\n return new Response(JSON.stringify({ success: true }));\n } catch (e: any) {\n if (e.message?.includes(\"UNIQUE constraint failed\")) return new Response(\"The username is already taken\", { status: 400 });\n return new Response(\"Failed to update settings\", { status: 500 });\n }\n }\n\n return new Response(\"Method Not Allowed\", { status: 405 });\n }\n\n // DELETE /api/account/logs\n if (action === 'logs' && request.method === 'DELETE') {\n await logModel.deleteByOwner(user.id);\n return new Response(JSON.stringify({ success: true }));\n }\n\n // POST /api/account/delete (delete account)\n if (action === 'delete' && request.method === 'POST') {\n const { invalidateSession } = await import(\"../../lib/auth\");\n const cookieHeader = request.headers.get(\"Cookie\") || \"\";\n const refreshToken = readRefreshTokenCookie(cookieHeader);\n const sessionId = refreshToken ? parseRefreshTokenString(refreshToken)?.sid || null : null;\n if (sessionId) await invalidateSession(env, sessionId);\n await userModel.delete(user.id);\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Set-Cookie\": createBlankRefreshTokenCookie(), \"Content-Type\": \"application/json\" }\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env, User, ExecutionContext } from \"../../../types\";\nimport { hashPassword, generateSessionHash } from \"../../../utils/crypto\";\nimport { UserModel } from \"../../../models/user\";\nimport { ActivityLogModel } from \"../../../models/activityLog\";\nimport { PASSWORD_REGEX } from \"../../../utils/validator\";\nimport { verifyUserReauth, ReauthPayload } from \"./reauth\";\n\n/**\n * Handles password management endpoints:\n * - POST /api/account/password (password change with multi-credential reauth)\n * - POST /api/account/migrate-password (password hash upgrade from v1 to v2)\n *\n * @param request - Inbound HTTP Request.\n * @param env - Cloudflare Worker environment bindings.\n * @param user - Current authenticated user.\n * @param pathParts - URL pathname components.\n * @param ctx - Execution context.\n * @returns Response indicating operation outcome.\n */\nexport async function handlePasswordRequest(\n request: Request,\n env: Env,\n user: User,\n pathParts: string[],\n _ctx: ExecutionContext\n): Promise<Response> {\n const userModel = new UserModel(env.DB, env);\n const activityLog = new ActivityLogModel(env.DB);\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const userAgent = request.headers.get(\"User-Agent\");\n const action = pathParts[2];\n const sessionHash = user.sessionId ? await generateSessionHash(user.sessionId, user.id) : null;\n\n // POST /api/account/password (password change supporting Old Password, Passkey, or TOTP)\n if (action === \"password\" && request.method === \"POST\") {\n const body = (await request.json()) as { newPassword?: string } & ReauthPayload;\n const { newPassword } = body;\n if (!newPassword || !PASSWORD_REGEX.test(newPassword)) {\n return new Response(\"Password format error\", { status: 400 });\n }\n const dbUser = await userModel.getById(user.id);\n if (!dbUser) return new Response(\"User not found\", { status: 404 });\n\n const authResult = await verifyUserReauth(dbUser, body, env, request);\n if (!authResult.success) {\n await activityLog.record(user.id, \"password_change_fail\", clientIp, userAgent, { reason: authResult.reason }, sessionHash);\n return new Response(authResult.error || \"Authentication failed\", { status: 400 });\n }\n\n const hashedPassword = await hashPassword(newPassword, 2);\n await userModel.updatePassword(user.id, hashedPassword, 2);\n await activityLog.record(user.id, \"password_change_success\", clientIp, userAgent, { method: authResult.method }, sessionHash);\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // POST /api/account/migrate-password (password migration to v2)\n if (action === \"migrate-password\" && request.method === \"POST\") {\n const body = (await request.json()) as { clientHash?: string } & ReauthPayload;\n const { clientHash } = body;\n if (!clientHash) {\n return new Response(\"Missing clientHash\", { status: 400 });\n }\n const dbUser = await userModel.getById(user.id);\n if (!dbUser) return new Response(\"User not found\", { status: 404 });\n\n // Enforce re-authentication verification: must provide valid credentials (e.g. oldPassword)\n const authResult = await verifyUserReauth(dbUser, body, env, request);\n if (!authResult.success) {\n await activityLog.record(user.id, \"password_change_fail\", clientIp, userAgent, { reason: authResult.reason, flow: \"migration\" }, sessionHash);\n return new Response(authResult.error || \"Authentication failed\", { status: 400 });\n }\n\n if ((dbUser.password_version ?? 1) === 1) {\n const hashedPassword = await hashPassword(clientHash, 2);\n await userModel.updatePassword(user.id, hashedPassword, 2);\n await activityLog.record(user.id, \"password_change_success\", clientIp, userAgent, { method: \"migration\" }, sessionHash);\n }\n\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env } from \"../../../types\";\nimport { verifyPassword } from \"../../../utils/crypto\";\nimport { verifyTOTP, findMatchingRecoveryKey } from \"../../../lib/totp\";\nimport { PasskeyModel } from \"../../../models/passkey\";\nimport { verifyAuthenticationResponse } from \"../../../lib/webauthn\";\nimport { cacheUtils } from \"../../../utils/cache\";\n\nexport interface ReauthPayload {\n password?: string;\n oldPassword?: string;\n totpTokenHash?: string;\n totpSalt?: string;\n passkeyAssertion?: any;\n recoveryKey?: string;\n}\n\nexport interface ReauthResult {\n success: boolean;\n method?: \"password\" | \"totp\" | \"passkey\" | \"recovery_key\";\n error?: string;\n reason?: string;\n}\n\n/**\n * Re-authenticates the current user using one of: Passkey, TOTP, or Current Password.\n *\n * @param dbUser - Target database user record containing credentials.\n * @param payload - Inbound client re-authentication payload.\n * @param env - Cloudflare Worker environment bindings.\n * @param request - Current HTTP request for origin and header resolution.\n * @returns Verification result detailing success status, authentication method, and failure reasons.\n */\nexport async function verifyUserReauth(\n dbUser: any,\n payload: ReauthPayload,\n env: Env,\n request: Request\n): Promise<ReauthResult> {\n // 1. Passkey assertion check\n if (payload.passkeyAssertion) {\n const cache = (caches as any).default;\n const cachedState = await cacheUtils.get<{ challenge: string; rpId: string }>(\n cache,\n `webauthn_auth_challenge:${dbUser.id}`\n );\n if (!cachedState) {\n return { success: false, error: \"Passkey session expired, please try again\", reason: \"passkey_session_expired\" };\n }\n\n const passkeyModel = new PasskeyModel(env.DB);\n const userPasskeys = await passkeyModel.listByUser(dbUser.id);\n const passkey = userPasskeys.find((p) => p.credential_id === payload.passkeyAssertion.id);\n if (!passkey) {\n return { success: false, error: \"Passkey not found\", reason: \"passkey_not_found\" };\n }\n\n const requestUrl = new URL(request.url);\n try {\n const { signCount } = await verifyAuthenticationResponse({\n clientDataJSON: payload.passkeyAssertion.response.clientDataJSON,\n authenticatorData: payload.passkeyAssertion.response.authenticatorData,\n signature: payload.passkeyAssertion.response.signature,\n publicKeySpki: passkey.public_key,\n algorithm: passkey.algorithm,\n expectedChallenge: cachedState.challenge,\n expectedOrigin: `${requestUrl.protocol}//${requestUrl.host}`,\n expectedRpId: cachedState.rpId,\n previousSignCount: passkey.sign_count\n });\n\n await passkeyModel.updateUsage(passkey.id, signCount);\n await cacheUtils.delete(cache, `webauthn_auth_challenge:${dbUser.id}`);\n return { success: true, method: \"passkey\" };\n } catch {\n return { success: false, error: \"Invalid Passkey signature\", reason: \"invalid_passkey\" };\n }\n }\n\n // 2. TOTP token hash check\n if (payload.totpTokenHash) {\n if (!dbUser.totp_enabled || !dbUser.totp_secret) {\n return { success: false, error: \"TOTP is not enabled\", reason: \"totp_not_enabled\" };\n }\n const valid = await verifyTOTP(dbUser.totp_secret, payload.totpTokenHash, payload.totpSalt);\n if (!valid) {\n return { success: false, error: \"Invalid TOTP code\", reason: \"invalid_totp\" };\n }\n return { success: true, method: \"totp\" };\n }\n\n // 3. Password check (oldPassword or password)\n const pwd = payload.oldPassword || payload.password;\n if (pwd) {\n const valid = await verifyPassword(pwd, dbUser.hashed_password, dbUser.password_version ?? 1);\n if (!valid) {\n return { success: false, error: \"Current password is incorrect\", reason: \"wrong_current_password\" };\n }\n return { success: true, method: \"password\" };\n }\n\n // 4. Recovery Key check\n if (payload.recoveryKey) {\n let storedHashes: any[] = [];\n try {\n storedHashes = typeof dbUser.totp_recovery_keys === \"string\"\n ? JSON.parse(dbUser.totp_recovery_keys)\n : dbUser.totp_recovery_keys || [];\n } catch {}\n const matchIndex = await findMatchingRecoveryKey(payload.recoveryKey, storedHashes);\n if (matchIndex === -1) {\n return { success: false, error: \"Invalid Recovery Key\", reason: \"invalid_recovery_key\" };\n }\n return { success: true, method: \"recovery_key\" };\n }\n\n return { success: false, error: \"Authentication required (Passkey, Recovery Key, or Password)\", reason: \"no_credentials_provided\" };\n}\n", "import { Env, User, ExecutionContext } from \"../../../types\";\nimport { generateSessionHash, verifyPassword } from \"../../../utils/crypto\";\nimport {\n generateTOTPSecret,\n getTOTPUri,\n generateRecoveryKeys,\n hashRecoveryKey,\n verifyTOTP,\n StoredRecoveryKeyItem\n} from \"../../../lib/totp\";\nimport { UserModel } from \"../../../models/user\";\nimport { ActivityLogModel } from \"../../../models/activityLog\";\nimport { PasskeyModel } from \"../../../models/passkey\";\n\n/**\n * Handles TOTP multi-factor authentication and unified MFA settings:\n * - GET /api/account/totp/setup (generate new secret and QR URI)\n * - POST /api/account/totp/confirm (verify token and activate TOTP + generate recovery keys)\n * - PATCH /api/account/totp/settings (skip_password toggle)\n * - DELETE /api/account/totp (disable TOTP)\n * - PATCH /api/account/mfa/settings (unified skip_password toggle)\n *\n * @param request - Inbound HTTP Request.\n * @param env - Cloudflare Worker environment bindings.\n * @param user - Current authenticated user.\n * @param pathParts - URL pathname components.\n * @param ctx - Execution context.\n * @returns Response indicating operation outcome.\n */\nexport async function handleTotpAndMfaRequest(\n request: Request,\n env: Env,\n user: User,\n pathParts: string[],\n _ctx: ExecutionContext\n): Promise<Response> {\n const userModel = new UserModel(env.DB, env);\n const activityLog = new ActivityLogModel(env.DB);\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const userAgent = request.headers.get(\"User-Agent\");\n const action = pathParts[2];\n const sessionHash = user.sessionId ? await generateSessionHash(user.sessionId, user.id) : null;\n\n // \u2500\u2500\u2500 TOTP \u7BA1\u7406\u63A5\u53E3 (/api/account/totp/...) \u2500\u2500\u2500\n if (action === \"totp\") {\n const subAction = pathParts[3];\n\n // GET /api/account/totp/setup \u2014 generate new TOTP secret (not yet saved)\n if (subAction === \"setup\" && request.method === \"GET\") {\n const dbUser = await userModel.getById(user.id);\n if (dbUser?.totp_enabled) {\n return new Response(\"TOTP is already enabled\", { status: 409 });\n }\n const secret = generateTOTPSecret();\n const host = new URL(request.url).hostname;\n const uri = getTOTPUri(secret, dbUser?.username || \"user\", host);\n return new Response(JSON.stringify({ secret, uri }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // POST /api/account/totp/confirm \u2014 verify TOTP code and activate\n if (subAction === \"confirm\" && request.method === \"POST\") {\n const { secret, totpTokenHash, salt } = (await request.json()) as {\n secret: string;\n totpTokenHash: string;\n salt?: string;\n };\n if (!secret || !totpTokenHash) return new Response(\"Missing secret or token\", { status: 400 });\n\n const isValid = await verifyTOTP(secret, totpTokenHash, salt);\n if (!isValid) return new Response(\"Invalid TOTP code\", { status: 400 });\n\n // Generate recovery keys, store only hash under envelope encryption\n const plaintextKeys = generateRecoveryKeys();\n const storedItems = await Promise.all(\n plaintextKeys.map(async (k) => ({\n hash: await hashRecoveryKey(k)\n }))\n );\n\n await userModel.updateTOTP(user.id, secret, storedItems as any);\n // Default to passwordless login upon enabling MFA\n await userModel.updateTOTPSettings(user.id, true);\n await activityLog.record(user.id, \"totp_setup\", clientIp, userAgent, undefined, sessionHash);\n\n return new Response(JSON.stringify({ success: true, recovery_keys: plaintextKeys }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // PATCH /api/account/totp/settings \u2014 update skip_password toggle\n if (subAction === \"settings\" && request.method === \"PATCH\") {\n const dbUser = await userModel.getById(user.id);\n const passkeyModel = new PasskeyModel(env.DB);\n const passkeys = await passkeyModel.listByUser(user.id);\n const hasMfa = !!dbUser?.totp_enabled || passkeys.length > 0;\n if (!hasMfa) return new Response(\"MFA is not enabled\", { status: 400 });\n\n const { skip_password } = (await request.json()) as { skip_password: boolean };\n await userModel.updateTOTPSettings(user.id, !!skip_password);\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // DELETE /api/account/totp \u2014 disable TOTP (requires password verification unless in skip_password mode)\n if (!subAction && request.method === \"DELETE\") {\n const { password } = (await request.json()) as { password?: string };\n const dbUser = await userModel.getById(user.id);\n if (!dbUser) return new Response(\"User not found\", { status: 404 });\n\n if (!dbUser.totp_skip_password) {\n if (!password || !(await verifyPassword(password, dbUser.hashed_password, dbUser.password_version ?? 1))) {\n return new Response(\"Incorrect password\", { status: 400 });\n }\n }\n\n const passkeyModel = new PasskeyModel(env.DB);\n const passkeys = await passkeyModel.listByUser(user.id);\n const hasPasskey = passkeys.length > 0;\n\n await userModel.removeTOTP(user.id, hasPasskey);\n await activityLog.record(user.id, \"totp_removed\", clientIp, userAgent, undefined, sessionHash);\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n }\n\n // \u2500\u2500\u2500 \u7EDF\u4E00 MFA \u8BBE\u7F6E\u63A5\u53E3 (/api/account/mfa/settings) \u2500\u2500\u2500\n if (action === \"mfa\" && pathParts[3] === \"settings\" && request.method === \"PATCH\") {\n const dbUser = await userModel.getById(user.id);\n const passkeyModel = new PasskeyModel(env.DB);\n const passkeys = await passkeyModel.listByUser(user.id);\n const hasMfa = !!dbUser?.totp_enabled || passkeys.length > 0;\n if (!hasMfa) return new Response(\"MFA is not enabled\", { status: 400 });\n\n const { skip_password } = (await request.json()) as { skip_password: boolean };\n await userModel.updateTOTPSettings(user.id, !!skip_password);\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env, User, ExecutionContext } from \"../../../types\";\nimport { UserModel } from \"../../../models/user\";\nimport { verifyUserReauth, ReauthPayload } from \"./reauth\";\n\n/**\n * Handles security PIN management endpoints:\n * - POST /api/account/pin (verify identity and set client PIN hash)\n * - DELETE /api/account/pin (verify identity and remove PIN)\n *\n * @param request - Inbound HTTP Request.\n * @param env - Cloudflare Worker environment bindings.\n * @param user - Current authenticated user.\n * @param pathParts - URL pathname components.\n * @param ctx - Execution context.\n * @returns Response indicating operation outcome.\n */\nexport async function handlePinRequest(\n request: Request,\n env: Env,\n user: User,\n _pathParts: string[],\n _ctx: ExecutionContext\n): Promise<Response> {\n const userModel = new UserModel(env.DB, env);\n const dbUser = await userModel.getById(user.id);\n if (!dbUser) return new Response(\"User not found\", { status: 404 });\n\n const body = (await request.json()) as { pinHash?: string } & ReauthPayload;\n\n // \u9A8C\u8BC1\u7528\u6237\u8EAB\u4EFD (\u5BC6\u7801\u3001Passkey \u6216 TOTP)\n const authResult = await verifyUserReauth(dbUser, body, env, request);\n if (!authResult.success) {\n return new Response(authResult.error || \"Authentication failed\", { status: 400 });\n }\n\n // POST /api/account/pin \u2014 \u7ED1\u5B9A/\u66F4\u65B0\u5B89\u5168 PIN \u7801\n if (request.method === \"POST\") {\n const { pinHash } = body;\n // \u9A8C\u8BC1 PIN \u683C\u5F0F\u662F\u5426\u4E3A 64 \u4F4D\u5341\u516D\u8FDB\u5236\u54C8\u5E0C\n if (!pinHash || !/^[a-fA-F0-9]{64}$/.test(pinHash)) {\n return new Response(\"Invalid PIN hash format\", { status: 400 });\n }\n\n // Store the client-side PIN hash directly to support challenge-response unlock verification\n await userModel.updatePinHash(user.id, pinHash);\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // DELETE /api/account/pin \u2014 \u89E3\u7ED1\u5B89\u5168 PIN \u7801\n if (request.method === \"DELETE\") {\n await userModel.updatePinHash(user.id, null);\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n return new Response(\"Method Not Allowed\", { status: 405 });\n}\n", "import { Env, User, ExecutionContext } from \"../../../types\";\nimport { generateSessionHash } from \"../../../utils/crypto\";\nimport { UserModel, StoredRecoveryKey } from \"../../../models/user\";\nimport { ActivityLogModel } from \"../../../models/activityLog\";\nimport { PasskeyModel } from \"../../../models/passkey\";\nimport { PASSKEY_NAME_REGEX } from \"../../../utils/validator\";\nimport {\n generateWebAuthnChallenge,\n base64UrlEncode,\n verifyRegistrationResponse\n} from \"../../../lib/webauthn\";\nimport { cacheUtils } from \"../../../utils/cache\";\nimport { generateRecoveryKeys, hashRecoveryKey } from \"../../../lib/totp\";\n\n/**\n * Handles WebAuthn Passkeys lifecycle endpoints:\n * - GET /api/account/passkeys (list user passkeys)\n * - POST /api/account/passkeys/auth-options (generate authentication challenge for logged-in user)\n * - POST /api/account/passkeys/register/options (generate registration options & challenge)\n * - POST /api/account/passkeys/register/verify (verify registration response and persist passkey)\n * - PATCH /api/account/passkeys/:id (rename passkey)\n * - DELETE /api/account/passkeys/:id (delete passkey)\n *\n * @param request - Inbound HTTP Request.\n * @param env - Cloudflare Worker environment bindings.\n * @param user - Current authenticated user.\n * @param pathParts - URL pathname components.\n * @param ctx - Execution context.\n * @returns Response indicating operation outcome.\n */\nexport async function handlePasskeysRequest(\n request: Request,\n env: Env,\n user: User,\n pathParts: string[],\n _ctx: ExecutionContext\n): Promise<Response> {\n const userModel = new UserModel(env.DB, env);\n const passkeyModel = new PasskeyModel(env.DB);\n const activityLog = new ActivityLogModel(env.DB);\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const userAgent = request.headers.get(\"User-Agent\");\n const subAction = pathParts[3];\n const sessionHash = user.sessionId ? await generateSessionHash(user.sessionId, user.id) : null;\n\n // GET /api/account/passkeys \u2014 \u5217\u51FA\u5F53\u524D\u7528\u6237\u7684\u6240\u6709\u901A\u884C\u5BC6\u94A5\n if (!subAction && request.method === \"GET\") {\n const passkeys = await passkeyModel.listByUser(user.id);\n return new Response(JSON.stringify(passkeys), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // POST /api/account/passkeys/auth-options \u2014 \u751F\u6210\u5DF2\u767B\u5F55\u7528\u6237\u7684\u901A\u884C\u5BC6\u94A5\u8BA4\u8BC1\u9009\u9879\u4E0E\u6311\u6218\n if (subAction === \"auth-options\" && request.method === \"POST\") {\n const existingPasskeys = await passkeyModel.listByUser(user.id);\n if (existingPasskeys.length === 0) {\n return new Response(\"No passkeys registered\", { status: 400 });\n }\n\n const challenge = generateWebAuthnChallenge();\n const requestUrl = new URL(request.url);\n const rpId = requestUrl.hostname;\n const cache = (caches as any).default;\n await cacheUtils.set(cache, `webauthn_auth_challenge:${user.id}`, { challenge, rpId }, 300);\n\n const options = {\n challenge,\n rpId,\n timeout: 60000,\n userVerification: \"preferred\",\n allowCredentials: existingPasskeys.map((p) => ({\n id: p.credential_id,\n passkey_id: p.id,\n type: \"public-key\",\n transports: p.transports ? JSON.parse(p.transports) : undefined\n }))\n };\n\n return new Response(JSON.stringify(options), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // POST /api/account/passkeys/register/options \u2014 \u751F\u6210\u901A\u884C\u5BC6\u94A5\u6CE8\u518C\u6311\u6218\u4E0E\u53C2\u6570\n if (subAction === \"register\" && pathParts[4] === \"options\" && request.method === \"POST\") {\n const existingPasskeys = await passkeyModel.listByUser(user.id);\n const challenge = generateWebAuthnChallenge();\n const requestUrl = new URL(request.url);\n const rpId = requestUrl.hostname;\n const cache = (caches as any).default;\n await cacheUtils.set(cache, `webauthn_reg_challenge:${user.id}`, { challenge, rpId }, 300);\n\n const dbUser = await userModel.getById(user.id);\n const username = dbUser?.username || user.username || \"\";\n const userDisplayName = username ? `${username} (${rpId})` : rpId;\n\n const options = {\n challenge,\n rp: {\n name: rpId,\n id: rpId\n },\n user: {\n id: base64UrlEncode(new TextEncoder().encode(user.id)),\n name: username || rpId,\n displayName: userDisplayName\n },\n pubKeyCredParams: [\n { type: \"public-key\", alg: -7 }, // ES256\n { type: \"public-key\", alg: -257 } // RS256\n ],\n authenticatorSelection: {\n userVerification: \"preferred\",\n residentKey: \"preferred\"\n },\n timeout: 60000,\n excludeCredentials: existingPasskeys.map((p) => ({\n id: p.credential_id,\n type: \"public-key\",\n transports: p.transports ? JSON.parse(p.transports) : undefined\n }))\n };\n\n return new Response(JSON.stringify(options), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // POST /api/account/passkeys/register/verify \u2014 \u9A8C\u8BC1\u6CE8\u518C\u54CD\u5E94\u5E76\u4FDD\u5B58\u901A\u884C\u5BC6\u94A5\n if (subAction === \"register\" && pathParts[4] === \"verify\" && request.method === \"POST\") {\n const cache = (caches as any).default;\n const cachedState = await cacheUtils.get<{ challenge: string; rpId: string }>(cache, `webauthn_reg_challenge:${user.id}`);\n if (!cachedState) {\n return new Response(\"Registration session expired, please try again\", { status: 400 });\n }\n await cacheUtils.delete(cache, `webauthn_reg_challenge:${user.id}`);\n\n const body = (await request.json()) as any;\n const { name, credential } = body;\n if (!credential || !credential.response) {\n return new Response(\"Invalid credential data\", { status: 400 });\n }\n\n const requestUrl = new URL(request.url);\n const expectedOrigin = `${requestUrl.protocol}//${requestUrl.host}`;\n try {\n const parsed = await verifyRegistrationResponse({\n clientDataJSON: credential.response.clientDataJSON,\n attestationObject: credential.response.attestationObject,\n expectedChallenge: cachedState.challenge,\n expectedOrigin,\n expectedRpId: cachedState.rpId\n });\n\n // Check if credential ID is already registered\n const existing = await passkeyModel.getByCredentialId(parsed.credentialId);\n if (existing) {\n return new Response(\"This passkey is already registered\", { status: 409 });\n }\n\n const passkeyName = (name || \"\").trim();\n if (!PASSKEY_NAME_REGEX.test(passkeyName)) {\n return new Response(\"Invalid Passkey name format\", { status: 400 });\n }\n const transports = Array.isArray(credential.response.transports) ? credential.response.transports : undefined;\n\n const created = await passkeyModel.create({\n userId: user.id,\n name: passkeyName,\n credentialId: parsed.credentialId,\n publicKey: parsed.publicKeySpki,\n algorithm: parsed.algorithm,\n signCount: parsed.signCount,\n transports,\n aaguid: parsed.aaguid\n });\n\n // Default to passwordless login upon enabling MFA\n await userModel.updateTOTPSettings(user.id, true);\n\n const dbUser = await userModel.getById(user.id);\n let recoveryKeys: string[] = [];\n if (dbUser) {\n let hasExistingRecoveryKeys = false;\n if (dbUser.totp_recovery_keys) {\n try {\n const parsed = typeof dbUser.totp_recovery_keys === \"string\"\n ? JSON.parse(dbUser.totp_recovery_keys)\n : dbUser.totp_recovery_keys;\n if (Array.isArray(parsed) && parsed.length > 0) {\n hasExistingRecoveryKeys = true;\n }\n } catch {\n if (dbUser.totp_recovery_keys.length > 0) {\n hasExistingRecoveryKeys = true;\n }\n }\n } else if (dbUser.totp_recovery_keys_encrypted) {\n hasExistingRecoveryKeys = true;\n }\n\n // Only generate emergency recovery keys if the account does not already have recovery keys configured.\n // Existing recovery keys (hashed or envelope-encrypted) must never be overwritten on passkey addition.\n if (!hasExistingRecoveryKeys) {\n const plaintextKeys = generateRecoveryKeys();\n const storedItems: StoredRecoveryKey[] = await Promise.all(\n plaintextKeys.map(async (k) => ({\n hash: await hashRecoveryKey(k)\n }))\n );\n await userModel.updateRecoveryKeys(user.id, storedItems);\n recoveryKeys = plaintextKeys;\n }\n }\n\n await activityLog.record(user.id, \"passkey_registered\", clientIp, userAgent, { name: passkeyName }, sessionHash);\n return new Response(\n JSON.stringify({\n ...created,\n recovery_keys: recoveryKeys.length > 0 ? recoveryKeys : undefined\n }),\n {\n status: 201,\n headers: { \"Content-Type\": \"application/json\" }\n }\n );\n } catch (err: any) {\n console.warn(\"[Passkey Registration] Verification failed:\", err.message || err);\n return new Response(err.message || \"Passkey registration failed\", { status: 400 });\n }\n }\n\n // PATCH /api/account/passkeys/:id \u2014 \u91CD\u547D\u540D\u901A\u884C\u5BC6\u94A5\n if (subAction && request.method === \"PATCH\") {\n const passkeyId = subAction;\n const { name } = (await request.json()) as { name?: string };\n const trimmedName = (name || \"\").trim();\n if (!trimmedName || !PASSKEY_NAME_REGEX.test(trimmedName)) {\n return new Response(\"Invalid Passkey name format\", { status: 400 });\n }\n\n const passkey = await passkeyModel.getById(passkeyId, user.id);\n if (!passkey) return new Response(\"Passkey not found\", { status: 404 });\n\n const success = await passkeyModel.updateName(passkeyId, user.id, trimmedName);\n return new Response(JSON.stringify({ success }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // DELETE /api/account/passkeys/:id \u2014 \u5220\u9664\u901A\u884C\u5BC6\u94A5\n if (subAction && request.method === \"DELETE\") {\n const passkeyId = subAction;\n const passkey = await passkeyModel.getById(passkeyId, user.id);\n if (!passkey) return new Response(\"Passkey not found\", { status: 404 });\n\n await passkeyModel.delete(passkeyId, user.id);\n await activityLog.record(user.id, \"passkey_deleted\", clientIp, userAgent, { name: passkey.name }, sessionHash);\n\n // \u82E5\u7528\u6237\u4E0D\u518D\u62E5\u6709\u4EFB\u4F55 Passkey \u4E14\u505C\u7528 TOTP\uFF0C\u5219\u81EA\u52A8\u5173\u95ED\u65E0\u5BC6\u7801\u767B\u5F55\n const remainingPasskeys = await passkeyModel.countByUser(user.id);\n const dbUser = await userModel.getById(user.id);\n if (remainingPasskeys === 0 && !dbUser?.totp_enabled) {\n await userModel.updateTOTPSettings(user.id, false);\n }\n\n return new Response(JSON.stringify({ success: true }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env, User, ExecutionContext } from \"../../../types\";\nimport { generateSessionHash } from \"../../../utils/crypto\";\nimport { UserModel } from \"../../../models/user\";\nimport { ActivityLogModel } from \"../../../models/activityLog\";\nimport { generateRecoveryKey, hashRecoveryKey, StoredRecoveryKeyItem } from \"../../../lib/totp\";\nimport { verifyUserReauth, ReauthPayload } from \"./reauth\";\n\n/**\n * Handles emergency recovery keys management endpoints:\n * - POST /api/account/recovery-keys/view (re-authenticate and view decrypted recovery keys)\n * - POST /api/account/recovery-keys/rotate (re-authenticate and rotate recovery keys)\n *\n * @param request - Inbound HTTP Request.\n * @param env - Cloudflare Worker environment bindings.\n * @param user - Current authenticated user.\n * @param pathParts - URL pathname components.\n * @param ctx - Execution context.\n * @returns Response indicating operation outcome.\n */\nexport async function handleRecoveryKeysRequest(\n request: Request,\n env: Env,\n user: User,\n pathParts: string[],\n _ctx: ExecutionContext\n): Promise<Response> {\n const userModel = new UserModel(env.DB, env);\n const activityLog = new ActivityLogModel(env.DB);\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const userAgent = request.headers.get(\"User-Agent\");\n const subAction = pathParts[3];\n const sessionHash = user.sessionId ? await generateSessionHash(user.sessionId, user.id) : null;\n\n // POST /api/account/recovery-keys/rotate \u2014 \u9A8C\u8BC1\u901A\u884C\u5BC6\u94A5\u6216\u539F\u6062\u590D\u5BC6\u94A5\u540E\u8F6E\u6362\n if (subAction === \"rotate\" && request.method === \"POST\") {\n const body = (await request.json()) as ReauthPayload;\n const dbUser = await userModel.getById(user.id);\n if (!dbUser) return new Response(\"User not found\", { status: 404 });\n\n const authResult = await verifyUserReauth(dbUser, body, env, request);\n if (!authResult.success) {\n return new Response(authResult.error || \"Authentication failed\", { status: 400 });\n }\n\n // \u4EC5\u5141\u8BB8\u901A\u8FC7\u539F Recovery Key \u6216 Passkey \u8F6E\u6362\n if (authResult.method !== \"passkey\" && authResult.method !== \"recovery_key\") {\n return new Response(\"Recovery Key rotation only permits authentication via existing Passkey or original Recovery Key\", {\n status: 403\n });\n }\n\n const plaintextKey = generateRecoveryKey();\n const hashedKey = await hashRecoveryKey(plaintextKey);\n const newItems: StoredRecoveryKeyItem[] = [{ hash: hashedKey } as any];\n\n await userModel.updateRecoveryKeys(user.id, newItems);\n await activityLog.record(user.id, \"recovery_key_rotated\" as any, clientIp, userAgent, { method: authResult.method }, sessionHash);\n\n return new Response(\n JSON.stringify({\n success: true,\n recovery_key: plaintextKey\n }),\n { headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env, User, ExecutionContext } from \"../../../types\";\nimport { handlePasswordRequest } from \"./password\";\nimport { handleTotpAndMfaRequest } from \"./totp\";\nimport { handlePinRequest } from \"./pin\";\nimport { handlePasskeysRequest } from \"./passkeys\";\nimport { handleRecoveryKeysRequest } from \"./recoveryKeys\";\n\nexport * from \"./reauth\";\nexport * from \"./password\";\nexport * from \"./totp\";\nexport * from \"./pin\";\nexport * from \"./passkeys\";\nexport * from \"./recoveryKeys\";\n\n/**\n * Handle security credentials requests to /api/account/password, /api/account/totp/...,\n * /api/account/passkeys/..., /api/account/mfa/..., /api/account/pin, and /api/account/recovery-keys/...\n * by dispatching to specialized sub-handlers.\n *\n * @param request - Inbound HTTP Request.\n * @param env - Cloudflare Worker environment bindings.\n * @param user - Current authenticated user.\n * @param pathParts - URL pathname components.\n * @param ctx - Execution context.\n * @returns Response indicating operation outcome.\n */\nexport async function handleSecurityRequest(\n request: Request,\n env: Env,\n user: User,\n pathParts: string[],\n ctx: ExecutionContext\n): Promise<Response> {\n const action = pathParts[2];\n\n switch (action) {\n case \"password\":\n case \"migrate-password\":\n return handlePasswordRequest(request, env, user, pathParts, ctx);\n\n case \"totp\":\n case \"mfa\":\n return handleTotpAndMfaRequest(request, env, user, pathParts, ctx);\n\n case \"pin\":\n return handlePinRequest(request, env, user, pathParts, ctx);\n\n case \"passkeys\":\n return handlePasskeysRequest(request, env, user, pathParts, ctx);\n\n case \"recovery-keys\":\n return handleRecoveryKeysRequest(request, env, user, pathParts, ctx);\n\n default:\n return new Response(\"Not Found\", { status: 404 });\n }\n}\n\n", "import { Env, User, ExecutionContext } from \"../../types\";\nimport { createBlankRefreshTokenCookie, readRefreshTokenCookie, parseRefreshTokenString, generateSessionHash } from \"../../lib/auth\";\nimport { ActivityLogModel } from \"../../models/activityLog\";\n\n/**\n * Handle user sessions and activity logs requests under /api/account/sessions and /api/account/activity\n */\nexport async function handleSessionsRequest(\n request: Request,\n env: Env,\n user: User,\n pathParts: string[],\n ctx: ExecutionContext\n): Promise<Response> {\n const activityLog = new ActivityLogModel(env.DB);\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const userAgent = request.headers.get(\"User-Agent\");\n const action = pathParts[2];\n\n // GET /api/account/activity (user activity log)\n if (action === 'activity' && request.method === 'GET') {\n const params = new URL(request.url).searchParams;\n const limit = Math.min(parseInt(params.get('limit') || '20', 10), 50);\n const before = params.get('before') ? parseInt(params.get('before')!, 10) : undefined;\n const entries = await activityLog.listByUser(user.id, limit, before);\n return new Response(JSON.stringify(entries), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // Active Sessions\n if (action === 'sessions') {\n // GET /api/account/sessions (active sessions)\n if (!pathParts[3] && request.method === 'GET') {\n const { SessionModel } = await import(\"../../models/session\");\n const sessionModel = new SessionModel(env.DB);\n const sessions = await sessionModel.getSessionsByUser(user.id);\n \n const cookieHeader = request.headers.get(\"Cookie\") || \"\";\n const refreshToken = readRefreshTokenCookie(cookieHeader);\n const currentSessionId = refreshToken ? parseRefreshTokenString(refreshToken)?.sid || null : null;\n \n const sessionData = await Promise.all(sessions.map(async s => ({\n ...s,\n id: await generateSessionHash(s.id, user.id),\n is_current: s.id === currentSessionId\n })));\n \n return new Response(JSON.stringify(sessionData), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // DELETE /api/account/sessions/others (revoke all other sessions)\n if (pathParts[3] === 'others' && request.method === 'DELETE') {\n const { SessionModel } = await import(\"../../models/session\");\n const sessionModel = new SessionModel(env.DB);\n\n const refreshToken = readRefreshTokenCookie(request.headers.get(\"Cookie\") || \"\");\n const currentSessionId = refreshToken ? parseRefreshTokenString(refreshToken)?.sid || null : null;\n\n if (!currentSessionId) {\n return new Response(\"Current session not found\", { status: 400 });\n }\n\n const sessions = await sessionModel.getSessionsByUser(user.id);\n const otherSessions = sessions.filter(s => s.id !== currentSessionId);\n\n const { invalidateSession } = await import(\"../../lib/auth\");\n const { invalidateAuthUserCache } = await import(\"../../lib/middleware\");\n for (const s of otherSessions) {\n await invalidateSession(env, s.id);\n invalidateAuthUserCache(s.id);\n }\n\n const currentSessionHash = await generateSessionHash(currentSessionId, user.id);\n await activityLog.record(user.id, 'session_revoked', clientIp, userAgent, { reason: 'user_revoke_others', count: otherSessions.length }, currentSessionHash);\n\n return new Response(JSON.stringify({ success: true, revoked_count: otherSessions.length }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n\n // DELETE /api/account/sessions/:id (revoke session)\n if (pathParts[3] && request.method === 'DELETE') {\n const targetSessionHash = pathParts[3];\n const { SessionModel } = await import(\"../../models/session\");\n const sessionModel = new SessionModel(env.DB);\n \n const sessions = await sessionModel.getSessionsByUser(user.id);\n let targetSessionId: string | null = null;\n for (const s of sessions) {\n if (await generateSessionHash(s.id, user.id) === targetSessionHash) {\n targetSessionId = s.id;\n break;\n }\n }\n \n if (!targetSessionId) {\n return new Response(\"Not Found\", { status: 404 });\n }\n \n const { invalidateSession } = await import(\"../../lib/auth\");\n await invalidateSession(env, targetSessionId);\n \n const refreshToken = readRefreshTokenCookie(request.headers.get(\"Cookie\") || \"\");\n const currentSessionId = refreshToken ? parseRefreshTokenString(refreshToken)?.sid || null : null;\n const currentSessionHash = currentSessionId ? await generateSessionHash(currentSessionId, user.id) : null;\n \n await activityLog.record(user.id, 'session_revoked', clientIp, userAgent, { reason: 'user_revoke', target_session: targetSessionHash }, targetSessionHash);\n \n // If revoking current session, clear cookie\n if (targetSessionId === currentSessionId) {\n return new Response(JSON.stringify({ success: true, is_current: true }), {\n headers: { \"Set-Cookie\": createBlankRefreshTokenCookie(), \"Content-Type\": \"application/json\" }\n });\n }\n \n return new Response(JSON.stringify({ success: true, is_current: false }), {\n headers: { \"Content-Type\": \"application/json\" }\n });\n }\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "/**\n * @file e2ee.ts\n * @description API router for User/Account-level End-to-End Encryption (E2EE) key management.\n * Provides user-scoped endpoints to inspect status, store Passkey wrapped private keys,\n * and enable/disable E2EE across all user profiles simultaneously.\n */\n\nimport { Env, User, ExecutionContext } from \"../../types\";\nimport { PasskeyModel } from \"../../models/passkey\";\nimport { ProfileModel } from \"../../models/profile\";\nimport { invalidateProfileLogKeyCache } from \"../../pipeline/logBatcher\";\n\n/**\n * Routes /api/account/e2ee/* endpoints.\n *\n * @param request Incoming HTTP request\n * @param env Cloudflare worker environment bindings\n * @param user Authenticated user context\n * @param pathParts URL pathname segments\n * @param _ctx Worker execution context\n * @returns JSON response or HTTP error status\n */\nexport async function handleAccountE2eeRequest(\n request: Request,\n env: Env,\n user: User,\n pathParts: string[],\n _ctx: ExecutionContext\n): Promise<Response> {\n const passkeyModel = new PasskeyModel(env.DB);\n const profileModel = new ProfileModel(env.DB);\n const subResource = pathParts[3]; // e.g. /api/account/e2ee/status -> subResource = \"status\"\n\n // 1. GET /api/account/e2ee/status\n if (subResource === \"status\" && request.method === \"GET\") {\n const [profiles, passkeys] = await Promise.all([\n profileModel.listByOwner(user.id),\n passkeyModel.listByUser(user.id),\n ]);\n\n let logKey: { public_key: string; created_at: number; is_active?: number } | null = null;\n let wrappedKeys: { passkey_id: string }[] = [];\n let recoveryKey: { profile_id: string } | null = null;\n\n if (profiles.length > 0) {\n const [keyRes, wrappedRes, recRes] = await Promise.all([\n env.DB.prepare(\n \"SELECT public_key, created_at, is_active FROM user_log_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?) LIMIT 1\"\n )\n .bind(user.id)\n .first<{ public_key: string; created_at: number; is_active?: number }>(),\n env.DB.prepare(`\n SELECT upwk.passkey_id \n FROM user_passkey_wrapped_keys upwk\n JOIN passkeys pk ON upwk.passkey_id = pk.id\n WHERE pk.user_id = ?\n `)\n .bind(user.id)\n .all<{ passkey_id: string }>(),\n env.DB.prepare(\n \"SELECT profile_id FROM user_recovery_wrapped_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?) LIMIT 1\"\n )\n .bind(user.id)\n .first<{ profile_id: string }>(),\n ]);\n\n logKey = keyRes;\n wrappedKeys = wrappedRes.results || [];\n recoveryKey = recRes;\n }\n\n return Response.json({\n hasKeys: Boolean(logKey),\n enabled: Boolean(logKey && (logKey.is_active === undefined || logKey.is_active === 1)),\n publicKey: logKey?.public_key || null,\n wrappedPasskeys: wrappedKeys.map((w) => w.passkey_id),\n hasRecoveryKey: Boolean(recoveryKey),\n userPasskeyCount: passkeys.length,\n createdAt: logKey?.created_at || null,\n });\n }\n\n // 2. GET /api/account/e2ee/wrapped-key?passkey_id=...\n if (subResource === \"wrapped-key\" && request.method === \"GET\") {\n const url = new URL(request.url);\n const passkeyId = url.searchParams.get(\"passkey_id\");\n\n let wrapped: { encrypted_sk: string; iv: string } | null = null;\n\n if (passkeyId) {\n const passkey = await passkeyModel.getByIdOrCredentialId(passkeyId, user.id);\n if (passkey) {\n wrapped = await env.DB.prepare(\n \"SELECT encrypted_sk, iv FROM user_passkey_wrapped_keys WHERE passkey_id = ?\"\n )\n .bind(passkey.id)\n .first<{ encrypted_sk: string; iv: string }>();\n }\n }\n\n if (!wrapped) {\n // Fallback: If specific passkey_id is not matched or omitted, check for any wrapped key for this user\n wrapped = await env.DB.prepare(`\n SELECT upwk.encrypted_sk, upwk.iv \n FROM user_passkey_wrapped_keys upwk\n JOIN passkeys pk ON upwk.passkey_id = pk.id\n WHERE pk.user_id = ?\n LIMIT 1\n `)\n .bind(user.id)\n .first<{ encrypted_sk: string; iv: string }>();\n }\n\n if (!wrapped) {\n return new Response(\"Wrapped key not found for this passkey\", { status: 404 });\n }\n\n return Response.json({\n encryptedSk: wrapped.encrypted_sk,\n iv: wrapped.iv,\n });\n }\n\n // 3. GET /api/account/e2ee/recovery-wrapped-key\n if (subResource === \"recovery-wrapped-key\" && request.method === \"GET\") {\n const wrapped = await env.DB.prepare(\n \"SELECT encrypted_sk, iv, salt FROM user_recovery_wrapped_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?) LIMIT 1\"\n )\n .bind(user.id)\n .first<{ encrypted_sk: string; iv: string; salt: string }>();\n\n if (!wrapped) {\n return new Response(\"Recovery wrapped key not found\", { status: 404 });\n }\n\n return Response.json({\n encryptedSk: wrapped.encrypted_sk,\n iv: wrapped.iv,\n salt: wrapped.salt,\n });\n }\n\n // 4. POST /api/account/e2ee/init (Enable/Initialize E2EE across all user profiles)\n if (subResource === \"init\" && request.method === \"POST\") {\n let body: any;\n try {\n body = await request.json();\n } catch {\n return new Response(\"Invalid JSON body\", { status: 400 });\n }\n\n const { publicKey, passkeyId, encryptedSk, iv, recovery } = body;\n if (!publicKey || (!passkeyId && !recovery)) {\n return new Response(\"Missing required fields (publicKey and either passkey or recovery)\", {\n status: 400,\n });\n }\n\n let passkey: any = null;\n if (passkeyId) {\n passkey = await passkeyModel.getByIdOrCredentialId(passkeyId, user.id);\n if (!passkey) {\n const userPasskeys = await passkeyModel.listByUser(user.id);\n if (userPasskeys.length > 0) {\n passkey = userPasskeys[0];\n }\n }\n }\n\n const profiles = await profileModel.listByOwner(user.id);\n const now = Math.floor(Date.now() / 1000);\n const publicKeyStr = typeof publicKey === \"string\" ? publicKey : JSON.stringify(publicKey);\n\n const stmts: any[] = [];\n const primaryProfileId = profiles.length > 0 ? profiles[0].id : null;\n\n if (primaryProfileId) {\n // 1. Store wrapped key linked to primary profile if passkey provided\n if (passkey && encryptedSk && iv) {\n stmts.push(\n env.DB.prepare(\n \"INSERT INTO user_passkey_wrapped_keys (passkey_id, profile_id, encrypted_sk, iv, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(passkey_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, created_at = excluded.created_at\"\n ).bind(passkey.id, primaryProfileId, encryptedSk, iv, now)\n );\n }\n\n // 2. Set public keys for all user profiles\n for (const p of profiles) {\n stmts.push(\n env.DB.prepare(\n \"INSERT INTO user_log_keys (profile_id, public_key, created_at, is_active) VALUES (?, ?, ?, 1) ON CONFLICT(profile_id) DO UPDATE SET public_key = excluded.public_key, created_at = excluded.created_at, is_active = 1\"\n ).bind(p.id, publicKeyStr, now)\n );\n\n if (recovery && recovery.encryptedSk && recovery.iv && recovery.salt) {\n stmts.push(\n env.DB.prepare(\n \"INSERT INTO user_recovery_wrapped_keys (profile_id, encrypted_sk, iv, salt, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(profile_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, salt = excluded.salt, created_at = excluded.created_at\"\n ).bind(p.id, recovery.encryptedSk, recovery.iv, recovery.salt, now)\n );\n }\n }\n }\n\n if (stmts.length > 0) {\n await env.DB.batch(stmts);\n }\n\n for (const p of profiles) {\n invalidateProfileLogKeyCache(p.id);\n }\n\n return Response.json({ success: true });\n }\n\n // 5. POST /api/account/e2ee/wrap (Wrap existing SK for newly registered passkey)\n if (subResource === \"wrap\" && request.method === \"POST\") {\n let body: any;\n try {\n body = await request.json();\n } catch {\n return new Response(\"Invalid JSON body\", { status: 400 });\n }\n\n const { passkeyId, encryptedSk, iv } = body;\n if (!passkeyId || !encryptedSk || !iv) {\n return new Response(\"Missing required fields (passkeyId, encryptedSk, iv)\", { status: 400 });\n }\n\n let passkey = await passkeyModel.getByIdOrCredentialId(passkeyId, user.id);\n if (!passkey) {\n const userPasskeys = await passkeyModel.listByUser(user.id);\n if (userPasskeys.length > 0) {\n passkey = userPasskeys[0];\n }\n }\n if (!passkey) {\n return new Response(\"Passkey not found or unauthorized\", { status: 403 });\n }\n\n const profiles = await profileModel.listByOwner(user.id);\n const primaryProfileId = profiles.length > 0 ? profiles[0].id : null;\n if (!primaryProfileId) {\n return new Response(\"No profiles found for user\", { status: 400 });\n }\n\n const now = Math.floor(Date.now() / 1000);\n await env.DB.prepare(\n \"INSERT INTO user_passkey_wrapped_keys (passkey_id, profile_id, encrypted_sk, iv, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(passkey_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, created_at = excluded.created_at\"\n )\n .bind(passkey.id, primaryProfileId, encryptedSk, iv, now)\n .run();\n\n return Response.json({ success: true });\n }\n\n // 6. POST /api/account/e2ee/wrap-recovery (Update Recovery Key wrapped SK on rotation)\n if (subResource === \"wrap-recovery\" && request.method === \"POST\") {\n let body: any;\n try {\n body = await request.json();\n } catch {\n return new Response(\"Invalid JSON body\", { status: 400 });\n }\n\n const { encryptedSk, iv, salt } = body;\n if (!encryptedSk || !iv || !salt) {\n return new Response(\"Missing required fields (encryptedSk, iv, salt)\", { status: 400 });\n }\n\n const profiles = await profileModel.listByOwner(user.id);\n const now = Math.floor(Date.now() / 1000);\n const stmts: any[] = [];\n for (const p of profiles) {\n stmts.push(\n env.DB.prepare(\n \"INSERT INTO user_recovery_wrapped_keys (profile_id, encrypted_sk, iv, salt, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(profile_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, salt = excluded.salt, created_at = excluded.created_at\"\n ).bind(p.id, encryptedSk, iv, salt, now)\n );\n }\n if (stmts.length > 0) {\n await env.DB.batch(stmts);\n }\n return Response.json({ success: true });\n }\n\n // 7. POST /api/account/e2ee/enable (Re-enable log encryption for new logs)\n if (subResource === \"enable\" && request.method === \"POST\") {\n const profiles = await profileModel.listByOwner(user.id);\n await env.DB.prepare(\n \"UPDATE user_log_keys SET is_active = 1 WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)\"\n ).bind(user.id).run();\n\n for (const p of profiles) {\n invalidateProfileLogKeyCache(p.id);\n }\n\n return Response.json({ success: true, enabled: true });\n }\n\n // 8. DELETE /api/account/e2ee (Disable log encryption non-destructively, or purge if ?purge=true)\n if (request.method === \"DELETE\" && (!subResource || subResource === \"keys\")) {\n const url = new URL(request.url);\n const purge = url.searchParams.get(\"purge\") === \"true\";\n const profiles = await profileModel.listByOwner(user.id);\n\n if (purge) {\n // Destructive purge explicitly requested by user\n await env.DB.batch([\n env.DB.prepare(\"DELETE FROM user_log_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)\").bind(user.id),\n env.DB.prepare(\"DELETE FROM user_passkey_wrapped_keys WHERE passkey_id IN (SELECT id FROM passkeys WHERE user_id = ?)\").bind(user.id),\n env.DB.prepare(\"DELETE FROM user_recovery_wrapped_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)\").bind(user.id),\n ]);\n } else {\n // Non-destructive toggle off: set is_active = 0 to stop encrypting new logs while keeping keys intact!\n await env.DB.prepare(\n \"UPDATE user_log_keys SET is_active = 0 WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)\"\n ).bind(user.id).run();\n }\n\n for (const p of profiles) {\n invalidateProfileLogKeyCache(p.id);\n }\n\n return Response.json({ success: true, purged: purge, enabled: false });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env, User, ExecutionContext } from \"../../types\";\nimport { handleMeRequest } from \"./me\";\nimport { handleSecurityRequest } from \"./security\";\nimport { handleSessionsRequest } from \"./sessions\";\nimport { handleAccountE2eeRequest } from \"./e2ee\";\n\n/**\n * Handle personal account requests to /api/account/... by delegating to specialized handlers.\n */\nexport async function handlePersonalAccountRequest(\n request: Request,\n env: Env,\n user: User,\n pathParts: string[],\n ctx: ExecutionContext\n): Promise<Response> {\n const action = pathParts[2];\n\n // Delegate basic account info and lifecycle actions\n if (action === 'me' || action === 'delete' || action === 'logs') {\n return handleMeRequest(request, env, user, pathParts, ctx);\n }\n\n // Delegate password updates, TOTP and Passkey settings, MFA settings, and recovery keys\n if (action === 'password' || action === 'totp' || action === 'passkeys' || action === 'mfa' || action === 'migrate-password' || action === 'pin' || action === 'recovery-keys') {\n return handleSecurityRequest(request, env, user, pathParts, ctx);\n }\n\n // Delegate session lists/revocation and activity log querying\n if (action === 'activity' || action === 'sessions') {\n return handleSessionsRequest(request, env, user, pathParts, ctx);\n }\n\n // Delegate user-level End-to-End Encryption (E2EE) key management\n if (action === 'e2ee') {\n return handleAccountE2eeRequest(request, env, user, pathParts, ctx);\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env, User, ExecutionContext } from \"../../types\";\nimport { generateId } from \"../../lib/auth\";\nimport { hashPassword } from \"../../utils/crypto\";\nimport { UserModel } from \"../../models/user\";\nimport { ProfileModel } from \"../../models/profile\";\nimport { SystemSettingsModel } from \"../../models/systemSettings\";\nimport { PASSWORD_REGEX, USERNAME_REGEX } from \"../../utils/validator\";\n\n/**\n * Handle admin requests to /api/admin/...\n */\nexport async function handleAdminRequest(\n request: Request,\n env: Env,\n user: User,\n pathParts: string[],\n ctx: ExecutionContext\n): Promise<Response> {\n const userModel = new UserModel(env.DB, env);\n const profileModel = new ProfileModel(env.DB);\n\n if (pathParts[2] === 'users') {\n if (request.method === 'GET') {\n const users = await userModel.listAll();\n return new Response(JSON.stringify(users), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (request.method === 'POST') {\n const { username, password, role } = await request.json() as any;\n if (!username || !USERNAME_REGEX.test(username)) {\n return new Response(\"Invalid username format\", { status: 400 });\n }\n if (!password || !PASSWORD_REGEX.test(password)) {\n return new Response(\"Password format error\", { status: 400 });\n }\n const hashedPassword = await hashPassword(password, 2);\n const userId = generateId(15);\n try {\n await userModel.create({ id: userId, username, passwordHash: hashedPassword, role: role || 'user', passwordVersion: 2 });\n return new Response(JSON.stringify({ id: userId }), { status: 201 });\n } catch (e: any) {\n return new Response(e.message, { status: 400 });\n }\n }\n\n if (request.method === 'DELETE' && pathParts[3]) {\n const targetId = pathParts[3];\n if (targetId === user.id) return new Response(\"Cannot delete yourself\", { status: 400 });\n await profileModel.deleteByOwner(targetId);\n await userModel.delete(targetId);\n return new Response(null, { status: 204 });\n }\n }\n\n // \u7CFB\u7EDF\u8BBE\u7F6E\u63A5\u53E3: /api/admin/settings\n if (pathParts[2] === 'settings') {\n const systemSettings = new SystemSettingsModel(env.DB);\n if (request.method === 'GET') {\n const settings = await systemSettings.getAll();\n // \u654F\u611F\u914D\u7F6E\u8131\u654F\uFF0Cjwt_secret \u7EDD\u4E0D\u80FD\u66B4\u9732\u7ED9\u5916\u90E8/\u5BA2\u6237\u7AEF\n delete settings.jwt_secret;\n return new Response(JSON.stringify(settings), { headers: { 'Content-Type': 'application/json' } });\n }\n if (request.method === 'PATCH') {\n const body = await request.json() as Record<string, string>;\n // \u7981\u6B62\u4FEE\u6539/\u8986\u76D6\u654F\u611F\u7684 jwt_secret\n delete body.jwt_secret;\n await systemSettings.setMany(body);\n return new Response(JSON.stringify({ success: true }));\n }\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env, User, ExecutionContext } from \"../types\";\nimport { RBAC } from \"../lib/rbac\";\nimport { handlePersonalAccountRequest } from \"./account/personal\";\nimport { handleAdminRequest } from \"./account/admin\";\n\nexport async function handleAccountRequest(\n request: Request,\n env: Env,\n user: User,\n ctx: ExecutionContext\n): Promise<Response> {\n const url = new URL(request.url);\n const pathParts = url.pathname.split('/').filter(Boolean);\n\n // \u2500\u2500\u2500 \u4E2A\u4EBA\u8D26\u53F7\u63A5\u53E3 (/api/account/...) \u2500\u2500\u2500\n if (pathParts[1] === 'account') {\n return handlePersonalAccountRequest(request, env, user, pathParts, ctx);\n }\n\n // \u2500\u2500\u2500 \u7BA1\u7406\u5458\u63A5\u53E3 (/api/admin/...) \u2500\u2500\u2500\n if (pathParts[1] === 'admin') {\n if (!RBAC.isAdmin(user)) return new Response(\"Forbidden\", { status: 403 });\n return handleAdminRequest(request, env, user, pathParts, ctx);\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { Env } from '../types';\nimport { cacheUtils } from '../utils/cache';\nimport { getPresetEchFrontingDomains } from '../utils/ech/constants';\n\n/**\n * Handles system/utility routes like /api/clientinfo and /api/substitute\n */\nexport async function handleSystemRequest(request: Request, env: Env): Promise<Response> {\n const url = new URL(request.url);\n const cache = typeof caches !== 'undefined' ? (caches as any).default : null;\n\n if (url.pathname === '/api/clientinfo') {\n const clientIp = request.headers.get(\"CF-Connecting-IP\") || \"127.0.0.1\";\n const connectedProfileId = await cacheUtils.get<string>(cache, `active_dns:${clientIp}`);\n const cf = (request as any).cf;\n\n const country = cf?.country || request.headers.get(\"CF-IPCountry\") || \"UNKNOWN\";\n const region = cf?.region || request.headers.get(\"CF-Region\") || \"UNKNOWN\";\n const city = cf?.city || request.headers.get(\"CF-IPCity\") || \"UNKNOWN\";\n const timezone = cf?.timezone || request.headers.get(\"CF-Timezone\") || \"UNKNOWN\";\n const asn = cf?.asn ? Number(cf.asn) : (request.headers.get(\"CF-ASN\") ? Number(request.headers.get(\"CF-ASN\")) : 0);\n const asOrganization = cf?.asOrganization || request.headers.get(\"CF-AS-Org\") || \"UNKNOWN\";\n\n return new Response(JSON.stringify({\n ip: clientIp,\n country,\n region,\n city,\n timezone,\n asn,\n asOrganization,\n connectedProfileId: connectedProfileId || null,\n substituteDomain: env.SUBSTITUTE_DOMAIN || \"pages.dev\"\n }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (url.pathname === '/api/regions') {\n const regions: Record<string, any> = {};\n for (const [key, value] of Object.entries(env)) {\n if (key.startsWith('IP_REGION_') && typeof value === 'string') {\n try {\n const regionKey = key.replace('IP_REGION_', '');\n let cleanVal = value.trim();\n cleanVal = cleanVal\n .replace(/^\"\"\"|\"\"\"$/g, \"\")\n .replace(/^\"|\"$/g, \"\")\n .replace(/^'|'$/g, \"\")\n .trim();\n regions[regionKey] = JSON.parse(cleanVal);\n } catch (e) {\n // Ignore parse errors for malformed env variables\n }\n }\n }\n return new Response(JSON.stringify(regions), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (url.pathname === '/api/substitute') {\n const subDomain = env.SUBSTITUTE_DOMAIN || \"pages.dev\";\n let substituteDomainIp: string | null = null;\n let substituteDomainIpv6: string | null = null;\n\n const resolveRecord = async (type: 'A' | 'AAAA'): Promise<string | null> => {\n const dnsServers = [\n 'https://cloudflare-dns.com/dns-query',\n 'https://1.1.1.1/dns-query'\n ];\n for (const server of dnsServers) {\n try {\n const res = await fetch(`${server}?name=${subDomain}&type=${type}`, {\n headers: { 'Accept': 'application/dns-json' },\n signal: AbortSignal.timeout(3000)\n });\n if (res.ok) {\n const data = await res.json() as any;\n if (data?.Answer?.length > 0) {\n const record = data.Answer.find((a: any) => a.type === (type === 'A' ? 1 : 28));\n if (record?.data) {\n return record.data;\n }\n }\n }\n } catch (e) {\n console.error(`[Substitute Resolve] Failed resolving ${type} via ${server}:`, e);\n }\n }\n return null;\n };\n\n try {\n const [ip, ipv6] = await Promise.all([\n resolveRecord('A'),\n resolveRecord('AAAA')\n ]);\n substituteDomainIp = ip;\n substituteDomainIpv6 = ipv6;\n } catch (e) {\n console.error('[Substitute API] Error resolving substitute domain:', e);\n }\n\n return new Response(JSON.stringify({\n ip: substituteDomainIp,\n ipv6: substituteDomainIpv6\n }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (url.pathname === '/api/presets/upstreams') {\n const upstreams = env.PRESET_UPSTREAMS ? JSON.parse(env.PRESET_UPSTREAMS) : [];\n return new Response(JSON.stringify(upstreams), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (url.pathname === '/api/presets/filters') {\n const filters = env.PRESET_EXTERNAL_FILTERS ? JSON.parse(env.PRESET_EXTERNAL_FILTERS) : [];\n return new Response(JSON.stringify(filters), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (url.pathname === '/api/presets/ech-fronting-domains') {\n const domains = getPresetEchFrontingDomains(env);\n return new Response(JSON.stringify(domains), { headers: { 'Content-Type': 'application/json' } });\n }\n\n if (url.pathname.startsWith('/api/icon/')) {\n const domain = url.pathname.replace('/api/icon/', '');\n if (!domain) {\n return new Response(\"Missing domain\", { status: 400 });\n }\n \n // Proxy request to DuckDuckGo\n const targetUrl = `https://icons.duckduckgo.com/ip3/${domain}`;\n try {\n const iconRes = await fetch(targetUrl);\n \n const newHeaders = new Headers(iconRes.headers);\n newHeaders.set('Cache-Control', 'public, max-age=2592000'); // 30 days\n newHeaders.delete('Access-Control-Allow-Origin'); // Ensure no upstream wildcard bleeds through\n \n return new Response(iconRes.body, {\n status: iconRes.status,\n statusText: iconRes.statusText,\n headers: newHeaders\n });\n } catch (e) {\n return new Response(\"Error fetching icon\", { status: 500 });\n }\n }\n\n return new Response(\"Not Found\", { status: 404 });\n}\n", "import { ScheduledEvent } from '@cloudflare/workers-types';\nimport { Env, ExecutionContext } from './types';\nimport { LogModel } from './models/log';\nimport { UserModel } from './models/user';\nimport { ProfileModel } from './models/profile';\nimport { syncNextListForProfile } from './utils/sync';\nimport { syncCloudflareIpRanges } from './utils/ech';\nimport { cacheUtils } from './utils/cache';\n\n/**\n * Handles cron-scheduled events to run background cleanup and list synchronization.\n *\n * Each trigger performs tasks in sequence:\n * 1. Cleanup \u2014 delete stale logs and inactive users (hourly throttled, lightweight)\n * 2. Cloudflare IPs \u2014 sync official Cloudflare IP ranges once daily\n * 3. Sync \u2014 process ONE pending list for a profile (CPU-bound, incremental)\n */\nexport async function handleScheduled(\n _event: ScheduledEvent,\n env: Env,\n ctx: ExecutionContext\n): Promise<void> {\n try {\n const cache = (caches as any).default;\n const now = Math.floor(Date.now() / 1000);\n\n // \u2500\u2500 CLEANUP (Hourly Throttled) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Heavy D1 cleanups are throttled to run at most once per hour (3,600s)\n // rather than every minute, saving ~98% of cron D1 read/write operations.\n const lastCleanupKey = \"cron:last_cleanup_timestamp\";\n const lastCleanup = await cacheUtils.get<number>(cache, lastCleanupKey);\n\n if (!lastCleanup || now - lastCleanup >= 3600) {\n try {\n const userModel = new UserModel(env.DB, env);\n const { clearedProfiles, deletedUsers } = await userModel.applyInactivityPolicy(now);\n if (clearedProfiles > 0 || deletedUsers > 0) {\n console.log(`[Cron] Inactivity cleanup: cleared ${clearedProfiles} profile(s), deleted ${deletedUsers} user(s).`);\n }\n } catch (e) {\n console.error(\"[Cron] Inactivity policy execution failed:\", e);\n }\n\n try {\n const logModel = new LogModel(env.DB);\n const maxRetentionDays = env.MAX_LOG_RETENTION_DAYS !== undefined && env.MAX_LOG_RETENTION_DAYS !== ''\n ? Number(env.MAX_LOG_RETENTION_DAYS)\n : 30;\n const cleanupBatchLimit = env.LOG_CLEANUP_BATCH_LIMIT !== undefined && env.LOG_CLEANUP_BATCH_LIMIT !== ''\n ? Number(env.LOG_CLEANUP_BATCH_LIMIT)\n : 1000;\n const cleanupDailyBudget = env.LOG_CLEANUP_DAILY_BUDGET !== undefined && env.LOG_CLEANUP_DAILY_BUDGET !== ''\n ? Number(env.LOG_CLEANUP_DAILY_BUDGET)\n : 20000;\n await logModel.cleanupGlobal(maxRetentionDays, cleanupBatchLimit, cleanupDailyBudget);\n } catch (e) {\n console.error(\"[Cron] Global log cleanup failed:\", e);\n }\n\n try {\n const { SessionModel } = await import('./models/session');\n const sessionModel = new SessionModel(env.DB);\n const { deletedSessions, deletedPending } = await sessionModel.deleteExpiredSessions(now);\n if (deletedSessions > 0 || deletedPending > 0) {\n console.log(`[Cron] Purged ${deletedSessions} expired session(s) and ${deletedPending} pending token(s).`);\n }\n } catch (e) {\n console.error(\"[Cron] Session cleanup failed:\", e);\n }\n\n await cacheUtils.set(cache, lastCleanupKey, now, 3600);\n console.log(\"[Cron] Cleanup phase completed at\", new Date().toISOString());\n }\n\n // \u2500\u2500 CLOUDFLARE IPS SYNC \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Syncs official Cloudflare IP ranges daily from https://www.cloudflare.com/ips-v4 / v6\n try {\n await syncCloudflareIpRanges(env.DB);\n } catch (e) {\n console.error(\"[Cron] Cloudflare IP range sync failed:\", e);\n }\n\n // \u2500\u2500 SYNC \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Processes ONE list for a single stale profile per trigger.\n // The profile's active Bloom Filter is NOT updated until all its lists are\n // done (A/B pattern): staging accumulates incrementally, active stays intact.\n // Idle periods are throttled to every 10 minutes (600s) to eliminate redundant D1 polls.\n try {\n const lastIdleSyncKey = \"cron:last_idle_sync_check\";\n const IDLE_SYNC_INTERVAL_SEC = 600;\n const lastIdleCheck = await cacheUtils.get<number>(cache, lastIdleSyncKey);\n const isIdleThrottled = Boolean(lastIdleCheck && (now - lastIdleCheck < IDLE_SYNC_INTERVAL_SEC));\n\n if (!isIdleThrottled) {\n const syncIntervalSec = Number(env.SYNC_PROFILE_INTERVAL_SEC) || 86400;\n const cutoffTime = now - syncIntervalSec;\n const profileModel = new ProfileModel(env.DB);\n const batchSize = Number(env.SYNC_BATCH_SIZE) || 1;\n const syncTargets = await profileModel.getSyncTargets(cutoffTime, batchSize);\n\n if (syncTargets.length > 0) {\n // Active targets found: clear idle throttle so next minute immediately processes the next list\n await cacheUtils.delete(cache, lastIdleSyncKey);\n\n for (const target of syncTargets) {\n try {\n // Each call processes ONE list; the profile stays in getSyncTargets\n // until its full cycle completes (list_updated_at gets refreshed).\n await syncNextListForProfile(target.id, env, ctx);\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(`[Cron] Sync failed for profile ${target.id}:`, errorMsg);\n }\n }\n console.log(`[Cron] Sync: processed ${syncTargets.length} profile(s).`);\n } else {\n // No profiles pending sync: enter 10-minute idle throttle\n await cacheUtils.set(cache, lastIdleSyncKey, now, IDLE_SYNC_INTERVAL_SEC);\n }\n }\n } catch (e: unknown) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n console.error(\"[Cron] Sync phase failed:\", errorMsg);\n }\n } catch (e: unknown) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n console.error(\"[Cron] Critical Failure:\", errorMsg);\n }\n}\n", "import { Env, ExecutionContext } from '../types';\n\n/**\n * Intercepts /world-110m.json requests, fetches map topology from openlayers.org,\n * transforms numeric ids to 3-digit padded string IDs for client-side country code lookup compatibility,\n * and caches the response on Cloudflare Edge nodes for 30 days (1 month).\n */\nexport async function handleMapDataRequest(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {\n const cacheKey = new Request(new URL('https://obex.local/world-110m-v2.json'), request);\n const cache = (caches as any).default;\n\n let response = await cache.match(cacheKey);\n if (!response) {\n try {\n const url = 'https://openlayers.org/en/latest/examples/data/topojson/world-110m.json';\n const res = await fetch(url, {\n headers: {\n 'User-Agent': 'DNS Worker/1.0 (Cloudflare Worker Map Proxy)'\n }\n });\n if (!res.ok) {\n throw new Error(`Failed to fetch map data from openlayers.org: ${res.status}`);\n }\n\n const data = await res.json() as any;\n\n // Ensure \"countries\" is the first key in data.objects because react-simple-maps\n // defaults to parsing the first key returned by Object.keys(data.objects).\n if (data && data.objects) {\n const sortedObjects: any = {};\n if (data.objects.countries) {\n sortedObjects.countries = data.objects.countries;\n }\n if (data.objects.land) {\n sortedObjects.land = data.objects.land;\n }\n data.objects = sortedObjects;\n }\n\n // Transform numeric IDs to 3-digit padded string IDs (e.g. 4 -> \"004\")\n // to match the keys of numericToAlpha2 mapping on the client side.\n if (data && data.objects && data.objects.countries && Array.isArray(data.objects.countries.geometries)) {\n data.objects.countries.geometries.forEach((geom: any) => {\n if (geom.id !== undefined && geom.id !== null) {\n geom.id = String(geom.id).padStart(3, '0');\n }\n });\n }\n\n response = new Response(JSON.stringify(data), {\n headers: {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'public, max-age=2592000', // Cache for 30 days (1 month)\n 'Access-Control-Allow-Origin': '*'\n }\n });\n\n // Cache the response\n ctx.waitUntil(cache.put(cacheKey, response.clone()));\n } catch (e) {\n console.error('[MapData API Proxy] Error fetching or transforming TopoJSON:', e);\n return new Response(JSON.stringify({ error: 'Failed to fetch map data' }), {\n status: 502,\n headers: {\n 'Content-Type': 'application/json'\n }\n });\n }\n }\n\n return response;\n}\n", "/**\n * @file status.ts\n * @description Runtime status and service diagnostic inspector for Serverfull mode.\n */\n\nimport fs from 'node:fs';\nimport net from 'node:net';\nimport dgram from 'node:dgram';\nimport { DatabaseSync } from 'node:sqlite';\nimport { ServerfullConfig } from './config';\nimport { formatBanner, formatKeyValueSection, formatTip } from './format';\nimport { isUsableJwtSecret, isStrongJwtSecret } from '../lib/jwt';\n\n/**\n * Result of a network port accessibility probe.\n */\nexport type PortProbeState = 'active' | 'free' | 'disabled';\n\n/**\n * Probes a TCP port to determine if a service is actively listening.\n *\n * @param port - TCP port number to probe.\n * @param host - Network address to connect to.\n * @param timeoutMs - Connection timeout in milliseconds.\n * @returns 'active' if listening, 'free' if connection is refused/times out.\n */\nexport function probeTcpPort(port: number, host: string, timeoutMs: number = 300): Promise<'active' | 'free'> {\n return new Promise((resolve) => {\n const socket = new net.Socket();\n let isSettled = false;\n\n const finish = (state: 'active' | 'free') => {\n if (isSettled) return;\n isSettled = true;\n try { socket.destroy(); } catch {}\n resolve(state);\n };\n\n socket.setTimeout(timeoutMs);\n socket.once('connect', () => finish('active'));\n socket.once('timeout', () => finish('free'));\n socket.once('error', () => finish('free'));\n\n // If host is wildcard 0.0.0.0 or ::, probe loopback\n const targetHost = host === '0.0.0.0' || host === '::' ? '127.0.0.1' : host;\n try {\n socket.connect(port, targetHost);\n } catch {\n finish('free');\n }\n });\n}\n\n/**\n * Probes a UDP port to determine if it is currently occupied or available to bind.\n *\n * @param port - UDP port number to probe.\n * @param host - Network host to test.\n * @returns 'active' if already occupied (EADDRINUSE), 'free' if available.\n */\nexport function probeUdpPort(port: number, host: string): Promise<'active' | 'free'> {\n return new Promise((resolve) => {\n const socket = dgram.createSocket('udp4');\n let isSettled = false;\n\n const finish = (state: 'active' | 'free') => {\n if (isSettled) return;\n isSettled = true;\n try { socket.close(); } catch {}\n resolve(state);\n };\n\n socket.once('error', (err: unknown) => {\n const code = (err as { code?: string })?.code;\n if (code === 'EADDRINUSE') {\n finish('active');\n } else {\n finish('free');\n }\n });\n\n try {\n socket.bind(port, host, () => {\n finish('free');\n });\n } catch {\n finish('active');\n }\n });\n}\n\n/**\n * Information regarding SQLite database file and schema state.\n */\nexport interface DbStatusInfo {\n exists: boolean;\n sizeFormatted?: string;\n migrationsCount?: number;\n usersCount?: number;\n profilesCount?: number;\n statusText: string;\n}\n\n/**\n * Inspects the SQLite database file and queries metadata if available.\n *\n * @param dbPath - Path to the SQLite database file.\n * @returns Database status metrics.\n */\nexport function inspectDatabase(dbPath: string): DbStatusInfo {\n if (!fs.existsSync(dbPath)) {\n return {\n exists: false,\n statusText: 'Not Initialized (File does not exist yet; created on first start)'\n };\n }\n\n try {\n const stats = fs.statSync(dbPath);\n const sizeFormatted = stats.size > 1024 * 1024\n ? `${(stats.size / (1024 * 1024)).toFixed(2)} MB`\n : `${(stats.size / 1024).toFixed(1)} KB`;\n\n let migrationsCount = 0;\n let usersCount = 0;\n let profilesCount = 0;\n\n try {\n const rawDb = new DatabaseSync(dbPath, { readOnly: true });\n\n const migTable = rawDb.prepare(\"SELECT count(*) as cnt FROM sqlite_master WHERE type='table' AND name='d1_migrations'\").get() as { cnt: number } | undefined;\n if (migTable && migTable.cnt > 0) {\n const migRow = rawDb.prepare('SELECT count(*) as count FROM d1_migrations').get() as { count: number } | undefined;\n migrationsCount = migRow?.count || 0;\n }\n\n const usersTable = rawDb.prepare(\"SELECT count(*) as cnt FROM sqlite_master WHERE type='table' AND name='users'\").get() as { cnt: number } | undefined;\n if (usersTable && usersTable.cnt > 0) {\n const userRow = rawDb.prepare('SELECT count(*) as count FROM users').get() as { count: number } | undefined;\n usersCount = userRow?.count || 0;\n }\n\n const profTable = rawDb.prepare(\"SELECT count(*) as cnt FROM sqlite_master WHERE type='table' AND name='profiles'\").get() as { cnt: number } | undefined;\n if (profTable && profTable.cnt > 0) {\n const profRow = rawDb.prepare('SELECT count(*) as count FROM profiles').get() as { count: number } | undefined;\n profilesCount = profRow?.count || 0;\n }\n\n rawDb.close();\n } catch {\n // Ignore database inspection query errors (e.g. locked database)\n }\n\n const details = [\n sizeFormatted,\n migrationsCount > 0 ? `${migrationsCount} migrations` : null,\n usersCount > 0 ? `${usersCount} user(s)` : null,\n profilesCount > 0 ? `${profilesCount} profile(s)` : null\n ].filter(Boolean).join(', ');\n\n return {\n exists: true,\n sizeFormatted,\n migrationsCount,\n usersCount,\n profilesCount,\n statusText: `Ready (${details})`\n };\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n return {\n exists: true,\n statusText: `Exists (Read warning: ${errorMsg})`\n };\n }\n}\n\n/**\n * Inspects all Serverfull service ports, database, and configurations, printing a declarative status report.\n *\n * @param config - The parsed serverfull configuration.\n * @param envJwtSecret - The JWT_SECRET environment variable value.\n */\nexport async function showServerfullStatus(config: ServerfullConfig, envJwtSecret?: string): Promise<void> {\n const [httpState, udpState, dotState] = await Promise.all([\n probeTcpPort(config.httpPort, config.host),\n !config.disableUdp ? probeUdpPort(config.udpPort, config.host) : Promise.resolve('disabled' as const),\n !config.disableDot ? probeTcpPort(config.dotPort, config.host) : Promise.resolve('disabled' as const)\n ]);\n\n const dbStatus = inspectDatabase(config.dbPath);\n\n const tlsConfigured = Boolean(config.tlsKeyPath && config.tlsCertPath);\n const tlsFilesExist = tlsConfigured && fs.existsSync(config.tlsKeyPath) && fs.existsSync(config.tlsCertPath);\n\n let jwtStatus = 'Default (Insecure fallback, please configure JWT_SECRET)';\n if (isUsableJwtSecret(envJwtSecret)) {\n jwtStatus = isStrongJwtSecret(envJwtSecret)\n ? 'Configured (Strong, >= 32 characters)'\n : 'Configured (Warning: Shorter than 32 characters)';\n }\n\n const httpStatusText = httpState === 'active'\n ? `http://${config.host}:${config.httpPort} [Active / Listening]`\n : `http://${config.host}:${config.httpPort} [Stopped / Port Available]`;\n\n const udpStatusText = config.disableUdp\n ? 'Disabled (--disable-udp)'\n : udpState === 'active'\n ? `udp://${config.host}:${config.udpPort} [Active / In Use]`\n : `udp://${config.host}:${config.udpPort} [Stopped / Port Available]`;\n\n let dotStatusText = '';\n if (config.disableDot) {\n dotStatusText = 'Disabled (--disable-dot)';\n } else if (!tlsConfigured) {\n dotStatusText = `tls://${config.host}:${config.dotPort} [Disabled - TLS Certificates Not Configured]`;\n } else if (!tlsFilesExist) {\n dotStatusText = `tls://${config.host}:${config.dotPort} [Warning - TLS Certificate Files Missing]`;\n } else if (dotState === 'active') {\n dotStatusText = `tls://${config.host}:${config.dotPort} [Active / Listening]`;\n } else {\n dotStatusText = `tls://${config.host}:${config.dotPort} [Stopped / Port Available]`;\n }\n\n const defaultProfileText = config.defaultProfileKey\n ? `Explicit (${config.defaultProfileKey})`\n : 'Auto (Database default profile)';\n\n const banner = formatBanner({\n title: 'DNS Worker Runtime Status (Serverfull Mode)',\n borderChar: '=',\n bullet: '\u2022 '\n });\n\n const servicesSection = formatKeyValueSection({\n title: '\\nService Transports:',\n items: [\n { label: 'Web Dashboard & DoH', value: httpStatusText },\n { label: 'Classic UDP DNS', value: udpStatusText },\n { label: 'DNS over TLS (DoT)', value: dotStatusText }\n ]\n });\n\n const databaseSection = formatKeyValueSection({\n title: '\\nDatabase & Storage:',\n items: [\n { label: 'SQLite Path', value: config.dbPath },\n { label: 'Database Status', value: dbStatus.statusText }\n ]\n });\n\n const envSection = formatKeyValueSection({\n title: '\\nEnvironment & Security:',\n items: [\n { label: 'Node.js Runtime', value: `v${process.versions.node} (${process.platform} ${process.arch})` },\n { label: 'JWT Secret', value: jwtStatus },\n { label: 'Default Profile Key', value: defaultProfileText }\n ]\n });\n\n let tipSection = '';\n if (httpState === 'free' && (udpState === 'free' || config.disableUdp)) {\n const serviceInstallCmd = process.platform === 'win32'\n ? 'dns-worker service install (in Administrator terminal)'\n : 'sudo dns-worker service install';\n\n tipSection = formatTip([\n 'Services are currently stopped.',\n `To run continuously as a persistent background daemon: ${serviceInstallCmd}`\n ]);\n }\n\n const report = [banner, servicesSection, databaseSection, envSection, tipSection]\n .filter(Boolean)\n .join('\\n');\n\n console.log(report);\n}\n", "/**\n * @file env.ts\n * @description System privilege detection and execution path resolution for service runners.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { ServiceExecDetails } from './types';\n\n/**\n * Checks whether the current process has Administrator privileges on Windows.\n *\n * @returns True if running as elevated Administrator.\n */\nexport function isWindowsAdmin(): boolean {\n try {\n execSync('net session', { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Checks whether the current process has root privileges on Linux.\n *\n * @returns True if running with root UID.\n */\nexport function isLinuxRoot(): boolean {\n return typeof process.getuid === 'function' && process.getuid() === 0;\n}\n\n/**\n * Resolves appropriate binary execution path, working directory, and user account.\n *\n * @returns Service execution parameters.\n */\nexport function getServiceExecDetails(): ServiceExecDetails {\n const nodePath = process.execPath;\n const scriptPath = process.argv[1] ? path.resolve(process.argv[1]) : '';\n const user = process.env.SUDO_USER || process.env.USER || 'root';\n const homeDir = process.env.SUDO_USER\n ? `/home/${process.env.SUDO_USER}`\n : (process.env.HOME || process.cwd());\n\n let execCmd = '';\n if (scriptPath && fs.existsSync(scriptPath)) {\n execCmd = `\"${nodePath}\" \"${scriptPath}\"`;\n } else {\n execCmd = 'dns-worker';\n }\n\n return {\n execCmd,\n nodePath,\n scriptPath,\n workDir: process.platform === 'win32' ? process.cwd() : homeDir,\n user\n };\n}\n", "/**\n * @file systemd.ts\n * @description Linux systemd background service management provider.\n * Configures systemd unit files with CAP_NET_BIND_SERVICE capabilities for unprivileged low-port binding.\n */\n\nimport fs from 'node:fs';\nimport { execSync } from 'node:child_process';\nimport { ServiceAction, ServiceExecDetails } from './types';\nimport { getServiceExecDetails, isLinuxRoot } from './env';\nimport {\n formatBanner,\n formatDiagnostic,\n formatCommandList\n} from '../format';\n\nexport const SERVICE_NAME = 'dns-worker';\nexport const SERVICE_FILE_NAME = `${SERVICE_NAME}.service`;\nexport const SYSTEMD_SERVICE_PATH = `/etc/systemd/system/${SERVICE_FILE_NAME}`;\n\n/**\n * Generates the Linux systemd unit file content with CAP_NET_BIND_SERVICE capabilities.\n *\n * @param details - The execution parameters.\n * @returns Systemd unit file content string.\n */\nexport function generateSystemdUnit(details: ServiceExecDetails): string {\n const { execCmd, workDir, user } = details;\n\n return `[Unit]\nDescription=DNS Worker Serverfull Service (UDP DNS, DoT & Web Dashboard)\nAfter=network.target\n\n[Service]\nType=simple\nUser=${user}\nWorkingDirectory=${workDir}\nExecStart=${execCmd}\nRestart=always\nRestartSec=5\nLimitNOFILE=65535\n\n# Grant capability to bind ports 53 and 853 without running as full root\nAmbientCapabilities=CAP_NET_BIND_SERVICE\nCapabilityBoundingSet=CAP_NET_BIND_SERVICE\n\n# Logging\nStandardOutput=journal\nStandardError=journal\nSyslogIdentifier=dns-worker\n\n[Install]\nWantedBy=multi-user.target\n`;\n}\n\n/**\n * Handles Linux systemd service actions (install, uninstall, start, stop, restart, status, logs).\n *\n * @param action - Action verb.\n */\nexport async function handleLinuxSystemd(action: ServiceAction | string): Promise<void> {\n const isRoot = isLinuxRoot();\n\n switch (action) {\n case 'install': {\n if (!isRoot) {\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Permission Denied',\n message: 'Installing a systemd service requires elevated privileges (root).',\n solutions: [\n 'Run the command with sudo: sudo dns-worker service install'\n ]\n }));\n process.exit(1);\n }\n\n const details = getServiceExecDetails();\n const unitContent = generateSystemdUnit(details);\n\n try {\n fs.writeFileSync(SYSTEMD_SERVICE_PATH, unitContent, 'utf-8');\n\n execSync('systemctl daemon-reload', { stdio: 'inherit' });\n execSync(`systemctl enable ${SERVICE_NAME}`, { stdio: 'inherit' });\n execSync(`systemctl restart ${SERVICE_NAME}`, { stdio: 'inherit' });\n\n const banner = formatBanner({\n title: 'DNS Worker Linux Service Installed & Started',\n borderChar: '=',\n bullet: '\u2022 ',\n items: [\n { label: 'Service File', value: SYSTEMD_SERVICE_PATH },\n { label: 'Running As User', value: `${details.user} (CAP_NET_BIND_SERVICE)` },\n { label: 'Privileged Ports', value: 'Port 53 & 853 enabled without root' },\n { label: 'Auto-restart', value: 'Enabled on system boot (Restart=always)' }\n ]\n });\n\n const commandGuide = formatCommandList({\n title: 'Useful service management commands:',\n items: [\n { command: 'dns-worker service status', desc: 'Check live status' },\n { command: 'dns-worker service logs', desc: 'Tail live syslog / journal logs' },\n { command: 'sudo dns-worker service restart', desc: 'Restart service' },\n { command: 'sudo dns-worker service stop', desc: 'Stop service' },\n { command: 'sudo dns-worker service uninstall', desc: 'Remove service' }\n ]\n });\n\n console.log('\\n' + banner + '\\n\\n' + commandGuide);\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Service Installation Failed',\n message: errorMsg,\n solutions: [\n 'Ensure systemd is running as PID 1 on your Linux distribution.',\n `Verify permissions to write to ${SYSTEMD_SERVICE_PATH}.`\n ]\n }));\n process.exit(1);\n }\n break;\n }\n\n case 'uninstall': {\n if (!isRoot) {\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Permission Denied',\n message: 'Uninstalling a systemd service requires elevated privileges (root).',\n solutions: [\n 'Run the command with sudo: sudo dns-worker service uninstall'\n ]\n }));\n process.exit(1);\n }\n\n try {\n try { execSync(`systemctl stop ${SERVICE_NAME}`, { stdio: 'ignore' }); } catch {}\n try { execSync(`systemctl disable ${SERVICE_NAME}`, { stdio: 'ignore' }); } catch {}\n\n if (fs.existsSync(SYSTEMD_SERVICE_PATH)) {\n fs.unlinkSync(SYSTEMD_SERVICE_PATH);\n }\n\n execSync('systemctl daemon-reload', { stdio: 'inherit' });\n console.log(formatDiagnostic({\n level: 'success',\n title: 'Service Removed',\n message: `DNS Worker systemd service removed from ${SYSTEMD_SERVICE_PATH}.`\n }));\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Service Uninstall Failed',\n message: errorMsg\n }));\n process.exit(1);\n }\n break;\n }\n\n case 'start':\n case 'stop':\n case 'restart': {\n if (!isRoot) {\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Permission Denied',\n message: `Managing systemd service (${action}) requires elevated privileges.`,\n solutions: [\n `Run with sudo: sudo dns-worker service ${action}`\n ]\n }));\n process.exit(1);\n }\n\n try {\n execSync(`systemctl ${action} ${SERVICE_NAME}`, { stdio: 'inherit' });\n console.log(formatDiagnostic({\n level: 'success',\n title: 'Service Action Executed',\n message: `systemctl ${action} ${SERVICE_NAME}`\n }));\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(formatDiagnostic({\n level: 'error',\n title: `Service ${action} Failed`,\n message: errorMsg\n }));\n process.exit(1);\n }\n break;\n }\n\n case 'status': {\n try {\n execSync(`systemctl status ${SERVICE_NAME}`, { stdio: 'inherit' });\n } catch {\n // systemctl status exits with code 3 when service is stopped; stdio already printed status output\n }\n break;\n }\n\n case 'logs': {\n try {\n execSync(`journalctl -u ${SERVICE_NAME} -f -n 50`, { stdio: 'inherit' });\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Service Logs Error',\n message: `journalctl exited: ${errorMsg}`\n }));\n }\n break;\n }\n\n default: {\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Service Command Error',\n message: `Unknown service action \"${action}\".`,\n solutions: [\n 'Supported actions: install, start, stop, restart, status, logs, uninstall',\n 'Example: sudo dns-worker service install'\n ]\n }));\n process.exit(1);\n }\n }\n}\n", "/**\n * @file schtasks.ts\n * @description Windows Task Scheduler (schtasks.exe) background daemon management provider.\n * Registers boot tasks under NT AUTHORITY\\SYSTEM with log capture.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { ServiceAction, ServiceExecDetails } from './types';\nimport { getServiceExecDetails, isWindowsAdmin } from './env';\nimport {\n formatBanner,\n formatDiagnostic,\n formatCommandList,\n formatLogBox\n} from '../format';\n\nexport const WINDOWS_TASK_NAME = 'DNS-Worker';\nexport const WINDOWS_DATA_DIR = path.join(process.env.ProgramData || 'C:\\\\ProgramData', 'DNS-Worker');\nexport const WINDOWS_LOG_FILE = path.join(WINDOWS_DATA_DIR, 'service.log');\nexport const WINDOWS_BAT_FILE = path.join(WINDOWS_DATA_DIR, 'run-service.bat');\n\n/**\n * Generates the Windows launcher batch script content.\n *\n * @param details - The execution parameters.\n * @returns Windows batch script content.\n */\nexport function generateWindowsBat(details: ServiceExecDetails): string {\n return `@echo off\\r\\nsetlocal\\r\\ncd /d \"${details.workDir}\"\\r\\n\"${details.nodePath}\" \"${details.scriptPath}\" >> \"${WINDOWS_LOG_FILE}\" 2>&1\\r\\n`;\n}\n\n/**\n * Handles Windows Task Scheduler service actions (install, uninstall, start, stop, restart, status, logs).\n *\n * @param action - Action verb.\n */\nexport async function handleWindowsTask(action: ServiceAction | string): Promise<void> {\n const isAdmin = isWindowsAdmin();\n\n switch (action) {\n case 'install': {\n if (!isAdmin) {\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Permission Denied',\n message: 'Registering a Windows background service (scheduled task) requires Administrator privileges.',\n solutions: [\n 'Please open PowerShell or Command Prompt as Administrator (\"Run as Administrator\").',\n 'Then re-run: dns-worker service install'\n ]\n }));\n process.exit(1);\n }\n\n const details = getServiceExecDetails();\n\n try {\n if (!fs.existsSync(WINDOWS_DATA_DIR)) {\n fs.mkdirSync(WINDOWS_DATA_DIR, { recursive: true });\n }\n\n const batContent = generateWindowsBat(details);\n fs.writeFileSync(WINDOWS_BAT_FILE, batContent, 'utf-8');\n\n // Create Task in Task Scheduler to run on boot with highest privileges under SYSTEM account\n const createCmd = `schtasks /create /tn \"${WINDOWS_TASK_NAME}\" /tr \"\\\\\"${WINDOWS_BAT_FILE}\\\\\"\" /sc onstart /ru \"SYSTEM\" /rl HIGHEST /f`;\n execSync(createCmd, { stdio: 'inherit' });\n\n // Immediately start the task\n try {\n execSync(`schtasks /run /tn \"${WINDOWS_TASK_NAME}\"`, { stdio: 'inherit' });\n } catch {\n /* ignore */\n }\n\n const banner = formatBanner({\n title: 'DNS Worker Windows Service Installed & Started',\n borderChar: '=',\n bullet: '\u2022 ',\n items: [\n { label: 'Task Name', value: WINDOWS_TASK_NAME },\n { label: 'Account', value: 'NT AUTHORITY\\\\SYSTEM (Highest Privileges)' },\n { label: 'Auto-Start', value: 'On system startup (/sc onstart)' },\n { label: 'Launcher Script', value: WINDOWS_BAT_FILE },\n { label: 'Log Output', value: WINDOWS_LOG_FILE }\n ]\n });\n\n const commandGuide = formatCommandList({\n title: 'Useful Windows service management commands:',\n items: [\n { command: 'dns-worker service status', desc: 'View task status' },\n { command: 'dns-worker service logs', desc: 'View recent service logs' },\n { command: 'dns-worker service restart', desc: 'Restart background task' },\n { command: 'dns-worker service stop', desc: 'Stop background task' },\n { command: 'dns-worker service uninstall', desc: 'Remove background task' }\n ]\n });\n\n console.log('\\n' + banner + '\\n\\n' + commandGuide);\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Windows Service Installation Failed',\n message: errorMsg,\n solutions: [\n 'Ensure you are running the terminal as Administrator.',\n 'Verify that the Windows Task Scheduler service is active.'\n ]\n }));\n process.exit(1);\n }\n break;\n }\n\n case 'uninstall': {\n if (!isAdmin) {\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Permission Denied',\n message: 'Removing a Windows background service requires Administrator privileges.',\n solutions: [\n 'Run in an Administrator terminal: dns-worker service uninstall'\n ]\n }));\n process.exit(1);\n }\n\n try {\n try { execSync(`schtasks /end /tn \"${WINDOWS_TASK_NAME}\"`, { stdio: 'ignore' }); } catch {}\n execSync(`schtasks /delete /tn \"${WINDOWS_TASK_NAME}\" /f`, { stdio: 'inherit' });\n\n if (fs.existsSync(WINDOWS_BAT_FILE)) {\n fs.unlinkSync(WINDOWS_BAT_FILE);\n }\n\n console.log(formatDiagnostic({\n level: 'success',\n title: 'Service Removed',\n message: `Windows background task \"${WINDOWS_TASK_NAME}\" removed successfully.`\n }));\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Windows Service Removal Failed',\n message: errorMsg\n }));\n process.exit(1);\n }\n break;\n }\n\n case 'start': {\n try {\n execSync(`schtasks /run /tn \"${WINDOWS_TASK_NAME}\"`, { stdio: 'inherit' });\n console.log(formatDiagnostic({\n level: 'success',\n title: 'Service Started',\n message: `Started Windows background task: ${WINDOWS_TASK_NAME}`\n }));\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Task Start Failed',\n message: errorMsg,\n solutions: [\n `Verify the task exists with 'dns-worker service status'.`,\n `Or reinstall with 'dns-worker service install' in Administrator terminal.`\n ]\n }));\n process.exit(1);\n }\n break;\n }\n\n case 'stop': {\n try {\n execSync(`schtasks /end /tn \"${WINDOWS_TASK_NAME}\"`, { stdio: 'inherit' });\n console.log(formatDiagnostic({\n level: 'success',\n title: 'Service Stopped',\n message: `Stopped Windows background task: ${WINDOWS_TASK_NAME}`\n }));\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Task Stop Failed',\n message: errorMsg\n }));\n process.exit(1);\n }\n break;\n }\n\n case 'restart': {\n try {\n try { execSync(`schtasks /end /tn \"${WINDOWS_TASK_NAME}\"`, { stdio: 'ignore' }); } catch {}\n execSync(`schtasks /run /tn \"${WINDOWS_TASK_NAME}\"`, { stdio: 'inherit' });\n console.log(formatDiagnostic({\n level: 'success',\n title: 'Service Restarted',\n message: `Restarted Windows background task: ${WINDOWS_TASK_NAME}`\n }));\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Task Restart Failed',\n message: errorMsg\n }));\n process.exit(1);\n }\n break;\n }\n\n case 'status': {\n try {\n execSync(`schtasks /query /tn \"${WINDOWS_TASK_NAME}\" /fo LIST /v`, { stdio: 'inherit' });\n } catch {\n console.log(formatDiagnostic({\n level: 'info',\n title: 'Windows Task Status',\n message: `Task \"${WINDOWS_TASK_NAME}\" is not currently registered in Windows Task Scheduler.`,\n solutions: [\n 'Run \"dns-worker service install\" in an Administrator terminal to register and run it.'\n ]\n }));\n }\n break;\n }\n\n case 'logs': {\n if (!fs.existsSync(WINDOWS_LOG_FILE)) {\n console.log(formatDiagnostic({\n level: 'info',\n title: 'Service Logs Notice',\n message: `No log file found at ${WINDOWS_LOG_FILE} yet.`,\n details: [\n 'The background service has not produced any log output or has not been started yet.'\n ]\n }));\n return;\n }\n\n try {\n const content = fs.readFileSync(WINDOWS_LOG_FILE, 'utf-8');\n const lines = content.trim().split(/\\r?\\n/);\n const tailLines = lines.slice(-50).join('\\n');\n\n console.log(formatLogBox({\n title: `Recent 50 Service Log Lines: ${WINDOWS_LOG_FILE}`,\n content: tailLines,\n borderChar: '-',\n tip: `In PowerShell, run: Get-Content \"${WINDOWS_LOG_FILE}\" -Tail 50 -Wait`\n }));\n } catch (err: unknown) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Service Logs Error',\n message: `Error reading log file: ${errorMsg}`\n }));\n }\n break;\n }\n\n default: {\n console.error(formatDiagnostic({\n level: 'error',\n title: 'Service Command Error',\n message: `Unknown service action \"${action}\".`,\n solutions: [\n 'Supported actions: install, start, stop, restart, status, logs, uninstall',\n 'Example: dns-worker service install'\n ]\n }));\n process.exit(1);\n }\n }\n}\n", "/**\n * @file index.ts\n * @description Unified Facade and OS Dispatcher for background service management.\n */\n\nimport { ServiceAction } from './types';\nimport { getServiceExecDetails } from './env';\nimport { generateSystemdUnit, handleLinuxSystemd } from './systemd';\nimport { handleWindowsTask } from './schtasks';\nimport { formatDiagnostic, formatLogBox } from '../format';\n\nexport * from './types';\nexport * from './env';\nexport * from './systemd';\nexport * from './schtasks';\n\n/**\n * Handles 'dns-worker service <action>' subcommands across Linux and Windows.\n *\n * @param action - Action verb: 'install' | 'uninstall' | 'start' | 'stop' | 'restart' | 'status' | 'logs'\n */\nexport async function handleServiceAction(action: ServiceAction | string): Promise<void> {\n if (process.platform === 'win32') {\n await handleWindowsTask(action);\n } else if (process.platform === 'linux') {\n await handleLinuxSystemd(action);\n } else {\n // macOS or other platform\n const details = getServiceExecDetails();\n const unitContent = generateSystemdUnit(details);\n\n console.log(formatDiagnostic({\n level: 'info',\n title: 'Platform Service Notice',\n message: `Automatic service management is supported on Linux (systemd) and Windows (schtasks). Current platform: ${process.platform}.`,\n details: [\n 'A systemd service unit template is provided below for manual reference:'\n ]\n }));\n console.log(formatLogBox({\n title: 'systemd service unit template',\n content: unitContent.trim(),\n borderChar: '-'\n }));\n }\n}\n"],
5
+ "mappings": ";sLAAA,IAAaA,EAAbC,GAAAC,GAAA,kBAAaF,EAAa,CAIxB,iBAAiBG,EAAqB,CACpC,MAAO,4BAA4B,mBAAmBA,CAAG,CAAC,EAC5D,EAEA,MAAM,IAAOC,EAAiCD,EAAgC,CAC5E,GAAI,CAACC,EAAO,OAAO,KACnB,IAAMC,EAAM,KAAK,iBAAiBF,CAAG,EAC/BG,EAAW,MAAMF,EAAM,MAAMC,CAAG,EACtC,OAAKC,EACEA,EAAS,KAAK,EADC,IAExB,EAEA,MAAM,IAAIF,EAAiCD,EAAaI,EAAWC,EAAoBC,EAAiB,CAAC,EAAkB,CACzH,GAAI,CAACL,EAAO,OACZ,IAAMC,EAAM,KAAK,iBAAiBF,CAAG,EAC/BO,EAAkC,CACtC,eAAgB,mBAChB,gBAAiB,mBAAmBF,CAAU,EAChD,EACIC,GAAQA,EAAK,OAAS,IACxBC,EAAQ,WAAW,EAAID,EAAK,KAAK,GAAG,GAEtC,IAAMH,EAAW,IAAI,SAAS,KAAK,UAAUC,CAAI,EAAG,CAClD,QAAAG,CACF,CAAC,EACD,OAAON,EAAM,IAAIC,EAAKC,CAAQ,CAChC,EAEA,MAAM,OAAOF,EAAiCD,EAA+B,CAC3E,GAAI,CAACC,EAAO,MAAO,GACnB,IAAMC,EAAM,KAAK,iBAAiBF,CAAG,EACrC,OAAOC,EAAM,OAAOC,CAAG,CACzB,EAKA,MAAM,cAAcD,EAAiCD,EAAaQ,EAAeC,EAAqC,CACpH,GAAI,CAACR,EAAO,MAAO,GACnB,IAAMS,EAAW,aAAaV,CAAG,GAC3BW,EAAU,MAAM,KAAK,IAAsCV,EAAOS,CAAQ,EAE1EE,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACxC,MAAI,CAACD,GAAWC,EAAMD,EAAQ,OAC5B,MAAM,KAAK,IAAIV,EAAOS,EAAU,CAAE,MAAO,EAAG,MAAOE,EAAMH,CAAU,EAAGA,CAAS,EACxE,IAGLE,EAAQ,OAASH,EAAc,IAEnC,MAAM,KAAK,IAAIP,EAAOS,EAAU,CAAE,MAAOC,EAAQ,MAAQ,EAAG,MAAOA,EAAQ,KAAM,EAAG,KAAK,IAAI,EAAGA,EAAQ,MAAQC,CAAG,CAAC,EAC7G,GACT,CACF,ICrDO,SAASC,GAAsBC,EAAyE,CAE7G,IAAMC,EAAMD,EAAgB,GAC5B,GAAIC,GAAMA,EAAG,UAAYA,EAAG,UAAW,CACrC,IAAMC,EAAM,WAAWD,EAAG,QAAQ,EAC5BE,EAAM,WAAWF,EAAG,SAAS,EACnC,GAAI,CAAC,MAAMC,CAAG,GAAK,CAAC,MAAMC,CAAG,EAC3B,MAAO,CAAE,SAAUD,EAAK,UAAWC,CAAI,CAE3C,CAGA,IAAMC,EAAYJ,EAAQ,QAAQ,IAAI,eAAe,EAC/CK,EAAYL,EAAQ,QAAQ,IAAI,gBAAgB,EACtD,GAAII,GAAaC,EAAW,CAC1B,IAAMH,EAAM,WAAWE,CAAS,EAC1BD,EAAM,WAAWE,CAAS,EAChC,GAAI,CAAC,MAAMH,CAAG,GAAK,CAAC,MAAMC,CAAG,EAC3B,MAAO,CAAE,SAAUD,EAAK,UAAWC,CAAI,CAE3C,CAEA,MAAO,CAAE,SAAU,KAAM,UAAW,IAAK,CAC3C,CAKO,SAASG,GAAsBC,EAAcC,EAAcC,EAAcC,EAAsB,CAEpG,IAAMC,GAASF,EAAOF,GAAQ,KAAK,GAAM,IACnCK,GAASF,EAAOF,GAAQ,KAAK,GAAM,IACnC,EACJ,KAAK,IAAIG,EAAO,CAAC,EAAI,KAAK,IAAIA,EAAO,CAAC,EACtC,KAAK,IAAKJ,EAAO,KAAK,GAAM,GAAG,EAC7B,KAAK,IAAKE,EAAO,KAAK,GAAM,GAAG,EAC/B,KAAK,IAAIG,EAAO,CAAC,EACjB,KAAK,IAAIA,EAAO,CAAC,EAErB,MAAO,OADG,EAAI,KAAK,MAAM,KAAK,KAAK,CAAC,EAAG,KAAK,KAAK,EAAI,CAAC,CAAC,EAEzD,CA5CA,IAAAC,GAAAC,GAAA,oBCIA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EAAoB,IACC,CAErB,IAAMC,EADU,IAAI,YAAY,EACL,OAAOJ,CAAI,EAEhCK,EAAU,MAAM,OAAO,OAAO,UAClC,MACAD,EACA,SACA,GACA,CAAC,YAAY,CACf,EAEME,EAAa,MAAM,OAAO,OAAO,WACrC,CACE,KAAM,SACN,KAAML,EACN,WAAYC,EACZ,KAAM,SACR,EACAG,EACAF,CACF,EAEA,OAAO,IAAI,WAAWG,CAAU,CAClC,CASA,eAAsBC,GACpBP,EACAC,EACAC,EAAqB,IACrBC,EAAoB,IACH,CACjB,IAAMK,EAAaP,GAAQ,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC,EAC9DQ,EAAU,MAAMV,GAAOC,EAAMQ,EAAYN,EAAYC,CAAS,EAEpE,OAAO,MAAM,KAAKM,CAAO,EACtB,IAAKC,GAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAC1C,KAAK,EAAE,CACZ,CAcA,eAAsBC,GACpBC,EACAC,EAAkB,EACD,CACjB,IAAMZ,EAAO,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC,EAGhDQ,EAAU,MAAMV,GAAOa,EAAUX,EAFJ,IAEsB,GAAG,EAEtDa,EAAgB,IAAI,WAAWb,EAAK,OAASQ,EAAQ,MAAM,EACjE,OAAAK,EAAc,IAAIb,CAAI,EACtBa,EAAc,IAAIL,EAASR,EAAK,MAAM,EAC/B,KAAK,OAAO,aAAa,GAAGa,CAAa,CAAC,CACnD,CAEA,eAAsBC,GAAeH,EAAkBI,EAAoBH,EAAkB,EAAqB,CAEhH,IAAMI,EAAW,IAAI,WACnB,KAAKD,CAAU,EACZ,MAAM,EAAE,EACR,IAAKE,GAAMA,EAAE,WAAW,CAAC,CAAC,CAC/B,EAEMjB,EAAOgB,EAAS,MAAM,EAAG,EAAE,EAC3BE,EAAeF,EAAS,MAAM,EAAE,EAIhCG,EAAgB,MAAMrB,GAAOa,EAAUX,EAFV,IAE4B,GAAG,EAElE,GAAImB,EAAc,SAAWD,EAAa,OAAQ,MAAO,GAGzD,IAAIE,EAAS,EACb,QAASC,EAAI,EAAGA,EAAIF,EAAc,OAAQE,IACxCD,GAAUD,EAAcE,CAAC,EAAIH,EAAaG,CAAC,EAE7C,OAAOD,IAAW,CACpB,CAsBO,SAASE,GAAqBC,EAAiB,EAAW,CAC/D,IAAMC,EAAQ,IAAI,WAAWD,CAAM,EACnC,OAAO,gBAAgBC,CAAK,EAC5B,IAAIC,EAAQ,GACZ,QAASJ,EAAI,EAAGA,EAAIE,EAAQF,IAC1BI,GAASC,GAAiBF,EAAMH,CAAC,EAAI,EAAI,EAE3C,OAAOI,CACT,CAKO,SAASE,EAAWJ,EAAwB,CACjD,IAAMK,EAAa,KAAK,KAAKL,EAAS,CAAC,EACjCC,EAAQ,IAAI,WAAWI,CAAU,EAEvC,cAAO,gBAAgBJ,CAAK,EAErB,MAAM,KAAKA,CAAK,EACpB,IAAIf,GAAKA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EACxC,KAAK,EAAE,EACP,MAAM,EAAGc,CAAM,CACpB,CAKO,SAASM,GAAed,EAA4B,CAMzD,IAAMf,EALW,IAAI,WACnB,KAAKe,CAAU,EACZ,MAAM,EAAE,EACR,IAAKE,GAAMA,EAAE,WAAW,CAAC,CAAC,CAC/B,EACsB,MAAM,EAAG,EAAE,EACjC,OAAO,MAAM,KAAKjB,CAAI,EAAE,IAAIS,GAAKA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CAC3E,CAMA,eAAsBqB,GAAWC,EAA0BhC,EAA4C,CACrG,IAAMiC,EAAU,IAAI,YACdC,EAAY,OAAOF,GAAQ,SAAWC,EAAQ,OAAOD,CAAG,EAAIA,EAC5D5B,EAAa,OAAOJ,GAAS,SAAWiC,EAAQ,OAAOjC,CAAI,EAAIA,EAE/DmC,EAAY,MAAM,OAAO,OAAO,UACpC,MACAD,EACA,CAAE,KAAM,OAAQ,KAAM,SAAU,EAChC,GACA,CAAC,MAAM,CACT,EAEME,EAAY,MAAM,OAAO,OAAO,KAAK,OAAQD,EAAW/B,CAAU,EACxE,OAAO,MAAM,KAAK,IAAI,WAAWgC,CAAS,CAAC,EACxC,IAAI1B,GAAKA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EACxC,KAAK,EAAE,CACZ,CAmBA,eAAsB2B,GAAoBC,EAAmBC,EAAiC,CAC5F,IAAMtC,EAAO,IAAI,YAAY,EAAE,OAAOsC,CAAM,EAC5C,OAAOhC,GAAe+B,EAAWrC,EAAM,IAAK,GAAG,CACjD,CAMA,eAAsBuC,GAAcC,EAAiBC,EAAgC,CACnF,IAAMC,EAAY,IAAI,YAAY,EAAE,OAAOD,EAAM,YAAY,CAAC,EACxDjC,EAAU,MAAMV,GAAO0C,EAASE,EAAW,IAAO,GAAG,EAC3D,OAAO,MAAM,KAAKlC,CAAO,EACtB,IAAKC,GAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAC1C,KAAK,EAAE,CACZ,CA7NA,IAiHaiB,GAjHbiB,GAAAC,GAAA,kBAiHalB,GAA2B,qCCzGjC,SAASmB,GAAyBC,EAAsBC,EAAUC,EAAgC,CACvG,IAAIC,EACJ,OAAID,EAEFC,GADuB,OAAOF,EAAI,gCAAgC,GAAK,GAC7C,GAAK,GAAK,GAGpCE,GAD0B,OAAOF,EAAI,kCAAkC,GAAK,MAC/C,GAExB,GAAGG,EAAyB,IAAIJ,CAAY,6CAA6CG,CAAM,UACxG,CAKO,SAASE,IAAwC,CACtD,MAAO,GAAGD,EAAyB,sDACrC,CAKO,SAASE,GAAuBC,EAA4C,CACjF,GAAI,CAACA,EAAc,OAAO,KAC1B,IAAMC,EAAQD,EAAa,MAAM,IAAI,OAAO,cAAcH,EAAyB,UAAU,CAAC,EAC9F,OAAOI,EAAQA,EAAM,CAAC,EAAI,IAC5B,CAMO,SAASC,GAAiBC,EAAeT,EAAWC,EAAgC,CACzF,IAAIS,EAAY,GAChB,OAAIT,GAAgBD,IAGlBU,EAAY,cAFW,OAAOV,EAAI,gCAAgC,GAAK,GACvC,GAAK,GAAK,EACX,IAE1B,cAAcS,CAAK,iCAAiCC,CAAS,EACtE,CAKO,SAASC,GAAeL,EAA4C,CACzE,GAAI,CAACA,EAAc,OAAO,KAC1B,IAAMC,EAAQD,EAAa,MAAM,8BAA8B,EAC/D,OAAOC,EAAQA,EAAM,CAAC,EAAI,IAC5B,CAKO,SAASK,GAAoBH,EAAeT,EAAkB,CACnE,IAAMa,EAAa,OAAOb,EAAI,mBAAmB,GAAK,IACtD,MAAO,GAAGc,EAAmB,IAAIL,CAAK,6CAA6CI,CAAU,UAC/F,CAKO,SAASE,IAA6B,CAC3C,MAAO,GAAGD,EAAmB,sDAC/B,CAKO,SAASE,GAAkBV,EAA4C,CAC5E,GAAI,CAACA,EAAc,OAAO,KAC1B,IAAMC,EAAQD,EAAa,MAAM,IAAI,OAAO,cAAcQ,EAAmB,UAAU,CAAC,EACxF,OAAOP,EAAQA,EAAM,CAAC,EAAI,IAC5B,CAjFA,IAEaJ,GACAW,GAHbG,GAAAC,GAAA,kBAEaf,GAA4B,eAC5BW,GAAsB,oBCHnC,IAAAK,GAAA,GAAAC,GAAAD,GAAA,kBAAAE,KAAA,IAEaA,GAFbC,GAAAC,GAAA,kBAEaF,GAAN,KAAmB,CACxB,YAAoBG,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAEpB,MAAM,iBAAiBC,EAA2C,CAChE,IAAMC,EAAU,MAAM,KAAK,GAAG,QAAQ,uDAAuD,EAAE,KAAKD,CAAS,EAAE,MAA+C,EAC9J,OAAKC,EACDA,EAAQ,YAAc,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,GACpD,MAAM,KAAK,cAAcD,CAAS,EAC3B,MAEFC,EAAQ,QALM,IAMvB,CAEA,MAAM,WAAWD,EAAiC,CAChD,IAAMC,EAAU,MAAM,KAAK,GAAG,QAAQ,qCAAqC,EAAE,KAAKD,CAAS,EAAE,MAAW,EACxG,OAAKC,EACDA,EAAQ,YAAc,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,GACpD,MAAM,KAAK,cAAcD,CAAS,EAC3B,MAEFC,EALc,IAMvB,CAEA,MAAM,mBAAmBD,EAAiC,CACxD,IAAMC,EAAU,MAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMrC,EAAE,KAAKD,CAAS,EAAE,MAAW,EAE9B,OAAKC,EACDA,EAAQ,YAAc,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,GACpD,MAAM,KAAK,cAAcD,CAAS,EAC3B,MAEFC,EALc,IAMvB,CAEA,MAAM,cACJC,EACAC,EACAC,EACAC,EACAC,EAA2B,KAC3BC,EAA2B,KAC3BC,EAA0B,KAC1BC,EAA2B,KAC3BC,EAA8B,KACZ,CAOlB,OANe,MAAM,KAAK,GACvB,QACC,iLACF,EACC,KAAKR,EAAIC,EAAQC,EAAWC,EAAWC,EAAWC,EAAWC,EAAUC,EAAWC,GAAgBN,CAAS,EAC3G,IAAI,GACO,OAChB,CAEA,MAAM,iBAAiBF,EAAYQ,EAAwC,CAEzE,OADe,MAAM,KAAK,GAAG,QAAQ,qDAAqD,EAAE,KAAKA,EAAcR,CAAE,EAAE,IAAI,GACzG,OAChB,CAEA,MAAM,aAAaA,EAA8B,CAE/C,OADe,MAAM,KAAK,GAAG,QAAQ,gDAAgD,EAAE,KAAKA,CAAE,EAAE,IAAI,GACtF,OAChB,CAEA,MAAM,cAAcA,EAAYQ,EAAwC,CAEtE,OADe,MAAM,KAAK,GAAG,QAAQ,oEAAoE,EAAE,KAAKA,EAAcR,CAAE,EAAE,IAAI,GACxH,OAChB,CAEA,MAAM,kBAAkBC,EAAgBQ,EAAc,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAAmB,CAEnG,MAAM,KAAK,GAAG,QAAQ,4DAA4D,EAAE,KAAKR,EAAQQ,CAAG,EAAE,IAAI,EAE1G,GAAM,CAAE,QAAAC,CAAQ,EAAI,MAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,KAKzC,EAAE,KAAKT,EAAQQ,CAAG,EAAE,IAAI,EACzB,OAAOC,CACT,CAEA,MAAM,sBAAsBD,EAAc,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAAiE,CACrI,IAAME,EAAO,MAAM,KAAK,GAAG,QAAQ,4CAA4C,EAAE,KAAKF,CAAG,EAAE,IAAI,EACzFG,EAAO,MAAM,KAAK,GAAG,QAAQ,yDAAyD,EAAE,KAAKH,CAAG,EAAE,IAAI,EAC5G,MAAO,CACL,gBAAiBE,EAAK,MAAM,SAAW,EACvC,eAAgBC,EAAK,MAAM,SAAW,CACxC,CACF,CAEA,MAAM,cAAcZ,EAAYG,EAAqC,CAEnE,OADe,MAAM,KAAK,GAAG,QAAQ,iDAAiD,EAAE,KAAKA,EAAWH,CAAE,EAAE,IAAI,GAClG,OAChB,CAEA,MAAM,yBAAyBA,EAA8B,CAE3D,OADe,MAAM,KAAK,GAAG,QAAQ,0EAA0E,EAAE,KAAKA,CAAE,EAAE,IAAI,GAChH,OAChB,CAEA,MAAM,cAAcA,EAA8B,CAEhD,OADe,MAAM,KAAK,GAAG,QAAQ,mCAAmC,EAAE,KAAKA,CAAE,EAAE,IAAI,GACzE,OAChB,CAEA,MAAM,kBAAkBC,EAAkC,CAExD,OADe,MAAM,KAAK,GAAG,QAAQ,wCAAwC,EAAE,KAAKA,CAAM,EAAE,IAAI,GAClF,OAChB,CAEA,MAAM,yBAAyBD,EAAYC,EAAgBE,EAAqC,CAE9F,OADe,MAAM,KAAK,GAAG,QAAQ,8EAA8E,EAAE,KAAKH,EAAIC,EAAQE,CAAS,EAAE,IAAI,GACvI,OAChB,CAEA,MAAM,sBAAsBH,EAAqE,CAC/F,IAAMa,EAAM,MAAM,KAAK,GAAG,QAAQ,kDAAkD,EAAE,KAAKb,CAAE,EAAE,MAA+C,EAC9I,OAAKa,EACDA,EAAI,YAAc,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,GAChD,MAAM,KAAK,yBAAyBb,CAAE,EAC/B,MAEFa,EALU,IAMnB,CAEA,MAAM,yBAAyBb,EAA8B,CAE3D,OADe,MAAM,KAAK,GAAG,QAAQ,gDAAgD,EAAE,KAAKA,CAAE,EAAE,IAAI,GACtF,OAChB,CACF,IC1IO,SAASc,GAAgBC,EAAgD,CAC9E,IAAIC,EAAiB,GACrB,GAAI,OAAOD,GAAQ,SACjBC,EAAiBD,MACZ,CACL,IAAME,EAAQ,IAAI,WAAWF,CAAG,EAChC,QAASG,EAAI,EAAGA,EAAID,EAAM,WAAYC,IACpCF,GAAkB,OAAO,aAAaC,EAAMC,CAAC,CAAC,CAElD,CACA,OAAO,KAAKF,CAAc,EACvB,QAAQ,MAAO,GAAG,EAClB,QAAQ,MAAO,GAAG,EAClB,QAAQ,MAAO,EAAE,CACtB,CAEO,SAASG,GAAgBC,EAAyB,CACvD,IAAIC,EAASD,EAAI,QAAQ,KAAM,GAAG,EAAE,QAAQ,KAAM,GAAG,EACrD,KAAOC,EAAO,OAAS,GACrBA,GAAU,IAEZ,IAAMC,EAAe,KAAKD,CAAM,EAC1BJ,EAAQ,IAAI,WAAWK,EAAa,MAAM,EAChD,QAASJ,EAAI,EAAGA,EAAII,EAAa,OAAQJ,IACvCD,EAAMC,CAAC,EAAII,EAAa,WAAWJ,CAAC,EAEtC,OAAOD,CACT,CAEO,SAASM,GAAmBH,EAAyB,CAC1D,OAAO,IAAI,YAAY,EAAE,OAAOA,CAAG,CACrC,CAmBO,SAASI,GAAmBC,EAA4C,CAC7E,OAAI,OAAOA,GAAW,SAAiB,GAChCA,EAAO,KAAK,EAAE,SAAW,CAClC,CAKO,SAASC,GAAkBD,EAA4C,CAC5E,OAAI,OAAOA,GAAW,SAAiB,GAChCE,GAAwB,IAAIF,EAAO,KAAK,CAAC,CAClD,CAQO,SAASG,GAAmBH,EAAmE,CACpG,OAAID,GAAmBC,CAAM,EAAU,UACnCC,GAAkBD,CAAM,EAAU,SAC/B,OACT,CAMO,SAASI,GAAkBJ,EAAqD,CACrF,OAAOG,GAAmBH,CAAM,IAAM,OACxC,CAOO,SAASK,GAAkBL,EAA4C,CAC5E,OAAKI,GAAkBJ,CAAM,EACtBA,EAAO,KAAK,EAAE,QAAUM,GADQ,EAEzC,CAaA,eAAsBC,GAAgBP,EAAoC,CACxE,IAAMQ,EAAa,IAAI,YAAY,EAAE,OAAO,GAAGR,CAAM,IAAIS,EAAY,EAAE,EACjEC,EAAc,MAAM,OAAO,OAAO,OAAO,UAAWF,CAAU,EAEpE,OAAO,OAAO,OAAO,UACnB,MACAE,EACA,CAAE,KAAM,OAAQ,KAAM,SAAU,EAChC,GACA,CAAC,OAAQ,QAAQ,CACnB,CACF,CAEA,eAAsBC,GAAQC,EAAcC,EAAiC,CAE3E,IAAMC,EAAgBzB,GAAgB,KAAK,UAD5B,CAAE,IAAK,QAAS,IAAK,KAAM,CACiB,CAAC,EACtD0B,EAAiB1B,GAAgB,KAAK,UAAUuB,CAAO,CAAC,EAExDI,EAAa,GAAGF,CAAa,IAAIC,CAAc,GAC/CE,EAAkB,MAAM,OAAO,OAAO,KAC1C,OACAJ,EACAf,GAAmBkB,CAAU,CAC/B,EAEME,EAAmB7B,GAAgB4B,CAAe,EACxD,MAAO,GAAGD,CAAU,IAAIE,CAAgB,EAC1C,CAEA,eAAsBC,GAAmBC,EAAeP,EAAmC,CACzF,IAAMQ,EAAQD,EAAM,MAAM,GAAG,EAC7B,GAAIC,EAAM,SAAW,EAAG,OAAO,KAE/B,GAAM,CAACP,EAAeC,EAAgBG,CAAgB,EAAIG,EACpDC,EAAe,GAAGR,CAAa,IAAIC,CAAc,GAEjDQ,EAAiB7B,GAAgBwB,CAAgB,EASvD,GAAI,CAPY,MAAM,OAAO,OAAO,OAClC,OACAL,EACAU,EACAzB,GAAmBwB,CAAY,CACjC,EAEc,OAAO,KAErB,GAAI,CACF,IAAME,EAAe9B,GAAgBqB,CAAc,EAC7CU,EAAa,IAAI,YAAY,EAAE,OAAOD,CAAY,EAClDZ,EAAU,KAAK,MAAMa,CAAU,EAGrC,OAAIb,EAAQ,KAAO,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAAIA,EAAQ,IAClD,KAGFA,CACT,MAAY,CACV,OAAO,IACT,CACF,CArKA,IAkCaN,GAMAJ,GAqDPO,GA7FNiB,GAAAC,GAAA,kBAkCarB,GAAwB,GAMxBJ,GAA0B,IAAI,IAAI,CAC7C,iCACA,sDACA,sDACA,4DACF,CAAC,EAgDKO,GAAe,+BC/Dd,SAASmB,GAAyBC,EAAmBC,EAAyB,CACnF,IAAMC,EAAU,CACd,IAAKF,EACL,EAAGC,EACH,GAAI,KAAK,IAAI,CACf,EACA,OAAOE,GAAgB,KAAK,UAAUD,CAAO,CAAC,CAChD,CAKO,SAASE,GAAwBC,EAA8D,CACpG,GAAI,CACF,IAAMC,EAAU,IAAI,YAAY,EAAE,OAAOC,GAAgBF,CAAK,CAAC,EAC/D,OAAO,KAAK,MAAMC,CAAO,CAC3B,MAAY,CACV,OAAO,IACT,CACF,CAKA,eAAsBE,GACpBC,EACAC,EACAC,EAA2B,KAC3BC,EAA2B,KAC3BC,EAA0B,KAC1BC,EAA2B,KAC3BC,EACqD,CAErD,IAAMf,GADSe,EAAe,KAAO,MACVC,EAAWC,GAAoB,CAAC,EACrDC,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACpCC,EACJ,GAAIJ,EAAc,CAChB,IAAMK,EAAiB,OAAOX,EAAI,gCAAgC,GAAK,EACvEU,EAAYD,EAAME,EAAiB,GAAK,GAAK,EAC/C,KAAO,CACL,IAAMC,EAAoB,OAAOZ,EAAI,kCAAkC,GAAK,KAC5EU,EAAYD,EAAMG,EAAoB,EACxC,CAEA,IAAMC,EAAmB,CACvB,GAAItB,EACJ,QAASU,EACT,WAAYQ,EACZ,WAAYC,EACZ,WAAYR,EACZ,WAAYC,EACZ,SAAUC,EACV,UAAWC,EACX,eAAgBI,CAClB,EAGA,MADqB,IAAIK,GAAad,EAAI,EAAE,EACzB,cACjBa,EAAQ,GACRA,EAAQ,QACRA,EAAQ,WACRA,EAAQ,WACRA,EAAQ,WACRA,EAAQ,WACRA,EAAQ,SACRA,EAAQ,UACRA,EAAQ,cACV,EAEA,IAAME,EAAezB,GAAyBuB,EAAQ,GAAI,CAAC,EAE3D,MAAO,CAAE,QAAAA,EAAS,aAAAE,CAAa,CACjC,CAMA,eAAsBC,GACpBhB,EACAiB,EACAC,EAA4B,KAC5BC,EAA4B,KAC8E,CAC1G,IAAMC,EAASzB,GAAwBsB,CAAkB,EACzD,GAAI,CAACG,EACH,MAAO,CAAE,QAAS,KAAM,KAAM,KAAM,gBAAiB,KAAM,OAAQ,eAAgB,EAGrF,IAAMC,EAAe,IAAIP,GAAad,EAAI,EAAE,EACtCsB,EAAS,MAAMD,EAAa,mBAAmBD,EAAO,GAAG,EAE/D,GAAI,CAACE,EACH,MAAO,CAAE,QAAS,KAAM,KAAM,KAAM,gBAAiB,KAAM,OAAQ,mBAAoB,EAGzF,IAAMT,EAAmB,CACvB,GAAIS,EAAO,WACX,QAASA,EAAO,QAChB,WAAYA,EAAO,WACnB,WAAYA,EAAO,WACnB,WAAYA,EAAO,WACnB,WAAYA,EAAO,WACnB,SAAUA,EAAO,SACjB,UAAWA,EAAO,UAClB,iBAAkBA,EAAO,iBACzB,eAAgBA,EAAO,eACvB,UAAWA,EAAO,SACpB,EAEMC,EAAa,CACjB,GAAID,EAAO,KACX,SAAUA,EAAO,SACjB,KAAMA,EAAO,KACb,SAAUA,EAAO,SACjB,qBAAsBA,EAAO,oBAC/B,EAGA,GAAIT,EAAQ,mBAAqBO,EAAO,EAEtC,aAAMI,GAAkBxB,EAAKa,EAAQ,EAAE,EAChC,CAAE,QAAS,KAAM,KAAAU,EAAM,gBAAiB,KAAM,OAAQ,aAAc,EAI7E,GAAI,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,GAAKV,EAAQ,WAC3C,aAAMW,GAAkBxB,EAAKa,EAAQ,EAAE,EAChC,CAAE,QAAS,KAAM,KAAAU,EAAM,gBAAiB,KAAM,OAAQ,SAAU,EAGzE,IAAMd,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAGxC,GACEI,EAAQ,WAAa,MAAQA,EAAQ,WAAa,QAClDA,EAAQ,YAAc,MAAQA,EAAQ,YAAc,QACpDK,IAAe,MAAQC,IAAe,KAEtC,aAAMK,GAAkBxB,EAAKa,EAAQ,EAAE,EAChC,CAAE,QAAS,KAAM,KAAAU,EAAM,gBAAiB,KAAM,OAAQ,qBAAsB,EAGrF,IAAME,EAAWC,GAAsBb,EAAQ,SAAUA,EAAQ,UAAWK,EAAYC,CAAU,EAC5FQ,EAAc,OAAO3B,EAAI,uBAAuB,GAAK,GAC3D,GAAIyB,EAAWE,EACb,aAAMH,GAAkBxB,EAAKa,EAAQ,EAAE,EAChC,CAAE,QAAS,KAAM,KAAAU,EAAM,gBAAiB,KAAM,OAAQ,sBAAuB,EAItF,IAAMK,EAAaf,EAAQ,gBAAkBA,EAAQ,WACrD,GAAIU,EAAK,UAAY,CAACV,EAAQ,UAAW,CACvC,IAAMgB,GAAkBN,EAAK,sBAAwB,IAAM,GACvDd,EAAMmB,EAAaC,IACrB,MAAMR,EAAa,aAAaR,EAAQ,EAAE,EAC1CA,EAAQ,UAAY,EAExB,CAGA,IAAMiB,EAAgBjB,EAAQ,WAAa,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAEjEkB,EADiBlB,EAAQ,GAAG,WAAW,IAAI,GAE5C,OAAOb,EAAI,gCAAgC,GAAK,GAAK,GAAK,GAAK,IAC/D,OAAOA,EAAI,kCAAkC,GAAK,MAAQ,GACzDgC,EAAqB,KAAK,MAAMD,EAAyB,CAAC,EAC5DD,EAAgBE,IAClBnB,EAAQ,WAAa,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAAIkB,EACrD,MAAMV,EAAa,cAAcR,EAAQ,GAAIA,EAAQ,UAAU,GAIjE,MAAMQ,EAAa,yBAAyBR,EAAQ,EAAE,EACjDA,EAAQ,WACX,MAAMQ,EAAa,iBAAiBR,EAAQ,GAAIJ,CAAG,EAErD,IAAMwB,GAAcpB,EAAQ,kBAAoB,GAAK,EAC/CqB,EAAkB5C,GAAyBuB,EAAQ,GAAIoB,CAAU,EAEvE,MAAO,CAAE,QAAApB,EAAS,KAAAU,EAAM,gBAAAW,CAAgB,CAC1C,CAKA,eAAsBV,GAAkBxB,EAAUT,EAAkC,CAElF,MADqB,IAAIuB,GAAad,EAAI,EAAE,EACzB,cAAcT,CAAS,CAC5C,CA5NA,IAyBaiB,GAzBb2B,GAAAC,GAAA,kBACAD,KACAE,KACAC,KACAC,KAqBa/B,GAAoB,KCdjC,eAAsBgC,GAAqBC,EAAUC,EAAiC,CACpF,IAAMC,EAAQC,EAAWC,EAAiB,EACpCC,EAAa,OAAOL,EAAI,mBAAmB,GAAK,IAChDM,EAAY,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAAID,EAElD,aADqB,IAAIE,GAAaP,EAAI,EAAE,EACzB,yBAAyBE,EAAOD,EAAQK,CAAS,EAC7DJ,CACT,CAMA,eAAsBM,GAAuBR,EAAUE,EAAuC,CAC5F,IAAMO,EAAe,IAAIF,GAAaP,EAAI,EAAE,EACtCU,EAAM,MAAMD,EAAa,sBAAsBP,CAAK,EAE1D,OAAKQ,EACD,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,GAAKA,EAAI,YACvC,MAAMD,EAAa,yBAAyBP,CAAK,EAC1C,MAEFQ,EAAI,QALM,IAMnB,CAKA,eAAsBC,GAAyBX,EAAUE,EAA8B,CAErF,MADqB,IAAIK,GAAaP,EAAI,EAAE,EACzB,yBAAyBE,CAAK,CACnD,CAMA,eAAsBU,GAA2BC,EAAcX,EAAeF,EAA2B,CACvG,IAAMc,EAAW,iBAAiBZ,CAAK,GACjCa,EAAQ,MAAMC,EAAW,IAA+CH,EAAOC,CAAQ,EAC7F,GAAI,CAACC,EAAO,MAAO,GAEnB,IAAME,GAAkBF,EAAM,gBAAkB,GAAK,EAErD,GAAIE,GAAkB,EAEpB,aADqB,IAAIV,GAAaP,EAAI,EAAE,EACzB,yBAAyBE,CAAK,EACjD,MAAMc,EAAW,OAAOH,EAAOC,CAAQ,EAChC,EAGT,IAAMT,EAAa,OAAOL,EAAI,mBAAmB,GAAK,IACtD,aAAMgB,EAAW,IAAIH,EAAOC,EAAU,CAAE,GAAGC,EAAO,eAAAE,CAAe,EAAGZ,CAAU,EACvE,EAAIY,CACb,CAjEA,IAAAC,GAAAC,GAAA,kBACAC,KACAC,KACAD,KAEAE,OCLA,IAAAC,GAAA,GAAAC,GAAAD,GAAA,2BAAAE,GAAA,wBAAAC,GAAA,8BAAAC,GAAA,sBAAAC,GAAA,qBAAAC,GAAA,0BAAAC,GAAA,uBAAAC,GAAA,kCAAAC,GAAA,qBAAAC,GAAA,wBAAAC,GAAA,yBAAAC,GAAA,6BAAAC,GAAA,6BAAAC,GAAA,kBAAAC,GAAA,mBAAAC,GAAA,eAAAC,EAAA,wBAAAC,GAAA,yBAAAC,GAAA,uBAAAC,GAAA,yBAAAC,GAAA,0BAAAC,GAAA,eAAAC,GAAA,6BAAAC,GAAA,sBAAAC,GAAA,uBAAAC,GAAA,sBAAAC,GAAA,sBAAAC,GAAA,sBAAAC,GAAA,4BAAAC,GAAA,mBAAAC,GAAA,sBAAAC,GAAA,2BAAAC,GAAA,+BAAAC,GAAA,kBAAAC,GAAA,2BAAAC,KAyEA,eAAsBf,GAAqBgB,EAA2B,CACpE,IAAMC,EAASlB,GAAmBiB,EAAI,UAAU,EAChD,GAAIC,IAAW,UACb,MAAM,IAAI,MAAM,oBAAoB,EAEtC,GAAIA,IAAW,SACb,MAAM,IAAI,MAAM,mBAAmB,EAErC,OAAOD,EAAI,UACb,CAlFA,IAAAE,GAAAC,GAAA,kBAGAC,KAGAC,KAUAC,KAcAC,KAgBAC,KAMAC,OCpDA,IAAAC,GAAA,GAAAC,GAAAD,GAAA,0BAAAE,GAAA,mBAAAC,GAAA,4BAAAC,GAAA,iBAAAC,KAQO,SAASH,GAAqBI,EAAoBC,EAAyB,CAChF,IAAMC,EAAa,IAAI,QAAQF,EAAS,OAAO,EAC/C,OAAAE,EAAW,IAAI,yBAA0B,SAAS,EAClDA,EAAW,IAAI,kBAAmB,MAAM,EACxCA,EAAW,IAAI,mBAAoB,eAAe,EAClDA,EAAW,IAAI,4BAA6B,8CAA8C,EAC1FA,EAAW,IAAI,6BAA8B,aAAa,EAC1DA,EAAW,IAAI,+BAAgC,gBAAgB,EAE1DA,EAAW,IAAI,yBAAyB,GAC3CA,EAAW,IACT,0BACA,gDAAgDD,CAAK,mYACvD,EAGK,IAAI,SAASD,EAAS,KAAM,CACjC,OAAQA,EAAS,OACjB,WAAYA,EAAS,WACrB,QAASE,CACX,CAAC,CACH,CAqBO,SAASJ,GAAwBK,EAAyB,CAC/DC,GAAoB,OAAOD,CAAS,EACpCE,GAAsB,OAAOF,CAAS,CACxC,CAEA,eAAsBN,GAAeS,EAAkBC,EAAUC,EAA8C,CAC7G,IAAMC,EAAaH,EAAQ,QAAQ,IAAI,eAAe,GAAK,GACvDI,EAAc,GAClB,GAAID,EAAW,WAAW,SAAS,EACjCC,EAAcD,EAAW,MAAM,CAAC,UAEhCH,EAAQ,QAAQ,IAAI,SAAS,GAAG,YAAY,EAAE,SAAS,WAAW,GAClE,IAAI,IAAIA,EAAQ,GAAG,EAAE,SAAS,SAAS,KAAK,EAC5C,CACA,IAAMK,EAAM,IAAI,IAAIL,EAAQ,GAAG,EAC/BI,EAAcC,EAAI,aAAa,IAAI,OAAO,GAAKA,EAAI,aAAa,IAAI,cAAc,GAAK,EACzF,CAEA,GAAI,CAACD,EACH,OAAO,KAGT,GAAI,CACF,IAAME,EAAS,MAAMC,GAAqBN,CAAG,EACvCO,EAAS,MAAMC,GAAgBH,CAAM,EACrCI,EAAU,MAAMC,GACpBP,EACAI,CACF,EACA,GAAIE,EAAS,CAEX,IAAME,EAASd,GAAoB,IAAIY,EAAQ,SAAS,EACxD,GAAIE,GAAUA,EAAO,UAAY,KAAK,IAAI,EAAG,CAC3C,IAAMC,EAAWd,GAAsB,IAAIW,EAAQ,SAAS,EAC5D,OAAIG,IACFA,EAAS,aAAe,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,GAE/CD,EAAO,IAChB,CAGA,IAAME,EAAe,IAAIC,GAAad,EAAI,EAAE,EACtCe,EAAU,MAAMF,EAAa,mBAAmBJ,EAAQ,SAAS,EACvE,GAAI,CAACM,EACH,OAAAlB,GAAoB,OAAOY,EAAQ,SAAS,EAC5CX,GAAsB,OAAOW,EAAQ,SAAS,EACvC,KAGT,IAAMb,EAAYmB,EAAQ,IAAMA,EAAQ,YAAcN,EAAQ,UACxDO,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAClCC,EAAaF,EAAQ,gBAAkBA,EAAQ,WAC/CG,EAAiBpB,GAAsB,IAAIW,EAAQ,SAAS,EAC5DU,EAAsB,KAAK,IAAIF,EAAYC,GAAgB,cAAgB,CAAC,EAGlF,GAAIH,EAAQ,UAAY,CAACA,EAAQ,UAAW,CAC1C,IAAMK,GAAkBL,EAAQ,sBAAwB,IAAM,GAC9D,GAAIC,EAAMG,EAAsBC,EAAgB,CAC9C,GAAI,CACF,MAAMP,EAAa,aAAajB,CAAS,CAC3C,OAASyB,EAAG,CACV,QAAQ,MAAM,8CAA+CA,CAAC,CAChE,CACAN,EAAQ,UAAY,CACtB,CACF,CAEA,GAAIA,EAAQ,UAEV,MADyB,CAAE,GAAIN,EAAQ,OAAQ,SAAUM,EAAQ,UAAY,GAAI,KAAOA,EAAQ,MAAQN,EAAQ,KAAc,SAAU,GAAM,UAAWA,EAAQ,SAAU,EAK7K,IAAMa,EAAmBJ,GAAgB,kBAAoBD,EACvDM,EAAc,SAAS,OAAOvB,EAAI,qCAAuC,IAAI,EAAG,EAAE,GAAK,GAEzFwB,EAAkBF,EACtB,GAAIN,EAAMM,EAAmBC,EAAa,CACxCC,EAAkBR,EAClB,IAAMS,EAAeZ,EAClB,iBAAiBjB,EAAWoB,CAAG,EAC/B,MAAOK,GAAM,QAAQ,MAAM,2CAA4CA,CAAC,CAAC,EAExEpB,GAAO,OAAOA,EAAI,WAAc,YAClCA,EAAI,UAAUwB,CAAY,CAE9B,CAGA,GAAI3B,GAAsB,KAAO,IAAM,CACrC,IAAM4B,EAAY5B,GAAsB,KAAK,EAAE,KAAK,EAAE,MAClD4B,GAAW5B,GAAsB,OAAO4B,CAAS,CACvD,CACA5B,GAAsB,IAAIW,EAAQ,UAAW,CAC3C,aAAcO,EACd,iBAAkBQ,CACpB,CAAC,EAED,IAAMG,EAAsB,CAAE,GAAIlB,EAAQ,OAAQ,SAAUM,EAAQ,UAAY,GAAI,KAAOA,EAAQ,MAAQN,EAAQ,KAAc,UAAWA,EAAQ,SAAU,EAGxJmB,EAAc,SAAS,OAAO5B,EAAI,oBAAsB,IAAI,EAAG,EAAE,GAAK,GAC5E,GAAIH,GAAoB,KAAO,IAAK,CAClC,IAAM6B,EAAY7B,GAAoB,KAAK,EAAE,KAAK,EAAE,MAChD6B,GAAW7B,GAAoB,OAAO6B,CAAS,CACrD,CACA,OAAA7B,GAAoB,IAAIY,EAAQ,UAAW,CACzC,KAAMkB,EACN,UAAW,KAAK,IAAI,EAAIC,EAAc,GACxC,CAAC,EAEMD,CACT,CACF,MAAY,CAEZ,CACA,OAAO,IACT,CAKO,SAASnC,GAAaO,EAA2B,CACtD,IAAM8B,EAAe9B,EAAQ,QAAQ,IAAI,QAAQ,GAAK,GAChD+B,EAAaC,GAAeF,CAAY,EACxCG,EAAajC,EAAQ,QAAQ,IAAI,cAAc,EACrD,MAAO,CAAC,CAAC+B,GAAc,CAAC,CAACE,GAAcF,IAAeE,CACxD,CAlLA,IAsCMnC,GAUAC,GAhDNmC,GAAAC,GAAA,kBACAC,KACAC,KACAC,KAmCMxC,GAAsB,IAAI,IAU1BC,GAAwB,IAAI,MC1ClC,OAAS,aAAAwC,OAAiB,YCDnB,IAAMC,GAAN,KAAkB,CACf,MAAQ,IAAI,IAEpB,MAAM,MAAMC,EAA0D,CACpE,IAAMC,EAAM,OAAOD,GAAY,SAAWA,EAAUA,EAAQ,IACtDE,EAAQ,KAAK,MAAM,IAAID,CAAG,EAChC,GAAKC,EACL,IAAI,KAAK,IAAI,EAAIA,EAAM,QAAS,CAC9B,KAAK,MAAM,OAAOD,CAAG,EACrB,MACF,CACA,OAAO,IAAI,SAASC,EAAM,KAAM,CAC9B,OAAQA,EAAM,OACd,WAAYA,EAAM,WAClB,QAAS,IAAI,QAAQA,EAAM,OAAO,CACpC,CAAC,EACH,CAEA,MAAM,IAAIF,EAA2BG,EAAmC,CACtE,IAAMF,EAAM,OAAOD,GAAY,SAAWA,EAAUA,EAAQ,IAEtDI,GADKD,EAAS,QAAQ,IAAI,eAAe,GAAK,IAC7B,MAAM,gBAAgB,EACvCE,EAAMD,EAAc,SAASA,EAAY,CAAC,EAAG,EAAE,EAAI,KACnDE,EAAU,MAAMH,EAAS,MAAM,EAAE,YAAY,EAEnD,KAAK,MAAM,IAAIF,EAAK,CAClB,KAAM,IAAI,WAAWK,CAAO,EAC5B,QAAS,IAAI,QAAQH,EAAS,OAAO,EACrC,OAAQA,EAAS,OACjB,WAAYA,EAAS,WACrB,QAAS,KAAK,IAAI,EAAIE,EAAM,GAC9B,CAAC,CACH,CAEA,MAAM,OAAOL,EAA6C,CACxD,IAAMC,EAAM,OAAOD,GAAY,SAAWA,EAAUA,EAAQ,IAC5D,OAAO,KAAK,MAAM,OAAOC,CAAG,CAC9B,CACF,EAKO,SAASM,IAAwB,CACtC,GAAI,OAAQ,WAAmB,OAAW,IAAa,CACrD,IAAMC,EAAe,IAAIT,GACxB,WAAmB,OAAS,CAC3B,QAASS,EACT,KAAM,SAAY,IAAIT,EACxB,CACF,CAEI,OAAQ,WAAmB,aAAiB,MAC7C,WAAmB,aAAe,KAAM,CAC/B,SAA+D,CAAC,EAExE,GAAGU,EAAkBC,EAA0C,CAC7D,OAAIA,EAAQ,SACV,KAAK,SAAS,KAAK,CAAE,SAAAD,EAAU,GAAIC,EAAQ,OAAQ,CAAC,EAE/C,IACT,CAEA,UAAUP,EAA8B,CACtC,IAAMQ,EAAW,KAAK,SAChBC,EAAsBT,EAAS,KAAK,EAEpCU,EAAS,IAAI,eAAe,CAChC,MAAM,MAAMC,EAAY,CACtB,IAAIC,EAAO,MAAMH,EACjB,QAAWI,KAAKL,EACVK,EAAE,WAAa,QACjBA,EAAE,GAAG,CACH,QAAQC,EAAiB,CACvBF,EAAOA,EAAK,QAAQ,eAAgB,KAAKE,CAAO,EAAE,CACpD,EACA,OAAOA,EAAiB,CACtBF,EAAOA,EAAK,QAAQ,YAAa,GAAGE,CAAO,IAAI,CACjD,CACF,CAAC,EAGLH,EAAW,QAAQ,IAAI,YAAY,EAAE,OAAOC,CAAI,CAAC,EACjDD,EAAW,MAAM,CACnB,CACF,CAAC,EAEKI,EAAa,IAAI,QAAQf,EAAS,OAAO,EAC/C,OAAAe,EAAW,OAAO,gBAAgB,EAE3B,IAAI,SAASL,EAAQ,CAC1B,OAAQV,EAAS,OACjB,WAAYA,EAAS,WACrB,QAASe,CACX,CAAC,CACH,CACF,EAEJ,CClGA,OAAOC,OAAQ,UACf,OAAOC,OAAU,YACjB,OAAOC,OAAY,cACnB,OAAS,iBAAAC,OAAqB,WAkCvB,SAASC,IAAyB,CACvC,GAAI,CACF,IAAMC,EAAaJ,GAAK,QAAQE,GAAc,YAAY,GAAG,CAAC,EAE9D,GAAIH,GAAG,WAAWC,GAAK,KAAKI,EAAY,YAAY,CAAC,GAAKL,GAAG,WAAWC,GAAK,KAAKI,EAAY,QAAQ,CAAC,EACrG,OAAOA,EAGT,IAAMC,EAASL,GAAK,QAAQI,EAAY,IAAI,EAC5C,GAAIL,GAAG,WAAWC,GAAK,KAAKK,EAAQ,YAAY,CAAC,GAAKN,GAAG,WAAWC,GAAK,KAAKK,EAAQ,QAAQ,CAAC,EAC7F,OAAOA,EAGT,IAAMC,EAAcN,GAAK,QAAQI,EAAY,OAAO,EACpD,GAAIL,GAAG,WAAWC,GAAK,KAAKM,EAAa,YAAY,CAAC,GAAKP,GAAG,WAAWC,GAAK,KAAKM,EAAa,QAAQ,CAAC,EACvG,OAAOA,CAEX,MAAQ,CAAC,CACT,OAAO,QAAQ,IAAI,CACrB,CAKO,SAASC,IAA4B,CAC1C,GAAI,CACF,IAAMC,EAAUR,GAAK,KAAKG,GAAe,EAAG,cAAc,EAC1D,GAAIJ,GAAG,WAAWS,CAAO,EAAG,CAC1B,IAAMC,EAAM,KAAK,MAAMV,GAAG,aAAaS,EAAS,OAAO,CAAC,EACxD,GAAIC,EAAI,QAAS,OAAOA,EAAI,OAC9B,CACF,MAAQ,CAAC,CACT,MAAO,OACT,CAKA,SAASC,GAAWC,EAAwB,CAC1C,GAAI,CACF,GAAIZ,GAAG,WAAWY,CAAQ,EAAG,CAE3B,IAAMC,EADUb,GAAG,aAAaY,EAAU,OAAO,EAC3B,MAAM;AAAA,CAAI,EAChC,QAAWE,KAAQD,EAAO,CACxB,IAAME,EAAUD,EAAK,KAAK,EAC1B,GAAI,CAACC,GAAWA,EAAQ,WAAW,GAAG,EAAG,SACzC,IAAMC,EAAQD,EAAQ,QAAQ,GAAG,EACjC,GAAIC,IAAU,GAAI,CAChB,IAAMC,EAAMF,EAAQ,MAAM,EAAGC,CAAK,EAAE,KAAK,EACrCE,EAAMH,EAAQ,MAAMC,EAAQ,CAAC,EAAE,KAAK,GACnCE,EAAI,WAAW,GAAG,GAAKA,EAAI,SAAS,GAAG,GAAOA,EAAI,WAAW,GAAG,GAAKA,EAAI,SAAS,GAAG,KACxFA,EAAMA,EAAI,MAAM,EAAG,EAAE,GAEjBD,KAAO,QAAQ,MACnB,QAAQ,IAAIA,CAAG,EAAIC,EAEvB,CACF,CACF,CACF,OAASC,EAAK,CACZ,QAAQ,KAAK,2BAA2BP,CAAQ,IAAKO,CAAG,CAC1D,CACF,CAKO,SAASC,GAAaC,EAAkB,QAAQ,IAAI,EAAS,CAClEV,GAAWV,GAAK,KAAKoB,EAAS,WAAW,CAAC,EAC1CV,GAAWV,GAAK,KAAKoB,EAAS,MAAM,CAAC,EACrCV,GAAWV,GAAK,KAAKoB,EAAS,iBAAiB,CAAC,CAClD,CAKO,SAASC,GAAoBC,EAAqE,CACvGH,GAAa,EAEb,IAAMI,EAAcpB,GAAe,EAG7BqB,EAAa,QAAQ,IAAI,yBAA2B,QAAQ,IAAI,sBAAwB,GAExFC,EAAc,QAAQ,IAAI,0BACZ,QAAQ,IAAI,yBACZ,QAAQ,IAAI,gCACZ,QAAQ,IAAI,uBACZ,QAAQ,IAAI,sBAAwB,GAElDC,EAAU,SAASJ,IAAU,UAAU,GAAK,QAAQ,IAAI,qBAAuB,QAAQ,IAAI,UAAY,KAAM,EAAE,EAC/GK,EAAU,SAASL,IAAU,UAAU,GAAK,QAAQ,IAAI,qBAAuB,QAAQ,IAAI,UAAY,MAAO,EAAE,EAChHM,EAAW,SAASN,GAAS,MAAQ,QAAQ,IAAI,sBAAwB,QAAQ,IAAI,MAAQ,OAAQ,EAAE,EACvGO,EAAOP,GAAS,MAAQ,QAAQ,IAAI,iBAAmB,QAAQ,IAAI,yBAA2B,UAC9FQ,EAASR,GAAS,IAAM,QAAQ,IAAI,oBAAsB,QAAQ,IAAI,SAAWtB,GAAK,KAAK,QAAQ,IAAI,EAAG,OAAQ,mBAAmB,EACrI+B,EAAoBT,IAAU,iBAAiB,GAAK,QAAQ,IAAI,gCAAkC,QAAQ,IAAI,qBAAuB,GACrIU,EAAa,GAAQV,IAAU,aAAa,GAAK,QAAQ,IAAI,yBAA2B,QACxFW,EAAa,GAAQX,IAAU,aAAa,GAAK,QAAQ,IAAI,yBAA2B,QAExFY,EAA2B,CAC/B,WAAAV,EACA,YAAAC,EACA,QAAAC,EACA,QAAAC,EACA,SAAAC,EACA,KAAAC,EACA,OAAAC,EACA,kBAAAC,EACA,WAAAC,EACA,WAAAC,EACA,YAAAV,CACF,EAEMY,EAAW,CACf,GAAI,KACJ,OAAQ,KACR,WAAY,QAAQ,IAAI,YAAc,gCAAkClC,GAAO,WAAW,EAAE,QAAQ,KAAM,EAAE,EAAIA,GAAO,WAAW,EAAE,QAAQ,KAAM,EAAE,EACpJ,mBAAoB,QAAQ,IAAI,oBAAsB,mDACtD,8BAA+B,QAAQ,IAAI,+BAAiC,IAC5E,sBAAuB,QAAQ,IAAI,uBAAyB,GAC5D,mCAAoC,QAAQ,IAAI,oCAAsC,KACtF,iCAAkC,QAAQ,IAAI,kCAAoC,EAClF,gCAAiC,QAAQ,IAAI,iCAAmC,EAChF,wBAAyB,QAAQ,IAAI,yBAA2B,GAChE,oBAAqB,QAAQ,IAAI,qBAAuB,IACxD,cAAe,QAAQ,IAAI,eAAiB,IAC5C,gBAAiB,QAAQ,IAAI,iBAAmB,IAChD,0BAA2B,QAAQ,IAAI,2BAA6B,IACpE,oBAAqB,QAAQ,IAAI,qBAAuB,KACxD,0BAA2B,QAAQ,IAAI,2BAA6B,MACpE,0BAA2B,QAAQ,IAAI,2BAA6B,KACpE,iBAAkB,QAAQ,IAAI,kBAAoB,IAClD,iBAAkB,QAAQ,IAAI,kBAAoB,IAClD,uBAAwB,QAAQ,IAAI,wBAA0B,GAC9D,2BAA4B,QAAQ,IAAI,4BAA8B,EACtE,mCAAoC,QAAQ,IAAI,oCAAsC,EACtF,uCAAwC,QAAQ,IAAI,wCAA0C,EAC9F,qBAAsB,QAAQ,IAAI,sBAAwB,IAC1D,+BAAgC8B,EAChC,GAAG,QAAQ,GACb,EAEA,MAAO,CAAE,OAAAG,EAAQ,IAAAC,CAAI,CACvB,CCpLA,OAAOC,OAAQ,UACf,OAAOC,OAAU,YACjB,OAAS,gBAAAC,OAAoB,cAItB,IAAMC,GAAN,KAA6D,CAGlE,YACkBC,EACAC,EAChB,CAFgB,QAAAD,EACA,SAAAC,CACf,CAFe,GACA,IAJV,OAAgB,CAAC,EAOzB,QAAQC,EAAoC,CAC1C,YAAK,OAASA,EAAO,IAAIC,GAAM,OAAOA,GAAM,UAAaA,EAAI,EAAI,EAAKA,CAAE,EACjE,IACT,CAEA,MAAM,MAAmBC,EAAqC,CAC5D,GAAI,CAEF,IAAMC,EADO,KAAK,GAAG,QAAQ,KAAK,GAAG,EACpB,IAAI,GAAG,KAAK,MAAM,EACnC,OAAKA,EACDD,EACMC,EAAID,CAAO,GAAK,KAEnBC,EAJU,IAKnB,OAASC,EAAU,CACjB,cAAQ,KAAK,uCAAuCA,EAAI,OAAO;AAAA,OAAU,KAAK,GAAG,EAAE,EAC7EA,CACR,CACF,CAEA,MAAM,KAAyC,CAC7C,GAAI,CAEF,IAAMC,EADO,KAAK,GAAG,QAAQ,KAAK,GAAG,EACnB,IAAI,GAAG,KAAK,MAAM,EACpC,MAAO,CACL,QAASA,EACT,QAAS,GACT,KAAM,CACJ,SAAU,EACV,WAAY,EACZ,UAAWA,EAAK,OAChB,aAAc,EACd,YAAa,EACb,WAAY,GACZ,QAAS,CACX,CACF,CACF,OAASD,EAAU,CACjB,cAAQ,KAAK,qCAAqCA,EAAI,OAAO;AAAA,OAAU,KAAK,GAAG,EAAE,EAC3EA,CACR,CACF,CAEA,MAAM,KAAyC,CAC7C,GAAI,CAEF,IAAME,EADO,KAAK,GAAG,QAAQ,KAAK,GAAG,EACjB,IAAI,GAAG,KAAK,MAAM,EAChCC,EAAU,OAAOD,EAAO,OAAO,EAC/BE,EAAkB,OAAOF,EAAO,eAAe,EACrD,MAAO,CACL,QAAS,CAAC,EACV,QAAS,GACT,KAAM,CACJ,SAAU,EACV,WAAY,EACZ,UAAW,EACX,aAAcC,EACd,YAAaC,EACb,WAAYD,EAAU,EACtB,QAASA,CACX,CACF,CACF,OAASH,EAAU,CACjB,cAAQ,KAAK,qCAAqCA,EAAI,OAAO;AAAA,OAAU,KAAK,GAAG,EAAE,EAC3EA,CACR,CACF,CAEA,MAAM,IAAmBK,EAAmD,CAC1E,GAAI,CAEF,IAAMJ,EADO,KAAK,GAAG,QAAQ,KAAK,GAAG,EACnB,IAAI,GAAG,KAAK,MAAM,EAC9BK,EAAUL,EAAK,IAAIM,GAAK,OAAO,OAAOA,CAAC,CAAC,EAC9C,OAAIF,GAAS,aAAeJ,EAAK,OAAS,EACjC,CAAC,OAAO,KAAKA,EAAK,CAAC,CAAC,EAAG,GAAGK,CAAO,EAEnCA,CACT,OAASN,EAAU,CACjB,cAAQ,KAAK,qCAAqCA,EAAI,OAAO;AAAA,OAAU,KAAK,GAAG,EAAE,EAC3EA,CACR,CACF,CACF,EAEaQ,GAAN,KAA2C,CAChD,YAA4BC,EAAqB,CAArB,WAAAA,CAAsB,CAAtB,MAE5B,QAAQC,EAAoC,CAC1C,OAAO,IAAIjB,GAAwB,KAAK,MAAOiB,CAAK,CACtD,CAEA,MAAM,MAAmBC,EAA2D,CAClF,KAAK,MAAM,KAAK,iBAAiB,EACjC,GAAI,CACF,IAAMC,EAAyB,CAAC,EAChC,QAAWC,KAAQF,EAAY,CAC7B,IAAMG,EAAM,MAAMD,EAAK,IAAO,EAC9BD,EAAQ,KAAKE,CAAG,CAClB,CACA,YAAK,MAAM,KAAK,QAAQ,EACjBF,CACT,OAASZ,EAAK,CACZ,WAAK,MAAM,KAAK,UAAU,EACpBA,CACR,CACF,CAEA,MAAM,KAAKU,EAAsC,CAC/C,YAAK,MAAM,KAAKA,CAAK,EACd,CAAE,MAAO,EAAG,SAAU,CAAE,CACjC,CAEA,MAAM,MAA6B,CACjC,MAAM,IAAI,MAAM,2CAA2C,CAC7D,CAEA,YAAYK,EAAmD,CAI7D,OAHgB,OAAO,OAAO,KAAM,CAClC,YAAa,IAAM,IACrB,CAAC,CAEH,CACF,EAKO,SAASC,GAActB,EAAkBuB,EAA6B,CAU3E,GARAvB,EAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAMP,EAEG,CAACwB,GAAG,WAAWD,CAAa,EAAG,CACjC,QAAQ,KAAK,qCAAqCA,CAAa,EAAE,EACjE,MACF,CAEA,IAAME,EAAQD,GAAG,YAAYD,CAAa,EACvC,OAAQG,GAAcA,EAAE,SAAS,MAAM,CAAC,EACxC,KAAK,EAEFC,EAAU3B,EAAG,QAAQ,mDAAmD,EACxE4B,EAAa5B,EAAG,QAAQ,gEAAgE,EAE1F6B,EAAe,EACnB,QAAWC,KAAQL,EAAO,CAExB,GADiBE,EAAQ,IAAIG,CAAI,EACnB,SAEd,IAAMC,EAAWC,GAAK,KAAKT,EAAeO,CAAI,EACxC7B,EAAMuB,GAAG,aAAaO,EAAU,OAAO,EAE7C,QAAQ,IAAI,yBAAyBD,CAAI,KAAK,EAC9C9B,EAAG,KAAK,iBAAiB,EACzB,GAAI,CACFA,EAAG,KAAKC,CAAG,EACX2B,EAAW,IAAIE,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,CAAC,EAClD9B,EAAG,KAAK,QAAQ,EAChB6B,GACF,OAASvB,EAAU,CACjB,MAAAN,EAAG,KAAK,UAAU,EAClB,QAAQ,MAAM,gCAAgC8B,CAAI,IAAKxB,EAAI,SAAWA,CAAG,EACnEA,CACR,CACF,CAEIuB,EAAe,EACjB,QAAQ,IAAI,qCAAqCA,CAAY,gBAAgB,EAE7E,QAAQ,IAAI,6CAA6C,CAE7D,CAKO,SAASI,GAAiBC,EAAgBC,EAAkBC,GAAe,EAAmB,CACnG,IAAMC,EAAML,GAAK,QAAQE,CAAM,EAC1BV,GAAG,WAAWa,CAAG,GACpBb,GAAG,UAAUa,EAAK,CAAE,UAAW,EAAK,CAAC,EAGvC,QAAQ,IAAI,0CAA0CH,CAAM,EAAE,EAC9D,IAAMnB,EAAQ,IAAIuB,GAAaJ,CAAM,EAGrCnB,EAAM,KAAK,4BAA4B,EACvCA,EAAM,KAAK,8BAA8B,EACzCA,EAAM,KAAK,2BAA2B,EACtCA,EAAM,KAAK,6BAA6B,EACxCA,EAAM,KAAK,6BAA6B,EACxCA,EAAM,KAAK,+BAA+B,EAG1C,IAAMQ,EAAgBS,GAAK,KAAKG,EAAS,YAAY,EACrD,OAAAb,GAAcP,EAAOQ,CAAa,EAE3B,IAAIT,GAAeC,CAAK,CACjC,CCzNA,OAAOwB,OAAW,aCFX,IAAMC,GAAoC,CAC/C,EAAG,IACH,EAAG,KACH,EAAG,QACH,EAAG,MACH,GAAI,MACJ,GAAI,QACJ,GAAI,KACJ,GAAI,MACJ,GAAI,KACJ,GAAI,MACJ,GAAI,MACJ,GAAI,OACJ,GAAI,MACJ,GAAI,MACJ,GAAI,QACJ,GAAI,KACJ,GAAI,OACJ,GAAI,QACJ,GAAI,MACJ,GAAI,KACJ,GAAI,QACJ,GAAI,WACJ,GAAI,QACJ,GAAI,OACJ,GAAI,SACJ,GAAI,QACJ,GAAI,QACJ,GAAI,aACJ,GAAI,OACJ,GAAI,SACJ,GAAI,MACJ,GAAI,OACJ,GAAI,QACJ,GAAI,MACJ,IAAK,MACL,IAAK,MACL,IAAK,MACL,MAAO,KACT,EAKaC,GAA2C,OAAO,YAC7D,OAAO,QAAQD,EAAS,EAAE,IAAI,CAAC,CAACE,EAAMC,CAAI,IAAM,CAACA,EAAM,OAAOD,CAAI,CAAC,CAAC,CACtE,ECvCO,SAASE,GACdC,EACAC,EACgC,CAChC,IAAIC,EAAO,GACPC,EAAOF,EACPG,EAAS,GACTC,EAAW,EACXC,EAAa,EACbC,EAAa,GAEjB,KAAOD,EAAa,KACd,EAAAH,GAAQH,EAAO,SADI,CAEvB,IAAMQ,EAAMR,EAAOG,CAAI,EAEvB,GAAIK,IAAQ,EAAG,CACRJ,GAAQC,IACbE,EAAa,GACb,KACF,CAEA,IAAKC,EAAM,OAAU,IAAM,CACzB,GAAIL,EAAO,GAAKH,EAAO,OAAQ,MAC/B,IAAMS,GAAYD,EAAM,KAAS,EAAKR,EAAOG,EAAO,CAAC,EAChDC,IACHC,GAAY,EACZD,EAAS,IAEXD,EAAOM,EACPH,IACA,QACF,CASA,IANKE,EAAM,OAAU,IAIjBN,EAAK,OAAS,IAAGA,GAAQ,KAEzBC,EAAO,EAAIK,EAAMR,EAAO,QAAQ,MAEpC,QAASU,EAAI,EAAGA,EAAIF,EAAKE,IACvBR,GAAQ,OAAO,aAAaF,EAAOG,EAAO,EAAIO,CAAC,CAAC,EAE7CN,IAAQC,GAAYG,EAAM,GAC/BL,GAAQK,EAAM,EACdF,GACF,CAEA,OAAKC,EAIE,CAAE,KAAAL,EAAM,KAAMG,CAAS,EAHrB,CAAE,KAAM,GAAI,KAAM,CAAE,CAI/B,CAQO,SAASM,GAAaC,EAAsB,CACjD,OAAOC,GAAUD,CAAI,GAAK,OAAOA,CAAI,EACvC,CAWO,SAASE,GAAqBC,EAAkC,CACrE,GAAI,CAEF,GAAIA,EAAI,OAAS,GAAI,OAAO,KAE5B,GAAM,CAAE,KAAAb,EAAM,KAAAc,CAAK,EAAIjB,GAAWgB,EAAK,EAAE,EACzC,GAAIC,IAAS,EAAG,OAAO,KACvB,IAAMC,EAAc,GAAKD,EAGzB,GAAIC,EAAc,EAAIF,EAAI,OAAQ,OAAO,KAEzC,IAAMG,EAAaH,EAAIE,CAAW,GAAK,EAAKF,EAAIE,EAAc,CAAC,EAE/D,MAAO,CACL,KAAAf,EACA,KAAMS,GAAaO,CAAS,EAC5B,IAAAH,CACF,CACF,OAAS,EAAG,CACV,eAAQ,MAAM,mBAAoB,CAAC,EAC5B,IACT,CACF,CAQA,eAAsBI,GACpBC,EAC0B,CAC1B,GAAI,CACF,IAAIL,EAEJ,GAAIK,EAAQ,SAAW,MAAO,CAE5B,IAAMC,EADM,IAAI,IAAID,EAAQ,GAAG,EACV,aAAa,IAAI,KAAK,EAC3C,GAAI,CAACC,EAAU,OAAO,KAEtB,IAAIC,EAASD,EAAS,QAAQ,KAAM,GAAG,EAAE,QAAQ,KAAM,GAAG,EAC1D,KAAOC,EAAO,OAAS,GAAGA,GAAU,IAEpC,IAAMC,EAAS,KAAKD,CAAM,EAC1BP,EAAM,IAAI,WAAWQ,EAAO,MAAM,EAClC,QAAS,EAAI,EAAG,EAAIA,EAAO,OAAQ,IACjCR,EAAI,CAAC,EAAIQ,EAAO,WAAW,CAAC,CAEhC,SAAWH,EAAQ,SAAW,OAAQ,CACpC,IAAMpB,EAAS,MAAMoB,EAAQ,YAAY,EACzCL,EAAM,IAAI,WAAWf,CAAM,CAC7B,KACE,QAAO,KAGT,OAAOc,GAAqBC,CAAG,CACjC,OAAS,EAAG,CACV,eAAQ,MAAM,mBAAoB,CAAC,EAC5B,IACT,CACF,CAQO,SAASS,GACdT,EAC6D,CAC7D,GAAIA,EAAI,OAAS,GAAI,MAAO,CAAC,EAC7B,IAAMU,EAAYV,EAAI,CAAC,GAAK,EAAKA,EAAI,CAAC,EACtC,GAAIU,IAAa,EAAG,MAAO,CAAC,EAE5B,IAAMC,EAAuE,CAAC,EAC1EzB,EAAS,GAGP0B,EAAUZ,EAAI,CAAC,GAAK,EAAKA,EAAI,CAAC,EACpC,QAASL,EAAI,EAAGA,EAAIiB,EAAQjB,IAAK,CAC/B,GAAIT,GAAUc,EAAI,OAAQ,MAAO,CAAC,EAClC,GAAM,CAAE,KAAAC,CAAK,EAAIjB,GAAWgB,EAAKd,CAAM,EACvC,GAAIe,IAAS,EAAG,MAAO,CAAC,EAExB,GADAf,GAAUe,EAAO,EACbf,EAASc,EAAI,OAAQ,MAAO,CAAC,CACnC,CAEA,QAASL,EAAI,EAAGA,EAAIe,GACd,EAAAxB,GAAUc,EAAI,QADUL,IAAK,CAGjC,GAAM,CAAE,KAAAR,EAAM,KAAM0B,CAAS,EAAI7B,GAAWgB,EAAKd,CAAM,EAMvD,GALI2B,IAAa,IACjB3B,GAAU2B,EAIN3B,EAAS,GAAKc,EAAI,QACpB,MAGF,IAAMc,EAAYd,EAAId,CAAM,GAAK,EAAKc,EAAId,EAAS,CAAC,EAC9C6B,EACHf,EAAId,EAAS,CAAC,GAAK,GACnBc,EAAId,EAAS,CAAC,GAAK,GACnBc,EAAId,EAAS,CAAC,GAAK,EACpBc,EAAId,EAAS,CAAC,EACV8B,EAAYhB,EAAId,EAAS,CAAC,GAAK,EAAKc,EAAId,EAAS,CAAC,EAQxD,GAPAA,GAAU,GAONA,EAAS8B,EAAWhB,EAAI,OAC1B,MAGF,IAAMH,EAAOD,GAAakB,CAAQ,EAC9BG,EAAO,GAEX,GAAIpB,IAAS,KAAOmB,IAAa,EAC/BC,EAAO,GAAGjB,EAAId,CAAM,CAAC,IAAIc,EAAId,EAAS,CAAC,CAAC,IAAIc,EAAId,EAAS,CAAC,CAAC,IAAIc,EAAId,EAAS,CAAC,CAAC,WACrEW,IAAS,QAAUmB,IAAa,GAAI,CAC7C,IAAME,EAAkB,CAAC,EACzB,QAASC,EAAI,EAAGA,EAAI,GAAIA,GAAK,EAC3BD,EAAM,MACFlB,EAAId,EAASiC,CAAC,GAAK,EAAKnB,EAAId,EAASiC,EAAI,CAAC,GAAG,SAAS,EAAE,CAC5D,EAGF,IAAIC,EAAY,GACdC,EAAU,EACRC,EAAe,GACjBC,EAAa,EACf,QAASJ,EAAI,EAAGA,EAAID,EAAM,OAAQC,IAC5BD,EAAMC,CAAC,IAAM,KACXG,IAAiB,KAAIA,EAAeH,GACxCI,MAEIA,EAAaF,IACfD,EAAYE,EACZD,EAAUE,GAEZD,EAAe,GACfC,EAAa,GAQjB,GALIA,EAAaF,IACfD,EAAYE,EACZD,EAAUE,GAGRF,EAAU,EAAG,CAEf,IAAMG,EACJJ,IAAc,GAAKA,EAAYC,IAAYH,EAAM,OAAS,IAAM,GAClEA,EAAM,OAAOE,EAAWC,EAASG,CAAW,CAC9C,CACAP,EAAOC,EAAM,KAAK,GAAG,EAAE,QAAQ,MAAO,IAAI,CAC5C,SAAWrB,IAAS,SAAWA,IAAS,MAAQA,IAAS,MACvDoB,EAAOjC,GAAWgB,EAAKd,CAAM,EAAE,aACtBW,IAAS,MAAO,CAEzB,IAAI4B,EAAYvC,EACVwC,EAAqB,CAAC,EACtBC,EAASzC,EAAS8B,EACxB,KAAOS,EAAYE,GAAQ,CACzB,IAAMlC,EAAMO,EAAIyB,CAAS,EACzB,GAAIA,EAAY,EAAIhC,EAAMkC,EAAQ,MAClCD,EAAS,KACP,OAAO,aACL,GAAG1B,EAAI,MAAMyB,EAAY,EAAGA,EAAY,EAAIhC,CAAG,CACjD,CACF,EACAgC,GAAahC,EAAM,CACrB,CACAwB,EAAOS,EAAS,KAAK,EAAE,CACzB,SAAW7B,IAAS,SAAWA,IAAS,OAEtC,GAAImB,GAAY,EAAG,CACjB,IAAMY,EAAY5B,EAAId,CAAM,GAAK,EAAKc,EAAId,EAAS,CAAC,EAC9C,CAAE,KAAM2C,EAAQ,KAAMC,CAAW,EAAI9C,GAAWgB,EAAKd,EAAS,CAAC,EACjEE,EAAOF,EAAS,EAAI4C,EAClBC,EAAM7C,EAAS8B,EACfgB,EAAmB,CAAC,EAE1B,KAAO5C,EAAO,GAAK2C,GAAK,CACtB,IAAME,EAAOjC,EAAIZ,CAAI,GAAK,EAAKY,EAAIZ,EAAO,CAAC,EACrC8C,EAAUlC,EAAIZ,EAAO,CAAC,GAAK,EAAKY,EAAIZ,EAAO,CAAC,EAGlD,GAFAA,GAAQ,EAEJA,EAAO8C,EAASH,EAAK,MAEzB,GAAIE,IAAQ,EAAG,CACb,IAAIE,EAAQ/C,EACNgD,EAAOhD,EAAO8C,EACdG,EAAkB,CAAC,EACzB,KAAOF,EAAQC,GAAM,CACnB,IAAME,EAAOtC,EAAImC,CAAK,EACtB,GAAIA,EAAQ,EAAIG,EAAOF,EAAM,MAC7B,IAAIG,EAAO,GACX,QAASC,EAAI,EAAGA,EAAIF,EAAME,IACxBD,GAAQ,OAAO,aAAavC,EAAImC,EAAQ,EAAIK,CAAC,CAAC,EAEhDH,EAAM,KAAKE,CAAI,EACfJ,GAASG,EAAO,CAClB,CACAN,EAAO,KAAK,QAAQK,EAAM,KAAK,GAAG,CAAC,EAAE,CACvC,SAAWJ,IAAQ,EAAG,CACpB,IAAMQ,EAAgB,CAAC,EACvB,QAASD,EAAI,EAAGA,EAAI,GAAKN,EAAQM,GAAK,EACpCC,EAAI,KAAK,GAAGzC,EAAIZ,EAAOoD,CAAC,CAAC,IAAIxC,EAAIZ,EAAOoD,EAAI,CAAC,CAAC,IAAIxC,EAAIZ,EAAOoD,EAAI,CAAC,CAAC,IAAIxC,EAAIZ,EAAOoD,EAAI,CAAC,CAAC,EAAE,EAE5FR,EAAO,KAAK,YAAYS,EAAI,KAAK,GAAG,CAAC,EAAE,CACzC,SAAWR,IAAQ,EAAG,CACpB,IAAIS,EAAS,GACb,QAASF,EAAI,EAAGA,EAAIN,EAAQM,IAC1BE,GAAU,OAAO,aAAa1C,EAAIZ,EAAOoD,CAAC,CAAC,EAE7CR,EAAO,KAAK,OAAO,KAAKU,CAAM,CAAC,EAAE,CACnC,SAAWT,IAAQ,EAAG,CACpB,IAAMQ,EAAgB,CAAC,EACvB,QAASD,EAAI,EAAGA,EAAI,IAAMN,EAAQM,GAAK,GAAI,CACzC,IAAMtB,EAAkB,CAAC,EACzB,QAASC,EAAI,EAAGA,EAAI,GAAIA,GAAK,EAC3BD,EAAM,MAAOlB,EAAIZ,EAAOoD,EAAIrB,CAAC,GAAK,EAAKnB,EAAIZ,EAAOoD,EAAIrB,EAAI,CAAC,GAAG,SAAS,EAAE,CAAC,EAE5EsB,EAAI,KAAKvB,EAAM,KAAK,GAAG,CAAC,CAC1B,CACAc,EAAO,KAAK,YAAYS,EAAI,KAAK,GAAG,CAAC,EAAE,CACzC,MACET,EAAO,KAAK,MAAMC,CAAG,KAAKC,CAAM,IAAI,EAGtC9C,GAAQ8C,CACV,CAEAjB,EAAO,GAAGW,CAAQ,IAAIC,GAAU,GAAG,GAAGG,EAAO,OAAS,EAAI,IAAMA,EAAO,KAAK,GAAG,EAAI,EAAE,GAAG,KAAK,CAC/F,MACEf,EAAO,SAASD,CAAQ,eAG1BC,EAAO,SAASD,CAAQ,UAG1BL,EAAQ,KAAK,CAAE,KAAAxB,EAAM,KAAAU,EAAM,KAAAoB,EAAM,IAAAF,CAAI,CAAC,EACtC7B,GAAU8B,CACZ,CAEA,OAAOL,CACT,CCjVO,IAAMgC,GAAyD,CACpE,qBACA,wBACA,kBACA,qBACA,mBACA,WACF,EAYO,IAAMC,GAA8B,qBAK9BC,GACX,mGAQK,SAASC,GAA4BC,EAA0D,CACpG,GAAIA,GAAK,4BACP,GAAI,CACF,IAAMC,EAAS,KAAK,MAAMD,EAAI,2BAA2B,EACzD,GAAI,MAAM,QAAQC,CAAM,GAAKA,EAAO,OAAS,EAC3C,OAAOA,EACJ,IAAKC,GAAmB,OAAOA,GAAS,SAAWA,EAAQA,GAA8B,QAAU,EAAG,EACtG,OAAO,OAAO,CAErB,OAAS,EAAG,CACV,QAAQ,KAAK,8DAA+D,CAAC,CAC/E,CAEF,MAAO,CAAC,GAAGC,EAAmC,CAChD,CCxCO,SAASC,GACdC,EACAC,EAA4BC,GACpB,CACR,GAAI,CACF,IAAMC,EAAS,KAAKH,CAAS,EACvBI,EAAQ,IAAI,WAAWD,EAAO,MAAM,EAC1C,QAASE,EAAI,EAAGA,EAAIF,EAAO,OAAQE,IACjCD,EAAMC,CAAC,EAAIF,EAAO,WAAWE,CAAC,EAQhC,GALID,EAAM,OAAS,KAIFA,EAAM,CAAC,GAAK,EAAKA,EAAM,CAAC,KACzB,MAAQ,OAAOJ,EAM/B,IAAMM,EAAWF,EAAM,CAAC,EAClBG,EAASH,EAAM,CAAC,GAAK,EAAKA,EAAM,CAAC,EACjCI,EAASJ,EAAM,CAAC,GAAK,EAAKA,EAAM,EAAE,EAClCK,EAAU,GACVC,EAAQD,EAAUD,EACxB,GAAIE,EAAQ,EAAIN,EAAM,OAAQ,OAAOJ,EACrC,IAAMW,EAAYP,EAAM,MAAMK,EAASC,CAAK,EAExCE,EAASF,EACPG,EAAST,EAAMQ,CAAM,GAAK,EAAKR,EAAMQ,EAAS,CAAC,EAErD,GADAA,GAAU,EACNA,EAASC,EAAQ,EAAIT,EAAM,OAAQ,OAAOJ,EAC9C,IAAMc,EAAeV,EAAM,MAAMQ,EAAQA,EAASC,CAAK,EACvDD,GAAUC,EAEV,IAAME,EAAaX,EAAMQ,GAAQ,EAC3BI,EAAaZ,EAAMQ,GAAQ,EACjC,GAAIA,EAASI,EAAa,EAAIZ,EAAM,OAAQ,OAAOJ,EACnDY,GAAUI,EAEV,IAAMC,EAAUb,EAAMQ,CAAM,GAAK,EAAKR,EAAMQ,EAAS,CAAC,EAEtD,GADAA,GAAU,EACNA,EAASK,EAASb,EAAM,OAAQ,OAAOJ,EAC3C,IAAMkB,EAAad,EAAM,MAAMQ,EAAQA,EAASK,CAAM,EAEhDE,GAAclB,GAAqBC,IAA6B,KAAK,EAAE,YAAY,EACnFkB,EAAY,IAAI,WAAWD,EAAW,MAAM,EAClD,QAASd,EAAI,EAAGA,EAAIc,EAAW,OAAQd,IACrCe,EAAUf,CAAC,EAAIc,EAAW,WAAWd,CAAC,EAIxC,IAAMgB,EAAqB,CACzBf,EACCC,GAAS,EAAK,IACfA,EAAQ,IACPC,GAAS,EAAK,IACfA,EAAQ,IACR,GAAG,MAAM,KAAKG,CAAS,EACtBE,GAAS,EAAK,IACfA,EAAQ,IACR,GAAG,MAAM,KAAKC,CAAY,EAC1BC,EACAK,EAAU,OACV,GAAG,MAAM,KAAKA,CAAS,EACtBH,GAAU,EAAK,IAChBA,EAAS,IACT,GAAG,MAAM,KAAKC,CAAU,CAC1B,EAGMI,EAAmB,CACvB,IACA,GACCD,EAAS,QAAU,EAAK,IACzBA,EAAS,OAAS,IAClB,GAAGA,CACL,EAGME,EAAiB,CACpBD,EAAO,QAAU,EAAK,IACvBA,EAAO,OAAS,IAChB,GAAGA,CACL,EAEIE,EAAe,GACnB,QAASnB,EAAI,EAAGA,EAAIkB,EAAK,OAAQlB,IAC/BmB,GAAgB,OAAO,aAAaD,EAAKlB,CAAC,CAAC,EAE7C,OAAO,KAAKmB,CAAY,CAC1B,OAASC,EAAG,CACV,eAAQ,MAAM,8CAA+CA,CAAC,EACvDzB,CACT,CACF,CAUO,SAAS0B,GACdC,EAAqBzB,GACrB0B,EACQ,CAER,OAAO7B,GADM6B,GAAmBC,GACIF,CAAU,CAChD,CCxHO,SAASG,GAAeC,EAAsB,CACnD,IAAMC,EAAQ,IAAI,WAAW,EAAE,EAC/B,GAAID,EAAG,SAAS,IAAI,EAAG,CACrB,GAAM,CAACE,EAAMC,CAAK,EAAIH,EAAG,MAAM,IAAI,EAC7BI,EAAYF,EAAOA,EAAK,MAAM,GAAG,EAAE,OAAO,OAAO,EAAI,CAAC,EACtDG,EAAaF,EAAQA,EAAM,MAAM,GAAG,EAAE,OAAO,OAAO,EAAI,CAAC,EAE3D,EAAI,EACR,QAAWG,KAAQF,EAAW,CAC5B,IAAMG,EAAI,SAASD,EAAM,EAAE,EAC3BL,EAAM,GAAG,EAAKM,GAAK,EAAK,IACxBN,EAAM,GAAG,EAAIM,EAAI,GACnB,CAEA,EAAI,GAAKF,EAAW,OAAS,EAC7B,QAAWC,KAAQD,EAAY,CAC7B,IAAME,EAAI,SAASD,EAAM,EAAE,EAC3BL,EAAM,GAAG,EAAKM,GAAK,EAAK,IACxBN,EAAM,GAAG,EAAIM,EAAI,GACnB,CACF,KAAO,CACL,IAAMC,EAAQR,EAAG,MAAM,GAAG,EACtBS,EAAI,EACR,QAAWH,KAAQE,EAAO,CACxB,IAAMD,EAAI,SAASD,EAAM,EAAE,EAC3BL,EAAMQ,GAAG,EAAKF,GAAK,EAAK,IACxBN,EAAMQ,GAAG,EAAIF,EAAI,GACnB,CACF,CACA,OAAO,MAAM,KAAKN,CAAK,CACzB,CAKO,SAASS,GAA2BC,EAO9B,CACX,GAAM,CACJ,SAAAC,EAAW,EACX,OAAAC,EAAS,IACT,KAAAC,EAAO,CAAC,KAAM,IAAI,EAClB,UAAAC,EAAY,CAAC,EACb,UAAAC,EAAY,CAAC,EACb,UAAAC,EAAY,IACd,EAAIN,EAEEV,EAAkB,CAAC,EAMzB,GAHAA,EAAM,KAAMW,GAAY,EAAK,IAAMA,EAAW,GAAI,EAG9C,CAACC,GAAUA,IAAW,IACxBZ,EAAM,KAAK,CAAI,MACV,CACL,IAAMiB,EAASL,EAAO,MAAM,GAAG,EAC/B,QAAWM,KAASD,EAAQ,CAC1BjB,EAAM,KAAKkB,EAAM,MAAM,EACvB,QAASV,EAAI,EAAGA,EAAIU,EAAM,OAAQV,IAChCR,EAAM,KAAKkB,EAAM,WAAWV,CAAC,CAAC,CAElC,CACAR,EAAM,KAAK,CAAI,CACjB,CAIA,GAAIa,GAAQA,EAAK,OAAS,EAAG,CAC3B,IAAMM,EAAoB,CAAC,EAC3B,QAAWC,KAASP,EAAM,CACxBM,EAAQ,KAAKC,EAAM,MAAM,EACzB,QAASZ,EAAI,EAAGA,EAAIY,EAAM,OAAQZ,IAChCW,EAAQ,KAAKC,EAAM,WAAWZ,CAAC,CAAC,CAEpC,CACAR,EAAM,KAAK,EAAM,CAAI,EACrBA,EAAM,KAAMmB,EAAQ,QAAU,EAAK,IAAMA,EAAQ,OAAS,GAAI,EAC9DnB,EAAM,KAAK,GAAGmB,CAAO,CACvB,CAGA,GAAIL,GAAaA,EAAU,OAAS,EAAG,CACrC,IAAMO,EAAoB,CAAC,EAC3B,QAAWtB,KAAMe,EAAW,CAC1B,IAAMP,EAAQR,EAAG,MAAM,GAAG,EAAE,IAAI,MAAM,EAClCQ,EAAM,SAAW,GAAKA,EAAM,MAAOe,GAAM,CAAC,MAAMA,CAAC,GAAKA,GAAK,GAAKA,GAAK,GAAG,GAC1ED,EAAQ,KAAK,GAAGd,CAAK,CAEzB,CACIc,EAAQ,OAAS,IACnBrB,EAAM,KAAK,EAAM,CAAI,EACrBA,EAAM,KAAMqB,EAAQ,QAAU,EAAK,IAAMA,EAAQ,OAAS,GAAI,EAC9DrB,EAAM,KAAK,GAAGqB,CAAO,EAEzB,CAGA,GAAIL,EACF,GAAI,CACF,IAAMO,EAAY,KAAKP,CAAS,EAC1BQ,EAAqB,CAAC,EAC5B,QAAShB,EAAI,EAAGA,EAAIe,EAAU,OAAQf,IACpCgB,EAAS,KAAKD,EAAU,WAAWf,CAAC,CAAC,EAEvCR,EAAM,KAAK,EAAM,CAAI,EACrBA,EAAM,KAAMwB,EAAS,QAAU,EAAK,IAAMA,EAAS,OAAS,GAAI,EAChExB,EAAM,KAAK,GAAGwB,CAAQ,CACxB,OAASC,EAAG,CACV,QAAQ,MAAM,sCAAuCA,CAAC,CACxD,CAIF,GAAIV,GAAaA,EAAU,OAAS,EAAG,CACrC,IAAMW,EAAqB,CAAC,EAC5B,QAAW3B,KAAMgB,EAAW,CAC1B,IAAMY,EAAQ7B,GAAeC,CAAE,EAC3B4B,GAASA,EAAM,SAAW,IAC5BD,EAAS,KAAK,GAAGC,CAAK,CAE1B,CACID,EAAS,OAAS,IACpB1B,EAAM,KAAK,EAAM,CAAI,EACrBA,EAAM,KAAM0B,EAAS,QAAU,EAAK,IAAMA,EAAS,OAAS,GAAI,EAChE1B,EAAM,KAAK,GAAG0B,CAAQ,EAE1B,CAEA,OAAO1B,CACT,CASO,SAAS4B,GAA2BC,EAAyB,CAClE,IAAMC,EAASD,EAAM,KAAK,EAAE,MAAM,KAAK,EACjClB,EAAW,SAASmB,EAAO,CAAC,EAAG,EAAE,GAAK,EACtClB,EAASkB,EAAO,CAAC,GAAK,IAExBjB,EACAC,EACAE,EACAD,EAEJ,QAASP,EAAI,EAAGA,EAAIsB,EAAO,OAAQtB,IAAK,CACtC,IAAMuB,EAAQD,EAAOtB,CAAC,EAChBwB,EAAQD,EAAM,QAAQ,GAAG,EAC/B,GAAIC,IAAU,GAAI,SAClB,IAAMC,EAAIF,EAAM,UAAU,EAAGC,CAAK,EAC5B1B,EAAIyB,EAAM,UAAUC,EAAQ,CAAC,EAC/BC,IAAM,OACRpB,EAAOP,EAAE,MAAM,GAAG,EACT2B,IAAM,WACfnB,EAAYR,EAAE,MAAM,GAAG,EACd2B,IAAM,MACfjB,EAAYV,EACH2B,IAAM,aACflB,EAAYT,EAAE,MAAM,GAAG,EAE3B,CAEA,OAAOG,GAA2B,CAChC,SAAAE,EACA,OAAAC,EACA,KAAAC,EACA,UAAAC,EACA,UAAAC,EACA,UAAAC,CACF,CAAC,CACH,CCvJO,SAASkB,GAAcC,EAA2B,CACvD,IAAMC,EAAQD,EAAG,MAAM,GAAG,EAC1B,GAAIC,EAAM,SAAW,EAAG,OAAO,KAE/B,IAAIC,EAAW,GACTC,EAAMF,EAAM,OAAO,CAACG,EAAOC,IAAU,CACzC,IAAMC,EAAM,OAAOD,CAAK,EACxB,OAAIA,IAAU,IAAM,MAAMC,CAAG,GAAKA,EAAM,GAAKA,EAAM,KAAO,OAAOA,CAAG,IAAMD,KACxEH,EAAW,KAELE,GAAS,GAAKE,CACxB,EAAG,CAAC,IAAM,EAEV,OAAOJ,EAAW,KAAOC,CAC3B,CAQO,SAASI,GAAaP,EAA2B,CACtD,IAAIQ,EAAUR,EAAG,KAAK,EAAE,YAAY,EAChCQ,EAAQ,WAAW,GAAG,GAAKA,EAAQ,SAAS,GAAG,IACjDA,EAAUA,EAAQ,MAAM,EAAG,EAAE,GAI/B,IAAIC,EAAW,GACf,GAAID,EAAQ,SAAS,GAAG,EAAG,CACzB,IAAME,EAAYF,EAAQ,YAAY,GAAG,EACzC,GAAIE,IAAc,GAAI,OAAO,KAC7BD,EAAWD,EAAQ,UAAUE,EAAY,CAAC,EAC1CF,EAAUA,EAAQ,UAAU,EAAGE,CAAS,EAAI,MAC9C,CAGA,IAAMC,EAAmBH,EAAQ,MAAM,IAAI,EAC3C,GAAIG,EAAiB,OAAS,EAAG,OAAO,KAExC,IAAIC,EAASJ,EACb,GAAIG,EAAiB,SAAW,EAAG,CACjC,IAAME,EAAYF,EAAiB,CAAC,EAAIA,EAAiB,CAAC,EAAE,MAAM,GAAG,EAAI,CAAC,EACpEG,EAAaH,EAAiB,CAAC,EAAIA,EAAiB,CAAC,EAAE,MAAM,GAAG,EAAI,CAAC,EAC3E,GAAIE,EAAU,SAAS,EAAE,GAAKC,EAAW,SAAS,EAAE,EAAG,OAAO,KAE9D,IAAMC,EAAgB,GAAKF,EAAU,OAASC,EAAW,QACzD,GAAIC,EAAgB,EAAG,OAAO,KAE9B,IAAMC,EAAS,IAAI,MAAMD,CAAa,EAAE,KAAK,GAAG,EAAE,KAAK,GAAG,EAC1DH,EAAS,CAAC,GAAGC,EAAWG,EAAQ,GAAGF,CAAU,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,CACzE,CAEA,IAAMb,EAAQW,EAAO,MAAM,GAAG,EAC9B,GAAIX,EAAM,SAAW,EAAG,OAAO,KAE/B,GAAIQ,EAAU,CACZ,IAAMQ,EAAUlB,GAAcU,CAAQ,EACtC,GAAIQ,IAAY,KAAM,OAAO,KAC7BhB,EAAM,CAAC,GAAKgB,IAAY,IAAI,SAAS,EAAE,EACvChB,EAAM,CAAC,GAAKgB,EAAU,OAAQ,SAAS,EAAE,CAC3C,CAEA,IAAIC,EAAY,GAChB,QAAWC,KAAQlB,EAAO,CACxB,GAAIkB,IAAS,IAAMA,EAAK,OAAS,EAAG,OAAO,KAC3C,IAAMC,EAAS,SAASD,EAAM,EAAE,EAChC,GACE,MAAMC,CAAM,GACZA,EAAS,GACTA,EAAS,OACTD,EAAK,YAAY,IAAMC,EAAO,SAAS,EAAE,EAAE,SAASD,EAAK,OAAQ,GAAG,EAEpE,OAAO,KAETD,GAAaC,EAAK,SAAS,EAAG,GAAG,CACnC,CAEA,GAAI,CACF,OAAO,OAAO,KAAOD,CAAS,CAChC,MAAQ,CACN,OAAO,IACT,CACF,CAQO,SAASG,GAAOrB,EAAqB,CAC1C,OAAOD,GAAcC,CAAE,IAAM,IAC/B,CAQO,SAASsB,GAAOtB,EAAqB,CAC1C,OAAOO,GAAaP,CAAE,IAAM,IAC9B,CAQO,SAASuB,GAAUC,EAAiC,CACzD,GAAI,CAACA,GAAQ,OAAOA,GAAS,SAAU,OAAO,KAC9C,IAAMC,EAAWD,EAAK,YAAY,GAAG,EACrC,GAAIC,IAAa,GAAI,OAAO,KAE5B,IAAMC,EAASF,EAAK,MAAM,EAAGC,CAAQ,EAAE,KAAK,EACtCE,EAAYH,EAAK,MAAMC,EAAW,CAAC,EAAE,KAAK,EAC1CG,EAAY,SAASD,EAAW,EAAE,EACxC,GAAI,MAAMC,CAAS,GAAKA,EAAY,EAAG,OAAO,KAE9C,GAAIF,EAAO,SAAS,GAAG,EAAG,CACxB,GAAIE,EAAY,IAAK,OAAO,KAC5B,IAAMC,EAAQtB,GAAamB,CAAM,EACjC,GAAIG,IAAU,KAAM,OAAO,KAC3B,IAAMC,EAAOF,IAAc,EAAI,GAAO,CAAC,IAAM,OAAO,IAAMA,CAAS,GAAO,IAAM,MAAQ,GAExF,MAAO,CAAE,QAAS,EAAG,IADTC,EAAQC,EACM,KAAAA,CAAK,CACjC,KAAO,CACL,GAAIF,EAAY,GAAI,OAAO,KAC3B,IAAMC,EAAQ9B,GAAc2B,CAAM,EAClC,GAAIG,IAAU,KAAM,OAAO,KAC3B,IAAMC,EAAOF,IAAc,EAAI,EAAK,IAAO,GAAKA,IAAgB,EAEhE,MAAO,CAAE,QAAS,EAAG,KADRC,EAAQC,KAAU,EACL,KAAAA,CAAK,CACjC,CACF,CAyCO,SAASC,GAAkBC,EAAmD,CACnF,IAAMC,EAA2B,CAAC,EAC5BC,EAA2B,CAAC,EAElC,QAAWC,KAAQH,EAAO,CACxB,IAAMI,EAASC,GAAUF,CAAI,EACzBC,IACEA,EAAO,UAAY,EACrBH,EAAS,KAAKG,CAAM,EAEpBF,EAAS,KAAKE,CAAM,EAG1B,CAEA,OAAQE,GAAwB,CAC9B,GAAI,CAACA,EAAI,MAAO,GAChB,GAAIA,EAAG,SAAS,GAAG,EAAG,CACpB,IAAMC,EAAQC,GAAaF,CAAE,EAC7B,OAAIC,IAAU,KAAa,GACpBL,EAAS,KAAMO,IAAOF,EAAQE,EAAE,QAAUA,EAAE,GAAG,CACxD,KAAO,CACL,IAAMF,EAAQG,GAAcJ,CAAE,EAC9B,OAAIC,IAAU,KAAa,GACpBN,EAAS,KAAMQ,IAAQF,EAAQE,EAAE,QAAU,IAAOA,EAAE,GAAG,CAChE,CACF,CACF,CC1OA,IAAIE,GAAwC,CAAC,EACzCC,GAAoD,KAOjD,SAASC,GAAyBC,EAAgC,CACnEA,GAASA,EAAM,OAAS,IAC1BH,GAAqBG,EACrBF,GAAkBG,GAAkBJ,EAAkB,EAE1D,CAKO,SAASK,IAA2C,CACzD,OAAOL,EACT,CAQO,SAASM,GAAeC,EAAqB,CAClD,OAAON,GAAkBA,GAAgBM,CAAE,EAAI,EACjD,CCxBA,IAAMC,GAAe,IAAI,IACnBC,GAAe,IAAS,IAEjBC,GAAN,KAA0B,CAC/B,YAAoBC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAOpB,OAAO,iBAAiBC,EAAoB,CACtCA,EACFJ,GAAa,OAAOI,CAAG,EAEvBJ,GAAa,MAAM,CAEvB,CAKA,MAAc,aAA6B,CACzC,GAAI,CACF,MAAM,KAAK,GAAG,QACZ,qHACF,EAAE,IAAI,CACR,OAAS,EAAG,CACV,QAAQ,KAAK,gEAAiE,CAAC,CACjF,CACF,CAOA,MAAM,QAA0C,CAC9C,GAAI,CACF,GAAM,CAAE,QAAAK,CAAQ,EAAI,MAAM,KAAK,GAAG,QAAQ,wCAAwC,EAAE,IAAoC,EAClHC,EAAmC,CAAC,EACpCC,EAAY,KAAK,IAAI,EAAIN,GAC/B,QAAWO,KAAOH,EAChBC,EAASE,EAAI,GAAG,EAAIA,EAAI,MACxBR,GAAa,IAAIQ,EAAI,IAAK,CAAE,MAAOA,EAAI,MAAO,UAAAD,CAAU,CAAC,EAE3D,OAAOD,CACT,OAAS,EAAY,CAEnB,IADiB,aAAa,MAAQ,EAAE,QAAU,OAAO,CAAC,GAC7C,SAAS,eAAe,EACnC,aAAM,KAAK,YAAY,EAChB,CAAC,EAEV,MAAM,CACR,CACF,CAQA,MAAM,IAAIF,EAAqC,CAC7C,IAAMK,EAAST,GAAa,IAAII,CAAG,EACnC,GAAIK,GAAUA,EAAO,UAAY,KAAK,IAAI,EACxC,OAAOA,EAAO,MAGhB,GAAI,CACF,IAAMC,EAAM,MAAM,KAAK,GAAG,QAAQ,iDAAiD,EAAE,KAAKN,CAAG,EAAE,MAAyB,EAClHO,EAAMD,EAAMA,EAAI,MAAQ,KAC9B,OAAAV,GAAa,IAAII,EAAK,CAAE,MAAOO,EAAK,UAAW,KAAK,IAAI,EAAIV,EAAa,CAAC,EACnEU,CACT,OAASC,EAAY,CAEnB,IADiBA,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,GAC7C,SAAS,eAAe,EACnC,aAAM,KAAK,YAAY,EAChB,KAET,MAAMA,CACR,CACF,CASA,MAAM,IAAIR,EAAaS,EAAiC,CACtD,IAAMC,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACxC,GAAI,CACF,IAAMC,EAAS,MAAM,KAAK,GAAG,QAC3B,2JACF,EAAE,KAAKX,EAAKS,EAAOC,CAAG,EAAE,IAAI,EAE5B,OAAIC,EAAO,SACTf,GAAa,IAAII,EAAK,CAAE,MAAAS,EAAO,UAAW,KAAK,IAAI,EAAIZ,EAAa,CAAC,EAEhEc,EAAO,OAChB,OAASH,EAAY,CAEnB,IADiBA,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,GAC7C,SAAS,eAAe,EAAG,CACtC,MAAM,KAAK,YAAY,EACvB,IAAMI,EAAc,MAAM,KAAK,GAAG,QAChC,2JACF,EAAE,KAAKZ,EAAKS,EAAOC,CAAG,EAAE,IAAI,EAC5B,OAAIE,EAAY,SACdhB,GAAa,IAAII,EAAK,CAAE,MAAAS,EAAO,UAAW,KAAK,IAAI,EAAIZ,EAAa,CAAC,EAEhEe,EAAY,OACrB,CACA,MAAMJ,CACR,CACF,CAQA,MAAM,QAAQN,EAAoD,CAChE,IAAMQ,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAClCG,EAAQ,OAAO,QAAQX,CAAQ,EAAE,IAAI,CAAC,CAACF,EAAKS,CAAK,IACrD,KAAK,GAAG,QAAQ,2JAA2J,EAAE,KAAKT,EAAK,OAAOS,CAAK,EAAGC,CAAG,CAC3M,EACA,GAAI,CACEG,EAAM,OAAS,GACjB,MAAM,KAAK,GAAG,MAAMA,CAAK,CAE7B,OAASL,EAAY,CAEnB,IADiBA,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,GAC7C,SAAS,eAAe,EACnC,MAAM,KAAK,YAAY,EACnBK,EAAM,OAAS,GACjB,MAAM,KAAK,GAAG,MAAMA,CAAK,MAG3B,OAAML,CAEV,CACA,IAAML,EAAY,KAAK,IAAI,EAAIN,GAC/B,OAAW,CAACG,EAAKS,CAAK,IAAK,OAAO,QAAQP,CAAQ,EAChDN,GAAa,IAAII,EAAK,CAAE,MAAO,OAAOS,CAAK,EAAG,UAAAN,CAAU,CAAC,EAE3D,MAAO,EACT,CACF,ECzJA,IAAIW,GAAyC,KACzCC,GAAoC,KA2CxC,eAAsBC,GAA+BC,EAA+B,CAC7DC,GAAsB,EAC1B,OAAS,IAGtB,CAACC,IAAeF,IAClBE,IAAe,SAAY,CACzB,GAAI,CACF,IAAMC,EAAgB,IAAIC,GAAoBJ,CAAE,EAC1C,CAACK,EAAaC,CAAS,EAAI,MAAM,QAAQ,IAAI,CACjDH,EAAc,IAAI,gBAAgB,EAClCA,EAAc,IAAI,sBAAsB,CAC1C,CAAC,EAMD,GAJIG,IACFC,GAA0BD,GAGxBD,EAAa,CACf,IAAMG,EAAS,KAAK,MAAMH,CAAW,EACrC,GAAI,MAAM,QAAQG,CAAM,GAAKA,EAAO,OAAS,EAAG,CAC9CC,GAAyBD,CAAM,EAC/B,MACF,CACF,CAEA,MAAME,GAAuBV,EAAI,EAAI,CACvC,OAASW,EAAG,CACV,QAAQ,KAAK,qDAAsDA,CAAC,CACtE,QAAE,CACAT,GAAc,IAChB,CACF,GAAG,GAEDA,IACF,MAAMA,GAEV,CA8BA,eAAsBU,GACpBC,EACAC,EAAiB,GAC+C,CAChE,IAAMC,EAAgB,IAAIC,GAAoBH,CAAE,EAC1CI,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAExC,GAAI,CAACH,EAAO,CACV,IAAMI,EAAiB,MAAMH,EAAc,IAAI,2BAA2B,EAC1E,GAAIG,EAAgB,CAClB,IAAMC,EAAc,SAASD,EAAgB,EAAE,EAC/C,GAAI,CAAC,MAAMC,CAAW,GAAKF,EAAME,EAAc,MAC7C,MAAO,CAAE,QAAS,GAAM,QAAS,GAAO,MAAOC,GAAsB,EAAE,MAAO,CAElF,CACF,CAEA,GAAI,CACF,GAAM,CAACC,EAAOC,EAAOC,CAAM,EAAI,MAAM,QAAQ,IAAI,CAC/C,MAAM,oCAAqC,CACzC,QAAS,CAAE,aAAc,cAAe,EACxC,OAAQ,YAAY,QAAQ,GAAI,CAClC,CAAC,EACD,MAAM,oCAAqC,CACzC,QAAS,CAAE,aAAc,cAAe,EACxC,OAAQ,YAAY,QAAQ,GAAI,CAClC,CAAC,EACD,MAAM,0EAA2E,CAC/E,QAAS,CAAE,OAAU,sBAAuB,EAC5C,OAAQ,YAAY,QAAQ,GAAI,CAClC,CAAC,EAAE,MAAM,IAAM,IAAI,CACrB,CAAC,EAED,GAAI,CAACF,EAAM,IAAM,CAACC,EAAM,GACtB,MAAM,IAAI,MAAM,2CAA2CD,EAAM,MAAM,aAAaC,EAAM,MAAM,EAAE,EAGpG,IAAME,EAAS,MAAMH,EAAM,KAAK,EAC1BI,EAAS,MAAMH,EAAM,KAAK,EAM1BI,EAJU,CAAC,GAAGF,EAAO,MAAM;AAAA,CAAI,EAAG,GAAGC,EAAO,MAAM;AAAA,CAAI,CAAC,EAC1D,IAAKE,GAASA,EAAK,KAAK,CAAC,EACzB,OAAQA,GAASA,EAAK,OAAS,GAAK,CAACA,EAAK,WAAW,GAAG,CAAC,EAEjC,OAAQC,GAASC,GAAUD,CAAI,IAAM,IAAI,EAEhEE,EAA4B,KAChC,GAAIP,GAAUA,EAAO,GACnB,GAAI,CACF,IAAMQ,EAAe,MAAMR,EAAO,KAAK,EACvC,GAAIQ,EAAQ,OACV,QAAWC,KAAOD,EAAQ,OAAQ,CAChC,IAAME,EAAQ,OAAOD,EAAI,IAAI,EAAE,MAAM,uBAAuB,EAC5D,GAAIC,EAAO,CACTH,EAAaG,EAAM,CAAC,EACpB,KACF,CACF,CAEJ,OAASC,EAAG,CACV,QAAQ,KAAK,oDAAqDA,CAAC,CACrE,CAGF,GAAIR,EAAW,QAAU,GAAI,CAC3B,IAAMS,EAAkC,CACtC,eAAgB,KAAK,UAAUT,CAAU,EACzC,0BAA2B,OAAOT,CAAG,CACvC,EAEA,OAAIa,IACFK,EAAQ,qBAAuBL,EAC/BM,GAA0BN,GAG5B,MAAMf,EAAc,QAAQoB,CAAO,EACnCE,GAAyBX,CAAU,EACnC,QAAQ,IAAI,gBAAgBA,EAAW,MAAM,8CAA8C,EACpF,CAAE,QAAS,GAAM,QAAS,GAAM,MAAOA,EAAW,MAAO,CAClE,KACE,OAAM,IAAI,MAAM,sCAAsCA,EAAW,MAAM,EAAE,CAE7E,OAASQ,EAAQ,CACf,eAAQ,KAAK,uCAAwCA,EAAE,SAAWA,CAAC,EAC5D,CAAE,QAAS,GAAO,QAAS,GAAO,MAAOd,GAAsB,EAAE,MAAO,CACjF,CACF,CCjMO,SAASkB,GAAYC,EAAcC,EAAyB,CACjE,IAAIC,EAAiB,CAAC,EACtB,GAAIF,IAAS,IACXE,EAAOD,EAAM,MAAM,GAAG,EAAE,IAAKE,GAAM,SAASA,CAAC,GAAK,CAAC,UAC1CH,IAAS,OAAQ,CAC1B,IAAMI,EAAQ,IAAI,WAAW,EAAE,EAAE,KAAK,CAAC,EACvC,GAAIH,EAAM,SAAS,IAAI,EAAG,CACxB,GAAM,CAACI,EAAMC,CAAK,EAAIL,EAAM,MAAM,IAAI,EAChCM,EAAYF,EAAK,MAAM,GAAG,EAAE,OAAQG,GAAMA,CAAC,EAC3CC,EAAaH,EAAM,MAAM,GAAG,EAAE,OAAQE,GAAMA,CAAC,EAE/CE,EAAI,EACR,QAAWC,KAAQJ,EAAW,CAC5B,IAAMJ,EAAI,SAASQ,EAAM,EAAE,EAC3BP,EAAMM,GAAG,EAAKP,GAAK,EAAK,IACxBC,EAAMM,GAAG,EAAIP,EAAI,GACnB,CAEAO,EAAI,GAAKD,EAAW,OAAS,EAC7B,QAAWE,KAAQF,EAAY,CAC7B,IAAMN,EAAI,SAASQ,EAAM,EAAE,EAC3BP,EAAMM,GAAG,EAAKP,GAAK,EAAK,IACxBC,EAAMM,GAAG,EAAIP,EAAI,GACnB,CACF,KAAO,CACL,IAAMS,EAAQX,EAAM,MAAM,GAAG,EACzBS,EAAI,EACR,QAAWC,KAAQC,EAAO,CACxB,IAAMT,EAAI,SAASQ,EAAM,EAAE,EAC3BP,EAAMM,GAAG,EAAKP,GAAK,EAAK,IACxBC,EAAMM,GAAG,EAAIP,EAAI,GACnB,CACF,CACAD,EAAO,MAAM,KAAKE,CAAK,CACzB,SAAWJ,IAAS,QAAS,CAC3B,IAAMa,EAASZ,EAAM,MAAM,GAAG,EAC9B,QAAWa,KAASD,EAAQ,CAC1BX,EAAK,KAAKY,EAAM,MAAM,EACtB,QAASJ,EAAI,EAAGA,EAAII,EAAM,OAAQJ,IAAKR,EAAK,KAAKY,EAAM,WAAWJ,CAAC,CAAC,CACtE,CACAR,EAAK,KAAK,CAAC,CACb,SAAWF,IAAS,MAClB,QAASU,EAAI,EAAGA,EAAIT,EAAM,OAAQS,GAAK,IAAK,CAC1C,IAAMK,EAAQd,EAAM,UAAUS,EAAGA,EAAI,GAAG,EACxCR,EAAK,KAAKa,EAAM,MAAM,EACtB,QAASC,EAAI,EAAGA,EAAID,EAAM,OAAQC,IAChCd,EAAK,KAAKa,EAAM,WAAWC,CAAC,CAAC,CAEjC,MACShB,IAAS,SAAWA,IAAS,UACtCE,EAAOe,GAA2BhB,CAAK,GAEzC,OAAOC,CACT,CCrCO,SAASgB,GAAcC,EAAcC,EAA0B,CACpE,IAAMC,EAAS,IAAI,WAAW,EAAE,EAC1BC,EAAK,KAAK,MAAM,KAAK,OAAO,EAAI,KAAK,EAC3CD,EAAO,CAAC,EAAIC,GAAM,EAClBD,EAAO,CAAC,EAAIC,EAAK,IACjBD,EAAO,CAAC,EAAI,EACZA,EAAO,CAAC,EAAI,EACZA,EAAO,CAAC,EAAI,EACZA,EAAO,CAAC,EAAI,EACZA,EAAO,CAAC,EAAI,EACZA,EAAO,CAAC,EAAI,EACZA,EAAO,CAAC,EAAI,EACZA,EAAO,CAAC,EAAI,EACZA,EAAO,EAAE,EAAI,EACbA,EAAO,EAAE,EAAI,EAEb,IAAME,EAASJ,EAAK,MAAM,GAAG,EACvBK,EAAqB,CAAC,EAC5B,QAAWC,KAASF,EAAQ,CAC1BC,EAAS,KAAKC,EAAM,MAAM,EAC1B,QAASC,EAAI,EAAGA,EAAID,EAAM,OAAQC,IAChCF,EAAS,KAAKC,EAAM,WAAWC,CAAC,CAAC,CAErC,CACAF,EAAS,KAAK,CAAC,EAEf,IAAMG,EAAWC,GAAiBR,CAAI,GAAK,EAC3CI,EAAS,KAAKG,GAAY,CAAC,EAC3BH,EAAS,KAAKG,EAAW,GAAI,EAC7BH,EAAS,KAAK,CAAI,EAClBA,EAAS,KAAK,CAAI,EAElB,IAAMK,EAAM,IAAI,WAAWR,EAAO,OAASG,EAAS,MAAM,EAC1D,OAAAK,EAAI,IAAIR,CAAM,EACdQ,EAAI,IAAI,IAAI,WAAWL,CAAQ,EAAGH,EAAO,MAAM,EACxCQ,CACT,CAQO,SAASC,GAAeC,EAAmC,CAChE,IAAMC,EAAM,IAAI,WAAW,EAAE,EAC7B,OAAID,GAAYA,EAAS,QAAU,IACjCC,EAAI,IAAID,EAAS,MAAM,EAAG,EAAE,CAAC,EAE/BC,EAAI,CAAC,EAAKA,EAAI,CAAC,EAAI,EAAQ,IAC3BA,EAAI,CAAC,EAAKA,EAAI,CAAC,EAAI,IAAQ,EACpBA,CACT,CAUO,SAASC,GACdF,EACAG,EACAC,EAAgB,EACJ,CACZ,GAAI,CACF,GAAI,CAACJ,GAAYA,EAAS,OAAS,GACjC,OAAOD,GAAeC,CAAQ,EAGhC,IAAMV,EAAS,IAAI,WAAW,EAAE,EAChCA,EAAO,IAAIU,EAAS,MAAM,EAAG,EAAE,CAAC,EAChCV,EAAO,CAAC,EAAKA,EAAO,CAAC,EAAI,EAAQ,IACjCA,EAAO,CAAC,EAAI,IAAQc,EAAQ,GAE5B,IAAIC,EAAO,GACLC,EAAUN,EAAS,CAAC,GAAK,EAAKA,EAAS,CAAC,EAC9C,QAASL,EAAI,EAAGA,EAAIW,EAAQX,IAAK,CAC/B,GAAM,CAAE,KAAAY,CAAK,EAAIC,GAAWR,EAAUK,CAAI,EAK1C,GAJIE,IAAS,IAGbF,GAAQE,EAAO,EACXF,EAAOL,EAAS,QAClB,OAAOD,GAAeC,CAAQ,CAElC,CACA,IAAMS,EAAkBT,EAAS,MAAM,GAAIK,CAAI,EAW/C,GATAf,EAAO,CAAC,EAAKgB,GAAU,EAAK,IAC5BhB,EAAO,CAAC,EAAIgB,EAAS,IACrBhB,EAAO,CAAC,EAAKa,EAAQ,QAAU,EAAK,IACpCb,EAAO,CAAC,EAAIa,EAAQ,OAAS,IAC7Bb,EAAO,CAAC,EAAI,EACZA,EAAO,CAAC,EAAI,EACZA,EAAO,EAAE,EAAI,EACbA,EAAO,EAAE,EAAI,EAETa,EAAQ,SAAW,EAAG,CACxB,IAAMF,EAAM,IAAI,WAAW,GAAKQ,EAAgB,MAAM,EACtD,OAAAR,EAAI,IAAIX,CAAM,EACdW,EAAI,IAAIQ,EAAiB,EAAE,EACpBR,CACT,CAEA,IAAMS,EAA0B,CAAC,EAC7BC,EAAc,GAAKF,EAAgB,OAEvC,QAAWG,KAAUT,EAAS,CAC5B,GAAM,CAAE,KAAAf,EAAM,KAAAC,EAAM,MAAAwB,EAAO,IAAAC,EAAM,EAAG,EAAIF,EAEpCG,EAAsB,CAAC,EAC3B,GAAI,CAAC3B,EACH2B,EAAY,CAAC,IAAM,EAAI,MAClB,CACL,IAAMvB,EAASJ,EAAK,MAAM,GAAG,EAC7B,QAAWM,KAASF,EAAQ,CAC1BuB,EAAU,KAAKrB,EAAM,MAAM,EAC3B,QAASC,EAAI,EAAGA,EAAID,EAAM,OAAQC,IAChCoB,EAAU,KAAKrB,EAAM,WAAWC,CAAC,CAAC,CACtC,CACAoB,EAAU,KAAK,CAAC,CAClB,CAEA,IAAMC,EAAOC,GAAY5B,EAAMwB,CAAK,EAC9BK,EAAWF,EAAK,OAEhBG,EAAK,IAAI,WAAWJ,EAAU,OAAS,GAAKG,CAAQ,EAC1DC,EAAG,IAAIJ,EAAW,CAAC,EACnB,IAAIK,EAASL,EAAU,OAEjBM,EAAQxB,GAAiBR,CAAI,GAAK,EACxC8B,EAAGC,GAAQ,EAAIC,GAAS,EACxBF,EAAGC,GAAQ,EAAIC,EAAQ,IACvBF,EAAGC,GAAQ,EAAI,EACfD,EAAGC,GAAQ,EAAI,EACfD,EAAGC,GAAQ,EAAKN,GAAO,GAAM,IAC7BK,EAAGC,GAAQ,EAAKN,GAAO,GAAM,IAC7BK,EAAGC,GAAQ,EAAKN,GAAO,EAAK,IAC5BK,EAAGC,GAAQ,EAAIN,EAAM,IACrBK,EAAGC,GAAQ,EAAKF,GAAY,EAAK,IACjCC,EAAGC,GAAQ,EAAIF,EAAW,IAC1BC,EAAG,IAAIH,EAAMI,CAAM,EAEnBV,EAAU,KAAKS,CAAE,EACjBR,GAAeQ,EAAG,MACpB,CAEA,IAAMlB,EAAM,IAAI,WAAWU,CAAW,EACtCV,EAAI,IAAIX,EAAQ,CAAC,EACjBW,EAAI,IAAIQ,EAAiB,EAAE,EAE3B,IAAIa,EAAgB,GAAKb,EAAgB,OACzC,QAAWU,KAAMT,EACfT,EAAI,IAAIkB,EAAIG,CAAa,EACzBA,GAAiBH,EAAG,OAGtB,OAAOlB,CACT,OAASsB,EAAG,CACV,QAAQ,MAAM,wCAAyCA,CAAC,EACxD,IAAMC,EAAW,IAAI,WAAW,EAAE,EAClC,OAAAA,EAAS,IAAIxB,EAAS,MAAM,EAAG,EAAE,CAAC,EAClCwB,EAAS,CAAC,GAAK,IACfA,EAAS,CAAC,EAAKA,EAAS,CAAC,EAAI,IAAQ,EAC9BA,CACT,CACF,CAYO,SAASC,GACdzB,EACAX,EACAwB,EACAC,EAAc,GACdV,EAAgB,EACJ,CACZ,GAAI,CACF,GAAI,CAACJ,GAAYA,EAAS,OAAS,GACjC,OAAOD,GAAeC,CAAQ,EAGhC,IAAMV,EAAS,IAAI,WAAW,EAAE,EAChCA,EAAO,IAAIU,EAAS,MAAM,EAAG,EAAE,CAAC,EAChCV,EAAO,CAAC,EAAKA,EAAO,CAAC,EAAI,EAAQ,IACjCA,EAAO,CAAC,EAAI,IAAQc,EAAQ,GAE5B,IAAIC,EAAO,GACLC,EAAUN,EAAS,CAAC,GAAK,EAAKA,EAAS,CAAC,EAC9C,QAASL,EAAI,EAAGA,EAAIW,EAAQX,IAAK,CAC/B,GAAM,CAAE,KAAAY,CAAK,EAAIC,GAAWR,EAAUK,CAAI,EAK1C,GAJIE,IAAS,IAGbF,GAAQE,EAAO,EACXF,EAAOL,EAAS,QAClB,OAAOD,GAAeC,CAAQ,CAElC,CACA,IAAMS,EAAkBT,EAAS,MAAM,GAAIK,CAAI,EAW/C,GATAf,EAAO,CAAC,EAAKgB,GAAU,EAAK,IAC5BhB,EAAO,CAAC,EAAIgB,EAAS,IACrBhB,EAAO,CAAC,EAAI,EACZA,EAAO,CAAC,EAAIuB,EAAQ,EAAI,EACxBvB,EAAO,CAAC,EAAI,EACZA,EAAO,CAAC,EAAI,EACZA,EAAO,EAAE,EAAI,EACbA,EAAO,EAAE,EAAI,EAET,CAACuB,EAAO,CACV,IAAMZ,EAAM,IAAI,WAAW,GAAKQ,EAAgB,MAAM,EACtD,OAAAR,EAAI,IAAIX,CAAM,EACdW,EAAI,IAAIQ,EAAiB,EAAE,EACpBR,CACT,CAEA,IAAMe,EAAOC,GAAY5B,EAAMwB,CAAK,EAC9BK,EAAWF,EAAK,OAEhBU,EAAW,IAAI,WAAW,GAAKR,CAAQ,EAC7CQ,EAAS,CAAC,EAAI,IACdA,EAAS,CAAC,EAAI,GACd,IAAML,EAAQxB,GAAiBR,CAAI,GAAK,EACxCqC,EAAS,CAAC,EAAIL,GAAS,EACvBK,EAAS,CAAC,EAAIL,EAAQ,IACtBK,EAAS,CAAC,EAAI,EACdA,EAAS,CAAC,EAAI,EACdA,EAAS,CAAC,EAAKZ,GAAO,GAAM,IAC5BY,EAAS,CAAC,EAAKZ,GAAO,GAAM,IAC5BY,EAAS,CAAC,EAAKZ,GAAO,EAAK,IAC3BY,EAAS,CAAC,EAAIZ,EAAM,IACpBY,EAAS,EAAE,EAAKR,GAAY,EAAK,IACjCQ,EAAS,EAAE,EAAIR,EAAW,IAC1BQ,EAAS,IAAIV,EAAM,EAAE,EAErB,IAAMf,EAAM,IAAI,WACd,GAAKQ,EAAgB,OAASiB,EAAS,MACzC,EACA,OAAAzB,EAAI,IAAIX,CAAM,EACdW,EAAI,IAAIQ,EAAiB,EAAE,EAC3BR,EAAI,IAAIyB,EAAU,GAAKjB,EAAgB,MAAM,EACtCR,CACT,OAASsB,EAAG,CACV,QAAQ,MAAM,mCAAoCA,CAAC,EACnD,IAAMC,EAAW,IAAI,WAAW,EAAE,EAClC,OAAAA,EAAS,IAAIxB,EAAS,MAAM,EAAG,EAAE,CAAC,EAClCwB,EAAS,CAAC,GAAK,IACfA,EAAS,CAAC,EAAKA,EAAS,CAAC,EAAI,IAAQ,EAC9BA,CACT,CACF,CC1PA,SAASG,GAAUC,EAAkF,CACnG,IAAMC,EAAWD,EAAK,YAAY,GAAG,EACrC,GAAIC,IAAa,GAAI,OAAO,KAE5B,IAAMC,EAAOF,EAAK,MAAM,EAAGC,CAAQ,EAC7BE,EAAY,SAASH,EAAK,MAAMC,EAAW,CAAC,EAAG,EAAE,EACvD,GAAI,MAAME,CAAS,EAAG,OAAO,KAE7B,GAAID,EAAK,SAAS,GAAG,EAAG,CAEtB,IAAME,EAAQC,GAAYH,CAAI,EAE9B,GADI,CAACE,GACDD,EAAY,GAAKA,EAAY,IAAK,OAAO,KAC7C,IAAMG,EAAYC,GAAUH,EAAOD,CAAS,EAC5C,MAAO,CAAE,OAAQ,EAAG,UAAAA,EAAW,UAAAG,CAAU,CAC3C,KAAO,CAEL,IAAME,EAAQN,EAAK,MAAM,GAAG,EAAE,IAAI,MAAM,EAExC,GADIM,EAAM,SAAW,GAAKA,EAAM,KAAKC,GAAK,MAAMA,CAAC,GAAKA,EAAI,GAAKA,EAAI,GAAG,GAClEN,EAAY,GAAKA,EAAY,GAAI,OAAO,KAC5C,IAAMC,EAAQ,IAAI,WAAWI,CAAK,EAC5BF,EAAYC,GAAUH,EAAOD,CAAS,EAC5C,MAAO,CAAE,OAAQ,EAAG,UAAAA,EAAW,UAAAG,CAAU,CAC3C,CACF,CAGA,SAASC,GAAUH,EAAmBD,EAA+B,CACnE,IAAMO,EAAU,KAAK,KAAKP,EAAY,CAAC,EACjCQ,EAAS,IAAI,WAAWD,CAAO,EACrC,QAASE,EAAI,EAAGA,EAAIF,EAASE,IAC3BD,EAAOC,CAAC,EAAIR,EAAMQ,CAAC,GAAK,EAG1B,IAAMC,EAAeV,EAAY,EACjC,OAAIU,IAAiB,GAAKH,EAAU,IAClCC,EAAOD,EAAU,CAAC,GAAM,KAAS,EAAIG,EAAiB,KAEjDF,CACT,CAGA,SAASN,GAAYH,EAAiC,CACpD,GAAI,CAEF,IAAMM,EAAQN,EAAK,MAAM,IAAI,EAC7B,GAAIM,EAAM,OAAS,EAAG,OAAO,KAE7B,IAAMM,EAAON,EAAM,CAAC,EAAIA,EAAM,CAAC,EAAE,MAAM,GAAG,EAAI,CAAC,EACzCO,EAAQP,EAAM,CAAC,EAAIA,EAAM,CAAC,EAAE,MAAM,GAAG,EAAI,CAAC,EAC1CQ,EAAU,EAAIF,EAAK,OAASC,EAAM,OAClCE,EAAS,CAAC,GAAGH,EAAM,GAAG,MAAME,CAAO,EAAE,KAAK,GAAG,EAAG,GAAGD,CAAK,EAE9D,GAAIE,EAAO,SAAW,EAAG,OAAO,KAEhC,IAAMb,EAAQ,IAAI,WAAW,EAAE,EAC/B,QAASQ,EAAI,EAAGA,EAAI,EAAGA,IAAK,CAC1B,IAAMM,EAAM,SAASD,EAAOL,CAAC,EAAG,EAAE,EAClC,GAAI,MAAMM,CAAG,EAAG,OAAO,KACvBd,EAAMQ,EAAI,CAAC,EAASM,GAAO,EAAK,IAChCd,EAAMQ,EAAI,EAAI,CAAC,EAAIM,EAAM,GAC3B,CACA,OAAOd,CACT,MAAQ,CACN,OAAO,IACT,CACF,CAQA,SAASe,GAAcnB,EAAiC,CACtD,IAAMoB,EAASrB,GAAUC,CAAI,EAC7B,GAAI,CAACoB,EAAQ,OAAO,KAEpB,GAAM,CAAE,OAAAC,EAAQ,UAAAlB,EAAW,UAAAG,CAAU,EAAIc,EAGnCE,EAAa,EAAIhB,EAAU,OAC3BiB,EAAY,IAAI,WAAW,EAAID,CAAU,EAC3CE,EAAI,EAERD,EAAUC,GAAG,EAAI,EACjBD,EAAUC,GAAG,EAAI,EAEjBD,EAAUC,GAAG,EAAKF,GAAc,EAAK,IACrCC,EAAUC,GAAG,EAAIF,EAAa,IAE9BC,EAAUC,GAAG,EAAKH,GAAU,EAAK,IACjCE,EAAUC,GAAG,EAAIH,EAAS,IAE1BE,EAAUC,GAAG,EAAIrB,EAEjBoB,EAAUC,GAAG,EAAI,EAEjBD,EAAU,IAAIjB,EAAWkB,CAAC,EAG1B,IAAMC,EAAQF,EAAU,OAClBG,EAAQ,IAAI,WAAW,GAAKD,CAAK,EACnChB,EAAI,EACR,OAAAiB,EAAMjB,GAAG,EAAI,EACbiB,EAAMjB,GAAG,EAAI,EACbiB,EAAMjB,GAAG,EAAI,GACbiB,EAAMjB,GAAG,EAAI,GACbiB,EAAMjB,GAAG,EAAI,EACbiB,EAAMjB,GAAG,EAAI,EACbiB,EAAMjB,GAAG,EAAI,EACbiB,EAAMjB,GAAG,EAAI,EACbiB,EAAMjB,GAAG,EAAI,EACbiB,EAAMjB,GAAG,EAAKgB,GAAS,EAAK,IAC5BC,EAAMjB,GAAG,EAAIgB,EAAQ,IACrBC,EAAM,IAAIH,EAAWd,CAAC,EAEfiB,CACT,CAcO,SAASC,GAAmBC,EAAiB5B,EAA0B,CAC5E,IAAM0B,EAAQP,GAAcnB,CAAI,EAChC,GAAI,CAAC0B,EACH,eAAQ,KAAK,uBAAuB1B,CAAI,wBAAwB,EACzD4B,EAGT,GAAIA,EAAI,OAAS,GAAI,OAAOA,EAG5B,IAAMC,EAAWD,EAAI,EAAE,GAAK,EAAKA,EAAI,EAAE,EAGnCE,EAAS,GAGPC,EAAWH,EAAI,CAAC,GAAK,EAAKA,EAAI,CAAC,EACrC,QAAShB,EAAI,EAAGA,EAAImB,GAAWD,EAASF,EAAI,OAAQhB,IAAK,CAEvD,KAAOkB,EAASF,EAAI,QAAQ,CAC1B,IAAMI,EAAMJ,EAAIE,CAAM,EACtB,GAAIE,IAAQ,EAAG,CAAEF,IAAU,KAAO,CAClC,IAAKE,EAAM,OAAU,IAAM,CAAEF,GAAU,EAAG,KAAO,CACjDA,GAAU,EAAIE,CAChB,CACAF,GAAU,CACZ,CAGA,IAAMG,GAAaL,EAAI,CAAC,GAAK,EAAKA,EAAI,CAAC,IAAOA,EAAI,CAAC,GAAK,EAAKA,EAAI,CAAC,GAClE,QAAShB,EAAI,EAAGA,EAAIqB,GAAYH,EAASF,EAAI,OAAQhB,IACnDkB,EAASI,GAAON,EAAKE,CAAM,EAI7B,IAAMK,EAAkBL,EAGpBM,EAAmB,GACnBC,EAAiB,GACjBC,EAAMH,EACV,QAASvB,EAAI,EAAGA,EAAIiB,GAAWS,EAAMV,EAAI,OAAQhB,IAAK,CACpD,IAAM2B,EAAUD,EAEhB,GAAIA,EAAMV,EAAI,QAAUA,EAAIU,CAAG,IAAM,IACpBA,EAAM,EAAIV,EAAI,OAAWA,EAAIU,EAAM,CAAC,GAAK,EAAKV,EAAIU,EAAM,CAAC,EAAK,MAC9D,GAAU,CACvBF,EAAmBG,EACnBD,EAAMJ,GAAON,EAAKU,CAAG,EACrBD,EAAiBC,EACjB,KACF,CAEFA,EAAMJ,GAAON,EAAKU,CAAG,CACvB,CAGA,IAAIE,EAEJ,GAAIJ,IAAqB,GAAI,CAE3B,IAAMK,EAASb,EAAI,MAAM,EAAGQ,CAAgB,EACtCM,EAASd,EAAI,MAAMS,CAAc,EACvCG,EAAS,IAAI,WAAWC,EAAO,OAASf,EAAM,OAASgB,EAAM,MAAM,EACnEF,EAAO,IAAIC,EAAQ,CAAC,EACpBD,EAAO,IAAId,EAAOe,EAAO,MAAM,EAC/BD,EAAO,IAAIE,EAAOD,EAAO,OAASf,EAAM,MAAM,CAEhD,KAAO,CAELc,EAAS,IAAI,WAAWZ,EAAI,OAASF,EAAM,MAAM,EACjDc,EAAO,IAAIZ,EAAK,CAAC,EACjBY,EAAO,IAAId,EAAOE,EAAI,MAAM,EAE5B,IAAMe,EAAad,EAAU,EAC7BW,EAAO,EAAE,EAAKG,GAAc,EAAK,IACjCH,EAAO,EAAE,EAAIG,EAAa,GAC5B,CAEA,OAAOH,CACT,CAGA,SAASN,GAAON,EAAiBE,EAAwB,CACvD,GAAIA,GAAUF,EAAI,OAAQ,OAAOE,EAGjC,KAAOA,EAASF,EAAI,QAAQ,CAC1B,IAAMI,EAAMJ,EAAIE,CAAM,EACtB,GAAIE,IAAQ,EAAG,CAAEF,IAAU,KAAO,CAClC,IAAKE,EAAM,OAAU,IAAM,CAAEF,GAAU,EAAG,KAAO,CACjDA,GAAU,EAAIE,CAChB,CAEA,GAAIF,EAAS,GAAKF,EAAI,OAAQ,OAAOA,EAAI,OAGzC,IAAMgB,EAAYhB,EAAIE,EAAS,CAAC,GAAK,EAAKF,EAAIE,EAAS,CAAC,EACxD,OAAAA,GAAU,GAAKc,EAER,KAAK,IAAId,EAAQF,EAAI,MAAM,CACpC,CC5QAiB,KAYA,IAAMC,GAAN,cAAkC,GAAU,CAC1C,YAAoBC,EAAoB,CACtC,MAAM,EADY,gBAAAA,CAEpB,CAFoB,WAIX,IAAIC,EAAQC,EAAgB,CAEnC,GAAI,CAAC,KAAK,IAAID,CAAG,GAAK,KAAK,MAAQ,KAAK,WAAY,CAClD,IAAME,EAAY,KAAK,KAAK,EAAE,KAAK,EAAE,MACjCA,IAAc,QAChB,KAAK,OAAOA,CAAS,CAEzB,CAEA,YAAK,OAAOF,CAAG,EACf,MAAM,IAAIA,EAAKC,CAAK,EACb,IACT,CACF,EAIaE,GAAiB,IAAIL,GAA0D,CAAC,EAGhFM,GAAc,IAAIN,GAAwC,EAAE,EAG5DO,GAAW,IAAIP,GAA2B,GAAG,EAG7CQ,GAAsB,IAAIR,GAAiD,GAAG,EAO3F,eAAeS,GAAUC,EAAUC,EAA+B,CAChE,GAAI,CACF,IAAMC,EAAM,MAAM,MAAM,8CAA8CF,EAAI,UAAU,eAAgB,CAClG,OAAQ,OACR,QAAS,CACP,cAAe,UAAUA,EAAI,cAAc,GAC3C,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAE,KAAAC,CAAK,CAAC,CAC/B,CAAC,EACD,GAAI,CAACC,EAAI,GAAI,CACX,IAAMC,EAAY,MAAMD,EAAI,KAAK,EACjC,QAAQ,MAAM,2CAA2CA,EAAI,MAAM,KAAMC,CAAS,CACpF,CACF,OAASC,EAAQ,CACf,QAAQ,MAAM,oCAAqCA,GAAG,SAAWA,CAAC,CACpE,CACF,CAEO,IAAMC,GAAgB,CAY3B,MAAM,MACJC,EACAC,EAAiC,GACjCC,EACe,CACf,IAAIC,EAAa,GACbC,EAEA,OAAOH,GAAoB,WAC7BE,EAAaF,EACTC,GAAmB,OAAOA,GAAoB,WAChDE,EAAYF,IAELD,GAAmB,OAAOA,GAAoB,WACvDG,EAAYH,EACR,OAAOC,GAAoB,YAC7BC,EAAaD,IAKjBZ,GAAY,OAAOU,CAAS,EACxBG,GACFd,GAAe,OAAOW,CAAS,EAEjCR,GAAoB,MAAM,EAC1B,QAAWN,KAAOK,GAAS,KAAK,EAC1BL,EAAI,WAAW,GAAGc,CAAS,GAAG,GAChCT,GAAS,OAAOL,CAAG,EAKvB,GAAI,CACF,GAAI,OAAO,OAAW,KAAe,QAAW,OAAe,QAAS,CACtE,IAAMmB,EAAS,OAAe,QACxBC,EAAwB,CAC5BC,EAAW,OAAOF,EAAO,cAAcL,CAAS,EAAE,CACpD,EACIG,GACFG,EAAM,KAAKD,EAAM,OAAO,gCAAgCL,CAAS,EAAE,CAAC,EAEtE,MAAM,QAAQ,IAAIM,CAAK,CACzB,CACF,OAASR,EAAG,CACV,QAAQ,MAAM,6BAA8BA,CAAC,CAC/C,CAGA,GAAIM,GAAaA,EAAU,YAAcA,EAAU,eAAgB,CACjE,IAAMT,EAAO,CAAC,WAAWK,CAAS,EAAE,EAChCG,GACFR,EAAK,KAAK,SAASK,CAAS,EAAE,EAEhC,MAAMP,GAAUW,EAAWT,CAAI,CACjC,CACF,CACF,EC1IAa,KAOO,IAAMC,EAAN,KAAmB,CACxB,YAAoBC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAGpB,MAAM,QAAQC,EAA8C,CAC1D,OAAO,MAAM,KAAK,GAAG,QAAQ,qCAAqC,EAC/D,KAAKA,CAAE,EACP,MAAwB,CAC7B,CAEA,MAAM,UAAUC,EAAiH,CAC/H,GAAI,CAEF,IAAMC,EAAU,MAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,OAKrC,EACE,KAAKD,CAAU,EACf,MAAmF,EAEtF,OAAIC,GAKiB,MAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,OAI1C,EACE,KAAKD,EAAYA,CAAU,EAC3B,MAAmF,GAE/D,IACzB,OAASE,EAAQ,CACf,eAAQ,KAAK,4CAA6CA,EAAE,SAAWA,CAAC,EACjE,IACT,CACF,CAEA,MAAM,KAAKC,EAAmBC,EAAmC,CAC/D,GAAM,CAAE,QAAAC,CAAQ,EAAI,MAAM,KAAK,GAAG,QAAQ,0BAA0BF,CAAS,EAAE,EAC5E,KAAK,GAAGC,CAAM,EAAE,IAAa,EAChC,OAAOC,CACT,CAEA,MAAM,YAAYC,EAAqC,CACrD,GAAM,CAAE,QAAAD,CAAQ,EAAI,MAAM,KAAK,GAAG,QAAQ,oEAAoE,EAC3G,KAAKC,CAAO,EAAE,IAAa,EAC9B,OAAOD,CACT,CAEA,MAAM,WAAWC,EAAiBC,EAAuC,CACvE,OAAO,MAAM,KAAK,GAAG,QAAQ,wDAAwD,EAClF,KAAKD,EAASC,CAAI,EAAE,MAAsB,CAC/C,CAEA,MAAM,OAAOC,EAA4H,CACvI,IAAMC,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAClCC,EAAOC,EAAW,EAAE,EACpBC,EAAc,EAEpB,QAASC,EAAU,EAAGA,EAAUD,EAAaC,IAAW,CACtD,IAAMC,EAAUC,GAAqB,CAAC,EAChCf,EAAaQ,EAAQ,aAAeM,EAEpCE,EAAa,CACjB,KAAK,GAAG,QACN,uHACF,EAAE,KAAKR,EAAQ,GAAIR,EAAYQ,EAAQ,SAAUA,EAAQ,KAAM,KAAK,UAAUA,EAAQ,QAAQ,EAAGC,EAAKA,CAAG,EACzG,KAAK,GAAG,QACN,2GACF,EAAE,KAAKC,EAAMF,EAAQ,GAAI,WAAYM,EAASL,EAAKA,CAAG,CACxD,EAEA,GAAI,CAEF,OADgB,MAAM,KAAK,GAAG,MAAMO,CAAU,GAC/B,MAAMC,GAAKA,EAAE,OAAO,CACrC,OAASC,EAAU,CACjB,GAAIL,EAAUD,EAAc,IAAM,OAAOM,CAAG,EAAE,SAAS,0BAA0B,GAAK,OAAOA,CAAG,EAAE,SAAS,mBAAmB,GAC5H,SAEF,MAAMA,CACR,CACF,CACA,MAAO,EACT,CAEA,MAAM,OAAOnB,EAA8B,CAKzC,OAJgB,MAAM,KAAK,GAAG,MAAM,CAClC,KAAK,GAAG,QAAQ,uCAAuC,EAAE,KAAKA,CAAE,EAChE,KAAK,GAAG,QAAQ,mCAAmC,EAAE,KAAKA,CAAE,CAC9D,CAAC,GACc,MAAM,GAAK,EAAE,OAAO,CACrC,CAEA,MAAM,cAAcO,EAAmC,CAKrD,OAJgB,MAAM,KAAK,GAAG,MAAM,CAClC,KAAK,GAAG,QAAQ,mFAAmF,EAAE,KAAKA,CAAO,EACjH,KAAK,GAAG,QAAQ,yCAAyC,EAAE,KAAKA,CAAO,CACzE,CAAC,GACc,MAAM,GAAK,EAAE,OAAO,CACrC,CAEA,MAAM,WAAWP,EAAYQ,EAAgC,CAC3D,IAAME,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAGxC,OAFe,MAAM,KAAK,GAAG,QAAQ,2DAA2D,EAC7F,KAAKF,EAAME,EAAKV,CAAE,EAAE,IAAI,GACb,OAChB,CAEA,MAAM,eAAeA,EAAYoB,EAA6C,CAC5E,IAAMV,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAMxC,OALe,MAAM,KAAK,GAAG,QAC3B,+DACF,EACG,KAAK,KAAK,UAAUU,CAAQ,EAAGV,EAAKV,CAAE,EACtC,IAAI,GACO,OAChB,CAEA,MAAM,UAAUA,EAAYqB,EAAkC,CAC5D,IAAMX,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAGxC,OAFe,MAAM,KAAK,GAAG,QAAQ,kEAAkE,EACpG,KAAKW,EAAQX,EAAKV,CAAE,EAAE,IAAI,GACf,OAChB,CAEA,MAAM,iBAAiBA,EAAYU,EAA+B,CAEhE,OADe,MAAM,KAAK,GAAG,QAAQ,qDAAqD,EAAE,KAAKA,EAAKV,CAAE,EAAE,IAAI,GAChG,OAChB,CAEA,MAAM,eAAesB,EAAmBC,EAAwC,CAC9E,GAAM,CAAE,QAAAjB,CAAQ,EAAI,MAAM,KAAK,GAAG,QAChC,4LACF,EAAE,KAAKgB,EAAWC,CAAK,EAAE,IAAoB,EAC7C,OAAOjB,CACT,CAEA,MAAM,oBAAoBkB,EAAmBd,EAA+B,CAE1E,OADe,MAAM,KAAK,GAAG,QAAQ,sDAAsD,EAAE,KAAKA,EAAKc,CAAS,EAAE,IAAI,GACxG,OAChB,CACF,ECxJO,IAAMC,GAAN,KAAgB,CACrB,YAAoBC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAEpB,MAAM,SAASC,EAAoC,CACjD,GAAM,CAAE,QAAAC,CAAQ,EAAI,MAAM,KAAK,GAAG,QAAQ,2DAA2D,EAClG,KAAKD,CAAS,EACd,IAAU,EACb,OAAOC,CACT,CAEA,MAAM,QAAQD,EAAmBE,EAAuC,CACtE,IAAMC,EAAoBD,EAAK,QAAUA,EAAK,QAAQ,KAAK,EAAE,YAAY,EAAI,GACvEE,EAAYF,EAAK,YAAc,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAMjE,OALe,MAAM,KAAK,GAAG,QAC3B,wHACF,EACG,KAAKF,EAAWE,EAAK,KAAMC,EAAmBD,EAAK,KAAO,KAAMA,EAAK,QAAU,KAAMA,EAAK,OAAS,KAAMA,EAAK,SAAW,KAAME,CAAS,EACxI,IAAI,GACO,OAChB,CAUA,MAAM,aAAaJ,EAAmBK,EAAyC,CAC7E,GAAI,CAACA,GAASA,EAAM,SAAW,EAAG,MAAO,GACzC,IAAMC,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAClCC,EAAaF,EAAM,IAAIH,GAAQ,CACnC,IAAMC,EAAoBD,EAAK,QAAUA,EAAK,QAAQ,KAAK,EAAE,YAAY,EAAI,GACvEE,EAAYF,EAAK,YAAcI,EACrC,OAAO,KAAK,GAAG,QACb,wHACF,EAAE,KACAN,EACAE,EAAK,KACLC,EACAD,EAAK,KAAO,KACZA,EAAK,QAAU,KACfA,EAAK,OAAS,KACdA,EAAK,SAAW,KAChBE,CACF,CACF,CAAC,EAEGI,EAAW,EACf,QAAS,EAAI,EAAG,EAAID,EAAW,OAAQ,GAAK,IAAK,CAC/C,IAAME,EAAQF,EAAW,MAAM,EAAG,EAAI,GAAG,EACnCN,EAAU,MAAM,KAAK,GAAG,MAAMQ,CAAK,EACzC,QAAWC,KAAOT,EACZS,EAAI,UAASF,GAAYE,EAAI,KAAK,SAAW,EAErD,CACA,OAAOF,CACT,CAEA,MAAM,WAAWG,EAAYX,EAAmBE,EAAuC,CACrF,IAAMC,EAAoBD,EAAK,QAAUA,EAAK,QAAQ,KAAK,EAAE,YAAY,EAAI,GAM7E,OALe,MAAM,KAAK,GAAG,QAC3B,qHACF,EACG,KAAKA,EAAK,KAAMC,EAAmBD,EAAK,KAAO,KAAMA,EAAK,QAAU,KAAMA,EAAK,OAAS,KAAMA,EAAK,SAAW,KAAMS,EAAIX,CAAS,EACjI,IAAI,GACO,OAChB,CAEA,MAAM,iBAAiBA,EAAmBY,EAAuC,CAC/E,OAAO,MAAM,KAAK,GAAG,QAAQ,iEAAiE,EAC3F,KAAKZ,EAAWY,EAAQ,KAAK,EAAE,YAAY,CAAC,EAC5C,MAAmB,CACxB,CAEA,MAAM,0BAA0BZ,EAAmBY,EAAiBD,EAAkC,CACpG,OAAO,MAAM,KAAK,GAAG,QAAQ,6EAA6E,EACvG,KAAKX,EAAWY,EAAQ,KAAK,EAAE,YAAY,EAAGD,CAAE,EAChD,MAAmB,CACxB,CAEA,MAAM,WAAWA,EAAYX,EAAqC,CAEhE,OADe,MAAM,KAAK,GAAG,QAAQ,mDAAmD,EAAE,KAAKW,EAAIX,CAAS,EAAE,IAAI,GACpG,OAChB,CACF,ECnEO,IAAMa,GAAN,MAAMC,CAAY,CACN,KACA,OACA,SAEjB,OAAwB,UAAY,SACpC,OAAwB,WAAa,WAGrC,OAAwB,QAAU,IAAI,YAQtC,YAAYC,EAAcC,EAAgBC,EAAwB,CAChE,KAAK,KAAOF,IAAS,EACrB,KAAK,OAASC,IAAW,EAEzB,IAAME,EAAU,KAAK,KAAO,KAAQ,EACpC,KAAK,SAAWD,GAAY,IAAI,YAAYC,CAAK,CACnD,CAOA,OAAO,OAAOC,EAAuBC,EAAoB,KAAqB,CAC5E,IAAMC,EAAI,KAAK,IAAIF,EAAe,GAAG,EAI/BG,EAAI,KAAK,KAAK,EAAED,EAAI,KAAK,IAHrBD,CAG0B,GAAM,KAAK,IAAI,CAAC,GAAK,CAAE,EAErDG,EAAI,KAAK,MAAOD,EAAID,EAAK,KAAK,IAAI,CAAC,CAAC,EAE1C,OAAO,IAAIP,EAAYQ,EAAGC,CAAC,CAC7B,CAMA,IAAIC,EAAuB,CACzB,IAAIC,EAAKX,EAAY,WACfY,EAAMF,EAAQ,OACpB,QAASG,EAAI,EAAGA,EAAID,EAAKC,IACvBF,GAAMD,EAAQ,WAAWG,CAAC,EAAI,IAC9BF,EAAK,KAAK,KAAKA,EAAIX,EAAY,SAAS,EAE1CW,EAAKA,IAAO,EAGZ,IAAIG,EAAKH,EAAMA,IAAO,GACtBG,EAAK,KAAK,KAAKA,EAAI,UAAU,EAC7BA,EAAKA,EAAMA,IAAO,GAClBA,EAAK,KAAK,KAAKA,EAAI,UAAU,EAC7BA,GAAMA,EAAMA,IAAO,MAAS,EAE5B,IAAMC,EAAM,KAAK,SACXd,EAAO,KAAK,KACZQ,EAAI,KAAK,OAEf,QAASI,EAAI,EAAGA,EAAIJ,EAAGI,IAAK,CAE1B,IAAMG,GAAOL,EAAKE,EAAIC,GAAMb,EAE5Bc,EAAIC,IAAQ,CAAC,GAAM,IAAMA,EAAM,GACjC,CACF,CAMA,KAAKN,EAA0B,CAC7B,IAAIC,EAAKX,EAAY,WACfY,EAAMF,EAAQ,OACpB,QAASG,EAAI,EAAGA,EAAID,EAAKC,IACvBF,GAAMD,EAAQ,WAAWG,CAAC,EAAI,IAC9BF,EAAK,KAAK,KAAKA,EAAIX,EAAY,SAAS,EAE1CW,EAAKA,IAAO,EAGZ,IAAIG,EAAKH,EAAMA,IAAO,GACtBG,EAAK,KAAK,KAAKA,EAAI,UAAU,EAC7BA,EAAKA,EAAMA,IAAO,GAClBA,EAAK,KAAK,KAAKA,EAAI,UAAU,EAC7BA,GAAMA,EAAMA,IAAO,MAAS,EAE5B,IAAMC,EAAM,KAAK,SACXd,EAAO,KAAK,KACZQ,EAAI,KAAK,OAEf,QAASI,EAAI,EAAGA,EAAIJ,EAAGI,IAAK,CAC1B,IAAMG,GAAOL,EAAKE,EAAIC,GAAMb,EAC5B,IAAKc,EAAIC,IAAQ,CAAC,EAAK,IAAMA,EAAM,OAAU,EAC3C,MAAO,EAEX,CACA,MAAO,EACT,CAMA,cAA2B,CACzB,IAAMC,EAAM,IAAI,WAAW,EAAI,KAAK,SAAS,UAAU,EACjDC,EAAO,IAAI,SAASD,EAAI,MAAM,EACpC,OAAAC,EAAK,UAAU,EAAG,KAAK,KAAM,EAAI,EACjCA,EAAK,UAAU,EAAG,KAAK,OAAQ,EAAI,EAGnCD,EAAI,IAAI,IAAI,WAAW,KAAK,SAAS,OAAQ,KAAK,SAAS,WAAY,KAAK,SAAS,UAAU,EAAG,CAAC,EAC5FA,CACT,CAKA,OAAO,eAAeE,EAAiC,CACrD,GAAI,CAACA,GAAUA,EAAO,WAAa,EACjC,MAAM,IAAI,MAAM,+CAA+C,EAEjE,IAAMD,EAAO,IAAI,SAASC,EAAO,OAAQA,EAAO,WAAY,CAAC,EACvDlB,EAAOiB,EAAK,UAAU,EAAG,EAAI,EAC7BhB,EAASgB,EAAK,UAAU,EAAG,EAAI,EAErC,GAAIjB,IAAS,GAAKC,IAAW,EAC3B,MAAM,IAAI,MAAM,4EAA4E,EAG9F,IAAMkB,EAASD,EAAO,WAAa,EAC7BE,EAASF,EAAO,WAAa,EAC7BG,EAAiBrB,EAAO,KAAQ,EAChCsB,EAAcF,GAAU,EAE9B,GAAIE,EAAcD,EAChB,MAAM,IAAI,MACR,8DAA8DA,CAAa,eAAeC,CAAW,GACvG,EAGF,IAAIC,EAEJ,GAAIJ,EAAS,IAAM,EACjBI,EAAU,IAAI,YAAYL,EAAO,OAAQC,EAAQE,CAAa,MACzD,CAEL,IAAMG,EAAO,IAAI,WAAWH,GAAiB,CAAC,EAC9CG,EAAK,IAAIN,EAAO,SAAS,EAAG,GAAKG,GAAiB,EAAE,CAAC,EACrDE,EAAU,IAAI,YAAYC,EAAK,OAAQ,EAAGH,CAAa,CACzD,CACA,OAAO,IAAItB,EAAYC,EAAMC,EAAQsB,CAAO,CAC9C,CAMA,MAAME,EAA0B,CAC9B,GAAI,KAAK,OAASA,EAAM,MAAQ,KAAK,SAAWA,EAAM,OACpD,MAAM,IAAI,MAAM,iEAAiE,EAEnF,IAAMC,EAAI,KAAK,SACTC,EAAIF,EAAM,SACVd,EAAMe,EAAE,OACd,QAASd,EAAI,EAAGA,EAAID,EAAKC,IACvBc,EAAEd,CAAC,GAAKe,EAAEf,CAAC,CAEf,CAKQ,MAAMgB,EAAkBC,EAAsB,CACpD,IAAIC,EAAOD,IAAS,EACpB,QAASjB,EAAI,EAAGA,EAAIgB,EAAK,OAAQhB,IAC/BkB,GAAQF,EAAKhB,CAAC,EACdkB,EAAO,KAAK,KAAKA,EAAM/B,EAAY,SAAS,EAE9C,OAAO+B,IAAS,CAClB,CAKQ,YAAYC,EAAaF,EAAsB,CACrD,IAAIC,EAAOD,IAAS,EACdlB,EAAMoB,EAAI,OAChB,QAASnB,EAAI,EAAGA,EAAID,EAAKC,IACvBkB,GAAQC,EAAI,WAAWnB,CAAC,EAAI,IAC5BkB,EAAO,KAAK,KAAKA,EAAM/B,EAAY,SAAS,EAE9C,OAAO+B,IAAS,CAClB,CAKA,MAAuD,CACrD,IAAME,EAAQ,IAAI,WAAW,KAAK,SAAS,OAAQ,KAAK,SAAS,WAAY,KAAK,SAAS,UAAU,EACjGC,EAAS,GACPtB,EAAMqB,EAAM,WACZE,EAAQ,MACd,QAAStB,EAAI,EAAGA,EAAID,EAAKC,GAAKsB,EAC5BD,GAAU,OAAO,aAAa,MAAM,KAAMD,EAAM,SAASpB,EAAG,KAAK,IAAIA,EAAIsB,EAAOvB,CAAG,CAAC,CAAQ,EAE9F,MAAO,CAAE,KAAM,KAAK,KAAM,OAAQ,KAAK,OAAQ,KAAM,KAAKsB,CAAM,CAAE,CACpE,CAKA,OAAO,KAAKE,EAAmE,CAC7E,IAAMF,EAAS,KAAKE,EAAK,IAAI,EACvBH,EAAQ,IAAI,WAAWC,EAAO,MAAM,EAC1C,QAAS,EAAI,EAAG,EAAIA,EAAO,OAAQ,IACjCD,EAAM,CAAC,EAAIC,EAAO,WAAW,CAAC,EAGhC,IAAM9B,EAAS6B,EAAM,OAAS,GAAM,EAC9BT,EAAU,IAAI,YAAYpB,CAAK,EACrC,WAAI,WAAWoB,EAAQ,MAAM,EAAE,IAAIS,CAAK,EACjC,IAAIjC,EAAYoC,EAAK,KAAMA,EAAK,OAAQZ,CAAO,CACxD,CACF,EC1PO,IAAMa,GAAN,KAAwB,CAC7B,YAAoBC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAOpB,MAAM,mBAAmBC,EAAqC,CAE5D,OADe,MAAM,KAAK,GAAG,QAAQ,iDAAiD,EAAE,KAAKA,CAAS,EAAE,IAAI,GAC9F,OAChB,CAEA,MAAM,mBAAmBA,EAAmBC,EAA0BC,EAA+B,CACnG,IAAMC,EAAa,IAAI,WAAWF,CAAW,EACvCG,EAAa,CAAC,EAGpBA,EAAW,KACT,KAAK,GAAG,QAAQ,iDAAiD,EAAE,KAAKJ,CAAS,CACnF,EAGA,IAAIK,EAAa,EACjB,QAASC,EAAS,EAAGA,EAASH,EAAW,OAAQG,GAAU,QAAkB,CAC3E,IAAMC,EAAYJ,EAAW,MAAMG,EAAQA,EAAS,OAAgB,EAAE,OACtEF,EAAW,KACT,KAAK,GAAG,QACN,0GACF,EAAE,KAAKJ,EAAWK,EAAYE,EAAWL,CAAG,CAC9C,EACAG,GACF,CAGA,OADgB,MAAM,KAAK,GAAG,MAAMD,CAAU,GAC/B,MAAMI,GAAKA,EAAE,OAAO,CACrC,CAEA,MAAM,gBAAgBR,EAAgD,CACpE,GAAM,CAAE,QAAAS,CAAQ,EAAI,MAAM,KAAK,GAAG,QAChC,6FACF,EAAE,KAAKT,CAAS,EAAE,IAAyC,EAE3D,GAAI,CAACS,GAAWA,EAAQ,SAAW,EAAG,OAAO,KAE7C,IAAMC,EAAcD,EAAQ,OAAO,CAACE,EAAKC,IAAQ,CAC/C,IAAMC,EAAMD,EAAI,mBAAmB,YAAeA,EAAI,mBAA2B,OACjF,OAAOD,EAAME,CACf,EAAG,CAAC,EACEC,EAAW,IAAI,WAAWJ,CAAW,EAEvCJ,EAAS,EACb,QAAWM,KAAOH,EAAS,CACzB,IAAMM,EAAQ,IAAI,WAAWH,EAAI,kBAAkB,EACnDE,EAAS,IAAIC,EAAOT,CAAM,EAC1BA,GAAUS,EAAM,MAClB,CAEA,OAAOD,EAAS,MAClB,CAiBA,MAAM,uBACJd,EACAgB,EACAC,EACAf,EACkB,CAElB,IAAMgB,EADaC,GAAY,OAAOH,EAAYC,CAAiB,EACzC,aAAa,EACvC,OAAO,KAAK,mBAAmBjB,EAAWkB,EAAO,OAAuBhB,CAAG,CAC7E,CAEA,MAAM,kBAAkBF,EAAqC,CAE3D,OADe,MAAM,KAAK,GAAG,QAAQ,yDAAyD,EAAE,KAAKA,CAAS,EAAE,IAAI,GACtG,OAChB,CAEA,MAAM,mBAAmBA,EAAmBC,EAA0BC,EAA+B,CACnG,IAAMC,EAAa,IAAI,WAAWF,CAAW,EACvCG,EAAa,CAAC,EAEpBA,EAAW,KACT,KAAK,GAAG,QAAQ,yDAAyD,EAAE,KAAKJ,CAAS,CAC3F,EAGA,IAAIK,EAAa,EACjB,QAASC,EAAS,EAAGA,EAASH,EAAW,OAAQG,GAAU,QAAkB,CAC3E,IAAMC,EAAYJ,EAAW,MAAMG,EAAQA,EAAS,OAAgB,EAAE,OACtEF,EAAW,KACT,KAAK,GAAG,QACN,kHACF,EAAE,KAAKJ,EAAWK,EAAYE,EAAWL,CAAG,CAC9C,EACAG,GACF,CAGA,OADgB,MAAM,KAAK,GAAG,MAAMD,CAAU,GAC/B,MAAMI,GAAKA,EAAE,OAAO,CACrC,CAEA,MAAM,gBAAgBR,EAAgD,CACpE,GAAM,CAAE,QAAAS,CAAQ,EAAI,MAAM,KAAK,GAAG,QAChC,qGACF,EAAE,KAAKT,CAAS,EAAE,IAAyC,EAE3D,GAAI,CAACS,GAAWA,EAAQ,SAAW,EAAG,OAAO,KAE7C,IAAMC,EAAcD,EAAQ,OAAO,CAACE,EAAKC,IAAQ,CAC/C,IAAMC,EAAMD,EAAI,mBAAmB,YAAeA,EAAI,mBAA2B,OACjF,OAAOD,EAAME,CACf,EAAG,CAAC,EACEC,EAAW,IAAI,WAAWJ,CAAW,EAEvCJ,EAAS,EACb,QAAWM,KAAOH,EAAS,CACzB,IAAMM,EAAQ,IAAI,WAAWH,EAAI,kBAAkB,EACnDE,EAAS,IAAIC,EAAOT,CAAM,EAC1BA,GAAUS,EAAM,MAClB,CAEA,OAAOD,EAAS,MAClB,CAgBA,MAAM,uBAAuBd,EAAmBE,EAA+B,CAE7E,GAAM,CAAE,QAASkB,CAAc,EAAI,MAAM,KAAK,GAAG,QAC/C,kHACF,EAAE,KAAKpB,CAAS,EAAE,IAA8D,EAEhF,GAAI,CAACoB,GAAiBA,EAAc,SAAW,EAE7C,aAAM,KAAK,mBAAmBpB,CAAS,EAChC,GAIT,IAAMI,EAAa,CACjB,KAAK,GAAG,QAAQ,iDAAiD,EAAE,KAAKJ,CAAS,EACjF,GAAGoB,EAAc,IAAIR,GACnB,KAAK,GAAG,QACN,0GACF,EAAE,KAAKZ,EAAWY,EAAI,YAAaA,EAAI,mBAAoBV,CAAG,CAChE,EACA,KAAK,GAAG,QAAQ,yDAAyD,EAAE,KAAKF,CAAS,CAC3F,EAGA,OADqB,MAAM,KAAK,GAAG,MAAMI,CAAU,GAC/B,MAAMI,GAAKA,EAAE,OAAO,CAC1C,CACF,ECjLAa,KAGO,IAAMC,GAAiB,CAC5B,MAAM,KAAKC,EAAkBC,EAAkH,CAC7I,GAAM,CAAE,UAAAC,EAAW,IAAAC,EAAK,IAAAC,CAAI,EAAIJ,EAG1BK,EAAQC,GAAe,IAAIJ,CAAS,EACpCK,EAAeC,GAAY,IAAIN,CAAS,EAExCO,EAAc,OAAON,EAAI,aAAa,GAAK,IAEjD,GAAII,GAAiB,KAAK,IAAI,GAAKA,EAAa,WAAa,GADxC,IAC4D,CAC/EN,EAAM,oBAAoB,EAC1B,IAAMS,EAAcL,GAAS,KAAK,IAAI,EAAIA,EAAM,GAAKI,EAAeJ,EAAM,MAAQ,OAClF,MAAO,CAAE,GAAGE,EAAc,MAAOG,CAAW,CAC9C,CAGA,IAAMC,EAAS,OAAe,QACxBC,EAAkB,cAAcV,CAAS,GACzCW,EAAmB,gCAAgCX,CAAS,GAE9DY,EAAY,MAAMC,EAAW,IAASJ,EAAOC,CAAe,EAC5DI,EAEJ,GAAIF,EAAW,CAEb,GADAb,EAAM,sBAAsB,EACxBI,GAAS,KAAK,IAAI,EAAIA,EAAM,GAAKI,EACnCO,EAAQX,EAAM,UAEd,IAAI,CACF,IAAMY,EAAW,MAAMN,EAAM,MAAME,CAAgB,EACnD,GAAII,EAAU,CACZhB,EAAM,qBAAqB,EAC3B,IAAMiB,EAAS,MAAMD,EAAS,YAAY,EAC1CD,EAAQG,GAAY,eAAe,IAAI,WAAWD,CAAM,CAAC,EACzDZ,GAAe,IAAIJ,EAAW,CAAE,MAAAc,EAAO,GAAI,KAAK,IAAI,CAAE,CAAC,CACzD,CACF,MAAQ,CAER,CAIF,OAAAR,GAAY,IAAIN,EAAW,CAAE,GAAGY,EAAW,UAAW,KAAK,IAAI,CAAE,CAAC,EAC3D,CAAE,GAAGA,EAAW,MAAAE,CAAM,CAC/B,CAGA,GAAI,CACF,IAAMI,EAAe,IAAIC,EAAalB,EAAI,EAAE,EACtCmB,EAAY,IAAIC,GAAUpB,EAAI,EAAE,EAChCqB,EAAa,IAAIC,GAAkBtB,EAAI,EAAE,EAGzC,CAACuB,EAASC,EAAOT,CAAM,EAAI,MAAM,QAAQ,IAAI,CACjDE,EAAa,QAAQlB,CAAS,EAC9BoB,EAAU,SAASpB,CAAS,EAC5BsB,EAAW,gBAAgBtB,CAAS,EAAE,MAAO0B,IAC3C,QAAQ,MAAM,oCAAqCA,CAAC,EAC7C,KACR,CACH,CAAC,EAED,GAAI,CAACF,EACH,OAAInB,GACF,QAAQ,KAAK,oBAAoBL,CAAS,wCAAwC,EAC3E,CAAE,GAAGK,EAAc,MAAOF,GAAO,KAAM,GAEzC,KAGT,IAAMwB,EAAW,KAAK,MAAMH,EAAQ,QAAQ,EAG5C,GAAIR,EAAQ,CACVjB,EAAM,kBAAkB,EACxB,IAAM6B,EAAQ,IAAI,WAAWZ,CAAM,EACnCF,EAAQG,GAAY,eAAeW,CAAK,EAGxC1B,EAAI,UAAUO,EAAM,IAAIE,EAAkB,IAAI,SAASiB,EAAO,CAC5D,QAAS,CACP,eAAgB,2BAChB,gBAAiB,yBACjB,YAAa,SAAS5B,CAAS,EACjC,CACF,CAAC,CAAC,CAAC,CACL,CAEA,IAAM6B,EAAS,CAAE,SAAAF,EAAU,MAAAF,CAAM,EACjC,OAAIX,GAAOV,GAAe,IAAIJ,EAAW,CAAE,MAAAc,EAAO,GAAI,KAAK,IAAI,CAAE,CAAC,EAElER,GAAY,IAAIN,EAAW,CAAE,GAAG6B,EAAQ,UAAW,KAAK,IAAI,CAAE,CAAC,EAE/D3B,EAAI,UAAUW,EAAW,IAAIJ,EAAOC,EAAiBmB,EAAQ,MAAO,CAAC,WAAW7B,CAAS,EAAE,CAAC,CAAC,EAE7FD,EAAM,uBAAuB,EACtB,CAAE,GAAG8B,EAAQ,MAAAf,CAAM,CAC5B,OAASY,EAAQ,CAIf,OAHA,QAAQ,KAAK,wCAAwC1B,CAAS,IAAK0B,GAAG,SAAWA,CAAC,EAG9ErB,GACF,QAAQ,KAAK,yDAAyDL,CAAS,EAAE,EAC1E,CAAE,GAAGK,EAAc,MAAOF,GAAO,KAAM,IAKhD,QAAQ,KAAK,kEAAkEH,CAAS,EAAE,EAQnF,CAAE,SANiC,CACxC,SAAU,CAFaC,EAAI,oBAAsB,kDAEtB,EAC3B,eAAgB,QAChB,mBAAoB,EACpB,IAAK,CAAE,QAAS,GAAM,cAAe,EAAK,CAC5C,EACqC,MAAO,CAAC,EAAG,MAAO,MAAU,EACnE,CACF,CACF,EC9FO,SAAS6B,GAAcC,EAA4B,CACxD,IAAMC,EAAQD,EAAS,KAAK,EAC5B,GAAI,CAACC,EAAM,WAAW,SAAS,EAC7B,MAAM,IAAI,MAAM,4CAA4C,EAG9D,IAAMC,EAASD,EAAM,MAAM,CAAC,EACxBE,EAAMD,EAAO,OACjB,KAAOC,EAAM,GAAKD,EAAO,WAAWC,EAAM,CAAC,IAAM,IAC/CA,IAEF,IAAMC,EAAMF,EAAO,MAAM,EAAGC,CAAG,EAC3BE,EAEJ,GAAI,OAAO,OAAW,IACpBA,EAAY,OAAO,KAAKD,EAAK,WAAW,EAAE,SAAS,QAAQ,MACtD,CAEL,IAAME,EAAcF,EAAI,QAAQ,KAAM,GAAG,EAAE,QAAQ,KAAM,GAAG,EACtDG,EAASD,EAAY,OAAS,EAC9BE,EAASD,EAASD,EAAc,IAAI,OAAO,EAAIC,CAAM,EAAID,EAC/DD,EAAY,KAAKG,CAAM,CACzB,CAEA,IAAMC,EAAQ,IAAI,WAAWJ,EAAU,MAAM,EAC7C,QAASK,EAAI,EAAGA,EAAIL,EAAU,OAAQK,IACpCD,EAAMC,CAAC,EAAIL,EAAU,WAAWK,CAAC,EAGnC,GAAID,EAAM,OAAS,EACjB,MAAM,IAAI,MAAM,+CAA+C,EAGjE,IAAIE,EAAS,EACPC,EAAaH,EAAME,GAAQ,EAG7BE,EAAQ,GACZ,QAASH,EAAI,EAAGA,EAAI,EAAGA,IACrBG,GAAS,OAAOJ,EAAME,GAAQ,CAAC,GAAK,OAAOD,EAAI,CAAC,EAGlD,IAAMI,EAAe,IAAc,CACjC,GAAIH,GAAUF,EAAM,OAAQ,MAAO,GACnC,IAAMM,EAAMN,EAAME,GAAQ,EAC1B,GAAIA,EAASI,EAAMN,EAAM,OAAQ,MAAO,GACxC,IAAMO,EAAWP,EAAM,MAAME,EAAQA,EAASI,CAAG,EACjD,OAAAJ,GAAUI,EACH,IAAI,YAAY,EAAE,OAAOC,CAAQ,CAC1C,EAEMC,EAAa,IAAgB,CACjC,IAAMC,EAAmB,CAAC,EAC1B,KAAOP,EAASF,EAAM,QAAQ,CAC5B,IAAMU,EAAOV,EAAME,GAAQ,EAC3B,GAAIQ,IAAS,EAAG,MAChB,IAAMC,EAAYD,EAAO,IACzB,GAAIR,EAASS,EAAYX,EAAM,OAAQ,MACvC,IAAMY,EAAYZ,EAAM,MAAME,EAAQA,EAASS,CAAS,EAGxD,GAFAT,GAAUS,EACVF,EAAO,KAAK,MAAM,KAAKG,CAAS,EAAE,IAAIC,GAAKA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,GAC/EH,EAAO,OAAU,EAAG,KAC3B,CACA,OAAOD,CACT,EAEIK,EAAiC,UACjCC,EACAC,EACAC,EACAR,EACAS,EACAC,EAAc,GAElB,OAAQhB,EAAY,CAClB,IAAK,GAAM,CACTW,EAAW,MACXC,EAAgBV,EAAa,EACzBU,IACFI,EAAcJ,EAAc,SAAS,GAAG,GAAK,CAACA,EAAc,WAAW,GAAG,EACtE,SAASA,CAAa,GACtB,SAASA,CAAa,OAE5B,KACF,CACA,IAAK,GAAM,CACTD,EAAW,WACXC,EAAgBV,EAAa,EAC7BH,GAAU,GACVgB,EAAeb,EAAa,EAC5Bc,EAAc,cAAcD,GAAgBH,CAAa,GACzD,KACF,CACA,IAAK,GAAM,CACTD,EAAW,MACXC,EAAgBV,EAAa,EAC7BI,EAASD,EAAW,EACpBQ,EAAWX,EAAa,EACxBY,EAAOZ,EAAa,GAAK,aACzB,IAAMe,EAAOJ,GAAYD,EACpBE,EAAK,WAAW,GAAG,IAAGA,EAAO,IAAIA,CAAI,IAC1CE,EAAc,WAAWC,CAAI,GAAGH,CAAI,GACpC,KACF,CACA,IAAK,GAAM,CACTH,EAAW,MACXC,EAAgBV,EAAa,EAC7BI,EAASD,EAAW,EACpBQ,EAAWX,EAAa,EACxB,IAAMe,EAAOJ,GAAYD,EACnBM,EAAON,GAAiBA,EAAc,SAAS,GAAG,EAAIA,EAAc,MAAM,GAAG,EAAE,IAAI,EAAI,MAC7FI,EAAcC,GAAM,SAAS,GAAG,EAAI,SAASA,CAAI,GAAK,SAASA,CAAI,IAAIC,CAAI,GAC3E,KACF,CACA,IAAK,GAAM,CACTP,EAAW,MACXC,EAAgBV,EAAa,EAC7BI,EAASD,EAAW,EACpBQ,EAAWX,EAAa,EACxBc,EAAc,UAAUH,GAAYD,CAAa,OACjD,KACF,CACA,IAAK,GAAM,CACTD,EAAW,OACXC,EAAgBV,EAAa,EAC7BI,EAASD,EAAW,EACpBQ,EAAWX,EAAa,EACxBY,EAAOZ,EAAa,GAAK,aACzBc,EAAc,WAAWH,GAAYD,CAAa,GAAGE,CAAI,GACzD,KACF,CACA,QACEE,EAAc3B,EACd,KACJ,CAEA,MAAO,CACL,SAAAsB,EACA,WAAAX,EACA,MAAAC,EACA,cAAeW,GAAiB,OAChC,SAAUC,GAAY,OACtB,KAAMC,GAAQ,OACd,OAAQR,GAAUA,EAAO,OAAS,EAAIA,EAAS,OAC/C,aAAcS,GAAgB,OAC9B,YAAAC,CACF,CACF,CC9JO,IAAMG,GAAiB,2DAMjBC,GAAiB,2BAMjBC,GAAgB,wBAMhBC,GAAqB,wBAgB3B,IAAMC,GAAqB,6BAMrBC,GAAmB,sBAMnBC,GAAmB,UAE1BC,GAAyC,CAC7C,YACA,2BACA,gBACA,kBACA,gBACA,cACF,EAEMC,GAAiD,CACrD,aACA,SACA,YACA,OACA,aACA,QACA,QACA,eACF,EAQA,SAASC,GAAqBC,EAAiC,CAE7D,IAAMC,EAAWD,EAAS,MAAM,uEAAuE,EACvG,GAAIC,EAAU,OAAOA,EAAS,CAAC,EAG/B,IAAMC,EAAYF,EAAS,MAAM,0EAA0E,EAC3G,OAAIE,EAAkB,GAAGA,EAAU,CAAC,CAAC,IAAIA,EAAU,CAAC,CAAC,IAAIA,EAAU,CAAC,CAAC,IAAIA,EAAU,CAAC,CAAC,GAE9E,IACT,CAMO,IAAMC,GAAgD,CAE3D,YACA,aACA,gBACA,cACA,iBACA,gBACA,eACA,eACA,iBACA,iBACA,gBACA,kBACA,iBACA,cACA,cACA,qBAGA,SACA,UACA,eACA,WACA,gBACA,WACA,YACA,UACF,EAEMC,GAAiDC,GAAkBF,EAA0B,EAU5F,SAASG,GAAmBC,EAAqB,CACtD,GAAI,CAACA,GAAM,OAAOA,GAAO,SAAU,MAAO,GAE1C,IAAIC,EAAUD,EAAG,KAAK,EAMtB,GALIC,EAAQ,WAAW,GAAG,GAAKA,EAAQ,SAAS,GAAG,IACjDA,EAAUA,EAAQ,MAAM,EAAG,EAAE,GAI3BA,EAAQ,SAAS,GAAG,GAAKA,EAAQ,SAAS,GAAG,EAAG,CAClD,IAAMC,EAAYD,EAAQ,YAAY,GAAG,EACnCE,EAAWF,EAAQ,UAAUC,EAAY,CAAC,EAChD,OAAKE,GAAOD,CAAQ,EACb,CAACN,GAAsBM,CAAQ,EADR,EAEhC,CAGA,GAAI,CAACC,GAAOH,CAAO,GAAK,CAACI,GAAOJ,CAAO,EACrC,MAAO,GAIT,IAAMK,EAAUC,GAAaN,CAAO,EACpC,GAAIK,IAAY,MAASA,GAAW,MAAS,QAAS,CACpD,IAAME,EAAU,OAAOF,EAAU,WAAW,EACtCG,EAAU,GAAID,IAAY,GAAM,GAAI,IAAKA,IAAY,GAAM,GAAI,IAAKA,IAAY,EAAK,GAAI,IAAIA,EAAU,GAAI,GACjH,MAAO,CAACX,GAAsBY,CAAO,CACvC,CAEA,MAAO,CAACZ,GAAsBI,CAAO,CACvC,CAUO,SAASS,GAAUC,EAA4B,CACpD,GAAI,CACF,GAAIA,EAAU,WAAW,SAAS,EAAG,CACnC,IAAMC,EAAQC,GAAcF,CAAS,EACrC,OAAOD,GAAUE,EAAM,WAAW,CACpC,CAEA,IAAIE,EACAH,EAAU,WAAW,QAAQ,GAAKA,EAAU,WAAW,QAAQ,EACjEG,EAAeH,EAAU,QAAQ,kBAAmB,SAAS,EACpDA,EAAU,WAAW,QAAQ,GAAKA,EAAU,WAAW,QAAQ,EACxEG,EAAeH,EAAU,QAAQ,kBAAmB,SAAS,EACpDA,EAAU,WAAW,SAAS,GAAKA,EAAU,WAAW,UAAU,EAC3EG,EAAeH,EAEfG,EAAe,UAAUH,CAAS,GAGpC,IAAMI,EAAM,IAAI,IAAID,CAAY,EAC1BE,EAAcD,EAAI,SAAS,QAAQ,WAAY,EAAE,EAAE,YAAY,EAErE,GAAIzB,GAAoB,SAAS0B,CAAW,GAAK1B,GAAoB,SAASyB,EAAI,SAAS,YAAY,CAAC,EACtG,MAAO,GAIT,QAAWE,KAAU1B,GACnB,GAAIyB,EAAY,SAASC,CAAM,GAAKD,IAAgBC,EAAO,MAAM,CAAC,EAChE,MAAO,GAKX,GAAID,EAAY,SAAS,iBAAiB,EACxC,MAAO,GAIT,IAAME,EAAa1B,GAAqBwB,CAAW,EAanD,MAZI,EAAAE,GACE,CAACnB,GAAmBmB,CAAU,IAM/Bd,GAAOY,CAAW,GAAKX,GAAOW,CAAW,IAAM,CAACjB,GAAmBiB,CAAW,GAK/ED,EAAI,SAAS,SAAS,GAAG,GAAKA,EAAI,UAAYA,EAAI,SAKxD,MAAY,CACV,MAAO,EACT,CACF,CClOA,eAAsBI,GAAiBC,EAAmD,CAKxF,GAFe,EADO,OAAO,UAAc,KAAe,UAAU,YAAc,sBAAyB,OAAO,cAAkB,MACpG,OAAO,QAAY,KAAe,CAAC,CAAC,QAAQ,UAAU,KAE1E,CACV,GAAIA,EAAQ,kBAAoB,KAAM,CACpC,IAAMC,EAAM,KAAM,QAAO,UAAU,EAC7BC,EAAM,KAAM,QAAO,UAAU,EACnC,OAAO,IAAI,QAAyB,CAACC,EAASC,IAAW,CACvD,IAAMC,EAASJ,EAAI,QAAQ,CACzB,KAAMD,EAAQ,SACd,KAAMA,EAAQ,KACd,WAAYE,EAAI,KAAKF,EAAQ,QAAQ,EAAI,OAAYA,EAAQ,QAC/D,EAAG,IAAM,CACP,IAAIM,EAAW,GAETC,EAAW,IAAI,eAA2B,CAC9C,MAAMC,EAAY,CAChBH,EAAO,GAAG,OAASI,GAA+B,CAChDD,EAAW,QAAQ,IAAI,WAAWC,EAAM,OAAQA,EAAM,WAAYA,EAAM,UAAU,CAAC,CACrF,CAAC,EACDJ,EAAO,GAAG,MAAO,IAAM,CACrB,GAAI,CAAEG,EAAW,MAAM,CAAG,MAAQ,CAAe,CACnD,CAAC,EACDH,EAAO,GAAG,QAAUK,GAAe,CACjC,GAAI,CAAEF,EAAW,MAAME,CAAG,CAAG,MAAQ,CAAe,CACtD,CAAC,CACH,EACA,QAAS,CACFJ,IACHA,EAAW,GACXD,EAAO,QAAQ,EAEnB,CACF,CAAC,EAEKM,EAAW,IAAI,eAA2B,CAC9C,MAAMF,EAAO,CACX,OAAO,IAAI,QAAQ,CAACG,EAAKC,IAAQ,CAC/BR,EAAO,MAAMI,EAAQC,GAAmCA,EAAMG,EAAIH,CAAG,EAAIE,EAAI,CAAE,CACjF,CAAC,CACH,EACA,OAAQ,CACN,OAAO,IAAI,QAAQA,GAAO,CACxBP,EAAO,IAAI,IAAMO,EAAI,CAAC,CACxB,CAAC,CACH,EACA,MAAMF,EAAK,CACTL,EAAO,QAAQK,CAAU,CAC3B,CACF,CAAC,EAEDP,EAAQ,CACN,SAAAI,EACA,SAAAI,EACA,MAAO,SAAY,CACZL,IACHA,EAAW,GACXD,EAAO,QAAQ,EAEnB,CACF,CAAC,CACH,CAAC,EAEDA,EAAO,GAAG,QAASD,CAAM,CAC3B,CAAC,CACH,CAEA,IAAMF,EAAM,KAAM,QAAO,UAAU,EACnC,OAAO,IAAI,QAAyB,CAACC,EAASC,IAAW,CACvD,IAAMC,EAASH,EAAI,iBAAiB,CAAE,KAAMF,EAAQ,SAAU,KAAMA,EAAQ,IAAK,EAAG,IAAM,CACxF,IAAIM,EAAW,GAETC,EAAW,IAAI,eAA2B,CAC9C,MAAMC,EAAY,CAChBH,EAAO,GAAG,OAASI,GAA+B,CAChDD,EAAW,QAAQ,IAAI,WAAWC,EAAM,OAAQA,EAAM,WAAYA,EAAM,UAAU,CAAC,CACrF,CAAC,EACDJ,EAAO,GAAG,MAAO,IAAM,CACrB,GAAI,CAAEG,EAAW,MAAM,CAAG,MAAQ,CAAe,CACnD,CAAC,EACDH,EAAO,GAAG,QAAUK,GAAe,CACjC,GAAI,CAAEF,EAAW,MAAME,CAAG,CAAG,MAAQ,CAAe,CACtD,CAAC,CACH,EACA,QAAS,CACFJ,IACHA,EAAW,GACXD,EAAO,QAAQ,EAEnB,CACF,CAAC,EAEKM,EAAW,IAAI,eAA2B,CAC9C,MAAMF,EAAO,CACX,OAAO,IAAI,QAAQ,CAACG,EAAKC,IAAQ,CAC/BR,EAAO,MAAMI,EAAQC,GAAmCA,EAAMG,EAAIH,CAAG,EAAIE,EAAI,CAAE,CACjF,CAAC,CACH,EACA,OAAQ,CACN,OAAO,IAAI,QAAQA,GAAO,CACxBP,EAAO,IAAI,IAAMO,EAAI,CAAC,CACxB,CAAC,CACH,EACA,MAAMF,EAAK,CACTL,EAAO,QAAQK,CAAU,CAC3B,CACF,CAAC,EAEDP,EAAQ,CACN,SAAAI,EACA,SAAAI,EACA,MAAO,SAAY,CACZL,IACHA,EAAW,GACXD,EAAO,QAAQ,EAEnB,CACF,CAAC,CACH,CAAC,EAEDA,EAAO,GAAG,QAASD,CAAM,CAC3B,CAAC,CACH,CAGA,GAAI,CAGF,OADW,KAAM,QAAO,oBAAoB,GAClC,QACR,CAAE,SAAUJ,EAAQ,SAAU,KAAMA,EAAQ,IAAK,EACjD,CAAE,gBAAiBA,EAAQ,iBAAmB,MAAO,cAAe,EAAM,CAC5E,CACF,OAASU,EAAK,CACZ,MAAM,IAAI,MAAM,oDAAoDA,CAAG,EAAE,CAC3E,CACF,CChKO,IAAMI,GAAN,cAAgC,KAAM,CAC3C,YACkBC,EACAC,EACAC,EACAC,EAChB,CACA,MAAM,iBAAiBH,CAAM,GAAGC,EAAa,IAAIA,CAAU,GAAK,EAAE,EAAE,EALpD,YAAAD,EACA,gBAAAC,EACA,yBAAAC,EACA,WAAAC,EAGhB,KAAK,KAAO,mBACd,CAPkB,OACA,WACA,oBACA,KAKpB,ECWA,eAAsBC,GACpBC,EACAC,EAAY,IACS,CACrB,IAAMC,EAAuB,CAAC,EAC1BC,EAAa,EACbC,EAAoC,KAEpCC,EACEC,EAAiB,IAAI,QAAe,CAACC,EAAGC,IAAW,CACvDH,EAAQ,WAAW,IAAMG,EAAO,IAAI,MAAM,yBAAyB,CAAC,EAAGP,CAAS,CAClF,CAAC,EAED,GAAI,CACF,OAAa,CACX,GAAM,CAAE,MAAAQ,EAAO,KAAAC,CAAK,EAAI,MAAM,QAAQ,KAAK,CAACV,EAAO,KAAK,EAAGM,CAAc,CAAC,EAC1E,GAAII,GAAQ,CAACD,EACX,MAAM,IAAI,MAAM,qDAAqD,EAOvE,GAJAP,EAAO,KAAKO,CAAK,EACjBN,GAAcM,EAAM,OAGhBN,EAAa,MACf,MAAM,IAAI,MAAM,sEAAsE,EAGxF,GAAIC,IAAuB,MAAQD,GAAc,EAAG,CAClD,IAAMQ,EAAKT,EAAO,CAAC,EAAE,CAAC,EAChBU,EAAKV,EAAO,CAAC,EAAE,OAAS,EAAIA,EAAO,CAAC,EAAE,CAAC,EAAIA,EAAO,CAAC,EAAE,CAAC,EAC5DE,EAAsBO,GAAM,EAAKC,CACnC,CAEA,GAAIR,IAAuB,MAAQD,GAAc,EAAIC,EAAoB,CACvE,IAAMS,EAAW,IAAI,WAAWV,CAAU,EACtCW,EAAS,EACb,QAAWC,KAASb,EAClBW,EAAS,IAAIE,EAAOD,CAAM,EAC1BA,GAAUC,EAAM,OAElB,OAAOF,EAAS,MAAM,EAAG,EAAIT,CAAkB,CACjD,CACF,CACF,QAAE,CACIC,IAAU,QACZ,aAAaA,CAAK,CAEtB,CACF,CASO,SAASW,GACdC,EACAC,EACgC,CAChC,IAAIC,EAAOF,EACPG,EAAOF,EAEX,GAAIC,EAAK,WAAW,GAAG,EAAG,CACxB,IAAME,EAAWF,EAAK,QAAQ,GAAG,EACjC,GAAIE,IAAa,GAAI,CACnB,IAAMC,EAAKH,EAAK,MAAM,EAAGE,CAAQ,EAC3BE,EAAOJ,EAAK,MAAME,EAAW,CAAC,EACpCF,EAAOG,EACHC,EAAK,WAAW,GAAG,IACrBH,EAAO,SAASG,EAAK,MAAM,CAAC,EAAG,EAAE,GAAKL,EAE1C,CACF,SAAWC,EAAK,SAAS,GAAG,EAAG,CAC7B,IAAMK,EAAQL,EAAK,MAAM,GAAG,EAC5BA,EAAOK,EAAM,CAAC,EACdJ,EAAO,SAASI,EAAM,CAAC,EAAG,EAAE,GAAKN,CACnC,CAEA,MAAO,CAAE,KAAAC,EAAM,KAAAC,CAAK,CACtB,CAQO,SAASK,GAAwBC,EAA8C,CACpF,IAAIC,EAAeD,EAEnB,GAAIA,EAAe,WAAW,SAAS,EAAG,CACxC,IAAME,EAAQC,GAAcH,CAAc,EAC1C,GAAIE,EAAM,WAAa,WACrB,MAAM,IAAI,MACR,wGACF,EAEF,GAAIA,EAAM,WAAa,MACrB,MAAM,IAAI,MACR,oGACF,EAEF,GAAI,CAACA,EAAM,YACT,MAAM,IAAI,MAAM,qCAAqCA,EAAM,WAAW,SAAS,EAAE,CAAC,EAAE,EAEtFD,EAAeC,EAAM,WACvB,CAMA,GAJID,EAAa,WAAW,QAAQ,IAClCA,EAAeA,EAAa,QAAQ,YAAa,QAAQ,GAGvDA,EAAa,WAAW,QAAQ,EAAG,CACrC,IAAMG,EAAUH,EAAa,QAAQ,YAAa,EAAE,EAC9C,CAAE,KAAAR,EAAM,KAAAC,CAAK,EAAIJ,GAAiBc,EAAS,GAAG,EACpD,MAAO,CACL,aAAAH,EACA,WAAY,MACZ,WAAY,SAASR,CAAI,IAAIC,CAAI,EACnC,CACF,CAEA,GAAI,CAACO,EAAa,WAAW,SAAS,GAAK,CAACA,EAAa,WAAW,UAAU,EAAG,CAC/E,IAAMG,EAAUH,EAAa,QAAQ,kBAAmB,EAAE,EACpD,CAAE,KAAAR,EAAM,KAAAC,CAAK,EAAIJ,GAAiBc,EAAS,EAAE,EACnD,MAAO,CACL,aAAAH,EACA,WAAY,MACZ,WAAY,SAASR,CAAI,IAAIC,CAAI,EACnC,CACF,CAEA,MAAO,CACL,aAAAO,EACA,WAAY,OACZ,WAAYA,CACd,CACF,CASA,eAAsBI,GACpBC,EACAC,EACkC,CAClC,GAAM,CAAE,aAAAN,EAAc,WAAAO,EAAY,WAAAC,CAAW,EAAIH,EAC3CI,EAAa,KAAK,IAAI,EACxBC,EAEJ,GAAIV,EAAa,WAAW,QAAQ,GAAKA,EAAa,WAAW,QAAQ,EAAG,CAE1E,IAAMG,EAAUH,EAAa,QAAQ,kBAAmB,EAAE,EACpD,CAAE,KAAMW,EAAS,KAAMC,CAAQ,EAAIvB,GAAiBc,EAAS,GAAG,EAEhEU,EAAS,MAAMC,GAAiB,CACpC,SAAUH,EACV,KAAMC,EACN,gBAAiB,IACnB,CAAC,EAED,GAAI,CACF,IAAMG,EAASF,EAAO,SAAS,UAAU,EACnCxC,EAASwC,EAAO,SAAS,UAAU,EAGnCG,EAAc,IAAI,WAAWV,EAAS,OAAS,CAAC,EACtDU,EAAY,CAAC,EAAKV,EAAS,QAAU,EAAK,IAC1CU,EAAY,CAAC,EAAIV,EAAS,OAAS,IACnCU,EAAY,IAAIV,EAAU,CAAC,EAE3B,MAAMS,EAAO,MAAMC,CAAW,EAC9BD,EAAO,YAAY,EAEnBL,EAAS,MAAMtC,GAAsBC,EAAQ,GAAI,CACnD,QAAE,CACA,MAAMwC,EAAO,MAAM,EAAE,MAAM,IAAM,CAAC,CAAC,CACrC,CACF,SACE,CAACb,EAAa,WAAW,SAAS,GAClC,CAACA,EAAa,WAAW,UAAU,EACnC,CAEA,IAAMG,EAAUH,EAAa,QAAQ,kBAAmB,EAAE,EACpD,CAAE,KAAMiB,EAAS,KAAMC,CAAQ,EAAI7B,GAAiBc,EAAS,EAAE,EAE/DU,EAAS,MAAMC,GAAiB,CACpC,SAAUG,EACV,KAAMC,EACN,gBAAiB,KACnB,CAAC,EAED,GAAI,CACF,IAAMH,EAASF,EAAO,SAAS,UAAU,EACnCxC,EAASwC,EAAO,SAAS,UAAU,EAEnCG,EAAc,IAAI,WAAWV,EAAS,OAAS,CAAC,EACtDU,EAAY,CAAC,EAAKV,EAAS,QAAU,EAAK,IAC1CU,EAAY,CAAC,EAAIV,EAAS,OAAS,IACnCU,EAAY,IAAIV,EAAU,CAAC,EAE3B,MAAMS,EAAO,MAAMC,CAAW,EAC9BD,EAAO,YAAY,EAEnBL,EAAS,MAAMtC,GAAsBC,EAAQ,GAAI,CACnD,QAAE,CACA,MAAMwC,EAAO,MAAM,EAAE,MAAM,IAAM,CAAC,CAAC,CACrC,CACF,KAAO,CAEL,IAAMM,EAAW,MAAM,MAAMnB,EAAc,CACzC,OAAQ,OACR,QAAS,CACP,OAAQ,0BACR,eAAgB,0BAChB,aAAc,cAChB,EACA,KAAMM,EACN,OAAQ,YAAY,QAAQ,GAAI,CAClC,CAAC,EAED,GAAI,CAACa,EAAS,GAAI,CAChB,IAAIC,EAAU,GACd,GAAI,CAGFA,GAFgB,MAAMD,EAAS,KAAK,GAElB,QAAQ,aAAc,GAAG,EAAE,QAAQ,OAAQ,GAAG,EAAE,KAAK,EAAE,MAAM,EAAG,GAAG,CACvF,MAAQ,CAAC,CACT,IAAME,EAAQF,EAAS,QAAQ,IAAI,QAAQ,GAAK,OAChD,MAAM,IAAIG,GAAkBH,EAAS,OAAQA,EAAS,WAAYC,EAASC,CAAK,CAClF,CAEA,IAAME,EAAe,MAAMJ,EAAS,YAAY,EAChDT,EAAS,IAAI,WAAWa,CAAY,CACtC,CAEA,IAAMC,EAAU,KAAK,IAAI,EAAIf,EAC7B,MAAO,CAAE,OAAAC,EAAQ,QAAAc,EAAS,WAAAjB,EAAY,WAAAC,CAAW,CACnD,CC9PO,SAASiB,GACdC,EACAC,EACAC,EACsB,CACtB,IAAIC,EACAC,EAAWH,EAAM,IAErB,GAAIC,EAAS,KAAK,QAAS,CACzB,IAAMG,EAAWL,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YAC5DG,EAAMD,EAAS,IAAI,cACf,GAAGG,CAAQ,IAAIA,EAAS,SAAS,GAAG,EAAI,GAAK,EAAE,GAC/CJ,EAAM,OAAS,OACfC,EAAS,IAAI,WAAaA,EAAS,IAAI,UACvCA,EAAS,IAAI,WAAaA,EAAS,IAAI,UAEvCC,IACFC,EAAWE,GAAmBL,EAAM,IAAKE,CAAG,EAEhD,CAEA,MAAO,CAAE,SAAAC,EAAU,IAAAD,CAAI,CACzB,CCCA,eAAsBI,GACpBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EAC2B,CAC3B,IAAMC,EACJR,EAAM,OAAS,SACfA,EAAM,OAAS,QACfA,EAAM,OAAS,UACfA,EAAM,OAAS,SAEXS,EACJ,OAAOL,EAAS,iBAAoB,UAChCA,EAAS,gBACT,EAAQA,EAAS,iBAAiB,QAEpCM,EAASR,EACTS,EAAgBV,EAChBW,EAAkBL,EAEtB,GAAIC,GAAiBC,EAAY,CAC/B,MAAMI,GAA+BV,EAAQ,IAAI,EAAE,EACnD,IAAMW,EAA0B,CAAC,EAC3BC,EAA0B,CAAC,EAC7BC,EAEJ,QAAWC,KAAKN,EAAe,CAC7B,IAAMO,EAAUD,EAAE,KAAK,MAAM,mBAAmB,EAC5CC,GAASJ,EAAc,KAAK,GAAGI,EAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,EAExD,IAAMC,EAAUF,EAAE,KAAK,MAAM,mBAAmB,EAC5CE,GAASJ,EAAc,KAAK,GAAGI,EAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,EAExD,IAAMC,EAAWH,EAAE,KAAK,MAAM,uBAAuB,EACjDG,IACFJ,EAAcI,EAAS,CAAC,EAE5B,CAEA,IAAIC,EAAOP,EAAc,KAAKQ,EAAc,GAAKP,EAAc,KAAKO,EAAc,EAGlF,GAAI,CAACD,EACH,GAAI,CACF,IAAME,EAAYC,GAAcxB,EAAM,KAAM,GAAG,EACzCyB,EAAO,MAAMnB,EACjBD,EACA,CAAE,KAAML,EAAM,KAAM,KAAM,IAAK,IAAKuB,CAAU,EAC9CpB,EACAC,EACA,MACF,EACA,GAAIqB,EAAK,QAAUA,EAAK,OAAO,OAAS,EAAG,CACzC,IAAMC,EAAWC,GAAeF,EAAK,MAAM,EAC3C,QAAWG,KAAOF,EACZE,EAAI,OAAS,MACfd,EAAc,KAAKc,EAAI,IAAI,EACvBN,GAAeM,EAAI,IAAI,IAAGP,EAAO,IAG3C,CACF,OAASQ,EAAG,CACV,QAAQ,KAAK,2CAA4CA,CAAC,CAC5D,CAGF,GAAIR,EAAM,CACR,IAAMS,EACJ,OAAO1B,EAAS,iBAAoB,UAAYA,EAAS,iBAAiB,gBACtEA,EAAS,gBAAgB,gBACzB2B,GAEAC,EAAkBC,GAAyBH,EAAgBd,CAAW,EACtEkB,EACJlC,EAAM,OAAS,QAAUA,EAAM,OAAS,SAAW,OAAS,QAExDmC,EAAmB,CAAC,YAAY,EAClCrB,EAAc,OAAS,GACzBqB,EAAO,KAAK,YAAY,MAAM,KAAK,IAAI,IAAIrB,CAAa,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,EAExEqB,EAAO,KAAK,OAAOH,CAAe,EAAE,EAChCjB,EAAc,OAAS,GACzBoB,EAAO,KAAK,YAAY,MAAM,KAAK,IAAI,IAAIpB,CAAa,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,EAGxE,IAAMqB,EAAa,OAAOD,EAAO,KAAK,GAAG,CAAC,GAC1CzB,EAAS2B,GAAcrC,EAAM,IAAKkC,EAAYE,EAAY,IAAK,CAAC,EAChEzB,EAAgBgB,GAAejB,CAAM,EACrCE,EAAkB,eACpB,CACF,CAEA,MAAO,CAAE,OAAAF,EAAQ,cAAAC,EAAe,gBAAAC,CAAgB,CAClD,CCrIA,IAAI0B,GAAgB,EAWb,SAASC,IAAwB,CACtC,IAAMC,EAAOF,KAAmB,IAChC,OAAO,KAAK,IAAI,EAAI,IAAOE,CAC7B,CA0BO,IAAMC,GAAN,KAAmB,CACxB,YAA6BC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAQ7B,sBAAsBC,EAAyC,CAC7D,IAAMC,EAAQD,EAAI,IAAMJ,GAAc,EACtC,OAAO,KAAK,GAAG,QACb,0UACF,EAAE,KACAI,EAAI,WACJA,EAAI,UACJC,EACAD,EAAI,iBAAmB,KACvBA,EAAI,UACJA,EAAI,aAAe,KACnBA,EAAI,OACJA,EAAI,YACJA,EAAI,OACJA,EAAI,QAAU,KACdA,EAAI,QAAU,KACdA,EAAI,YAAc,KAClBA,EAAI,KAAO,KACXA,EAAI,UAAY,KAChBA,EAAI,SAAW,KACfA,EAAI,mBAAqB,KACzBA,EAAI,cAAgB,KACpBA,EAAI,UAAY,KAChBA,EAAI,iBAAmB,EACvBA,EAAI,YAAc,KAClBA,EAAI,mBAAqB,IAC3B,CACF,CAQA,MAAM,OAAOA,EAAsC,CAEjD,OADe,MAAM,KAAK,sBAAsBA,CAAG,EAAE,IAAI,GAC3C,OAChB,CASA,MAAM,QAAQE,EAAmBC,EAAmD,CAClF,IAAIC,EAAa,GACbD,EAAQ,OACVC,EAAa;AAAA;AAAA;AAAA;AAAA,QAMbA,EAAa;AAAA;AAAA;AAAA;AAAA;AAAA,QAQf,IAAMC,EAAyB,CAAC,kBAAkB,EAC5CC,EAA8B,CAACJ,CAAS,EAG1CC,EAAQ,kBACVE,EAAa,KAAK,uBAAuB,EACzCC,EAAO,KAAKH,EAAQ,eAAe,GAEjCA,EAAQ,SACVE,EAAa,KAAK,cAAc,EAChCC,EAAO,KAAKH,EAAQ,MAAM,GAExBA,EAAQ,cACVE,EAAa,KAAK,mBAAmB,EACrCC,EAAO,KAAKH,EAAQ,YAAY,YAAY,CAAC,GAE3CA,EAAQ,SACVE,EAAa,KAAK,cAAc,EAChCC,EAAO,KAAKH,EAAQ,MAAM,GAExBA,EAAQ,SACVE,EAAa,KAAK,cAAc,EAChCC,EAAO,KAAKH,EAAQ,MAAM,GAExBA,EAAQ,cACVE,EAAa,KAAK,mBAAmB,EACrCC,EAAO,KAAKH,EAAQ,WAAW,GAE7BA,EAAQ,SACVE,EAAa,KAAK,iBAAiB,EACnCC,EAAO,KAAK,IAAIH,EAAQ,MAAM,GAAG,GAE/BA,EAAQ,SACVE,EAAa,KAAK,iBAAiB,EACnCC,EAAO,KAAKH,EAAQ,MAAM,GAG5BE,EAAa,KAAK,kBAAkB,EACpCC,EAAO,KAAKH,EAAQ,KAAK,EACzBE,EAAa,KAAK,kBAAkB,EACpCC,EAAO,KAAKH,EAAQ,KAAK,EAErBA,EAAQ,eACVE,EAAa,KAAK,iFAAiF,EACnGC,EAAO,KAAKH,EAAQ,aAAa,YAAY,CAAC,GAE5CA,EAAQ,MACVE,EAAa,KAAK,+DAA+D,EACjFC,EAAO,KAAKH,EAAQ,GAAG,GAGzB,IAAII,EAAWH,EAAa,UAAYC,EAAa,KAAK,OAAO,EAEjE,GAAIF,EAAQ,OACVI,GAAY,4CACP,CACL,IAAIC,EAAQL,EAAQ,QAAU,QAAa,CAAC,MAAMA,EAAQ,KAAK,GAAKA,EAAQ,MAAQ,EAAIA,EAAQ,MAAQ,GACpGK,EAAQ,MACVA,EAAQ,KAEVD,GAAY,oCAAoCC,CAAK,EACvD,CAEA,GAAM,CAAE,QAAAC,CAAQ,EAAI,MAAM,KAAK,GAAG,QAAQF,CAAQ,EAAE,KAAK,GAAGD,CAAM,EAAE,IAAmB,EACvF,OAAOG,CACT,CAUA,MAAM,OAAOP,EAAmBD,EAAeS,EAAmD,CAChG,OAAIA,IAAc,QAAa,CAAC,MAAMA,CAAS,EACtC,MAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAO5B,EACE,KAAKR,EAAWQ,EAAWT,CAAK,EAChC,MAA4B,EAG1B,MAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAQ5B,EACE,KAAKC,EAAWD,CAAK,EACrB,MAA4B,CACjC,CAQA,MAAM,cAAcU,EAAmC,CAKrD,OAJe,MAAM,KAAK,GACvB,QAAQ,mFAAmF,EAC3F,KAAKA,CAAO,EACZ,IAAI,GACO,OAChB,CAUA,MAAM,QAAQT,EAAmBU,EAA4BC,EAAU,IAAuB,CAC5F,IAAIC,EAAe,EACbC,EAAY,KAAK,IAAI,IAAMF,CAAO,EACxC,KAAOC,EAAeD,GAAS,CAC7B,IAAMG,EAAe,KAAK,IAAID,EAAWF,EAAUC,CAAY,EAQzDG,GAPS,MAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,OAIpC,EACE,KAAKf,EAAWU,EAAoBI,CAAY,EAChD,IAAI,GACc,KAAK,SAAW,EAErC,GADAF,GAAgBG,EACZA,EAAQD,EACV,KAEJ,CACA,OAAOF,CACT,CACF,EC5PO,IAAMI,GAAN,KAA0B,CAC/B,YAA6BC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAK7B,MAAM,oBAAoBC,EAA4C,CACpE,OAAO,IACT,CAKA,MAAM,0BAA0BA,EAA4C,CAC1E,OAAO,IACT,CAKA,MAAM,+BAA+BA,EAA4C,CAC/E,OAAO,IACT,CAMA,MAAM,uBACJC,EACAC,EACAC,EAAkB,EACD,CACjB,MAAO,EACT,CACF,EC3BO,IAAMC,GAAN,KAAkB,CACvB,YAA6BC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAQ7B,MAAM,QAAQC,EAA0C,CACtD,GAAI,CACF,OAAO,MAAM,KAAK,GACf,QAAQ,kFAAkF,EAC1F,KAAKA,CAAE,EACP,MAA2B,CAChC,OAASC,EAAK,CACZ,eAAQ,MAAM,yCAAyCD,CAAE,IAAKC,CAAG,EAC1D,IACT,CACF,CAQA,MAAM,cAAcD,EAA8B,CAChD,GAAI,CAKF,OAJY,MAAM,KAAK,GACpB,QAAQ,iDAAiD,EACzD,KAAKA,CAAE,EACP,MAA4B,IAChB,IACjB,OAASC,EAAK,CACZ,eAAQ,MAAM,wDAAwDD,CAAE,IAAKC,CAAG,EACzE,EACT,CACF,CAeA,MAAM,eAAeC,EAAkB,KAAMC,EAAa,IAAuB,CAC/E,IAAMC,EAAkB,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAAIF,EACxD,GAAI,CACF,IAAMG,EAAS,MAAM,KAAK,GACvB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAUR,EACA,KAAKD,EAAiBD,CAAU,EAChC,IAAI,EAGP,OADqBE,EAAO,MAAM,SAAWA,EAAO,MAAM,cAAgB,CAE5E,OAASJ,EAAK,CACZ,eAAQ,MAAM,qDAAsDA,CAAG,EAChE,CACT,CACF,CAUA,MAAM,sBAAsBK,EAAmBJ,EAAkB,KAAuB,CACtF,IAAME,EAAkB,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAAIF,EACxD,GAAI,CACF,IAAMG,EAAS,MAAM,KAAK,GACvB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAOR,EACA,KAAKC,EAAWF,CAAe,EAC/B,IAAI,EAGP,OADqBC,EAAO,MAAM,SAAWA,EAAO,MAAM,cAAgB,CAE5E,OAASJ,EAAK,CACZ,eAAQ,MAAM,+DAA+DK,CAAS,IAAKL,CAAG,EACvF,CACT,CACF,CACF,ECrHO,IAAMM,GAAN,KAAwB,CAG7B,YACmBC,EACjBC,EACA,CAFiB,QAAAD,EAGjB,KAAK,cAAgBC,GAAiB,IAAIC,GAAoBF,CAAE,CAClE,CAJmB,GAHF,cA0BjB,MAAM,cACJG,EAAmB,GACnBC,EAAa,IACbC,EAAc,IACC,CACf,GAAI,CACF,GAAM,CAAE,QAASC,CAAS,EAAI,MAAM,KAAK,GAAG,QAC1C,mCACF,EAAE,IAAsC,EAExC,GAAI,CAACA,GAAYA,EAAS,SAAW,EACnC,OAIF,IAAMC,EAAY,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,EAClDC,EAAe,EAEnB,GAAIH,EAAc,EAChB,GAAI,CACF,IAAMI,EAAY,MAAM,KAAK,cAAc,IAAI,wBAAwB,EACjEC,EAAa,MAAM,KAAK,cAAc,IAAI,yBAAyB,EACrED,IAAcF,GAAaG,IAC7BF,EAAe,SAASE,EAAY,EAAE,GAAK,EAE/C,OAASC,EAAK,CACZ,QAAQ,KAAK,6DAA8DA,CAAG,CAChF,CAGF,IAAMC,EAAkBP,EAAc,EAAI,KAAK,IAAI,EAAGA,EAAcG,CAAY,EAAIJ,EAC9ES,EAAgBR,GAAe,GAAKO,EAAkB,EAExDP,EAAc,GAAKO,GAAmB,GACxC,QAAQ,IACN,4DAA4DJ,CAAY,IAAIH,CAAW,2CAA2CE,CAAS,GAC7I,EAIF,IAAMO,EAAsBT,EAAc,EAAI,KAAK,IAAID,EAAYQ,CAAe,EAAIR,EAChFW,EAAkB,KAAK,IAAI,EAAG,KAAK,MAAMD,EAAsBR,EAAS,MAAM,CAAC,EAE/EU,EAA6C,CAAC,EAEpD,QAAWC,KAAWX,EAAU,CAE9B,IAAIY,EAAO,GACX,GAAI,CACF,IAAMC,EAAW,KAAK,MAAMF,EAAQ,QAAQ,EACxCE,GAAU,oBAAsB,OAClCD,EAAO,OAAOC,EAAS,kBAAkB,EAE7C,MAAQ,CAER,CAGA,IAAMC,EAAgB,KAAK,IAAIF,EAAMf,CAAgB,EAC/CkB,EAAY,KAAK,MAAM,KAAK,IAAI,EAAI,IAAOD,EAAgB,GAAK,IAAI,EAGtEP,GAAiBC,EAAsB,GACzCE,EAAoB,KAClB,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,aAIf,EAAE,KAAKC,EAAQ,GAAII,EAAWN,CAAe,CAChD,CAEJ,CAGA,IAAMO,EAAa,GACfC,EAAmB,EACvB,GAAIP,EAAoB,OAAS,EAAG,CAClC,QAASQ,EAAI,EAAGA,EAAIR,EAAoB,OAAQQ,GAAKF,EAAY,CAC/D,IAAMG,EAAQT,EAAoB,MAAMQ,EAAGA,EAAIF,CAAU,EACnDI,EAAU,MAAM,KAAK,GAAG,MAAMD,CAAK,EACzC,GAAI,MAAM,QAAQC,CAAO,EACvB,QAAWC,KAAOD,EACZC,GAAK,MAAM,UAAY,OACzBJ,GAAoBI,EAAI,KAAK,QACpBA,GAAK,MAAM,eAAiB,SACrCJ,GAAoBI,EAAI,KAAK,aAIrC,CAGA,GAAItB,EAAc,EAAG,CACnB,IAAMuB,EAAepB,EAAee,EACpC,GAAI,CACF,MAAM,KAAK,cAAc,QAAQ,CAC/B,uBAAwBhB,EACxB,wBAAyB,OAAOqB,CAAY,CAC9C,CAAC,CACH,OAASjB,EAAK,CACZ,QAAQ,KAAK,6DAA8DA,CAAG,CAChF,CACF,CACF,CAKA,GAAI,CAEF,IAAMkB,EAAoB,MADN,IAAIC,GAAY,KAAK,EAAE,EACC,eAAe,EACvDD,EAAoB,GACtB,QAAQ,IACN,kCAAkCA,CAAiB,gDACrD,CAEJ,OAASE,EAAQ,CACf,QAAQ,KAAK,2DAA4DA,CAAM,CACjF,CAEA,QAAQ,IACN,gDAAgDzB,EAAS,MAAM,iCAAiCH,CAAgB,iBAAiBC,CAAU,eAAeU,CAAmB,gBAAgBC,CAAe,kBAAkBV,CAAW,YAAYG,EAAee,CAAgB,IAAIlB,CAAW,GACrS,CACF,OAAS2B,EAAQ,CACf,QAAQ,MAAM,4CAA6CA,EAAE,SAAWA,CAAC,CAC3E,CACF,CACF,EC1JO,IAAMC,GAAN,KAA0B,CAC/B,YAA6BC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAY7B,MAAM,WACJC,EACAC,EACAC,EACAC,EACAC,EAC8B,CAC9B,IAAIC,EACF,wGACIC,EAA8B,CAACN,EAAWC,EAAOC,CAAK,EACxDC,IACFE,GAAY,qBACZC,EAAO,KAAK,IAAIH,CAAM,GAAG,GAEvBC,IACFC,GAAY,2BACZC,EAAO,KAAKF,CAAa,GAE3BC,GAAY,mBACZ,GAAM,CAAE,QAAAE,CAAQ,EAAI,MAAM,KAAK,GAC5B,QAAQF,CAAQ,EAChB,KAAK,GAAGC,CAAM,EACd,IAAuB,EAC1B,OAAOC,CACT,CAYA,MAAM,SACJP,EACAC,EACAC,EACAM,EACAJ,EACiC,CASjC,IAAMK,EAPiB,IAAI,IAAI,CAC7B,oBACA,sBACA,sBACA,wBACA,yBACF,CAAC,EACmC,IAAID,EAAS,KAAK,CAAC,EAAIA,EAAS,KAAK,EAAI,wBAEzEH,EAAW,UAAUI,CAAY,gHAC/BH,EAA8B,CAACN,EAAWC,EAAOC,CAAK,EACxDE,IACFC,GAAY,2BACZC,EAAO,KAAKF,CAAa,GAE3BC,GAAY,aAAaI,CAAY,kCACrC,GAAM,CAAE,QAAAF,CAAQ,EAAI,MAAM,KAAK,GAC5B,QAAQF,CAAQ,EAChB,KAAK,GAAGC,CAAM,EACd,IAA0B,EAC7B,OAAOC,CACT,CACF,EC/EO,IAAMG,GAAN,KAAyB,CAC9B,YAA6BC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAW7B,MAAM,cACJC,EACAC,EACAC,EACAC,EAC8B,CAC9B,IAAIC,EACF,4HACIC,EAA8B,CAACL,EAAWC,EAAOC,CAAK,EACxDC,IACFC,GAAY,2BACZC,EAAO,KAAKF,CAAa,GAE3BC,GAAY,gDACZ,GAAM,CAAE,QAAAE,CAAQ,EAAI,MAAM,KAAK,GAC5B,QAAQF,CAAQ,EAChB,KAAK,GAAGC,CAAM,EACd,IAAuB,EAC1B,OAAOC,CACT,CAWA,MAAM,cACJN,EACAC,EACAC,EACAC,EAC8B,CAC9B,IAAIC,EACF,4IACIC,EAA8B,CAACL,EAAWC,EAAOC,CAAK,EACxDC,IACFC,GAAY,2BACZC,EAAO,KAAKF,CAAa,GAE3BC,GAAY,gDACZ,GAAM,CAAE,QAAAE,CAAQ,EAAI,MAAM,KAAK,GAC5B,QAAQF,CAAQ,EAChB,KAAK,GAAGC,CAAM,EACd,IAAuB,EAC1B,OAAOC,CACT,CACF,EC9DO,IAAMC,GAAN,KAAyB,CAC9B,YAA6BC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAW7B,MAAM,WACJC,EACAC,EACAC,EACAC,EAC8B,CAC9B,IAAIC,EACF,wHACIC,EAA8B,CAACL,EAAWC,EAAOC,CAAK,EACxDC,IACFC,GAAY,2BACZC,EAAO,KAAKF,CAAa,GAE3BC,GAAY,gEACZ,GAAM,CAAE,QAAAE,CAAQ,EAAI,MAAM,KAAK,GAC5B,QAAQF,CAAQ,EAChB,KAAK,GAAGC,CAAM,EACd,IAAuB,EAC1B,OAAOC,CACT,CACF,EChCO,IAAMC,GAAN,KAA8B,CACnC,YAA6BC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAY7B,MAAM,gBACJC,EACAC,EACAC,EACAC,EACAC,EAAQ,IAC2B,CACnC,IAAIC,EAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQTC,EAA8B,CAACN,EAAWC,EAAOC,CAAK,EACxDC,IACFE,GAAY,2BACZC,EAAO,KAAKH,CAAa,GAE3BE,GAAY,oDAAoDD,CAAK,GACrE,GAAM,CAAE,QAAAG,CAAQ,EAAI,MAAM,KAAK,GAC5B,QAAQF,CAAQ,EAChB,KAAK,GAAGC,CAAM,EACd,IAA4B,EAC/B,OAAOC,CACT,CAaA,MAAM,gBACJP,EACAQ,EACAP,EACAC,EACAC,EACAC,EAAQ,IACmB,CAC3B,IAAIC,EAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAYTC,EAA8B,CAACN,EAAWC,EAAOC,CAAK,EACxDM,IACFH,GAAY,0FACZC,EAAO,KAAKE,EAAY,YAAY,CAAC,GAEnCL,IACFE,GAAY,2BACZC,EAAO,KAAKH,CAAa,GAE3BE,GAAY,4CAA4CD,CAAK,GAC7D,GAAM,CAAE,QAAAG,CAAQ,EAAI,MAAM,KAAK,GAC5B,QAAQF,CAAQ,EAChB,KAAK,GAAGC,CAAM,EACd,IAA4C,EAE/C,OAAOC,EAAQ,IAAKE,IAAO,CACzB,KAAMA,EAAE,MAAQ,UAChB,MAAOA,EAAE,KACX,EAAE,CACJ,CACF,ECxEO,IAAMC,GAAN,KAAwB,CAM7B,YACmBC,EACjBC,EACA,CAFiB,QAAAD,EAGjB,KAAK,QAAU,IAAIE,GAAoBF,CAAE,EACzC,KAAK,QAAU,IAAIG,GAAmBH,CAAE,EACxC,KAAK,QAAU,IAAII,GAAmBJ,CAAE,EACxC,KAAK,aAAe,IAAIK,GAAwBL,CAAE,CACpD,CAPmB,GANH,QACA,QACA,QACA,aAehB,MAAM,WACJM,EACAC,EACAC,EACAC,EACAC,EAC8B,CAC9B,OAAO,KAAK,QAAQ,WAAWJ,EAAWC,EAAOC,EAAOC,EAAQC,CAAa,CAC/E,CAKA,MAAM,SACJJ,EACAC,EACAC,EACAG,EACAD,EACiC,CACjC,OAAO,KAAK,QAAQ,SAASJ,EAAWC,EAAOC,EAAOG,EAAUD,CAAa,CAC/E,CAKA,MAAM,cACJJ,EACAC,EACAC,EACAE,EAC8B,CAC9B,OAAO,KAAK,QAAQ,cAAcJ,EAAWC,EAAOC,EAAOE,CAAa,CAC1E,CAKA,MAAM,cACJJ,EACAC,EACAC,EACAE,EAC8B,CAC9B,OAAO,KAAK,QAAQ,cAAcJ,EAAWC,EAAOC,EAAOE,CAAa,CAC1E,CAKA,MAAM,WACJJ,EACAC,EACAC,EACAE,EAC8B,CAC9B,OAAO,KAAK,QAAQ,WAAWJ,EAAWC,EAAOC,EAAOE,CAAa,CACvE,CAKA,MAAM,gBACJJ,EACAC,EACAC,EACAE,EACAE,EAAQ,IAC2B,CACnC,OAAO,KAAK,aAAa,gBAAgBN,EAAWC,EAAOC,EAAOE,EAAeE,CAAK,CACxF,CAKA,MAAM,gBACJN,EACAO,EACAN,EACAC,EACAE,EACAE,EAAQ,IACmB,CAC3B,OAAO,KAAK,aAAa,gBACvBN,EACAO,EACAN,EACAC,EACAE,EACAE,CACF,CACF,CAKA,MAAM,aACJN,EACAC,EACAC,EACAG,EACAD,EACmC,CACnC,GAAM,CAACI,EAASC,EAAOC,EAAYC,EAAYC,EAASC,CAAY,EAClE,MAAM,QAAQ,IAAI,CAChB,KAAK,WAAWb,EAAWC,EAAOC,EAAO,OAAWE,CAAa,EACjE,KAAK,SAASJ,EAAWC,EAAOC,EAAOG,EAAUD,CAAa,EAC9D,KAAK,cAAcJ,EAAWC,EAAOC,EAAOE,CAAa,EACzD,KAAK,cAAcJ,EAAWC,EAAOC,EAAOE,CAAa,EACzD,KAAK,WAAWJ,EAAWC,EAAOC,EAAOE,CAAa,EACtD,KAAK,gBAAgBJ,EAAWC,EAAOC,EAAOE,CAAa,CAC7D,CAAC,EACH,MAAO,CACL,QAAAI,EACA,MAAAC,EACA,YAAaC,EACb,YAAaC,EACb,QAAAC,EACA,aAAAC,CACF,CACF,CACF,ECzIO,IAAMC,GAAN,KAAe,CAOpB,YAA6BC,EAAgB,CAAhB,QAAAA,EAC3B,KAAK,KAAO,IAAIC,GAAaD,CAAE,EAC/B,KAAK,YAAc,IAAIE,GAAoBF,CAAE,EAC7C,KAAK,UAAY,IAAIG,GAAkBH,CAAE,EACzC,KAAK,UAAY,IAAII,GAAkBJ,EAAI,KAAK,WAAW,EAC3D,KAAK,QAAU,IAAIK,GAAYL,CAAE,CACnC,CAN6B,GANb,KACA,YACA,UACA,UACA,QAUhB,sBAAsBM,EAAyC,CAC7D,OAAO,KAAK,KAAK,sBAAsBA,CAAG,CAC5C,CAEA,MAAM,OAAOA,EAAsC,CACjD,OAAO,KAAK,KAAK,OAAOA,CAAG,CAC7B,CAEA,MAAM,QAAQC,EAAmBC,EAAmD,CAClF,OAAO,KAAK,KAAK,QAAQD,EAAWC,CAAO,CAC7C,CAEA,MAAM,OAAOD,EAAmBE,EAAeC,EAAmD,CAChG,OAAO,KAAK,KAAK,OAAOH,EAAWE,EAAOC,CAAS,CACrD,CAEA,MAAM,cAAcC,EAAmC,CACrD,OAAO,KAAK,KAAK,cAAcA,CAAO,CACxC,CAEA,MAAM,QAAQJ,EAAmBK,EAA4BC,EAAU,IAAuB,CAC5F,OAAO,KAAK,KAAK,QAAQN,EAAWK,EAAoBC,CAAO,CACjE,CAEA,MAAM,cAAcC,EAAmB,GAAIC,EAAa,IAAMC,EAAc,IAAsB,CAChG,OAAO,KAAK,UAAU,cAAcF,EAAkBC,EAAYC,CAAW,CAC/E,CAEA,MAAM,oBAAoBT,EAA2C,CACnE,OAAO,KAAK,YAAY,oBAAoBA,CAAS,CACvD,CAEA,MAAM,0BAA0BA,EAA2C,CACzE,OAAO,KAAK,YAAY,0BAA0BA,CAAS,CAC7D,CAEA,MAAM,+BAA+BA,EAA2C,CAC9E,OAAO,KAAK,YAAY,+BAA+BA,CAAS,CAClE,CAEA,MAAM,uBAAuBU,EAAmBC,EAAmBC,EAAiB,EAAoB,CACtG,OAAO,KAAK,YAAY,uBAAuBF,EAAUC,EAAUC,CAAc,CACnF,CAEA,MAAM,WACJZ,EACAa,EACAC,EACAC,EACAC,EAC8B,CAC9B,OAAO,KAAK,UAAU,WAAWhB,EAAWa,EAAOC,EAAOC,EAAQC,CAAa,CACjF,CAEA,MAAM,SACJhB,EACAa,EACAC,EACAG,EACAD,EACiC,CACjC,OAAO,KAAK,UAAU,SAAShB,EAAWa,EAAOC,EAAOG,EAAUD,CAAa,CACjF,CAEA,MAAM,cACJhB,EACAa,EACAC,EACAE,EAC8B,CAC9B,OAAO,KAAK,UAAU,cAAchB,EAAWa,EAAOC,EAAOE,CAAa,CAC5E,CAEA,MAAM,cACJhB,EACAa,EACAC,EACAE,EAC8B,CAC9B,OAAO,KAAK,UAAU,cAAchB,EAAWa,EAAOC,EAAOE,CAAa,CAC5E,CAEA,MAAM,WACJhB,EACAa,EACAC,EACAE,EAC8B,CAC9B,OAAO,KAAK,UAAU,WAAWhB,EAAWa,EAAOC,EAAOE,CAAa,CACzE,CAEA,MAAM,gBACJhB,EACAa,EACAC,EACAE,EACAE,EAAQ,IAC2B,CACnC,OAAO,KAAK,UAAU,gBAAgBlB,EAAWa,EAAOC,EAAOE,EAAeE,CAAK,CACrF,CAEA,MAAM,gBACJlB,EACAmB,EACAN,EACAC,EACAE,EACAE,EAAQ,IACmB,CAC3B,OAAO,KAAK,UAAU,gBAAgBlB,EAAWmB,EAAaN,EAAOC,EAAOE,EAAeE,CAAK,CAClG,CAEA,MAAM,aACJlB,EACAa,EACAC,EACAG,EACAD,EACmC,CACnC,OAAO,KAAK,UAAU,aAAahB,EAAWa,EAAOC,EAAOG,EAAUD,CAAa,CACrF,CACF,ECpKAI,KCOO,SAASC,GAAQC,EAAG,CAKvB,OAAQA,aAAa,YAChB,YAAY,OAAOA,CAAC,GACjBA,EAAE,YAAY,OAAS,cACvB,sBAAuBA,GACvBA,EAAE,oBAAsB,CACpC,CAGA,IAAMC,GAAUC,GAAWA,EAAQ,IAAIA,CAAK,KAAO,GAc5C,SAASC,GAAQ,EAAGD,EAAQ,GAAI,CACnC,GAAI,OAAO,GAAM,SACb,MAAM,IAAI,UAAUD,GAAOC,CAAK,EAAI,wBAA0B,OAAO,CAAC,EAC1E,GAAI,CAAC,OAAO,cAAc,CAAC,GAAK,EAAI,EAChC,MAAM,IAAI,WAAWD,GAAOC,CAAK,EAAI,8BAAgC,CAAC,EAC1E,OAAO,CACX,CAaO,SAASE,GAAMC,EAAOH,EAAQ,GAAI,CACrC,GAAI,OAAOG,GAAU,UACjB,MAAM,IAAI,UAAUJ,GAAOC,CAAK,EAAI,8BAAgC,OAAOG,CAAK,EACpF,OAAOA,CACX,CAeO,SAASC,EAAOD,EAAOE,EAAQL,EAAQ,GAAI,CAG9C,GAAIH,GAAQM,CAAK,IAAME,IAAW,QAAaF,EAAM,SAAWE,GAC5D,OAAOF,EAEPE,IAAW,QACXJ,GAAQI,EAAQ,QAAQ,EAC5B,IAAMC,EAAQT,GAAQM,CAAK,EACrBI,EAAQF,IAAW,OAAY,cAAcA,CAAM,GAAK,GACxDG,EAAMF,EAAQ,UAAUH,EAAM,MAAM,GAAK,QAAQ,OAAOA,CAAK,GAC7DM,EAAUV,GAAOC,CAAK,EAAI,sBAAwBO,EAAQ,SAAWC,EAC3E,MAAKF,EAEC,IAAI,WAAWG,CAAO,EADlB,IAAI,UAAUA,CAAO,CAEnC,CAgCO,SAASC,GAAMC,EAAG,CACrB,GAAI,OAAOA,GAAM,YAAc,OAAOA,EAAE,QAAW,WAC/C,MAAM,IAAI,UAAU,6CAA6C,EAKrE,GAJAC,GAAQD,EAAE,SAAS,EACnBC,GAAQD,EAAE,QAAQ,EAGdA,EAAE,UAAY,GAAKA,EAAE,SAAW,EAChC,MAAM,IAAI,MAAM,wCAAwC,CAChE,CACA,IAAME,GAAU,CAACC,EAAOC,IAAU,CAC9B,GAAID,IAAU,MAAQ,OAAOA,GAAU,UAAY,MAAM,QAAQA,CAAK,EAClE,MAAM,IAAI,WAAWC,IAAU,SAAW,GAAK,IAAIA,CAAK,MAAQ,6BAA+B,OAAOD,CAAK,CACnH,EACME,GAAQ,CAACF,EAAOC,IAAU,CAC5BF,GAAQC,EAAOC,CAAK,EACpB,IAAME,EAAQ,OAAO,eAAeH,CAAK,EACzC,GAAIG,IAAU,OAAO,WAAaA,IAAU,KACxC,MAAM,IAAI,UAAU,IAAIF,CAAK,yBAAyB,EAG1D,GAAI,OAAO,OAAOD,EAAO,WAAW,EAChC,MAAM,IAAI,UAAU,IAAIC,CAAK,4BAA4B,CACjE,EAeO,SAASG,GAAQC,EAAUC,EAAgB,GAAM,CAGpD,GAAID,EAAS,UACT,MAAM,IAAI,MAAM,oBAAoB,EACxC,GAAIC,GAAiBD,EAAS,SAC1B,MAAM,IAAI,MAAM,6BAA6B,CACrD,CAiBO,SAASE,GAAQC,EAAKH,EAAU,CACnCI,EAAOD,EAAK,OAAW,QAAQ,EAG/B,IAAME,EAAML,EAAS,UACrB,GAAI,EAAEG,EAAI,QAAUE,GAChB,MAAM,IAAI,WAAW,+BAAiCA,CAAG,CAEjE,CA0BO,SAASC,GAAIC,EAAK,CACrB,OAAO,IAAI,YAAYA,EAAI,OAAQA,EAAI,WAAY,KAAK,MAAMA,EAAI,WAAa,CAAC,CAAC,CACrF,CAUO,SAASC,MAASC,EAAQ,CAC7B,QAASC,EAAI,EAAGA,EAAID,EAAO,OAAQC,IAC/BD,EAAOC,CAAC,EAAE,KAAK,CAAC,CAExB,CAWO,SAASC,GAAWJ,EAAK,CAC5B,OAAO,IAAI,SAASA,EAAI,OAAQA,EAAI,WAAYA,EAAI,UAAU,CAClE,CAYO,SAASK,GAAKC,EAAMC,EAAO,CAC9B,OAAQD,GAAS,GAAKC,EAAWD,IAASC,CAC9C,CAgBO,IAAMC,GAA8B,IAAI,WAAW,IAAI,YAAY,CAAC,SAAU,CAAC,EAAE,MAAM,EAAE,CAAC,IAAM,GAWhG,SAASC,GAASC,EAAM,CAC3B,OAAUA,GAAQ,GAAM,WAClBA,GAAQ,EAAK,SACbA,IAAS,EAAK,MACdA,IAAS,GAAM,GACzB,CAwBO,SAASC,GAAWC,EAAK,CAC5B,QAASC,EAAI,EAAGA,EAAID,EAAI,OAAQC,IAC5BD,EAAIC,CAAC,EAAIC,GAASF,EAAIC,CAAC,CAAC,EAE5B,OAAOD,CACX,CAYO,IAAMG,GAAaC,GACnBC,GAAMA,EACPN,GAEAO,GAEN,OAAO,WAAW,KAAK,CAAC,CAAC,EAAE,OAAU,YAAc,OAAO,WAAW,SAAY,WAE3EC,GAAwB,MAAM,KAAK,CAAE,OAAQ,GAAI,EAAG,CAACC,EAAGP,IAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAc5F,SAASQ,GAAWC,EAAO,CAG9B,GAFAC,EAAOD,CAAK,EAERJ,GACA,OAAOI,EAAM,MAAM,EAEvB,IAAIE,EAAM,GACV,QAASX,EAAI,EAAGA,EAAIS,EAAM,OAAQT,IAC9BW,GAAOL,GAAMG,EAAMT,CAAC,CAAC,EAEzB,OAAOW,CACX,CAIA,SAASC,GAAcC,EAAI,CACvB,OAAOA,GAAM,IAAMA,GAAM,GAAKA,EAAK,GAC7BA,GAAM,IAAMA,GAAM,GAAKA,EAAM,GACzBA,GAAM,IAAMA,GAAM,IAAMA,EAAM,GAC1B,MAClB,CAaO,SAASC,GAAWH,EAAK,CAC5B,GAAI,OAAOA,GAAQ,SACf,MAAM,IAAI,UAAU,4BAA8B,OAAOA,CAAG,EAChE,GAAIN,GACA,GAAI,CACA,OAAO,WAAW,QAAQM,CAAG,CACjC,OACOI,EAAO,CACV,MAAIA,aAAiB,YACX,IAAI,WAAWA,EAAM,OAAO,EAChCA,CACV,CAEJ,IAAMC,EAAKL,EAAI,OACTM,EAAKD,EAAK,EAChB,GAAIA,EAAK,EACL,MAAM,IAAI,WAAW,mDAAqDA,CAAE,EAChF,IAAME,EAAQ,IAAI,WAAWD,CAAE,EAC/B,QAASE,EAAK,EAAGC,EAAK,EAAGD,EAAKF,EAAIE,IAAMC,GAAM,EAAG,CAC7C,IAAMC,EAAKT,GAAcD,EAAI,WAAWS,CAAE,CAAC,EACrCE,EAAKV,GAAcD,EAAI,WAAWS,EAAK,CAAC,CAAC,EAC/C,GAAIC,IAAO,QAAaC,IAAO,OAAW,CACtC,IAAMC,EAAOZ,EAAIS,CAAE,EAAIT,EAAIS,EAAK,CAAC,EACjC,MAAM,IAAI,WAAW,+CAAiDG,EAAO,cAAgBH,CAAE,CACnG,CACAF,EAAMC,CAAE,EAAIE,EAAK,GAAKC,CAC1B,CACA,OAAOJ,CACX,CAgHO,SAASM,MAAeC,EAAQ,CACnC,IAAIC,EAAM,EACV,QAASC,EAAI,EAAGA,EAAIF,EAAO,OAAQE,IAAK,CACpC,IAAMC,EAAIH,EAAOE,CAAC,EAClBE,EAAOD,CAAC,EACRF,GAAOE,EAAE,MACb,CACA,IAAME,EAAM,IAAI,WAAWJ,CAAG,EAC9B,QAASC,EAAI,EAAGI,EAAM,EAAGJ,EAAIF,EAAO,OAAQE,IAAK,CAC7C,IAAMC,EAAIH,EAAOE,CAAC,EAClBG,EAAI,IAAIF,EAAGG,CAAG,EACdA,GAAOH,EAAE,MACb,CACA,OAAOE,CACX,CAsDO,SAASE,GAAUC,EAAUC,EAAMC,EAAQ,OAAQ,CACtD,OAAAC,GAAMH,EAAU,UAAU,EACtBC,IAAS,QACTE,GAAMF,EAAMC,CAAK,EAGN,OAAO,OAAO,OAAO,OAAO,IAAI,EAAGF,EAAUC,CAAI,CAEpE,CAmBO,SAASG,GAAaC,EAAUC,EAAO,CAAC,EAAG,CAC9C,GAAI,OAAOD,GAAa,WACpB,MAAM,IAAI,UAAU,0CAA4C,OAAOA,CAAQ,EACnFC,EAAOP,GAAU,CAAC,EAAGO,EAAM,MAAM,EACjC,IAAMC,EAAQ,CAACC,EAAKP,IAASI,EAASJ,CAAI,EACrC,OAAOO,CAAG,EACV,OAAO,EACNC,EAAMJ,EAAS,MAAS,EAC9B,OAAAE,EAAM,UAAYE,EAAI,UACtBF,EAAM,SAAWE,EAAI,SACrBF,EAAM,OAASE,EAAI,OACnBF,EAAM,OAAUN,GAASI,EAASJ,CAAI,EACtC,OAAO,OAAOM,EAAOD,CAAI,EAClB,OAAO,OAAOC,CAAK,CAC9B,CAiBO,SAASG,GAAYC,EAAc,GAAI,CAE1CC,GAAQD,EAAa,aAAa,EAClC,IAAME,EAAK,OAAO,YAAe,SAAW,WAAW,OAAS,KAChE,GAAI,OAAOA,GAAI,iBAAoB,WAC/B,MAAM,IAAI,MAAM,wCAAwC,EAM5D,GAAIF,EAAc,MACd,MAAM,IAAI,WAAW,wCAAwCA,CAAW,EAAE,EAC9E,OAAOE,EAAG,gBAAgB,IAAI,WAAWF,CAAW,CAAC,CACzD,CAaO,IAAMG,GAAWC,IAAY,CAGhC,IAAK,WAAW,KAAK,CAAC,EAAM,EAAM,GAAM,IAAM,GAAM,EAAM,IAAM,EAAM,EAAM,EAAMA,CAAM,CAAC,CAC7F,GC3pBO,SAASC,GAAOC,EAAMC,EAAOC,EAAQ,IAAM,CAAE,EAAG,CACnD,GAAI,CAAC,MAAM,QAAQF,CAAI,EACnB,MAAM,IAAI,UAAU,IAAIC,CAAK,8BAA8B,OAAOD,CAAI,EAAE,EAC5E,QAASG,EAAI,EAAGA,EAAIH,EAAK,OAAQG,IAC7BD,EAAMF,EAAKG,CAAC,EAAG,GAAGF,CAAK,IAAIE,CAAC,GAAG,EACnC,OAAOH,CACX,CAcO,IAAMI,GAAS,CAACC,EAAOC,EAAQL,IAAUG,EAAQC,EAAOC,EAAQL,CAAK,EAa/DM,GAAUA,GAchB,SAASC,GAAQH,EAAOJ,EAAQ,GAAI,CACvC,GAAI,OAAOI,GAAU,SAAU,CAC3B,IAAMI,EAASR,GAAS,IAAIA,CAAK,KACjC,MAAM,IAAI,UAAUQ,EAAS,6BAA+B,OAAOJ,CAAK,CAC5E,CACA,OAAOA,CACX,CAcO,SAASK,GAAQL,EAAOJ,EAAQ,SAAU,CAC7C,GAAII,IAAU,MAAQ,OAAOA,GAAU,UAAY,MAAM,QAAQA,CAAK,EAClE,MAAM,IAAI,UAAUJ,IAAU,SACxB,gCACA,IAAIA,CAAK,+BAA+B,OAAOI,CAAK,EAAE,EAChE,OAAOA,CACX,CAcO,SAASM,GAAUN,EAAOJ,EAAO,CACpC,GAAI,OAAOI,GAAU,WACjB,MAAM,IAAI,UAAU,IAAIJ,CAAK,wCAAwC,OAAOI,CAAK,EAAE,EACvF,OAAOA,CACX,CAYO,IAAMO,GAAaA,GAYbC,GAAc,IAAIC,IAAWD,GAAa,GAAGC,CAAM,EAYnDC,GAAcC,GAAQD,GAAYC,CAAG,EAYrCC,GAAUA,GAYVC,GAAeC,GAAgBD,GAAaC,CAAW,EAC9DC,GAAsB,OAAO,CAAC,EAC9BC,GAAsB,OAAO,CAAC,EAG9BC,GAAUrB,GAAWA,EAAQ,IAAIA,CAAK,KAAO,GAc5C,SAASsB,GAAMlB,EAAOJ,EAAQ,GAAI,CACrC,GAAI,OAAOI,GAAU,UACjB,MAAM,IAAI,UAAUiB,GAAOrB,CAAK,EAAI,8BAAgC,OAAOI,CAAK,EACpF,OAAOA,CACX,CAaO,SAASmB,GAAW,EAAG,CAC1B,GAAI,OAAO,GAAM,UACb,GAAI,CAACC,GAAS,CAAC,EACX,MAAM,IAAI,WAAW,iCAAmC,CAAC,OAG7DlB,GAAQ,CAAC,EACb,OAAO,CACX,CAcO,SAASmB,GAAYrB,EAAOJ,EAAQ,GAAI,CAC3C,GAAI,OAAOI,GAAU,SAAU,CAC3B,IAAMI,EAASR,GAAS,IAAIA,CAAK,KACjC,MAAM,IAAI,UAAUQ,EAAS,6BAA+B,OAAOJ,CAAK,CAC5E,CACA,GAAI,CAAC,OAAO,cAAcA,CAAK,EAAG,CAC9B,IAAMI,EAASR,GAAS,IAAIA,CAAK,KACjC,MAAM,IAAI,WAAWQ,EAAS,8BAAgCJ,CAAK,CACvE,CACJ,CAeO,SAASsB,GAAoBC,EAAK,CACrC,IAAMZ,EAAMQ,GAAWI,CAAG,EAAE,SAAS,EAAE,EACvC,OAAOZ,EAAI,OAAS,EAAI,IAAMA,EAAMA,CACxC,CAeO,SAASa,GAAYb,EAAK,CAC7B,GAAI,OAAOA,GAAQ,SACf,MAAM,IAAI,UAAU,4BAA8B,OAAOA,CAAG,EAChE,OAAOA,IAAQ,GAAKI,GAAM,OAAO,KAAOJ,CAAG,CAC/C,CAcO,SAASc,GAAgBC,EAAO,CACnC,OAAOF,GAAYjB,GAAYmB,CAAK,CAAC,CACzC,CAaO,SAASC,GAAgBD,EAAO,CACnC,OAAOF,GAAYjB,GAAYqB,GAAU7B,EAAQ2B,CAAK,CAAC,EAAE,QAAQ,CAAC,CAAC,CACvE,CAeO,SAASG,GAAgB,EAAGC,EAAK,CAEpC,GADA5B,GAAS4B,CAAG,EACRA,IAAQ,EACR,MAAM,IAAI,MAAM,+BAA+B,EACnD,EAAIX,GAAW,CAAC,EAChB,IAAMY,EAAcD,EAAM,EACpBnB,EAAM,EAAE,SAAS,EAAE,EAEzB,GAAIA,EAAI,OAASoB,EACb,MAAM,IAAI,WAAW,qBAAqB,EAC9C,OAAOrB,GAAYC,EAAI,SAASoB,EAAa,GAAG,CAAC,CACrD,CAeO,SAASC,GAAgB,EAAGF,EAAK,CACpC,OAAOD,GAAgB,EAAGC,CAAG,EAAE,QAAQ,CAC3C,CAoDO,SAASG,GAAUC,EAAO,CAG7B,OAAO,WAAW,KAAKC,GAAOD,CAAK,CAAC,CACxC,CAeO,SAASE,GAAaC,EAAO,CAChC,GAAI,OAAOA,GAAU,SACjB,MAAM,IAAI,UAAU,8BAAgC,OAAOA,CAAK,EACpE,OAAO,WAAW,KAAKA,EAAO,CAACC,EAAGC,IAAM,CACpC,IAAMC,EAAWF,EAAE,WAAW,CAAC,EAC/B,GAAIA,EAAE,SAAW,GAAKE,EAAW,IAC7B,MAAM,IAAI,WAAW,wCAAwCH,EAAME,CAAC,CAAC,eAAeC,CAAQ,gBAAgBD,CAAC,EAAE,EAEnH,OAAOC,CACX,CAAC,CACL,CAYO,SAASC,GAAS,EAAG,CACxB,OAAO,OAAO,GAAM,UAAYC,IAAO,CAC3C,CAcO,SAASC,GAAQ,EAAGC,EAAKC,EAAK,CACjC,OAAOJ,GAAS,CAAC,GAAKA,GAASG,CAAG,GAAKH,GAASI,CAAG,GAAKD,GAAO,GAAK,EAAIC,CAC5E,CAiBO,SAASC,GAASC,EAAOC,EAAGJ,EAAKC,EAAK,CAMzC,GAAI,CAACF,GAAQK,EAAGJ,EAAKC,CAAG,EACpB,MAAM,IAAI,WAAW,kBAAoBE,EAAQ,KAAOH,EAAM,WAAaC,EAAM,SAAWG,CAAC,CACrG,CAgBO,SAASC,GAAO,EAAG,CAGtB,GAAI,EAAIP,GACJ,MAAM,IAAI,MAAM,qCAAuC,CAAC,EAE5D,OAAO,IAAMA,GAAM,EAAI,EAAE,SAAS,CAAC,EAAE,MACzC,CA2DO,IAAMQ,GAAW,IACpBC,GAAY,EAAG,GAAG,GACVC,IAAO,OAAO,CAAC,GAAKA,IAwBzB,SAASC,GAAeC,EAASC,EAAUC,EAAQ,CAGtD,GAFAC,GAASH,EAAS,SAAS,EAC3BG,GAASF,EAAU,UAAU,EACzB,OAAOC,GAAW,WAClB,MAAM,IAAI,UAAU,2BAA2B,EAEnD,IAAME,EAAOC,GAAQ,IAAI,WAAWA,CAAG,EACjCC,EAAO,WAAW,GAAG,EACrBC,EAAQ,WAAW,GAAG,CAAI,EAC1BC,EAAQ,WAAW,GAAG,CAAI,EAC1BC,EAAgB,IAGlBC,EAAIN,EAAIJ,CAAO,EAEfW,EAAIP,EAAIJ,CAAO,EACfY,EAAI,EACFC,EAAQ,IAAM,CAChBH,EAAE,KAAK,CAAC,EACRC,EAAE,KAAK,CAAC,EACRC,EAAI,CACR,EAEME,EAAI,IAAIC,IAASb,EAAOS,EAAGK,GAAYN,EAAG,GAAGK,CAAI,CAAC,EAClDE,EAAS,CAACC,EAAOZ,IAAS,CAE5BK,EAAIG,EAAEP,EAAOW,CAAI,EACjBR,EAAII,EAAE,EACFI,EAAK,SAAW,IAEpBP,EAAIG,EAAEN,EAAOU,CAAI,EACjBR,EAAII,EAAE,EACV,EACMK,EAAM,IAAM,CAEd,GAAIP,KAAOH,EACP,MAAM,IAAI,MAAM,sCAAsC,EAC1D,IAAIJ,EAAM,EACJe,EAAM,CAAC,EACb,KAAOf,EAAMJ,GAAU,CACnBS,EAAII,EAAE,EACN,IAAMO,EAAKX,EAAE,MAAM,EACnBU,EAAI,KAAKC,CAAE,EACXhB,GAAOK,EAAE,MACb,CACA,OAAOM,GAAY,GAAGI,CAAG,CAC7B,EAWA,MAViB,CAACF,EAAMI,IAAS,CAC7BT,EAAM,EACNI,EAAOC,CAAI,EACX,IAAIK,EAEJ,MAAQA,EAAMD,EAAKH,EAAI,CAAC,KAAO,QAC3BF,EAAO,EACX,OAAAJ,EAAM,EACCU,CACX,CAEJ,CAmBO,SAASC,GAAeC,EAAQC,EAAS,CAAC,EAAGC,EAAY,CAAC,EAAGC,EAAQ,SAAU,CAClFC,GAAQJ,EAAQG,CAAK,EACrBC,GAAQH,EAAQ,QAAQ,EACxBG,GAAQF,EAAW,WAAW,EAC9B,SAASG,EAAWC,EAAWC,EAAcC,EAAO,CAChD,IAAMC,EAAQN,IAAU,SAAW,UAAU,OAAOG,CAAS,CAAC,IAAM,IAAIH,CAAK,IAAI,OAAOG,CAAS,CAAC,IAG5FI,EAAMV,EAAOM,CAAS,EAG5B,GAAI,CAAC,OAAO,OAAON,EAAQM,CAAS,IAC/BE,EAAQE,IAAQ,OAAYH,IAAiB,YAC9C,MAAM,IAAI,UAAU,GAAGE,CAAK,oCAAoC,EAEpE,GAAID,GAASE,IAAQ,OACjB,OACJ,IAAMC,EAAU,OAAOD,EACvB,GAAIC,IAAYJ,GAAgBG,IAAQ,KACpC,MAAM,IAAI,UAAU,GAAGD,CAAK,yBAAyBF,CAAY,SAASI,CAAO,EAAE,CAC3F,CACA,IAAMC,EAAO,CAACC,EAAGL,IAAU,OAAO,QAAQK,CAAC,EAAE,QAAQ,CAAC,CAAC3B,EAAGD,CAAC,IAAMoB,EAAWnB,EAAGD,EAAGuB,CAAK,CAAC,EACxFI,EAAKX,EAAQ,EAAK,EAClBW,EAAKV,EAAW,EAAI,CACxB,CC3qBA,IAAMY,GAAsB,OAAO,CAAC,EAAGC,EAAsB,OAAO,CAAC,EAAGC,GAAsB,OAAO,CAAC,EAEhGC,GAAsB,OAAO,CAAC,EAAGC,GAAsB,OAAO,CAAC,EAAGC,GAAsB,OAAO,CAAC,EAEhGC,GAAsB,OAAO,CAAC,EAAGC,GAAsB,OAAO,CAAC,EAAGC,GAAsB,OAAO,CAAC,EAChGC,GAAuB,OAAO,EAAE,EAAGC,GAAuB,OAAO,EAAE,EAGnEC,GAAmC,OAAO,qBAAqB,EAa9D,SAASC,GAAIC,EAAGC,EAAG,CACtB,GAAIA,GAAKd,GACL,MAAM,IAAI,MAAM,uCAAyCc,CAAC,EAC9D,IAAMC,EAASF,EAAIC,EACnB,OAAOC,GAAUf,GAAMe,EAASD,EAAIC,CACxC,CAkBO,SAASC,GAAIC,EAAKC,EAAOC,EAAQ,CACpC,GAAIA,GAAUlB,EACV,MAAM,IAAI,MAAM,kCAAoCkB,CAAM,EAE9D,GAAI,OAAOD,GAAU,SACjB,MAAM,IAAI,UAAU,0CAA4C,OAAOA,CAAK,EAChF,GAAIA,EAAQlB,GACR,MAAM,IAAI,MAAM,yCAAyC,EAC7D,GAAIkB,IAAUlB,GACV,OAAOC,EACX,GAAIiB,IAAUjB,EACV,OAAOgB,EACX,IAAIG,EAAIH,EAAME,EAKd,GAJIC,EAAIpB,KACJoB,GAAKD,GAGLD,EAAQP,GAAkB,CAE1B,IAAIU,EAAIpB,EACR,KAAOiB,EAAQlB,IACPkB,EAAQjB,IACRoB,EAAKA,EAAID,EAAKD,GAClBC,EAAKA,EAAIA,EAAKD,EACdD,IAAUjB,EAEd,OAAOoB,CACX,CAIA,IAAMC,EAAS,CAAC,EAChB,KAAOJ,EAAQlB,IACXsB,EAAO,KAAK,OAAOJ,EAAQT,EAAI,CAAC,EAChCS,IAAUd,GAEd,IAAMmB,EAAQ,IAAI,MAAM,EAAE,EAC1BA,EAAM,CAAC,EAAItB,EACXsB,EAAM,CAAC,EAAIH,EACX,QAASI,EAAI,EAAGA,EAAI,GAAIA,IACpBD,EAAMC,CAAC,EAAKD,EAAMC,EAAI,CAAC,EAAIJ,EAAKD,EACpC,IAAIE,EAAIE,EAAMD,EAAOA,EAAO,OAAS,CAAC,CAAC,EACvC,QAASG,EAAIH,EAAO,OAAS,EAAGG,GAAK,EAAGA,IAAK,CACzCJ,EAAKA,EAAIA,EAAKF,EACdE,EAAKA,EAAIA,EAAKF,EACdE,EAAKA,EAAIA,EAAKF,EACdE,EAAKA,EAAIA,EAAKF,EACd,IAAMO,EAAQJ,EAAOG,CAAC,EAClBC,IAAU,IACVL,EAAKA,EAAIE,EAAMG,CAAK,EAAKP,EACjC,CACA,OAAOE,CACX,CA2CO,SAASM,GAAOC,EAAQC,EAAQ,CACnC,GAAID,IAAWE,GACX,MAAM,IAAI,MAAM,kCAAkC,EAGtD,GAAID,GAAUE,EACV,MAAM,IAAI,MAAM,qCAAuCF,CAAM,EAGjE,IAAIG,EAAIC,GAAIL,EAAQC,CAAM,EACtBK,EAAIL,EAIJM,EAAIL,GAAKM,EAAIL,EACjB,KAAOC,IAAMF,IAAK,CACd,IAAMO,EAAIH,EAAIF,EACRM,EAAIJ,EAAIF,EAAIK,EACZE,EAAIJ,EAAIC,EAAIC,EAElBH,EAAIF,EAAGA,EAAIM,EAAGH,EAAIC,EAAGA,EAAIG,CAC7B,CAEA,GADYL,IACAH,EACR,MAAM,IAAI,MAAM,wBAAwB,EAC5C,OAAOE,GAAIE,EAAGN,CAAM,CACxB,CA0BO,SAASW,GAASR,EAAGS,EAAO,CAC/B,GAAIA,GAASV,EACT,MAAM,IAAI,MAAM,6CAA+CU,CAAK,EACxE,IAAMC,EAAKT,GAAID,EAAGS,CAAK,EACvB,GAAIC,IAAOZ,GACP,MAAM,IAAI,MAAM,oCAAoC,EAExD,IAAMa,EAAUC,GAAIF,EAAID,EAAQI,GAAKJ,CAAK,EAE1C,GAAIR,GAAIS,EAAKC,EAASF,CAAK,IAAMV,EAC7B,MAAM,IAAI,MAAM,0BAA0B,EAC9C,OAAOY,CACX,CACA,SAASG,GAAeC,EAAIC,EAAMC,EAAG,CACjC,IAAMC,EAAIH,EACV,GAAI,CAACG,EAAE,IAAIA,EAAE,IAAIF,CAAI,EAAGC,CAAC,EACrB,MAAM,IAAI,MAAM,yBAAyB,CACjD,CAMA,SAASE,GAAYC,EAAOC,EAAQ,CAChC,IAAKD,EAAQrB,KAASD,GAClB,MAAM,IAAI,MAAMuB,EAAS,+BAAiCD,CAAK,CACvE,CAKA,SAASE,GAAUP,EAAIE,EAAG,CACtB,IAAMC,EAAIH,EACJQ,GAAUL,EAAE,MAAQnB,GAAOyB,GAC3BR,EAAOE,EAAE,IAAID,EAAGM,CAAM,EAC5B,OAAAT,GAAeI,EAAGF,EAAMC,CAAC,EAClBD,CACX,CAGA,SAASS,GAAUV,EAAIE,EAAG,CACtB,IAAMC,EAAIH,EACJW,GAAUR,EAAE,MAAQS,IAAOC,GAC3BC,EAAKX,EAAE,IAAID,EAAGJ,EAAG,EACjBiB,EAAIZ,EAAE,IAAIW,EAAIH,CAAM,EACpBK,EAAKb,EAAE,IAAID,EAAGa,CAAC,EACfE,EAAId,EAAE,IAAIA,EAAE,IAAIa,EAAIlB,EAAG,EAAGiB,CAAC,EAC3Bd,EAAOE,EAAE,IAAIa,EAAIb,EAAE,IAAIc,EAAGd,EAAE,GAAG,CAAC,EACtC,OAAAJ,GAAeI,EAAGF,EAAMC,CAAC,EAClBD,CACX,CAGA,SAASiB,GAAWC,EAAG,CACnB,IAAMC,EAAMC,GAAMF,CAAC,EACbG,EAAKC,GAAcJ,CAAC,EACpBK,EAAKF,EAAGF,EAAKA,EAAI,IAAIA,EAAI,GAAG,CAAC,EAC7BK,EAAKH,EAAGF,EAAKI,CAAE,EACfE,EAAKJ,EAAGF,EAAKA,EAAI,IAAII,CAAE,CAAC,EACxBG,GAAMR,EAAIS,IAAOC,GACvB,OAAQ,CAAC7B,EAAIE,IAAM,CACf,IAAMC,EAAIH,EACN8B,EAAM3B,EAAE,IAAID,EAAGyB,CAAE,EACjBI,EAAM5B,EAAE,IAAI2B,EAAKN,CAAE,EACjBQ,EAAM7B,EAAE,IAAI2B,EAAKL,CAAE,EACnBQ,EAAM9B,EAAE,IAAI2B,EAAKJ,CAAE,EACnBQ,EAAK/B,EAAE,IAAIA,EAAE,IAAI4B,CAAG,EAAG7B,CAAC,EACxBiC,EAAKhC,EAAE,IAAIA,EAAE,IAAI6B,CAAG,EAAG9B,CAAC,EAC9B4B,EAAM3B,EAAE,KAAK2B,EAAKC,EAAKG,CAAE,EACzBH,EAAM5B,EAAE,KAAK8B,EAAKD,EAAKG,CAAE,EACzB,IAAMC,EAAKjC,EAAE,IAAIA,EAAE,IAAI4B,CAAG,EAAG7B,CAAC,EACxBD,EAAOE,EAAE,KAAK2B,EAAKC,EAAKK,CAAE,EAChC,OAAArC,GAAeI,EAAGF,EAAMC,CAAC,EAClBD,CACX,EACJ,CAmBO,SAASsB,GAAcJ,EAAG,CAG7B,GAAIA,EAAIkB,GACJ,MAAM,IAAI,MAAM,qCAAqC,EACzDjC,GAAYe,EAAG,eAAe,EAE9B,IAAImB,EAAInB,EAAInC,EACRuD,EAAI,EACR,KAAOD,EAAIxC,KAAQf,IACfuD,GAAKxC,GACLyC,IAGJ,IAAIC,EAAI1C,GACF2C,EAAMpB,GAAMF,CAAC,EACnB,KAAOuB,GAAWD,EAAKD,CAAC,IAAM,GAG1B,GAAIA,IAAM,IACN,MAAM,IAAI,MAAM,+CAA+C,EAGvE,GAAID,IAAM,EACN,OAAOhC,GAGX,IAAIoC,EAAKF,EAAI,IAAID,EAAGF,CAAC,EACfM,GAAUN,EAAItD,GAAOc,GAC3B,OAAO,SAAqBE,EAAIE,EAAG,CAC/B,IAAMC,EAAIH,EACV,GAAIG,EAAE,IAAID,CAAC,EACP,OAAOA,EAEX,GAAIwC,GAAWvC,EAAGD,CAAC,IAAM,EACrB,MAAM,IAAI,MAAM,yBAAyB,EAE7C,IAAI2C,EAAIN,EACJO,EAAI3C,EAAE,IAAIA,EAAE,IAAKwC,CAAE,EACnBI,EAAI5C,EAAE,IAAID,EAAGoC,CAAC,EACdU,EAAI7C,EAAE,IAAID,EAAG0C,CAAM,EAGvB,KAAO,CAACzC,EAAE,IAAI4C,EAAG5C,EAAE,GAAG,GAAG,CAGrB,GAAIA,EAAE,IAAI4C,CAAC,EACP,MAAM,IAAI,MAAM,+CAA+C,EACnE,IAAI9B,EAAI,EAEJgC,EAAQ9C,EAAE,IAAI4C,CAAC,EACnB,KAAO,CAAC5C,EAAE,IAAI8C,EAAO9C,EAAE,GAAG,GAGtB,GAFAc,IACAgC,EAAQ9C,EAAE,IAAI8C,CAAK,EACfhC,IAAM4B,EACN,MAAM,IAAI,MAAM,yBAAyB,EAGjD,IAAMK,EAAWlE,GAAO,OAAO6D,EAAI5B,EAAI,CAAC,EAClC,EAAId,EAAE,IAAI2C,EAAGI,CAAQ,EAE3BL,EAAI5B,EACJ6B,EAAI3C,EAAE,IAAI,CAAC,EACX4C,EAAI5C,EAAE,IAAI4C,EAAGD,CAAC,EACdE,EAAI7C,EAAE,IAAI6C,EAAG,CAAC,CAClB,CACA,OAAOA,CACX,CACJ,CAyBO,SAASG,GAAOhC,EAAG,CAGtB,OAFAf,GAAYe,EAAG,SAAS,EAEpBA,EAAIV,KAAQ4B,GACL9B,GAEPY,EAAIN,KAAQD,GACLF,GAEPS,EAAIU,KAASuB,GACNlC,GAAWC,CAAC,EAEhBI,GAAcJ,CAAC,CAC1B,CAkBA,IAAMkC,GAAe,CACjB,SAAU,UAAW,MAAO,MAAO,MAAO,OAAQ,MAClD,MAAO,MAAO,MAAO,MAAO,MAAO,MACnC,OAAQ,OAAQ,OAAQ,MAC5B,EAeO,SAASC,GAAcC,EAAO,CAEjC,GADAC,GAAQD,EAAO,OAAO,EAClB,OAAOA,EAAM,OAAU,SACvB,MAAM,IAAI,UAAU,kDAAoD,OAAOA,EAAM,KAAK,EAG9FE,GAAYF,EAAM,MAAO,OAAO,EAChCE,GAAYF,EAAM,KAAM,MAAM,EAC9B,QAAWG,KAAQL,GACfM,GAAUJ,EAAMG,CAAI,EAAG,SAAWA,CAAI,EAG1C,GAAIH,EAAM,MAAQ,GAAKA,EAAM,KAAO,EAChC,MAAM,IAAI,MAAM,wCAAwC,EAC5D,GAAIA,EAAM,OAASK,EACf,MAAM,IAAI,MAAM,0CAA4CL,EAAM,KAAK,EAC3E,OAAOA,CACX,CAkEO,SAASM,GAAcC,EAAIC,EAAMC,EAAW,GAAO,CACtDC,GAAcH,CAAE,EAChBI,GAAOH,EAAM,MAAM,EACnBI,GAAMH,EAAU,UAAU,EAC1B,IAAMI,EAAIN,EACJO,EAAW,IAAI,MAAMN,EAAK,MAAM,EAAE,KAAKC,EAAWI,EAAE,KAAO,MAAS,EAEpEE,EAAgBP,EAAK,OAAO,CAACQ,EAAKC,EAAKC,IACrCL,EAAE,IAAII,CAAG,EACFD,GACXF,EAASI,CAAC,EAAIF,EACPH,EAAE,IAAIG,EAAKC,CAAG,GACtBJ,EAAE,GAAG,EAEFM,EAAcN,EAAE,IAAIE,CAAa,EAEvC,OAAAP,EAAK,YAAY,CAACQ,EAAKC,EAAKC,IACpBL,EAAE,IAAII,CAAG,EACFD,GAEXF,EAASI,CAAC,EAAIL,EAAE,IAAIG,EAAKF,EAASI,CAAC,CAAC,EAC7BL,EAAE,IAAIG,EAAKC,CAAG,GACtBE,CAAW,EACPL,CACX,CA0CO,SAASM,GAAWC,EAAIC,EAAG,CAC9BC,GAAcF,CAAE,EAChB,IAAMG,EAAIH,EACVI,GAAYD,EAAE,MAAO,YAAY,EAGjC,IAAME,GAAUF,EAAE,MAAQG,GAAOC,GAC3BC,EAAUL,EAAE,IAAIF,EAAGI,CAAM,EACzBI,EAAMN,EAAE,IAAIK,EAASL,EAAE,GAAG,EAC1BO,EAAOP,EAAE,IAAIK,EAASL,EAAE,IAAI,EAC5BQ,EAAKR,EAAE,IAAIK,EAASL,EAAE,IAAIA,EAAE,GAAG,CAAC,EACtC,GAAI,CAACM,GAAO,CAACC,GAAQ,CAACC,EAClB,MAAM,IAAI,MAAM,gCAAgC,EACpD,OAAOF,EAAM,EAAIC,EAAO,EAAI,EAChC,CAkCO,SAASE,GAAQ,EAAGC,EAAY,CAInC,GAFIA,IAAe,QACfC,GAAQD,CAAU,EAClB,GAAKE,GACL,MAAM,IAAI,MAAM,8CAAgD,CAAC,EACrE,GAAIF,IAAe,QAAaA,EAAa,EACzC,MAAM,IAAI,MAAM,uDAAyDA,CAAU,EACvF,IAAMG,EAAOC,GAAO,CAAC,EAGrB,GAAIJ,IAAe,QAAaA,EAAaG,EACzC,MAAM,IAAI,MAAM,0CAA0CH,CAAU,mBAAmBG,CAAI,GAAG,EAClG,IAAME,EAAcL,IAAe,OAAYA,EAAaG,EACtDG,EAAc,KAAK,KAAKD,EAAc,CAAC,EAC7C,MAAO,CAAE,WAAYA,EAAa,YAAAC,CAAY,CAClD,CAGA,IAAMC,GAAa,IAAI,QACjBC,GAAN,KAAa,CACT,MACA,KACA,MACA,KACA,KAAON,GACP,IAAMO,EACN,SACA,KACA,YAAYC,EAAOC,EAAO,CAAC,EAAG,CAG1B,GAAID,GAASD,EACT,MAAM,IAAI,MAAM,0CAA4CC,CAAK,EACrE,IAAIE,EACJ,KAAK,KAAO,GACRD,GAAQ,MAAQ,OAAOA,GAAS,WAE5B,OAAOA,EAAK,MAAS,WACrBC,EAAcD,EAAK,MACnB,OAAOA,EAAK,MAAS,YAGrB,OAAO,eAAe,KAAM,OAAQ,CAAE,MAAOA,EAAK,KAAM,WAAY,EAAK,CAAC,EAC1E,OAAOA,EAAK,MAAS,YACrB,KAAK,KAAOA,EAAK,MACjBA,EAAK,iBACL,KAAK,SAAW,OAAO,OAAOA,EAAK,eAAe,MAAM,CAAC,GACzD,OAAOA,EAAK,cAAiB,YAC7B,KAAK,KAAOA,EAAK,eAEzB,GAAM,CAAE,WAAAX,EAAY,YAAAM,CAAY,EAAIP,GAAQW,EAAOE,CAAW,EAC9D,GAAIN,EAAc,KACd,MAAM,IAAI,MAAM,gDAAgD,EACpE,KAAK,MAAQI,EACb,KAAK,KAAOV,EACZ,KAAK,MAAQM,EACb,OAAO,OAAO,IAAI,CACtB,CACA,OAAOO,EAAK,CACR,OAAOC,GAAID,EAAK,KAAK,KAAK,CAC9B,CACA,QAAQA,EAAK,CACT,GAAI,OAAOA,GAAQ,SACf,MAAM,IAAI,UAAU,+CAAiD,OAAOA,CAAG,EACnF,OAAOX,IAAOW,GAAOA,EAAM,KAAK,KACpC,CACA,IAAIA,EAAK,CACL,OAAOA,IAAQX,EACnB,CAEA,YAAYW,EAAK,CACb,MAAO,CAAC,KAAK,IAAIA,CAAG,GAAK,KAAK,QAAQA,CAAG,CAC7C,CACA,MAAMA,EAAK,CACP,OAAQA,EAAMJ,KAASA,CAC3B,CACA,IAAII,EAAK,CACL,OAAOC,GAAI,CAACD,EAAK,KAAK,KAAK,CAC/B,CACA,IAAIE,EAAKC,EAAK,CACV,OAAOD,IAAQC,CACnB,CACA,IAAIH,EAAK,CACL,OAAOC,GAAID,EAAMA,EAAK,KAAK,KAAK,CACpC,CACA,IAAIE,EAAKC,EAAK,CACV,OAAOF,GAAIC,EAAMC,EAAK,KAAK,KAAK,CACpC,CACA,IAAID,EAAKC,EAAK,CACV,OAAOF,GAAIC,EAAMC,EAAK,KAAK,KAAK,CACpC,CACA,IAAID,EAAKC,EAAK,CACV,OAAOF,GAAIC,EAAMC,EAAK,KAAK,KAAK,CACpC,CACA,IAAIH,EAAKI,EAAO,CACZ,OAAOC,GAAIL,EAAKI,EAAO,KAAK,KAAK,CACrC,CACA,IAAIF,EAAKC,EAAK,CACV,OAAOF,GAAIC,EAAMI,GAAOH,EAAK,KAAK,KAAK,EAAG,KAAK,KAAK,CACxD,CAEA,KAAKH,EAAK,CACN,OAAOA,EAAMA,CACjB,CACA,KAAKE,EAAKC,EAAK,CACX,OAAOD,EAAMC,CACjB,CACA,KAAKD,EAAKC,EAAK,CACX,OAAOD,EAAMC,CACjB,CACA,KAAKD,EAAKC,EAAK,CACX,OAAOD,EAAMC,CACjB,CACA,IAAIH,EAAK,CACL,OAAOM,GAAON,EAAK,KAAK,KAAK,CACjC,CACA,KAAKA,EAAK,CAGN,IAAIO,EAAOb,GAAW,IAAI,IAAI,EAC9B,OAAKa,GACDb,GAAW,IAAI,KAAOa,EAAOC,GAAO,KAAK,KAAK,CAAE,EAC7CD,EAAK,KAAMP,CAAG,CACzB,CACA,QAAQA,EAAK,CAIT,OAAO,KAAK,KAAOS,GAAgBT,EAAK,KAAK,KAAK,EAAIU,GAAgBV,EAAK,KAAK,KAAK,CACzF,CACA,UAAUW,EAAOC,EAAiB,GAAO,CACrCC,GAAOF,CAAK,EACZ,GAAM,CAAE,SAAUG,EAAgB,MAAAC,EAAO,KAAAC,EAAM,MAAAnB,EAAO,KAAMoB,CAAa,EAAI,KAC7E,GAAIH,EAAgB,CAGhB,GAAIH,EAAM,OAAS,GAAK,CAACG,EAAe,SAASH,EAAM,MAAM,GAAKA,EAAM,OAASI,EAC7E,MAAM,IAAI,MAAM,6BAA+BD,EAAiB,eAAiBH,EAAM,MAAM,EAEjG,IAAMO,EAAS,IAAI,WAAWH,CAAK,EAEnCG,EAAO,IAAIP,EAAOK,EAAO,EAAIE,EAAO,OAASP,EAAM,MAAM,EACzDA,EAAQO,CACZ,CACA,GAAIP,EAAM,SAAWI,EACjB,MAAM,IAAI,MAAM,6BAA+BA,EAAQ,eAAiBJ,EAAM,MAAM,EACxF,IAAIQ,EAASH,EAAOI,GAAgBT,CAAK,EAAIU,GAAgBV,CAAK,EAGlE,GAFIM,IACAE,EAASlB,GAAIkB,EAAQtB,CAAK,GAC1B,CAACe,GACG,CAAC,KAAK,QAAQO,CAAM,EACpB,MAAM,IAAI,MAAM,kDAAkD,EAG1E,OAAOA,CACX,CAEA,YAAYG,EAAK,CAIb,OAAOC,GAAc,KAAMD,EAAK,EAAI,CACxC,CAGA,KAAKE,EAAGC,EAAGC,EAAW,CAGlB,OAAAC,GAAMD,EAAW,WAAW,EACrBA,EAAYD,EAAID,CAC3B,CACJ,EA2BO,SAASI,GAAM/B,EAAOC,EAAO,CAAC,EAAG,CAKpC,cAAO,OAAOH,GAAO,SAAS,EACvB,IAAIA,GAAOE,EAAOC,CAAI,CACjC,CA4DO,SAAS+B,GAAoBC,EAAY,CAC5C,GAAI,OAAOA,GAAe,SACtB,MAAM,IAAI,MAAM,4BAA4B,EAEhD,GAAIA,GAAcC,EACd,MAAM,IAAI,MAAM,oCAAoC,EAExD,IAAMC,EAAYC,GAAOH,EAAaC,CAAG,EACzC,OAAO,KAAK,KAAKC,EAAY,CAAC,CAClC,CAiBO,SAASE,GAAiBJ,EAAY,CACzC,IAAMK,EAASN,GAAoBC,CAAU,EAC7C,OAAOK,EAAS,KAAK,KAAKA,EAAS,CAAC,CACxC,CAwBO,SAASC,GAAeC,EAAKP,EAAYQ,EAAO,GAAO,CAC1DC,GAAOF,CAAG,EACV,IAAMG,EAAMH,EAAI,OACVI,EAAWZ,GAAoBC,CAAU,EACzCY,EAAS,KAAK,IAAIR,GAAiBJ,CAAU,EAAG,EAAE,EAGxD,GAAIU,EAAME,GAAUF,EAAM,KACtB,MAAM,IAAI,MAAM,YAAcE,EAAS,6BAA+BF,CAAG,EAC7E,IAAMG,EAAML,EAAOM,GAAgBP,CAAG,EAAIQ,GAAgBR,CAAG,EAMvDS,EAAUC,GAAIJ,EAAKb,EAAaC,CAAG,EAAIA,EAC7C,OAAOO,EAAOU,GAAgBF,EAASL,CAAQ,EAAIQ,GAAgBH,EAASL,CAAQ,CACxF,CCn8BA,IAAMS,GAAsB,OAAO,CAAC,EAC9BC,GAAsB,OAAO,CAAC,EAC9BC,GAAsB,OAAO,CAAC,EAC9BC,GAAc,GACdC,GAAa,IAIbC,GAAY,EAIZC,GAAyC,GAAK,GAgB7C,SAASC,GAAkBC,EAAO,CACrC,IAAMC,EAAKD,EACX,GAAI,OAAOC,GAAO,WACd,MAAM,IAAI,UAAU,0CAA4C,OAAOD,CAAK,EAChFE,GAAUD,EAAG,WAAY,kBAAkB,EAC3CC,GAAUD,EAAG,UAAW,iBAAiB,EACzCC,GAAUD,EAAG,QAAS,eAAe,EAGrCE,GAAQF,EAAG,KAAM,YAAY,EAC7BE,GAAQF,EAAG,KAAM,YAAY,EAC7BG,GAAcH,EAAG,EAAE,EACnBG,GAAcH,EAAG,EAAE,CACvB,CAmBO,SAASI,GAAWC,EAAGC,EAAQ,CAElCR,GAAkBO,CAAC,EACnBE,GAAkBD,EAAQD,CAAC,EAI3B,IAAMG,EAAaC,GAAcJ,EAAE,GAAIC,EAAO,IAAKI,GAAMA,EAAE,CAAC,CAAC,EAC7D,OAAOJ,EAAO,IAAI,CAACI,EAAGC,IAAMN,EAAE,WAAWK,EAAE,SAASF,EAAWG,CAAC,CAAC,CAAC,CAAC,CACvE,CACA,SAASC,GAAUC,EAAGC,EAAMC,EAAM,EAAG,CACjC,GAAI,CAAC,OAAO,cAAcF,CAAC,GAAKA,EAAIE,GAAOF,EAAIC,EAC3C,MAAM,IAAI,MAAM,kCAAoCC,EAAM,KAAOD,EAAO,YAAcD,CAAC,CAC/F,CAIA,SAASG,GAAmBC,EAAWC,EAAS,CAC5C,IAAMC,EAAQF,GAAa,EAAIC,EAAU,KACzC,GAAIC,EAAQtB,GACR,MAAM,IAAI,MAAM,0CACZ,KAAK,KAAKsB,EAAQ,GAAK,EAAE,EACzB,aACAtB,GAAkB,GAAK,GACvB,MAAM,CAClB,CAyBO,SAASuB,GAAiBC,EAAaC,EAAQ,CAClD,GAAID,IAAgB,OAEpB,CAAApB,GAAUoB,EAAa,aAAa,EACpC,GAAI,CACA,IAAME,EAAQF,EAAYC,CAAM,EAChC,GAAI,CAACE,GAAQD,CAAK,GAAKA,EAAM,SAAWD,EACpC,MACR,MACM,CACF,MACJ,CACA,OAAOD,EACX,CACA,SAASd,GAAkBD,EAAQD,EAAG,CAClCoB,GAAOnB,EAAQ,QAAQ,EACvBA,EAAO,QAAQ,CAACI,EAAGC,IAAM,CACrB,GAAI,EAAED,aAAaL,GACf,MAAM,IAAI,MAAM,0BAA4BM,CAAC,CACrD,CAAC,CACL,CAGA,SAASe,GAAmBC,EAASC,EAAOC,EAAW,CACnD,GAAI,CAAC,MAAM,QAAQF,CAAO,EACtB,MAAM,IAAI,MAAM,2BAA2B,EAC/CA,EAAQ,QAAQ,CAACG,EAAGnB,IAAM,CAEtB,GAAI,EADOkB,IAAc,OAAYD,EAAM,QAAQE,CAAC,EAAIC,GAASD,CAAC,GAAKA,EAAID,GAEvE,MAAM,IAAI,MAAM,2BAA6BlB,CAAC,CACtD,CAAC,CACL,CACA,IAAMqB,GAAmB,IAAI,QAC7B,SAASC,GAAcC,EAAG,CAEtB,OAAOF,GAAiB,IAAIE,CAAC,GAAK,CACtC,CAEA,SAASC,GAAazB,EAAG0B,EAAM,CAC3B,IAAMC,EAAM3B,EAAE,OAAO,EACf4B,EAAI,CAAC5B,CAAC,EACZ,QAAS6B,EAAI,EAAGA,EAAIH,EAAMG,IACtBD,EAAE,KAAKA,EAAEC,EAAI,CAAC,EAAE,IAAIF,CAAG,CAAC,EAC5B,OAAOC,CACX,CAKA,SAASE,GAAW,EAAG3B,EAAG,CACtB,IAAMuB,EAAO,GAAKvB,EACZ4B,EAAOL,EAAO,EACdM,EAAO,OAAON,EAAO,CAAC,EACtBO,EAAI,CAAC,EACX,KAAO,EAAIpD,IAAK,CACZ,IAAIqD,EAAI,EACJ,EAAIpD,KACJoD,EAAI,OAAO,EAAIF,CAAI,EACfE,GAAKH,IACLG,GAAKR,GACT,GAAK,OAAOQ,CAAC,GAEjBD,EAAE,KAAKC,CAAC,EACR,IAAMpD,EACV,CACA,OAAOmD,CACX,CAMA,SAASE,GAAmB,EAAGhC,EAAGiC,EAAS,CACvC,IAAMV,EAAO,GAAKvB,EACZ4B,EAAOL,EAAO,EACdM,EAAO,OAAON,EAAO,CAAC,EACtBW,EAAU,OAAOlC,CAAC,EAClB8B,EAAI,CAAC,EACX,QAASC,EAAI,EAAGA,EAAIE,EAASF,IAAK,CAC9B,IAAII,EAAI,OAAO,EAAIN,CAAI,EACvB,IAAMK,EACFC,EAAIP,IACJO,GAAKZ,EACL,GAAK5C,IAETmD,EAAE,KAAKK,CAAC,CACZ,CAEA,GAAI,IAAMzD,GACN,MAAM,IAAI,MAAM,cAAc,EAClC,OAAOoD,CACX,CAMA,SAASM,GAASC,EAAMC,EAAQC,EAAQ,CACpC,IAAIC,EAAM,EACV,QAAWV,KAAKS,EACZC,EAAM,KAAK,IAAIA,EAAKV,EAAE,MAAM,EAChC,IAAIW,EAAMJ,EACV,QAASK,EAAMF,EAAM,EAAGE,GAAO,EAAGA,IAAO,CACjCA,IAAQF,EAAM,IACdC,EAAMA,EAAI,OAAO,GACrB,QAAS,EAAI,EAAG,EAAIF,EAAO,OAAQ,IAAK,CACpC,IAAMR,EAAIQ,EAAO,CAAC,EAAEG,CAAG,EACvB,GAAIX,EAAG,CACH,IAAMY,EAAOL,EAAO,CAAC,EAAG,KAAK,IAAIP,CAAC,EAAI,GAAM,CAAC,EAC7CU,EAAMA,EAAI,IAAIV,EAAI,EAAIY,EAAK,OAAO,EAAIA,CAAI,CAC9C,CACJ,CACJ,CACA,OAAOF,CACX,CA4BO,IAAMG,GAAN,KAAuB,CAC1B,MACA,KACA,KACA,YACA,gBAAkB,IAAI,QACtB,iBACA,KAEA,YAAY1D,EAAOsB,EAAa,CAC5BvB,GAAkBC,CAAK,EAKvB,KAAK,YAAcqB,GAAiBC,EAAa3B,EAAW,EAC5D,KAAK,MAAQK,EACb,KAAK,KAAOA,EAAM,KAClB,KAAK,KAAOA,EAAM,KAClB,KAAK,KAAOA,EAAM,GAAG,IACzB,CAWA,eAAe2D,EAAO7C,EAAGC,EAAM,CAI3B,IAAMgC,EAAU,KAAK,KAAKhC,EAAOD,CAAC,EAAI,EAChC4B,EAAO,IAAM5B,EAAI,GACjB8C,EAAO,CAAC,EACVC,EAAOF,EACX,QAASd,EAAI,EAAGA,EAAIE,EAASF,IAAK,CAC9B,IAAIU,EAAMM,EACV,QAASjD,EAAI,EAAGA,EAAI8B,EAAM9B,IACtBgD,EAAK,KAAKL,CAAG,EACbA,EAAMA,EAAI,IAAIM,CAAI,EAEtBA,EAAOD,EAAKA,EAAK,OAAS,CAAC,EAAE,OAAO,CACxC,CACA,MAAO,CAAE,EAAA9C,EAAG,KAAAC,EAAM,QAAAgC,EAAS,KAAAa,CAAK,CACpC,CAUA,aAAaE,EAAaC,EAAG,CACzB,GAAM,CAAE,EAAAjD,EAAG,QAAAiC,EAAS,KAAAa,CAAK,EAAIE,EACvBpB,EAAO,IAAM5B,EAAI,GACjBuC,EAASP,GAAmBiB,EAAGjD,EAAGiC,CAAO,EAC3CpC,EAAI,KAAK,KACTqD,EAAI,KAAK,KACb,QAASnB,EAAI,EAAGA,EAAIE,EAASF,IAAK,CAC9B,IAAMoB,EAAQZ,EAAOR,CAAC,EAChBqB,EAAQrB,EAAIH,EAEZyB,EAAM,KAAK,IAAIF,CAAK,EAAI,EAC1BG,EAAMR,EAAKM,CAAK,EACpB,QAAStD,EAAI,EAAGA,EAAI8B,EAAM9B,IACtBwD,EAAMxD,IAAMuD,EAAMP,EAAKM,EAAQtD,CAAC,EAAIwD,EACxC,IAAMC,EAAMD,EAAI,OAAO,EACnBH,IAAU,EACVD,EAAIA,EAAE,IAAIJ,EAAKM,CAAK,CAAC,EAErBvD,EAAIA,EAAE,IAAIsD,EAAQ,EAAII,EAAMD,CAAG,CACvC,CACA,MAAO,CAAE,EAAAzD,EAAG,EAAAqD,CAAE,CAClB,CAIA,mBAAmBlD,EAAG6C,EAAO5C,EAAMuD,EAAW,CAC1C,IAAIC,EAAU,KAAK,gBAAgB,IAAIZ,CAAK,EACxCC,EAAOW,GAAS,KAAMC,GAAUA,EAAM,IAAM1D,GAAK0D,EAAM,OAASzD,CAAI,EACxE,OAAK6C,IACDA,EAAO,KAAK,eAAeD,EAAO7C,EAAGC,CAAI,EACrC,OAAOuD,GAAc,aACrBV,EAAO,CAAE,GAAGA,EAAM,KAAMU,EAAUV,EAAK,IAAI,CAAE,GAC5CW,IACDA,EAAU,CAAC,EACX,KAAK,gBAAgB,IAAIZ,EAAOY,CAAO,GAE3CA,EAAQ,KAAKX,CAAI,GAEdA,CACX,CACA,YAAYD,EAAO,CACf,GAAI,EAAEA,aAAiB,KAAK,OACxB,MAAM,IAAI,UAAU,6CAA+C,OAAOA,CAAK,CACvF,CAIA,iBAAiBA,EAAOc,EAAQ,CAE5B,GADA,KAAK,YAAYd,CAAK,EAClB,CAACe,GAAQD,EAAQhF,GAAK,KAAK,MAAM,GAAG,KAAK,EACzC,MAAM,IAAI,MAAM,gBAAgB,CACxC,CAIA,MAAMkE,EAAOI,EAAGhD,EAAMuD,EAAW,CAC7B,IAAMxD,EAAIoB,GAAcyB,CAAK,EAC7B,OAAI7C,IAAM,EACC,KAAK,cAAc6C,EAAOI,EAAGhD,CAAI,EACrC,KAAK,aAAa,KAAK,mBAAmBD,EAAG6C,EAAO5C,EAAMuD,CAAS,EAAGP,CAAC,CAClF,CACA,MAAMJ,EAAOc,EAAQH,EAAW,CAC5B,YAAK,iBAAiBX,EAAOc,CAAM,EAC5B,KAAK,MAAMd,EAAOc,EAAQ,KAAK,KAAMH,CAAS,CACzD,CACA,aAAaX,EAAOc,EAAQH,EAAW,CAMnC,GALA,KAAK,iBAAiBX,EAAOc,CAAM,EAK/B,KAAK,cAAgB,OACrB,MAAM,IAAI,MAAM,6CAA6C,EACjE,IAAM1D,EAAO,KAAK,MAAM,GAAG,KAAOnB,GAC5B+E,EAAQ,KAAK,YAAYhF,EAAW,EAC1C,GAAI,CAAC8B,GAAQkD,CAAK,GAAKA,EAAM,SAAWhF,GACpC,MAAM,IAAI,MAAM,yCAAyC,EAS7DgF,EAAM,CAAC,EAAKA,EAAM,CAAC,EAAI,GAAQ,IAK/B,IAAMZ,EAAIU,EAASG,GAAgBD,CAAK,EAAI,KAAK,MAAM,GAAG,MAC1D,OAAO,KAAK,MAAMhB,EAAOI,EAAGhD,EAAMuD,CAAS,CAC/C,CAgBA,cAAcX,EAAOI,EAAGhD,EAAM,CAC1B,IAAMD,EAAIjB,GACJwC,EAAO,GAAKvB,EACZ6B,EAAOkC,GAAQ/D,CAAC,EAEhBgE,EAAQ,IAAI,MAAMzC,CAAI,EAC5ByC,EAAM,CAAC,EAAI,KAAK,KAChB,QAASlE,EAAI,EAAGA,EAAIyB,EAAMzB,IACtBkE,EAAMlE,CAAC,EAAIkE,EAAMlE,EAAI,CAAC,EAAE,IAAI+C,CAAK,EAErC,IAAMZ,EAAU,KAAK,KAAKhC,EAAOD,CAAC,EAC9ByC,EAAM,KAAK,KACf,QAASwB,EAAShC,EAAU,EAAGgC,GAAU,EAAGA,IAAU,CAGlD,GAAIA,IAAWhC,EAAU,EACrB,QAASH,EAAI,EAAGA,EAAI9B,EAAG8B,IACnBW,EAAMA,EAAI,OAAO,EACzB,IAAMU,EAAQ,OAAQF,GAAK,OAAOgB,EAASjE,CAAC,EAAK6B,CAAI,EAEjDyB,EAAMU,EAAM,CAAC,EACjB,QAASlE,EAAI,EAAGA,EAAIyB,EAAMzB,IACtBwD,EAAMxD,IAAMqD,EAAQa,EAAMlE,CAAC,EAAIwD,EACnCb,EAAMA,EAAI,IAAIa,CAAG,CACrB,CACA,MAAO,CAAE,EAAGb,EAAK,EAAGA,CAAI,CAC5B,CACA,YAAYI,EAAOqB,EAAU,CAEzB,OAAI,KAAK,cAAgB,OACd,GACPA,IAAavF,GACN,GACPkE,IAAU,KAAK,KACR,IACP,KAAK,mBAAqB,SAC1B,KAAK,iBAAmB,KAAK,UAAU,KAAK,KAAM,KAAK,MAAM,GAAG,KAAK,EAAE,IAAI,GACxE,KAAK,iBAChB,CACA,UAAUA,EAAOc,EAAQO,EAAUV,EAAW,CAC1C,OAAO,KAAK,YAAYX,EAAOqB,CAAQ,EACjC,KAAK,aAAarB,EAAOc,EAAQH,CAAS,EAC1C,KAAK,MAAMX,EAAOc,EAAQH,CAAS,CAC7C,CACA,UAAUX,EAAOc,EAAQH,EAAW,CAEhC,GADA,KAAK,YAAYX,CAAK,EAClB,CAAC3B,GAASyC,CAAM,EAChB,MAAM,IAAI,MAAM,gBAAgB,EACpC,IAAM3D,EAAIoB,GAAcyB,CAAK,EAQ7B,GAAI7C,IAAM,GAAK2D,GAAU,KAAK,MAAM,GAAG,MACnC,OAAOQ,GAAa,KAAK,MAAO,CAACtB,CAAK,EAAG,CAACc,CAAM,EAAG,EAAI,EAI3D,IAAMX,EAAc,KAAK,mBAAmBhD,EAAG6C,EAAO,KAAK,KAAMW,CAAS,EAC1E,OAAO,KAAK,aAAaR,EAAaW,CAAM,EAAE,CAClD,CAKA,cAAcd,EAAO7C,EAAG,CACpB,KAAK,YAAY6C,CAAK,EACtB9C,GAAUC,EAAG,KAAK,IAAI,EAEtB,IAAMiC,EAAU,KAAK,MAAM,KAAK,KAAOnD,IAAckB,CAAC,EAAI,EAC1DG,GAAmB8B,EAAU,IAAMjC,EAAI,GAAI,KAAK,MAAM,GAAG,KAAK,EAC9DmB,GAAiB,IAAI0B,EAAO7C,CAAC,EAC7B,KAAK,gBAAgB,OAAO6C,CAAK,CACrC,CAEA,cAAcA,EAAO,CACjB,OAAOzB,GAAcyB,CAAK,IAAM,CACpC,CACJ,EA6BO,SAASsB,GAAa3E,EAAGC,EAAQqB,EAASsD,EAAiB,GAAO,CAMrE,GALAnF,GAAkBO,CAAC,EACnBE,GAAkBD,EAAQD,CAAC,EAC3B6E,GAAMD,EAAgB,gBAAgB,EAEtCvD,GAAmBC,EAAStB,EAAE,GAAI4E,EAAiB5E,EAAE,GAAG,OAASZ,GAAM,MAAS,EAC5Ea,EAAO,SAAWqB,EAAQ,OAC1B,MAAM,IAAI,MAAM,qDAAqD,EACzE,IAAMwB,EAAS7C,EAAO,IAAKI,GAAMyB,GAAazB,EAAG,CAAC,CAAC,EAC7C0C,EAASzB,EAAQ,IAAKmC,GAAMtB,GAAWsB,EAAG,CAAC,CAAC,EAClD,OAAOb,GAAS5C,EAAE,KAAM8C,EAAQC,CAAM,CAC1C,CAsHA,SAAS+B,GAAYC,EAAOC,EAAOC,EAAM,CACrC,GAAID,EAAO,CAIP,GAAIA,EAAM,QAAUD,EAChB,MAAM,IAAI,MAAM,gDAAgD,EACpE,OAAAG,GAAcF,CAAK,EACZA,CACX,KAEI,QAAOG,GAAMJ,EAAO,CAAE,KAAAE,CAAK,CAAC,CAEpC,CA4BO,SAASG,GAAkBC,EAAMC,EAAOC,EAAY,CAAC,EAAGC,EAAQ,CACnE,GAAIH,IAAS,eAAiBA,IAAS,UACnC,MAAM,IAAI,MAAM,gDAAgD,EAGpE,GAFIG,IAAW,SACXA,EAASH,IAAS,WAClB,CAACC,GAAS,OAAOA,GAAU,SAC3B,MAAM,IAAI,MAAM,kBAAkBD,CAAI,eAAe,EAEzDI,GAAeF,CAAS,EACxB,QAAWG,IAAK,CAAC,IAAK,IAAK,GAAG,EAAG,CAC7B,IAAMC,EAAML,EAAMI,CAAC,EACnB,GAAI,EAAEE,GAASD,CAAG,GAAKA,IAAQE,IAC3B,MAAM,IAAI,MAAM,SAASH,CAAC,0BAA0B,CAC5D,CACA,IAAMI,EAAKhB,GAAYQ,EAAM,EAAGC,EAAU,GAAIC,CAAM,EAC9CO,EAAKjB,GAAYQ,EAAM,EAAGC,EAAU,GAAIC,CAAM,EAE9CQ,EAAS,CAAC,KAAM,KAAM,IADjBX,IAAS,cAAgB,IAAM,GACP,EACnC,QAAWK,KAAKM,EAEZ,GAAI,CAACF,EAAG,QAAQR,EAAMI,CAAC,CAAC,EACpB,MAAM,IAAI,MAAM,SAASA,CAAC,0CAA0C,EAE5E,OAAAJ,EAAQ,OAAO,OAAO,OAAO,OAAO,CAAC,EAAGA,CAAK,CAAC,EACvC,CAAE,MAAAA,EAAO,GAAAQ,EAAI,GAAAC,CAAG,CAC3B,CAeO,SAASE,GAAaC,EAAiBC,EAAc,CACxD,OAAO,SAAgBC,EAAM,CACzB,IAAMC,EAAYH,EAAgBE,CAAI,EACtC,MAAO,CAAE,UAAAC,EAAW,UAAWF,EAAaE,CAAS,CAAE,CAC3D,CACJ,CCruBO,IAAMC,GAAN,KAAY,CACf,MACA,MACA,SACA,UACA,OAAS,GACT,SAAW,GACX,UAAY,GACZ,YAAYC,EAAMC,EAAK,CAInB,GAHAC,GAAMF,CAAI,EACVG,EAAOF,EAAK,OAAW,KAAK,EAC5B,KAAK,MAAQD,EAAK,OAAO,EACrB,OAAO,KAAK,MAAM,QAAW,WAC7B,MAAM,IAAI,MAAM,wBAAwB,EAC5C,KAAK,SAAW,KAAK,MAAM,SAC3B,KAAK,UAAY,KAAK,MAAM,UAC5B,IAAMI,EAAW,KAAK,SAChBC,EAAM,IAAI,WAAWD,CAAQ,EAEnCC,EAAI,IAAIJ,EAAI,OAASG,EAAWJ,EAAK,OAAO,EAAE,OAAOC,CAAG,EAAE,OAAO,EAAIA,CAAG,EACxE,QAASK,EAAI,EAAGA,EAAID,EAAI,OAAQC,IAC5BD,EAAIC,CAAC,GAAK,GACd,KAAK,MAAM,OAAOD,CAAG,EAGrB,KAAK,MAAQL,EAAK,OAAO,EAEzB,QAASM,EAAI,EAAGA,EAAID,EAAI,OAAQC,IAC5BD,EAAIC,CAAC,GAAK,IACd,KAAK,MAAM,OAAOD,CAAG,EACrBE,GAAMF,CAAG,CACb,CACA,OAAOG,EAAK,CACR,OAAAC,GAAQ,IAAI,EACZ,KAAK,MAAM,OAAOD,CAAG,EACd,IACX,CACA,WAAWE,EAAK,CACZD,GAAQ,IAAI,EACZE,GAAQD,EAAK,IAAI,EACjB,KAAK,SAAW,GAChB,IAAMF,EAAME,EAAI,SAAS,EAAG,KAAK,SAAS,EAG1C,KAAK,MAAM,WAAWF,CAAG,EACzB,KAAK,MAAM,OAAOA,CAAG,EACrB,KAAK,MAAM,WAAWA,CAAG,EACzB,KAAK,QAAQ,CACjB,CACA,QAAS,CACL,IAAME,EAAM,IAAI,WAAW,KAAK,MAAM,SAAS,EAC/C,YAAK,WAAWA,CAAG,EACZA,CACX,CACA,WAAWE,EAAI,CAGXA,IAAO,OAAO,OAAO,OAAO,eAAe,IAAI,EAAG,CAAC,CAAC,EACpD,GAAM,CAAE,MAAAC,EAAO,MAAAC,EAAO,SAAAC,EAAU,UAAAC,EAAW,SAAAZ,EAAU,UAAAa,EAAW,OAAAC,CAAO,EAAI,KAC3E,OAAAN,EAAKA,EACLA,EAAG,SAAWG,EACdH,EAAG,UAAYI,EACfJ,EAAG,SAAWR,EACdQ,EAAG,UAAYK,EACfL,EAAG,OAASM,EACZN,EAAG,MAAQC,EAAM,WAAWD,EAAG,KAAK,EACpCA,EAAG,MAAQE,EAAM,WAAWF,EAAG,KAAK,EAC7BA,CACX,CACA,OAAQ,CACJ,OAAO,KAAK,WAAW,CAC3B,CACA,SAAU,CACN,KAAK,UAAY,GACjB,KAAK,MAAM,QAAQ,EACnB,KAAK,MAAM,QAAQ,CACvB,CACJ,EACaO,IAAwB,IAAM,CACvC,IAAMC,GAAS,CAACpB,EAAMC,EAAKoB,IAAY,IAAItB,GAAMC,EAAMC,CAAG,EAAE,OAAOoB,CAAO,EAAE,OAAO,GACnF,OAAAD,EAAM,OAAS,CAACpB,EAAMC,IAAQ,IAAIF,GAAMC,EAAMC,CAAG,EAC1CmB,CACX,GAAG,ECrFH,IAAME,GAAsB,OAAO,CAAC,EAUvBC,GAAN,cAAqB,KAAM,CAC9B,YAAYC,EAAI,GAAI,CAChB,MAAMA,CAAC,CACX,CACJ,EAGMC,GAAO,CAET,IAAKF,GAEL,KAAM,CACF,OAAQ,CAACG,EAAKC,IAAS,CACnB,GAAM,CAAE,IAAKC,CAAE,EAAIH,GAEnB,GADAI,GAAYH,EAAK,KAAK,EAClBA,EAAM,GAAKA,EAAM,IACjB,MAAM,IAAIE,EAAE,uBAAuB,EAIvC,GAHAE,GAAQH,EAAM,MAAM,EAGhBA,EAAK,OAAS,EACd,MAAM,IAAIC,EAAE,2BAA2B,EAC3C,IAAMG,EAAUJ,EAAK,OAAS,EACxBK,EAAMC,GAAoBF,CAAO,EACvC,GAAKC,EAAI,OAAS,EAAK,IACnB,MAAM,IAAIJ,EAAE,sCAAsC,EAEtD,IAAMM,EAASH,EAAU,IAAME,GAAqBD,EAAI,OAAS,EAAK,GAAW,EAAI,GAErF,OADUC,GAAoBP,CAAG,EACtBQ,EAASF,EAAML,CAC9B,EAEA,OAAOD,EAAKC,EAAM,CACd,GAAM,CAAE,IAAKC,CAAE,EAAIH,GACnBE,EAAOQ,GAAOR,EAAM,OAAW,UAAU,EACzC,IAAIS,EAAM,EACV,GAAIV,EAAM,GAAKA,EAAM,IACjB,MAAM,IAAIE,EAAE,uBAAuB,EACvC,GAAID,EAAK,OAAS,GAAKA,EAAKS,GAAK,IAAMV,EACnC,MAAM,IAAIE,EAAE,uBAAuB,EACvC,IAAMS,EAAQV,EAAKS,GAAK,EAElBE,EAAS,CAAC,EAAED,EAAQ,KACtBE,EAAS,EACb,GAAI,CAACD,EACDC,EAASF,MACR,CAED,IAAMH,EAASG,EAAQ,IACvB,GAAI,CAACH,EACD,MAAM,IAAIN,EAAE,mDAAmD,EAEnE,GAAIM,EAAS,EACT,MAAM,IAAIN,EAAE,0CAA0C,EAC1D,IAAMY,EAAcb,EAAK,SAASS,EAAKA,EAAMF,CAAM,EACnD,GAAIM,EAAY,SAAWN,EACvB,MAAM,IAAIN,EAAE,uCAAuC,EACvD,GAAIY,EAAY,CAAC,IAAM,EACnB,MAAM,IAAIZ,EAAE,sCAAsC,EACtD,QAAWa,KAAKD,EACZD,EAAUA,GAAU,EAAKE,EAE7B,GADAL,GAAOF,EACHK,EAAS,IACT,MAAM,IAAIX,EAAE,wCAAwC,CAC5D,CACA,IAAMc,EAAIf,EAAK,SAASS,EAAKA,EAAMG,CAAM,EACzC,GAAIG,EAAE,SAAWH,EACb,MAAM,IAAIX,EAAE,gCAAgC,EAChD,MAAO,CAAE,EAAAc,EAAG,EAAGf,EAAK,SAASS,EAAMG,CAAM,CAAE,CAC/C,CACJ,EAKA,KAAM,CACF,OAAOI,EAAK,CACR,GAAM,CAAE,IAAKf,CAAE,EAAIH,GAEnB,GADAmB,GAAWD,CAAG,EACVA,EAAMrB,GACN,MAAM,IAAIM,EAAE,4CAA4C,EAC5D,IAAIiB,EAAMZ,GAAoBU,CAAG,EAIjC,GAFI,OAAO,SAASE,EAAI,CAAC,EAAG,EAAE,EAAI,IAC9BA,EAAM,KAAOA,GACbA,EAAI,OAAS,EACb,MAAM,IAAIjB,EAAE,gDAAgD,EAChE,OAAOiB,CACX,EACA,OAAOlB,EAAM,CACT,GAAM,CAAE,IAAKC,CAAE,EAAIH,GACnB,GAAIE,EAAK,OAAS,EACd,MAAM,IAAIC,EAAE,kCAAkC,EAClD,GAAID,EAAK,CAAC,EAAI,IACV,MAAM,IAAIC,EAAE,qCAAqC,EAErD,GAAID,EAAK,OAAS,GAAKA,EAAK,CAAC,IAAM,GAAQ,EAAEA,EAAK,CAAC,EAAI,KACnD,MAAM,IAAIC,EAAE,qDAAqD,EACrE,OAAOkB,GAAgBnB,CAAI,CAC/B,CACJ,EACA,MAAMoB,EAAOC,EAAgB,CAEzB,GAAM,CAAE,IAAKpB,EAAG,KAAMqB,EAAK,KAAMC,CAAI,EAAIzB,GACzC,GAAIuB,IAAmB,SACnBnB,GAAYmB,EAAgB,gBAAgB,EACxCA,EAAiB,GACjB,MAAM,IAAIpB,EAAE,oDAAoD,EAExE,IAAMD,EAAOQ,GAAOY,EAAO,OAAW,WAAW,EAC3C,CAAE,EAAGI,EAAU,EAAGC,CAAa,EAAIF,EAAI,OAAO,GAAMvB,CAAI,EAC9D,GAAIyB,EAAa,OACb,MAAM,IAAIxB,EAAE,6CAA6C,EAC7D,GAAM,CAAE,EAAGyB,EAAQC,CAAc,EAAIJ,EAAI,OAAO,EAAMC,CAAQ,EACxD,CAAE,EAAGI,EAAQ,EAAGC,CAAW,EAAIN,EAAI,OAAO,EAAMI,CAAU,EAChE,GAAIE,EAAW,OACX,MAAM,IAAI5B,EAAE,6CAA6C,EAE7D,GAAIoB,IAAmB,SAClBK,EAAO,OAASL,GAAkBO,EAAO,OAASP,GACnD,MAAM,IAAIpB,EAAE,sCAAsC,EACtD,MAAO,CAAE,EAAGqB,EAAI,OAAOI,CAAM,EAAG,EAAGJ,EAAI,OAAOM,CAAM,CAAE,CAC1D,EACA,WAAWE,EAAK,CACZ,GAAM,CAAE,KAAMP,EAAK,KAAMD,CAAI,EAAIxB,GACjCiC,GAAeD,EAAK,CAAE,EAAG,SAAU,EAAG,QAAS,EAAG,CAAC,EAAG,KAAK,EAC3D,IAAME,EAAKT,EAAI,OAAO,EAAMD,EAAI,OAAOQ,EAAI,CAAC,CAAC,EACvCG,EAAKV,EAAI,OAAO,EAAMD,EAAI,OAAOQ,EAAI,CAAC,CAAC,EACvCI,EAAMF,EAAKC,EACjB,OAAOV,EAAI,OAAO,GAAMW,CAAG,CAC/B,CACJ,EAeaC,IACT,OAAO,OAAOrC,GAAK,IAAI,EACvB,OAAO,OAAOA,GAAK,IAAI,EAChB,OAAO,OAAOA,EAAI,GC/H7B,IAAMsC,GAAa,CAACC,EAAKC,KAASD,GAAOA,GAAO,EAAIC,EAAM,CAACA,GAAOC,IAAOD,EAElE,SAASE,GAAiBC,EAAGC,EAAOC,EAAG,CAM1CC,GAAS,SAAUH,EAAGI,GAAKF,CAAC,EAE5B,GAAM,CAAC,CAACG,EAAIC,CAAE,EAAG,CAACC,EAAIC,CAAE,CAAC,EAAIP,EACvBQ,EAAKd,GAAWa,EAAKR,EAAGE,CAAC,EACzBQ,EAAKf,GAAW,CAACW,EAAKN,EAAGE,CAAC,EAG5BS,EAAKX,EAAIS,EAAKJ,EAAKK,EAAKH,EACxBK,EAAK,CAACH,EAAKH,EAAKI,EAAKF,EACnBK,EAAQF,EAAKP,GACbU,EAAQF,EAAKR,GACfS,IACAF,EAAK,CAACA,GACNG,IACAF,EAAK,CAACA,GAIV,IAAMG,EAAUC,GAAQ,KAAK,KAAKC,GAAOf,CAAC,EAAI,CAAC,CAAC,EAAIgB,GACpD,GAAIP,EAAKP,IAAOO,GAAMI,GAAWH,EAAKR,IAAOQ,GAAMG,EAC/C,MAAM,IAAI,MAAM,0CAA0C,EAE9D,MAAO,CAAE,MAAAF,EAAO,GAAAF,EAAI,MAAAG,EAAO,GAAAF,CAAG,CAClC,CACA,SAASO,GAAkBC,EAAQ,CAC/B,GAAI,CAAC,CAAC,UAAW,YAAa,KAAK,EAAE,SAASA,CAAM,EAChD,MAAM,IAAI,MAAM,2DAA2D,EAC/E,OAAOA,CACX,CACA,SAASC,GAAgBC,EAAMC,EAAK,CAChCC,GAAeF,CAAI,EACnB,IAAMG,EAAQ,CAAC,EAIf,QAASC,KAAW,OAAO,KAAKH,CAAG,EAE/BE,EAAMC,CAAO,EAAIJ,EAAKI,CAAO,IAAM,OAAYH,EAAIG,CAAO,EAAIJ,EAAKI,CAAO,EAE9E,OAAAC,GAAMF,EAAM,KAAM,MAAM,EACxBE,GAAMF,EAAM,QAAS,SAAS,EAC1BA,EAAM,SAAW,QACjBN,GAAkBM,EAAM,MAAM,EAC3BA,CACX,CAGA,IAAMrB,GAAsB,OAAO,CAAC,EAAGc,GAAsB,OAAO,CAAC,EAAGpB,GAAsB,OAAO,CAAC,EAAG8B,GAAsB,OAAO,CAAC,EAAGC,GAAsB,OAAO,CAAC,EA0BjK,SAASC,GAAYC,EAAQC,EAAY,CAAC,EAAG,CAChD,IAAMC,EAAYC,GAAkB,cAAeH,EAAQC,CAAS,EAC9DG,EAAKF,EAAU,GACfG,EAAKH,EAAU,GACjBI,EAAQJ,EAAU,MAChB,CAAE,EAAGK,EAAU,EAAGC,CAAY,EAAIF,EACxCb,GAAeQ,EAAW,CAAC,EAAG,CAC1B,mBAAoB,UACpB,cAAe,WACf,cAAe,WACf,UAAW,WACX,QAAS,WACT,KAAM,SACN,YAAa,UACjB,CAAC,EAGD,GAAM,CAAE,KAAMQ,EAAU,mBAAAC,EAAoB,cAAAC,EAAe,cAAAC,EAAe,UAAAC,EAAW,QAAAC,CAAS,EAAIb,EAC5Fc,EAAcd,EAAU,cAAgB,OAAYc,GAAgBd,EAAU,YACpF,GAAIQ,IACI,CAACL,EAAG,IAAIE,EAAM,CAAC,GAAK,OAAOG,EAAS,MAAS,UAAY,CAAC,MAAM,QAAQA,EAAS,OAAO,GACxF,MAAM,IAAI,MAAM,4DAA4D,EAGpF,IAAMO,EAAOP,EACP,CACE,KAAMA,EAAS,KACf,QAASA,EAAS,QAAQ,IAAKvC,GAAU,CAAC,GAAGA,CAAK,CAAC,CACvD,EACE,OACA+C,EAAUC,GAAYd,EAAIC,CAAE,EAClC,SAASc,GAA+B,CACpC,GAAI,CAACf,EAAG,MACJ,MAAM,IAAI,MAAM,4DAA4D,CACpF,CAEA,SAASgB,EAAaC,EAAIC,EAAOC,EAAc,CAI3C,GAAID,EAAM,IAAI,EAAG,CACb,GAAI,CAACZ,EACD,MAAM,IAAI,MAAM,iBAAiB,EACrC,OAAO,WAAW,GAAG,CAAC,CAC1B,CACA,GAAM,CAAE,EAAAc,EAAG,EAAAC,CAAE,EAAIH,EAAM,SAAS,EAC1BI,EAAKtB,EAAG,QAAQoB,CAAC,EAEvB,GADA5B,GAAM2B,EAAc,cAAc,EAC9BA,EAAc,CACdJ,EAA6B,EAC7B,IAAMQ,EAAW,CAACvB,EAAG,MAAMqB,CAAC,EAC5B,OAAOG,GAAYC,GAAQF,CAAQ,EAAGD,CAAE,CAC5C,KAEI,QAAOE,GAAY,WAAW,GAAG,CAAI,EAAGF,EAAItB,EAAG,QAAQqB,CAAC,CAAC,CAEjE,CACA,SAASK,EAAeC,EAAO,CAC3BC,GAAOD,EAAO,OAAW,OAAO,EAChC,GAAM,CAAE,UAAWE,EAAM,sBAAuBC,CAAO,EAAIjB,EACrDkB,EAASJ,EAAM,OACfK,EAAOL,EAAM,CAAC,EACdM,EAAON,EAAM,SAAS,CAAC,EAM7B,GAAIrB,GAAsByB,IAAW,GAAKC,IAAS,EAC/C,MAAO,CAAE,EAAGhC,EAAG,KAAM,EAAGA,EAAG,IAAK,EAEpC,GAAI+B,IAAWF,IAASG,IAAS,GAAQA,IAAS,GAAO,CACrD,IAAMZ,EAAIpB,EAAG,UAAUiC,CAAI,EAC3B,GAAI,CAACjC,EAAG,QAAQoB,CAAC,EACb,MAAM,IAAI,MAAM,qCAAqC,EACzD,IAAMc,EAAKC,EAAoBf,CAAC,EAC5BC,EACJ,GAAI,CACAA,EAAIrB,EAAG,KAAKkC,CAAE,CAClB,OACOE,EAAW,CACd,IAAMC,EAAMD,aAAqB,MAAQ,KAAOA,EAAU,QAAU,GACpE,MAAM,IAAI,MAAM,yCAA2CC,CAAG,CAClE,CACAtB,EAA6B,EAC7B,IAAMuB,EAAQtC,EAAG,MAAMqB,CAAC,EAExB,OADeW,EAAO,KAAO,IACfM,IACVjB,EAAIrB,EAAG,IAAIqB,CAAC,GACT,CAAE,EAAAD,EAAG,EAAAC,CAAE,CAClB,SACSU,IAAWD,GAAUE,IAAS,EAAM,CAEzC,IAAMO,EAAIvC,EAAG,MACPoB,EAAIpB,EAAG,UAAUiC,EAAK,SAAS,EAAGM,CAAC,CAAC,EACpClB,EAAIrB,EAAG,UAAUiC,EAAK,SAASM,EAAGA,EAAI,CAAC,CAAC,EAC9C,GAAI,CAACC,EAAUpB,EAAGC,CAAC,EACf,MAAM,IAAI,MAAM,4BAA4B,EAChD,MAAO,CAAE,EAAAD,EAAG,EAAAC,CAAE,CAClB,KAEI,OAAM,IAAI,MAAM,yBAAyBU,CAAM,yBAAyBF,CAAI,oBAAoBC,CAAM,EAAE,CAEhH,CACA,IAAMW,EAAc/B,IAAY,OAAYM,EAAeN,EACrDgC,EAAcjC,IAAc,OAAYiB,EAAiBjB,EAIzDkC,EAAK3C,EAAG,IAAIE,EAAM,EAAGT,EAAG,EACxBmD,EAAO5C,EAAG,IAAIE,EAAM,CAAC,EAAK2C,GAAM7C,EAAG,KAAQoB,GAAMpB,EAAG,IAAIE,EAAM,EAAGkB,CAAC,EACxE,SAASe,EAAoBf,EAAG,CAC5B,IAAM0B,EAAK9C,EAAG,IAAIoB,CAAC,EACb2B,EAAK/C,EAAG,IAAI8C,EAAI1B,CAAC,EACvB,OAAOpB,EAAG,IAAIA,EAAG,IAAI+C,EAAI/C,EAAG,IAAIoB,EAAGlB,EAAM,CAAC,CAAC,EAAGA,EAAM,CAAC,CACzD,CAGA,SAASsC,EAAUpB,EAAGC,EAAG,CACrB,IAAM2B,EAAOhD,EAAG,IAAIqB,CAAC,EACf4B,EAAQd,EAAoBf,CAAC,EACnC,OAAOpB,EAAG,IAAIgD,EAAMC,CAAK,CAC7B,CAIA,GAAI,CAACT,EAAUtC,EAAM,GAAIA,EAAM,EAAE,EAC7B,MAAM,IAAI,MAAM,mCAAmC,EAGvD,IAAMgD,EAAOlD,EAAG,IAAIA,EAAG,IAAIE,EAAM,EAAGT,EAAG,EAAGC,EAAG,EACvCyD,EAAQnD,EAAG,IAAIA,EAAG,IAAIE,EAAM,CAAC,EAAG,OAAO,EAAE,CAAC,EAChD,GAAIF,EAAG,IAAIA,EAAG,IAAIkD,EAAMC,CAAK,CAAC,EAC1B,MAAM,IAAI,MAAM,0BAA0B,EAE9C,SAASC,EAAOC,EAAOtF,EAAGuF,EAAU,GAAO,CACvC,GAAI,CAACtD,EAAG,QAAQjC,CAAC,GAAMuF,GAAWtD,EAAG,IAAIjC,CAAC,EACtC,MAAM,IAAI,MAAM,wBAAwBsF,CAAK,EAAE,EAInD,OAAO,OAAOtF,GAAM,UAAYA,IAAM,KAAOiC,EAAG,OAAOjC,CAAC,EAAIA,CAChE,CACA,SAASwF,EAAUC,EAAO,CACtB,GAAI,EAAEA,aAAiBC,GACnB,MAAM,IAAI,MAAM,4BAA4B,CACpD,CACA,SAASC,EAAiB7F,EAAG,CACzB,GAAI,CAAC+C,GAAQ,CAACA,EAAK,QACf,MAAM,IAAI,MAAM,SAAS,EAC7B,OAAOhD,GAAiBC,EAAG+C,EAAK,QAASX,EAAG,KAAK,CACrD,CAOA,SAAS0D,GAAaC,EAAQC,EAASC,EAAGjG,EAAG,CACzC,GAAI,CAACoC,EAAG,QAAQpC,CAAC,EACb,MAAM,IAAI,WAAW,8BAA8B,EACvD,GAAI+C,EAAM,CACN,GAAM,CAAE,MAAAlC,EAAO,GAAAF,EAAI,MAAAG,EAAO,GAAAF,CAAG,EAAIiF,EAAiB7F,CAAC,EAC7CkG,EAAM,IAAIN,EAAMzD,EAAG,IAAI8D,EAAE,EAAGlD,EAAK,IAAI,EAAGkD,EAAE,EAAGA,EAAE,CAAC,EACtDF,EAAO,KAAKlF,EAAQoF,EAAE,OAAO,EAAIA,EAAGnF,EAAQoF,EAAI,OAAO,EAAIA,CAAG,EAC9DF,EAAQ,KAAKrF,EAAIC,CAAE,CACvB,MAEImF,EAAO,KAAKE,CAAC,EACbD,EAAQ,KAAKhG,CAAC,CAEtB,CAMA,IAAMmG,GAAgB,IAAI,QAM1B,MAAMP,CAAM,CACR,OAAO,KAAO,IAAIA,EAAMvD,EAAM,GAAIA,EAAM,GAAIF,EAAG,GAAG,EAClD,OAAO,KAAO,IAAIyD,EAAMzD,EAAG,KAAMA,EAAG,IAAKA,EAAG,IAAI,EAChD,OAAO,GAAKA,EACZ,OAAO,GAAKC,EACZ,EACA,EACA,EAEA,YAAYgE,EAAGC,EAAGC,EAAG,CACjB,KAAK,EAAIf,EAAO,IAAKa,CAAC,EAItB,KAAK,EAAIb,EAAO,IAAKc,EAAG,EAAI,EAC5B,KAAK,EAAId,EAAO,IAAKe,CAAC,EACtB,OAAO,OAAO,IAAI,CACtB,CACA,OAAO,OAAQ,CACX,OAAOjE,CACX,CAEA,OAAO,WAAW4D,EAAG,CACjB,GAAM,CAAE,EAAA1C,EAAG,EAAAC,CAAE,EAAIyC,GAAK,CAAC,EACvB,GAAI,CAACA,GAAK,CAAC9D,EAAG,QAAQoB,CAAC,GAAK,CAACpB,EAAG,QAAQqB,CAAC,EACrC,MAAM,IAAI,MAAM,sBAAsB,EAC1C,GAAIyC,aAAaL,EACb,MAAM,IAAI,MAAM,8BAA8B,EAElD,OAAIzD,EAAG,IAAIoB,CAAC,GAAKpB,EAAG,IAAIqB,CAAC,EACdoC,EAAM,KACV,IAAIA,EAAMrC,EAAGC,EAAGrB,EAAG,GAAG,CACjC,CACA,OAAO,UAAU2B,EAAO,CACpB,IAAMyC,EAAIX,EAAM,WAAWf,EAAYd,GAAOD,EAAO,OAAW,OAAO,CAAC,CAAC,EACzE,OAAAyC,EAAE,eAAe,EACVA,CACX,CACA,OAAO,QAAQC,EAAK,CAChB,OAAOZ,EAAM,UAAUa,GAAWD,CAAG,CAAC,CAC1C,CACA,IAAI,GAAI,CACJ,OAAO,KAAK,SAAS,EAAE,CAC3B,CACA,IAAI,GAAI,CACJ,OAAO,KAAK,SAAS,EAAE,CAC3B,CAIA,WAAWE,EAAa,EAAGC,EAAS,GAAM,CACtC,OAAAC,GAAK,cAAc,KAAMF,CAAU,EAC9BC,GACD,KAAK,SAAS/E,EAAG,EACd,IACX,CAGA,gBAAiB,CACb,IAAMqE,EAAI,KACV,GAAIA,EAAE,IAAI,EAAG,CAKT,GAAIxD,GAAsBN,EAAG,IAAI8D,EAAE,CAAC,GAAK9D,EAAG,IAAI8D,EAAE,EAAG9D,EAAG,GAAG,GAAKA,EAAG,IAAI8D,EAAE,CAAC,EACtE,OACJ,MAAM,IAAI,MAAM,iBAAiB,CACrC,CACA,GAAIE,GAAc,IAAIF,CAAC,EACnB,OAEJ,GAAM,CAAE,EAAA1C,EAAG,EAAAC,CAAE,EAAIyC,EAAE,SAAS,EAC5B,GAAI,CAAC9D,EAAG,QAAQoB,CAAC,GAAK,CAACpB,EAAG,QAAQqB,CAAC,EAC/B,MAAM,IAAI,MAAM,sCAAsC,EAC1D,GAAI,CAACmB,EAAUpB,EAAGC,CAAC,EACf,MAAM,IAAI,MAAM,mCAAmC,EACvD,GAAI,CAACyC,EAAE,cAAc,EACjB,MAAM,IAAI,MAAM,wCAAwC,EAC5DE,GAAc,IAAIF,CAAC,CACvB,CACA,UAAW,CACP,GAAM,CAAE,EAAAzC,CAAE,EAAI,KAAK,SAAS,EAC5B,GAAI,CAACrB,EAAG,MACJ,MAAM,IAAI,MAAM,6BAA6B,EACjD,MAAO,CAACA,EAAG,MAAMqB,CAAC,CACtB,CAEA,OAAOmC,EAAO,CACVD,EAAUC,CAAK,EACf,GAAM,CAAE,EAAGkB,EAAI,EAAGC,EAAI,EAAGC,CAAG,EAAI,KAC1B,CAAE,EAAGC,EAAI,EAAGC,EAAI,EAAGC,CAAG,EAAIvB,EAC1BwB,EAAKhF,EAAG,IAAIA,EAAG,IAAI0E,EAAIK,CAAE,EAAG/E,EAAG,IAAI6E,EAAID,CAAE,CAAC,EAC1CK,EAAKjF,EAAG,IAAIA,EAAG,IAAI2E,EAAII,CAAE,EAAG/E,EAAG,IAAI8E,EAAIF,CAAE,CAAC,EAChD,OAAOI,GAAMC,CACjB,CAEA,QAAS,CACL,OAAO,IAAIxB,EAAM,KAAK,EAAGzD,EAAG,IAAI,KAAK,CAAC,EAAG,KAAK,CAAC,CACnD,CAKA,QAAS,CACL,GAAM,CAAE,EAAG0E,EAAI,EAAGC,EAAI,EAAGC,CAAG,EAAI,KAC5BM,EAAKlF,EAAG,KAAMmF,EAAKnF,EAAG,KAAMoF,EAAKpF,EAAG,KACpCqF,EAAKrF,EAAG,IAAI0E,EAAIA,CAAE,EAClBY,EAAKtF,EAAG,IAAI2E,EAAIA,CAAE,EAClBY,EAAKvF,EAAG,IAAI4E,EAAIA,CAAE,EAClBY,EAAKxF,EAAG,IAAI0E,EAAIC,CAAE,EACtB,OAAAa,EAAKxF,EAAG,IAAIwF,EAAIA,CAAE,EAClBJ,EAAKpF,EAAG,IAAI0E,EAAIE,CAAE,EAClBQ,EAAKpF,EAAG,IAAIoF,EAAIA,CAAE,EAClBF,EAAKtC,EAAKwC,CAAE,EACZD,EAAKnF,EAAG,IAAI2C,EAAI4C,CAAE,EAClBJ,EAAKnF,EAAG,IAAIkF,EAAIC,CAAE,EAClBD,EAAKlF,EAAG,IAAIsF,EAAIH,CAAE,EAClBA,EAAKnF,EAAG,IAAIsF,EAAIH,CAAE,EAClBA,EAAKnF,EAAG,IAAIkF,EAAIC,CAAE,EAClBD,EAAKlF,EAAG,IAAIwF,EAAIN,CAAE,EAClBE,EAAKpF,EAAG,IAAI2C,EAAIyC,CAAE,EAClBG,EAAK3C,EAAK2C,CAAE,EACZC,EAAKxF,EAAG,IAAIqF,EAAIE,CAAE,EAClBC,EAAK5C,EAAK4C,CAAE,EACZA,EAAKxF,EAAG,IAAIwF,EAAIJ,CAAE,EAClBA,EAAKpF,EAAG,IAAIqF,EAAIA,CAAE,EAClBA,EAAKrF,EAAG,IAAIoF,EAAIC,CAAE,EAClBA,EAAKrF,EAAG,IAAIqF,EAAIE,CAAE,EAClBF,EAAKrF,EAAG,IAAIqF,EAAIG,CAAE,EAClBL,EAAKnF,EAAG,IAAImF,EAAIE,CAAE,EAClBE,EAAKvF,EAAG,IAAI2E,EAAIC,CAAE,EAClBW,EAAKvF,EAAG,IAAIuF,EAAIA,CAAE,EAClBF,EAAKrF,EAAG,IAAIuF,EAAIC,CAAE,EAClBN,EAAKlF,EAAG,IAAIkF,EAAIG,CAAE,EAClBD,EAAKpF,EAAG,IAAIuF,EAAID,CAAE,EAClBF,EAAKpF,EAAG,IAAIoF,EAAIA,CAAE,EAClBA,EAAKpF,EAAG,IAAIoF,EAAIA,CAAE,EACX,IAAI3B,EAAMyB,EAAIC,EAAIC,CAAE,CAC/B,CAKA,IAAI5B,EAAO,CACPD,EAAUC,CAAK,EACf,GAAM,CAAE,EAAGkB,EAAI,EAAGC,EAAI,EAAGC,CAAG,EAAI,KAC1B,CAAE,EAAGC,EAAI,EAAGC,EAAI,EAAGC,CAAG,EAAIvB,EAC5B0B,EAAKlF,EAAG,KAAMmF,EAAKnF,EAAG,KAAMoF,EAAKpF,EAAG,KACpCqF,EAAKrF,EAAG,IAAI0E,EAAIG,CAAE,EAClBS,EAAKtF,EAAG,IAAI2E,EAAIG,CAAE,EAClBS,EAAKvF,EAAG,IAAI4E,EAAIG,CAAE,EAClBS,GAAKxF,EAAG,IAAI0E,EAAIC,CAAE,EAClBc,GAAKzF,EAAG,IAAI6E,EAAIC,CAAE,EACtBU,GAAKxF,EAAG,IAAIwF,GAAIC,EAAE,EAClBA,GAAKzF,EAAG,IAAIqF,EAAIC,CAAE,EAClBE,GAAKxF,EAAG,IAAIwF,GAAIC,EAAE,EAClBA,GAAKzF,EAAG,IAAI0E,EAAIE,CAAE,EAClB,IAAIc,GAAK1F,EAAG,IAAI6E,EAAIE,CAAE,EACtB,OAAAU,GAAKzF,EAAG,IAAIyF,GAAIC,EAAE,EAClBA,GAAK1F,EAAG,IAAIqF,EAAIE,CAAE,EAClBE,GAAKzF,EAAG,IAAIyF,GAAIC,EAAE,EAClBA,GAAK1F,EAAG,IAAI2E,EAAIC,CAAE,EAClBM,EAAKlF,EAAG,IAAI8E,EAAIC,CAAE,EAClBW,GAAK1F,EAAG,IAAI0F,GAAIR,CAAE,EAClBA,EAAKlF,EAAG,IAAIsF,EAAIC,CAAE,EAClBG,GAAK1F,EAAG,IAAI0F,GAAIR,CAAE,EAClBE,EAAKxC,EAAK6C,EAAE,EACZP,EAAKlF,EAAG,IAAI2C,EAAI4C,CAAE,EAClBH,EAAKpF,EAAG,IAAIkF,EAAIE,CAAE,EAClBF,EAAKlF,EAAG,IAAIsF,EAAIF,CAAE,EAClBA,EAAKpF,EAAG,IAAIsF,EAAIF,CAAE,EAClBD,EAAKnF,EAAG,IAAIkF,EAAIE,CAAE,EAClBE,EAAKtF,EAAG,IAAIqF,EAAIA,CAAE,EAClBC,EAAKtF,EAAG,IAAIsF,EAAID,CAAE,EAClBE,EAAK3C,EAAK2C,CAAE,EACZE,GAAKzF,EAAG,IAAI2C,EAAI8C,EAAE,EAClBH,EAAKtF,EAAG,IAAIsF,EAAIC,CAAE,EAClBA,EAAKvF,EAAG,IAAIqF,EAAIE,CAAE,EAClBA,EAAK3C,EAAK2C,CAAE,EACZE,GAAKzF,EAAG,IAAIyF,GAAIF,CAAE,EAClBF,EAAKrF,EAAG,IAAIsF,EAAIG,EAAE,EAClBN,EAAKnF,EAAG,IAAImF,EAAIE,CAAE,EAClBA,EAAKrF,EAAG,IAAI0F,GAAID,EAAE,EAClBP,EAAKlF,EAAG,IAAIwF,GAAIN,CAAE,EAClBA,EAAKlF,EAAG,IAAIkF,EAAIG,CAAE,EAClBA,EAAKrF,EAAG,IAAIwF,GAAIF,CAAE,EAClBF,EAAKpF,EAAG,IAAI0F,GAAIN,CAAE,EAClBA,EAAKpF,EAAG,IAAIoF,EAAIC,CAAE,EACX,IAAI5B,EAAMyB,EAAIC,EAAIC,CAAE,CAC/B,CACA,SAAS5B,EAAO,CAGZ,OAAAD,EAAUC,CAAK,EACR,KAAK,IAAIA,EAAM,OAAO,CAAC,CAClC,CACA,KAAM,CACF,OAAO,KAAK,OAAOC,EAAM,IAAI,CACjC,CAQA,SAASkC,EAAQ,CAIb,GAAI,CAAC1F,EAAG,YAAY0F,CAAM,EACtB,MAAM,IAAI,WAAW,8BAA8B,EACvD,GAAM,CAAE,EAAA7B,EAAG,EAAA8B,CAAE,EAAInB,GAAK,UAAU,KAAMkB,EAAQxF,EAAU0F,EAAS,EACjE,OAAOA,GAAU,CAAC/B,EAAG8B,CAAC,CAAC,EAAE,CAAC,CAC9B,CAOA,eAAeD,EAAQ,CACnB,IAAM7B,EAAI,KACJgC,EAAKH,EAGX,GAAI,CAAC1F,EAAG,QAAQ6F,CAAE,EACd,MAAM,IAAI,WAAW,8BAA8B,EACvD,GAAIA,IAAO7H,IAAO6F,EAAE,IAAI,EACpB,OAAOL,EAAM,KACjB,GAAIqC,IAAO/G,GACP,OAAO+E,EACX,GAAIW,GAAK,cAAc,IAAI,EACvB,OAAOA,GAAK,UAAUX,EAAGgC,EAAID,EAAS,EAC1C,IAAMjC,EAAS,CAAC,EACVC,EAAU,CAAC,EACjB,OAAAF,GAAaC,EAAQC,EAASC,EAAGgC,CAAE,EAC5BC,GAAatC,EAAOG,EAAQC,CAAO,CAC9C,CAQA,aAAamC,EAAGxC,EAAOyC,EAAG,CACtB1C,EAAUC,CAAK,EACf,IAAMI,EAAS,CAAC,EACVC,EAAU,CAAC,EACjB,OAAAF,GAAaC,EAAQC,EAAS,KAAMmC,CAAC,EACrCrC,GAAaC,EAAQC,EAASL,EAAOyC,CAAC,EAC/BF,GAAatC,EAAOG,EAAQC,CAAO,CAC9C,CAMA,SAASqC,EAAW,CAChB,IAAMpC,EAAI,KACNqC,EAAKD,EACT,GAAIC,GAAM,MAAQ,CAACnG,EAAG,QAAQmG,CAAE,EAC5B,MAAM,IAAI,WAAW,0CAA0C,EACnE,GAAM,CAAE,EAAAlC,EAAG,EAAAC,EAAG,EAAAC,CAAE,EAAIL,EAEpB,GAAI9D,EAAG,IAAImE,EAAGnE,EAAG,GAAG,EAChB,MAAO,CAAE,EAAGiE,EAAG,EAAGC,CAAE,EACxB,IAAMkC,EAAMtC,EAAE,IAAI,EAGdqC,GAAM,OACNA,EAAKC,EAAMpG,EAAG,IAAMA,EAAG,IAAImE,CAAC,GAChC,IAAM/C,EAAIpB,EAAG,IAAIiE,EAAGkC,CAAE,EAChB9E,EAAIrB,EAAG,IAAIkE,EAAGiC,CAAE,EAChBE,EAAKrG,EAAG,IAAImE,EAAGgC,CAAE,EACvB,GAAIC,EACA,MAAO,CAAE,EAAGpG,EAAG,KAAM,EAAGA,EAAG,IAAK,EACpC,GAAI,CAACA,EAAG,IAAIqG,EAAIrG,EAAG,GAAG,EAClB,MAAM,IAAI,MAAM,kBAAkB,EACtC,MAAO,CAAE,EAAAoB,EAAG,EAAAC,CAAE,CAClB,CAKA,eAAgB,CACZ,OAAIlB,IAAapB,GACN,GACPyB,EACOA,EAAciD,EAAO,IAAI,EAE7BgB,GAAK,UAAU,KAAMrE,CAAW,EAAE,IAAI,CACjD,CACA,eAAgB,CACZ,OAAID,IAAapB,GACN,KACPwB,EACOA,EAAckD,EAAO,IAAI,EAI7B,KAAK,eAAetD,CAAQ,CACvC,CACA,cAAe,CACX,OAAIA,IAAapB,GACN,KAAK,IAAI,EACb,KAAK,cAAc,EAAE,IAAI,CACpC,CACA,QAAQoC,EAAe,GAAM,CACzB,OAAA3B,GAAM2B,EAAc,cAAc,EAGlC,KAAK,eAAe,EACbsB,EAAYgB,EAAO,KAAMtC,CAAY,CAChD,CACA,MAAMA,EAAe,GAAM,CACvB,OAAOmF,GAAW,KAAK,QAAQnF,CAAY,CAAC,CAChD,CACA,UAAW,CACP,MAAO,UAAU,KAAK,IAAI,EAAI,OAAS,KAAK,MAAM,CAAC,GACvD,CACJ,CACA,IAAM0E,GAAajC,GAAW2C,GAAW9C,EAAOG,CAAM,EAChDa,GAAO,IAAI+B,GAAiB/C,EAAO9C,CAAW,EAGpD,OAAI8D,GAAK,MAAQ,GACbhB,EAAM,KAAK,WAAW,CAAC,EAC3B,OAAO,OAAOA,EAAM,SAAS,EAC7B,OAAO,OAAOA,CAAK,EACZA,CACX,CAEA,SAAShC,GAAQF,EAAU,CACvB,OAAO,WAAW,GAAGA,EAAW,EAAO,CAAI,CAC/C,CACA,SAAST,GAAYd,EAAIC,EAAI,CACzB,MAAO,CACH,UAAWA,EAAG,MACd,UAAW,EAAID,EAAG,MAClB,sBAAuB,EAAI,EAAIA,EAAG,MAClC,mBAAoB,GAGpB,UAAW,EAAIC,EAAG,KACtB,CACJ,CAmBO,SAASwG,GAAKhD,EAAOiD,EAAW,CAAC,EAAG,CACvCC,GAAkBlD,CAAK,EACvB,GAAM,CAAE,GAAAxD,CAAG,EAAIwD,EACTmD,EAAeF,EAAS,cAAgB,OAAY/F,GAAgB+F,EAAS,YAG7E7F,EAAU,OAAO,OAAOC,GAAY2C,EAAM,GAAIxD,CAAE,EAAG,CACrD,KAAM,KAAK,IAAI4G,GAAiB5G,EAAG,KAAK,EAAG,EAAE,CACjD,CAAC,EACD,SAAS6G,EAAiBC,EAAW,CACjC,GAAI,CACA,IAAMtJ,EAAMwC,EAAG,UAAU8G,CAAS,EAClC,OAAO9G,EAAG,YAAYxC,CAAG,CAC7B,MACc,CACV,MAAO,EACX,CACJ,CACA,SAASuJ,EAAiBC,EAAW9F,EAAc,CAC/C,GAAM,CAAE,UAAWU,EAAM,sBAAAqF,CAAsB,EAAIrG,EACnD,GAAI,CACA,IAAMsG,EAAIF,EAAU,OAGpB,OAFI9F,IAAiB,IAAQgG,IAAMtF,GAE/BV,IAAiB,IAASgG,IAAMD,EACzB,GAGJ,CAACzD,EAAM,UAAUwD,CAAS,EAAE,IAAI,CAC3C,MACc,CACV,MAAO,EACX,CACJ,CAKA,SAASG,EAAgBC,EAAM,CAC3B,OAAAA,EAAOA,IAAS,OAAYT,EAAa/F,EAAQ,IAAI,EAAIwG,EAClDC,GAAe1F,GAAOyF,EAAMxG,EAAQ,KAAM,MAAM,EAAGZ,EAAG,KAAK,CACtE,CAMA,SAASsH,EAAaR,EAAW5F,EAAe,GAAM,CAClD,OAAOsC,EAAM,KAAK,SAASxD,EAAG,UAAU8G,CAAS,CAAC,EAAE,QAAQ5F,CAAY,CAC5E,CAIA,SAASqG,EAAUC,EAAM,CACrB,GAAM,CAAE,UAAAV,EAAW,UAAAE,EAAW,sBAAAC,CAAsB,EAAIrG,EAClD6G,EAAiBzH,EAAG,SAC1B,GAAI,CAAC0H,GAAQF,CAAI,EACb,OACJ,IAAMN,EAAIvF,GAAO6F,EAAM,OAAW,KAAK,EAAE,OACnCG,EAAQT,IAAMF,GAAaE,IAAMD,EACjCW,EAAQV,IAAMJ,GAAa,CAAC,CAACW,GAAgB,SAASP,CAAC,EAE7D,GAAI,EAAAS,GAASC,GAEb,OAAOD,CACX,CAaA,SAASE,EAAgBC,EAAYC,EAAY7G,EAAe,GAAM,CAClE,GAAIqG,EAAUO,CAAU,IAAM,GAC1B,MAAM,IAAI,MAAM,+BAA+B,EACnD,GAAIP,EAAUQ,CAAU,IAAM,GAC1B,MAAM,IAAI,MAAM,+BAA+B,EACnD,IAAMC,EAAIhI,EAAG,UAAU8H,CAAU,EAC3B9B,EAAIxC,EAAM,UAAUuE,CAAU,EACpC,GAAI/B,EAAE,IAAI,EACN,MAAM,IAAI,MAAM,uCAAuC,EAC3D,OAAOA,EAAE,SAASgC,CAAC,EAAE,QAAQ9G,CAAY,CAC7C,CACA,IAAM+G,EAAQ,CACV,iBAAApB,EACA,iBAAAE,EACA,gBAAAI,CACJ,EACMe,EAASC,GAAahB,EAAiBG,CAAY,EACzD,cAAO,OAAOW,CAAK,EACnB,OAAO,OAAOrH,CAAO,EACd,OAAO,OAAO,CAAE,aAAA0G,EAAc,gBAAAO,EAAiB,OAAAK,EAAQ,MAAA1E,EAAO,MAAAyE,EAAO,QAAArH,CAAQ,CAAC,CACzF,CA4BO,SAASwH,GAAM5E,EAAO6E,EAAMC,EAAY,CAAC,EAAG,CAC/C5B,GAAkBlD,CAAK,EAEvB,IAAM+E,EAAQF,EACdG,GAAMD,CAAK,EACXnJ,GAAekJ,EAAW,CAAC,EAAG,CAC1B,KAAM,WACN,KAAM,UACN,YAAa,WACb,SAAU,WACV,cAAe,UACnB,CAAC,EACD,IAAMpJ,EAAO,OAAO,OAAO,CAAC,EAAGoJ,CAAS,EAClC5H,EAAcxB,EAAK,cAAgB,OAAYwB,GAAgBxB,EAAK,YACpEuJ,EAAOvJ,EAAK,OAAS,OACrB,CAACwJ,EAAKC,IAAQF,GAAUF,EAAOG,EAAKC,CAAG,EACvCzJ,EAAK,KACL,CAAE,GAAAa,EAAI,GAAAC,CAAG,EAAIwD,EACb,CAAE,MAAOrD,EAAa,KAAMyI,CAAO,EAAI5I,EAMvC6I,EAAcjC,GAAiBzG,CAAW,EAC1C2I,EAASC,GAAiBrI,EAAamI,CAAW,EAClD,CAAE,OAAAX,EAAQ,aAAAZ,EAAc,gBAAAO,EAAiB,MAAAI,EAAO,QAAArH,CAAQ,EAAI4F,GAAKhD,EAAOtE,CAAI,EAC5E8J,EAAiB,CACnB,QAAS,GACT,KAAM,OAAO9J,EAAK,MAAS,UAAYA,EAAK,KAAO,GACnD,OAAQ,UACR,aAAc,EAClB,EAKM+J,EAAwB9I,EAAczC,GAAMoB,GAAMiB,EAAG,MAC3D,SAASmJ,EAAsBC,EAAQ,CACnC,IAAMC,EAAOjJ,GAAerB,GAC5B,OAAOqK,EAASC,CACpB,CACA,SAASC,EAAWjG,EAAO5F,EAAK,CAC5B,GAAI,CAACwC,EAAG,YAAYxC,CAAG,EACnB,MAAM,IAAI,MAAM,qBAAqB4F,CAAK,kCAAkC,EAChF,OAAO5F,CACX,CACA,SAAS8L,GAA6B,CAClC,GAAI,CAACvJ,EAAG,MACJ,MAAM,IAAI,MAAM,6BAA6B,CACrD,CAGA,SAASwJ,EAAepI,EAAGC,EAAGoI,EAAG,CAC7B,OAAAF,EAA2B,GACnBnI,IAAMqI,EAAI,EAAI,GAAK,OAAOzJ,EAAG,MAAMqB,CAAC,CAAC,CACjD,CACA,SAASqI,GAAyB,CAQ9B,GAAIR,EACA,MAAM,IAAI,MAAM,8DAA8D,CACtF,CACA,SAASS,EAAkBhI,EAAO1C,EAAQ,CACtCD,GAAkBC,CAAM,EACxB,IAAM2K,EAAO/I,EAAQ,UACfgJ,EAAQ5K,IAAW,UAAY2K,EAAO3K,IAAW,YAAc2K,EAAO,EAAI,OAChF,OAAOhI,GAAOD,EAAOkI,CAAK,CAC9B,CAIA,MAAMC,CAAU,CACZ,EACA,EACA,SACA,YAAYL,EAAGxB,EAAG8B,EAAU,CAGxB,GAFA,KAAK,EAAIT,EAAW,IAAKG,CAAC,EAC1B,KAAK,EAAIH,EAAW,IAAKrB,CAAC,EACtB8B,GAAY,KAAM,CAElB,GADAL,EAAuB,EACnB,CAAC,CAAC,EAAG,EAAG,EAAG,CAAC,EAAE,SAASK,CAAQ,EAC/B,MAAM,IAAI,MAAM,qBAAqB,EACzC,KAAK,SAAWA,CACpB,CACA,OAAO,OAAO,IAAI,CACtB,CACA,OAAO,UAAUpI,EAAO1C,EAASgK,EAAe,OAAQ,CACpDU,EAAkBhI,EAAO1C,CAAM,EAC/B,IAAI+K,EACJ,GAAI/K,IAAW,MAAO,CAGlB,GAAI0C,EAAM,OAAS,EAAI1B,EAAG,MAAQ,GAC9B,MAAM,IAAIgK,GAAI,IAAI,2CAA2C,EACjE,GAAM,CAAE,EAAAR,EAAG,EAAAxB,CAAE,EAAIgC,GAAI,MAAMrI,GAAOD,CAAK,EAAG1B,EAAG,MAAQ,CAAC,EACtD,OAAO,IAAI6J,EAAUL,EAAGxB,CAAC,CAC7B,CACIhJ,IAAW,cACX+K,EAAQrI,EAAM,CAAC,EACf1C,EAAS,UACT0C,EAAQA,EAAM,SAAS,CAAC,GAE5B,IAAM,EAAId,EAAQ,UAAY,EACxB4I,EAAI9H,EAAM,SAAS,EAAG,CAAC,EACvBsG,EAAItG,EAAM,SAAS,EAAG,EAAI,CAAC,EACjC,OAAO,IAAImI,EAAU7J,EAAG,UAAUwJ,CAAC,EAAGxJ,EAAG,UAAUgI,CAAC,EAAG+B,CAAK,CAChE,CACA,OAAO,QAAQ3F,EAAKpF,EAAQ,CACxB,OAAO,KAAK,UAAUqF,GAAWD,CAAG,EAAGpF,CAAM,CACjD,CACA,gBAAiB,CACb,GAAM,CAAE,SAAA8K,CAAS,EAAI,KACrB,GAAIA,GAAY,KACZ,MAAM,IAAI,MAAM,sCAAsC,EAC1D,OAAOA,CACX,CACA,eAAeA,EAAU,CACrB,OAAO,IAAID,EAAU,KAAK,EAAG,KAAK,EAAGC,CAAQ,CACjD,CAGA,iBAAiBG,EAAa,CAC1B,GAAM,CAAE,EAAAT,EAAG,EAAAxB,CAAE,EAAI,KACX8B,EAAW,KAAK,eAAe,EAC/BI,EAAOJ,IAAa,GAAKA,IAAa,EAAIN,EAAIrJ,EAAcqJ,EAClE,GAAI,CAACzJ,EAAG,QAAQmK,CAAI,EAChB,MAAM,IAAI,MAAM,2CAA2C,EAC/D,IAAM/I,EAAIpB,EAAG,QAAQmK,CAAI,EACnBC,EAAI3G,EAAM,UAAUjC,GAAYC,IAASsI,EAAW,KAAO,CAAC,EAAG3I,CAAC,CAAC,EACjEiJ,EAAKpK,EAAG,IAAIkK,CAAI,EAChBG,EAAIC,EAAc3I,GAAOsI,EAAa,OAAW,SAAS,CAAC,EAC3DM,EAAKvK,EAAG,OAAO,CAACqK,EAAID,CAAE,EACtBI,EAAKxK,EAAG,OAAOgI,EAAIoC,CAAE,EAErBK,EAAIjH,EAAM,KAAK,aAAa+G,EAAIJ,EAAGK,CAAE,EAC3C,GAAIC,EAAE,IAAI,EACN,MAAM,IAAI,MAAM,qCAAqC,EACzD,OAAAA,EAAE,eAAe,EACVA,CACX,CAEA,UAAW,CACP,OAAOvB,EAAsB,KAAK,CAAC,CACvC,CACA,QAAQlK,EAASgK,EAAe,OAAQ,CAEpC,GADAjK,GAAkBC,CAAM,EACpBA,IAAW,MACX,OAAOqF,GAAW2F,GAAI,WAAW,IAAI,CAAC,EAC1C,GAAM,CAAE,EAAAR,EAAG,EAAAxB,CAAE,EAAI,KACX0C,EAAK1K,EAAG,QAAQwJ,CAAC,EACjBmB,EAAK3K,EAAG,QAAQgI,CAAC,EACvB,OAAIhJ,IAAW,aACXyK,EAAuB,EAChBlI,GAAY,WAAW,GAAG,KAAK,eAAe,CAAC,EAAGmJ,EAAIC,CAAE,GAE5DpJ,GAAYmJ,EAAIC,CAAE,CAC7B,CACA,MAAM3L,EAAQ,CACV,OAAOqH,GAAW,KAAK,QAAQrH,CAAM,CAAC,CAC1C,CACJ,CACA,OAAO,OAAO6K,EAAU,SAAS,EACjC,OAAO,OAAOA,CAAS,EAKvB,IAAMe,EAAW1L,EAAK,WAAa,OAC7B,SAAsBwC,EAAO,CAE3B,GAAIA,EAAM,OAAS,KACf,MAAM,IAAI,MAAM,oBAAoB,EAGxC,IAAMlE,EAAMqN,GAAgBnJ,CAAK,EAC3BoJ,EAAQpJ,EAAM,OAAS,EAAIkH,EACjC,OAAOkC,EAAQ,EAAItN,GAAO,OAAOsN,CAAK,EAAItN,CAC9C,EACE0B,EAAK,SACLoL,EAAgBpL,EAAK,gBAAkB,OACvC,SAA2BwC,EAAO,CAChC,OAAO1B,EAAG,OAAO4K,EAASlJ,CAAK,CAAC,CACpC,EACExC,EAAK,cACL6L,EAAanM,GAAQgK,CAAM,EAGjC,SAASoC,EAAWxN,EAAK,CACrB,OAAAO,GAAS,WAAa6K,EAAQpL,EAAKQ,GAAK+M,CAAU,EAC3C/K,EAAG,QAAQxC,CAAG,CACzB,CACA,SAASyN,GAAmBC,EAASC,EAAS,CAC1C,OAAAxJ,GAAOuJ,EAAS,OAAW,SAAS,EAC5BC,EAAUxJ,GAAO4G,EAAM2C,CAAO,EAAG,OAAW,mBAAmB,EAAIA,CAC/E,CASA,SAASE,GAAQF,EAASpE,EAAW5H,EAAM,CACvC,GAAM,CAAE,KAAAmM,EAAM,QAAAF,EAAS,aAAAG,CAAa,EAAIrM,GAAgBC,EAAM8J,CAAc,EAC5EkC,EAAUD,GAAmBC,EAASC,CAAO,EAI7C,IAAMI,EAAQjB,EAAcY,CAAO,EAC7BM,EAAIxL,EAAG,UAAU8G,CAAS,EAChC,GAAI,CAAC9G,EAAG,YAAYwL,CAAC,EACjB,MAAM,IAAI,MAAM,qBAAqB,EACzC,IAAMC,EAAW,CAACT,EAAWQ,CAAC,EAAGR,EAAWO,CAAK,CAAC,EAElD,GAAID,GAAgB,MAAQA,IAAiB,GAAO,CAGhD,IAAMI,EAAIJ,IAAiB,GAAO5K,EAAYE,EAAQ,SAAS,EAAI0K,EACnEG,EAAS,KAAK9J,GAAO+J,EAAG,OAAW,cAAc,CAAC,CACtD,CACA,IAAMtE,EAAO7F,GAAY,GAAGkK,CAAQ,EAC9BE,EAAIJ,EAYV,SAASK,EAAMC,EAAQ,CAGnB,IAAMjO,EAAIgN,EAASiB,CAAM,EACzB,GAAI,CAAC7L,EAAG,YAAYpC,CAAC,EACjB,OACJ,IAAMkO,GAAItI,EAAM,KAAK,SAAS5F,CAAC,EAAE,SAAS,EACpC4L,GAAIxJ,EAAG,OAAO8L,GAAE,CAAC,EACvB,GAAItC,KAAMxL,GACN,OACJ,IAAIgK,GACJ,GAAIc,IAAW,OAAW,CAEtB,IAAM9C,GAAI6E,GAAgBxD,GAAeyB,EAAOD,CAAW,EAAG1I,CAAW,CAAC,EACpE4L,GAAM/L,EAAG,IAAIA,EAAG,IAAIgG,GAAGpI,CAAC,CAAC,EACzBoO,GAAKhM,EAAG,IAAIgG,GAAG2F,CAAC,EAChBM,GAAKjM,EAAG,IAAIgG,GAAGwF,CAAC,EACtBxD,GAAIhI,EAAG,OAAO+L,GAAM/L,EAAG,OAAOgM,GAAKC,GAAKzC,EAAC,CAAC,CAC9C,KACK,CACD,IAAM0C,GAAKC,GAASvO,EAAGuC,CAAW,EAClC6H,GAAIhI,EAAG,OAAOkM,GAAKlM,EAAG,OAAO2L,EAAInC,GAAIgC,CAAC,CAAC,CAC3C,CACA,GAAIxD,KAAMhK,GACN,OACJ,IAAI8L,GAAWP,EAAeuC,GAAE,EAAGA,GAAE,EAAGtC,EAAC,EACrC4C,GAAQpE,GACZ,OAAIqD,GAAQnC,EAAsBlB,EAAC,IAC/BoE,GAAQpM,EAAG,IAAIgI,EAAC,EAChB8B,IAAY,GAET,IAAID,EAAUL,GAAG4C,GAAOnD,EAAwB,OAAYa,EAAQ,CAC/E,CACA,MAAO,CAAE,KAAA1C,EAAM,MAAAwE,CAAM,CACzB,CAcA,SAASS,EAAKnB,EAASpE,EAAW5H,EAAO,CAAC,EAAG,CACzC,GAAM,CAAE,KAAAkI,EAAM,MAAAwE,CAAM,EAAIR,GAAQF,EAASpE,EAAW5H,CAAI,EAGxD,OAFaoN,GAAe/D,EAAM,UAAWvI,EAAG,MAAOyI,CAAI,EAC1CrB,EAAMwE,CAAK,EACjB,QAAQ1M,EAAK,MAAM,CAClC,CAcA,SAASqN,GAAOC,EAAWtB,EAASlE,EAAW9H,EAAO,CAAC,EAAG,CACtD,GAAM,CAAE,KAAAmM,EAAM,QAAAF,EAAS,OAAAnM,CAAO,EAAIC,GAAgBC,EAAM8J,CAAc,EAGtE,GAFAhC,EAAYrF,GAAOqF,EAAW,OAAW,WAAW,EACpDkE,EAAUD,GAAmBC,EAASC,CAAO,EACzC,CAACzD,GAAQ8E,CAAS,EAAG,CACrB,IAAMC,EAAMD,aAAqB3C,EAAY,sBAAwB,GACrE,MAAM,IAAI,MAAM,sCAAwC4C,CAAG,CAC/D,CACA/C,EAAkB8C,EAAWxN,CAAM,EACnC,GAAI,CACA,IAAM0N,EAAM7C,EAAU,UAAU2C,EAAWxN,CAAM,EAC3CmF,EAAIX,EAAM,UAAUwD,CAAS,EAKnC,GAFI7C,EAAE,IAAI,GAENkH,GAAQqB,EAAI,SAAS,EACrB,MAAO,GACX,GAAM,CAAE,EAAAlD,EAAG,EAAAxB,CAAE,EAAI0E,EACXrC,EAAIC,EAAcY,CAAO,EACzByB,EAAK3M,EAAG,IAAIgI,CAAC,EACbuC,EAAKvK,EAAG,OAAOqK,EAAIsC,CAAE,EACrBnC,GAAKxK,EAAG,OAAOwJ,EAAImD,CAAE,EACrBxC,GAAI3G,EAAM,KAAK,aAAa+G,EAAIpG,EAAGqG,EAAE,EAC3C,GAAIL,GAAE,IAAI,EACN,MAAO,GACX,IAAM2B,GAAI3B,GAAE,SAAS,EAMrB,MAJI,EADMnK,EAAG,OAAO8L,GAAE,CAAC,IACbtC,GAINxK,IAAW,aAAe0N,EAAI,WAAanD,EAAeuC,GAAE,EAAGA,GAAE,EAAGtC,CAAC,EAG7E,MACU,CACN,MAAO,EACX,CACJ,CACA,SAASoD,GAAiBJ,EAAWtB,EAAShM,EAAO,CAAC,EAAG,CAGrD,GAAM,CAAE,QAAAiM,CAAQ,EAAIlM,GAAgBC,EAAM8J,CAAc,EACxD,OAAAkC,EAAUD,GAAmBC,EAASC,CAAO,EACtCtB,EAAU,UAAU2C,EAAW,WAAW,EAAE,iBAAiBtB,CAAO,EAAE,QAAQ,CACzF,CAEA,OAAO,OAAO,OAAO,CACjB,OAAAhD,EACA,aAAAZ,EACA,gBAAAO,EACA,MAAAI,EACA,QAAArH,EACA,MAAA4C,EACA,KAAA6I,EACA,OAAAE,GACA,iBAAAK,GACA,UAAA/C,EACA,KAAMtB,CACV,CAAC,CACL,CC5oCA,IAAMsE,GAAoC,OAAO,UAAW,EACtDC,GAAuB,OAAO,EAAE,EAGtC,SAASC,GAAQ,EAAGC,EAAK,GAAO,CAC5B,OAAIA,EACO,CAAE,EAAG,OAAO,EAAIH,EAAU,EAAG,EAAG,OAAQ,GAAKC,GAAQD,EAAU,CAAE,EACrE,CAAE,EAAG,OAAQ,GAAKC,GAAQD,EAAU,EAAI,EAAG,EAAG,OAAO,EAAIA,EAAU,EAAI,CAAE,CACpF,CAGA,SAASI,GAAMC,EAAKF,EAAK,GAAO,CAC5B,IAAMG,EAAMD,EAAI,OACZE,EAAK,IAAI,YAAYD,CAAG,EACxBE,EAAK,IAAI,YAAYF,CAAG,EAC5B,QAASG,EAAI,EAAGA,EAAIH,EAAKG,IAAK,CAC1B,GAAM,CAAE,EAAAC,EAAG,EAAAC,CAAE,EAAIT,GAAQG,EAAII,CAAC,EAAGN,CAAE,EACnC,CAACI,EAAGE,CAAC,EAAGD,EAAGC,CAAC,CAAC,EAAI,CAACC,EAAGC,CAAC,CAC1B,CACA,MAAO,CAACJ,EAAIC,CAAE,CAClB,CAOA,IAAMI,GAAY,GAAO,EAAI,GAAK,GAAM,EAClCC,GAAY,GAAM,IAAM,EAG9B,SAASC,GAAcC,EAAMC,EAAYC,EAAGC,EAAM,CAC9C,IAAMC,EAAIP,GAASK,CAAC,EACdG,EAAIP,GAASI,CAAC,EACpBF,EAAK,UAAUC,EAAYE,EAAOE,EAAID,EAAGD,CAAI,EAC7CH,EAAK,UAAUC,EAAa,EAAGE,EAAOC,EAAIC,EAAGF,CAAI,CACrD,CCfO,SAASG,GAAIC,EAAGC,EAAGC,EAAG,CACzB,OAAQF,EAAIC,EAAM,CAACD,EAAIE,CAC3B,CAcO,SAASC,GAAIH,EAAGC,EAAGC,EAAG,CACzB,OAAQF,EAAIC,EAAMD,EAAIE,EAAMD,EAAIC,CACpC,CAmBO,IAAME,GAAN,KAAa,CAChB,SACA,UACA,OAAS,GACT,UACA,KAEA,OACA,KACA,SAAW,GACX,OAAS,EACT,IAAM,EACN,UAAY,GACZ,YAAYC,EAAUC,EAAWC,EAAWC,EAAM,CAC9C,KAAK,SAAWH,EAChB,KAAK,UAAYC,EACjB,KAAK,UAAYC,EACjB,KAAK,KAAOC,EACZ,KAAK,OAAS,IAAI,WAAWH,CAAQ,EACrC,KAAK,KAAOI,GAAW,KAAK,MAAM,CACtC,CACA,OAAOC,EAAM,CACTC,GAAQ,IAAI,EACZC,EAAOF,CAAI,EACX,GAAM,CAAE,KAAAG,EAAM,OAAAC,EAAQ,SAAAT,CAAS,EAAI,KAC7BU,EAAML,EAAK,OACbM,EAAY,GAChB,QAASC,EAAM,EAAGA,EAAMF,GAAM,CAC1B,IAAMG,EAAO,KAAK,IAAIb,EAAW,KAAK,IAAKU,EAAME,CAAG,EAGpD,GAAIC,IAASb,EAAU,CACnB,IAAMc,EAAWV,GAAWC,CAAI,EAChC,KAAOL,GAAYU,EAAME,EAAKA,GAAOZ,EACjC,KAAK,QAAQc,EAAUF,CAAG,EAC9BD,EAAY,GACZ,QACJ,CAGAF,EAAO,IAAIG,IAAQ,GAAKC,IAASH,EAAML,EAAOA,EAAK,SAASO,EAAKA,EAAMC,CAAI,EAAG,KAAK,GAAG,EACtF,KAAK,KAAOA,EACZD,GAAOC,EACH,KAAK,MAAQb,IACb,KAAK,QAAQQ,EAAM,CAAC,EACpB,KAAK,IAAM,EACXG,EAAY,GAEpB,CACA,YAAK,QAAUN,EAAK,OAGhBM,GACA,KAAK,WAAW,EACb,IACX,CACA,WAAWI,EAAK,CACZT,GAAQ,IAAI,EACZU,GAAQD,EAAK,IAAI,EACjB,KAAK,SAAW,GAIhB,GAAM,CAAE,OAAAN,EAAQ,KAAAD,EAAM,SAAAR,EAAU,KAAAG,CAAK,EAAI,KACrC,CAAE,IAAAS,CAAI,EAAI,KAEdH,EAAOG,GAAK,EAAI,IAChBH,EAAO,KAAK,EAAGG,CAAG,EAGd,KAAK,UAAYZ,EAAWY,IAC5B,KAAK,QAAQJ,EAAM,CAAC,EACpBC,EAAO,KAAK,CAAC,GAMjBQ,GAAcT,EAAMR,EAAW,EAAG,KAAK,OAAS,EAAGG,CAAI,EACvD,KAAK,QAAQK,EAAM,CAAC,EAGpB,KAAK,WAAW,EAEhB,IAAMU,EAAQH,IAAQN,EAASD,EAAOJ,GAAWW,CAAG,EAC9CL,EAAM,KAAK,UAEXS,EAAST,EAAM,EACfU,EAAQ,KAAK,IAAI,EAEvB,GAAIV,EAAM,GAAKS,EAASC,EAAM,OAC1B,MAAM,IAAI,MAAM,mBAAmB,EACvC,QAASC,EAAI,EAAGA,EAAIF,EAAQE,IACxBH,EAAM,UAAU,EAAIG,EAAGD,EAAMC,CAAC,EAAGlB,CAAI,CAC7C,CACA,QAAS,CACL,GAAM,CAAE,OAAAM,EAAQ,UAAAR,CAAU,EAAI,KAC9B,KAAK,WAAWQ,CAAM,EAGtB,IAAMa,EAAMb,EAAO,MAAM,EAAGR,CAAS,EACrC,YAAK,QAAQ,EACNqB,CACX,CACA,eAAeC,EAAI,CACf,GAAM,CAAE,OAAAd,EAAQ,OAAAe,EAAQ,SAAAC,EAAU,UAAAC,EAAW,IAAAd,CAAI,EAAI,KACrD,OAAAW,EAAG,UAAYG,EACfH,EAAG,SAAWE,EACdF,EAAG,OAASC,EACZD,EAAG,IAAMX,EAGLA,GACAW,EAAG,OAAO,IAAId,CAAM,EACjBc,CACX,CACA,OAAQ,CACJ,OAAO,KAAK,WAAW,CAC3B,CACJ,EAQaI,GAA4B,YAAY,KAAK,CACtD,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,UAAY,UACxF,CAAC,EC5KD,IAAMC,GAA2B,YAAY,KAAK,CAC9C,WAAY,WAAY,WAAY,WAAY,UAAY,WAAY,WAAY,WACpF,WAAY,UAAY,UAAY,WAAY,WAAY,WAAY,WAAY,WACpF,WAAY,WAAY,UAAY,UAAY,UAAY,WAAY,WAAY,WACpF,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,UAAY,UACpF,UAAY,UAAY,WAAY,WAAY,WAAY,WAAY,WAAY,WACpF,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,UACpF,UAAY,UAAY,UAAY,UAAY,UAAY,WAAY,WAAY,WACpF,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,UACxF,CAAC,EAEKC,GAA2B,IAAI,YAAY,EAAE,EAE7CC,GAAN,cAAuBC,EAAO,CAK1B,EAAI,EACJ,EAAI,EACJ,EAAI,EACJ,EAAI,EACJ,EAAI,EACJ,EAAI,EACJ,EAAI,EACJ,EAAI,EACJ,YAAYC,EAAWC,EAAI,CACvB,MAAM,GAAID,EAAW,EAAG,EAAK,EAC7B,KAAK,EAAIC,EAAG,CAAC,EAAI,EACjB,KAAK,EAAIA,EAAG,CAAC,EAAI,EACjB,KAAK,EAAIA,EAAG,CAAC,EAAI,EACjB,KAAK,EAAIA,EAAG,CAAC,EAAI,EACjB,KAAK,EAAIA,EAAG,CAAC,EAAI,EACjB,KAAK,EAAIA,EAAG,CAAC,EAAI,EACjB,KAAK,EAAIA,EAAG,CAAC,EAAI,EACjB,KAAK,EAAIA,EAAG,CAAC,EAAI,CACrB,CACA,KAAM,CACF,GAAM,CAAE,EAAAC,EAAG,EAAAC,EAAG,EAAAC,EAAG,EAAAC,EAAG,EAAAC,EAAG,EAAAC,EAAG,EAAAC,EAAG,EAAAC,CAAE,EAAI,KACnC,MAAO,CAACP,EAAGC,EAAGC,EAAGC,EAAGC,EAAGC,EAAGC,EAAGC,CAAC,CAClC,CAEA,IAAIP,EAAGC,EAAGC,EAAGC,EAAGC,EAAGC,EAAGC,EAAGC,EAAG,CACxB,KAAK,EAAIP,EAAI,EACb,KAAK,EAAIC,EAAI,EACb,KAAK,EAAIC,EAAI,EACb,KAAK,EAAIC,EAAI,EACb,KAAK,EAAIC,EAAI,EACb,KAAK,EAAIC,EAAI,EACb,KAAK,EAAIC,EAAI,EACb,KAAK,EAAIC,EAAI,CACjB,CACA,WAAWC,EAAI,CACX,OAACA,IAAO,IAAI,KAAK,aAAe,IAAI,GAAG,KAAK,IAAI,CAAC,EAC1C,KAAK,eAAeA,CAAE,CACjC,CACA,QAAQC,EAAMC,EAAQ,CAElB,QAASC,EAAI,EAAGA,EAAI,GAAIA,IAAKD,GAAU,EACnCf,GAASgB,CAAC,EAAIF,EAAK,UAAUC,EAAQ,EAAK,EAC9C,QAASC,EAAI,GAAIA,EAAI,GAAIA,IAAK,CAC1B,IAAMC,EAAMjB,GAASgB,EAAI,EAAE,EACrBE,EAAKlB,GAASgB,EAAI,CAAC,EACnBG,EAAKC,GAAKH,EAAK,CAAC,EAAIG,GAAKH,EAAK,EAAE,EAAKA,IAAQ,EAC7CI,EAAKD,GAAKF,EAAI,EAAE,EAAIE,GAAKF,EAAI,EAAE,EAAKA,IAAO,GACjDlB,GAASgB,CAAC,EAAKK,EAAKrB,GAASgB,EAAI,CAAC,EAAIG,EAAKnB,GAASgB,EAAI,EAAE,EAAK,CACnE,CAEA,GAAI,CAAE,EAAAX,EAAG,EAAAC,EAAG,EAAAC,EAAG,EAAAC,EAAG,EAAAC,EAAG,EAAAC,EAAG,EAAAC,EAAG,EAAAC,CAAE,EAAI,KACjC,QAASI,EAAI,EAAGA,EAAI,GAAIA,IAAK,CACzB,IAAMM,EAASF,GAAKX,EAAG,CAAC,EAAIW,GAAKX,EAAG,EAAE,EAAIW,GAAKX,EAAG,EAAE,EAC9Cc,EAAMX,EAAIU,EAASE,GAAIf,EAAGC,EAAGC,CAAC,EAAIZ,GAASiB,CAAC,EAAIhB,GAASgB,CAAC,EAAK,EAE/DS,GADSL,GAAKf,EAAG,CAAC,EAAIe,GAAKf,EAAG,EAAE,EAAIe,GAAKf,EAAG,EAAE,GAC/BqB,GAAIrB,EAAGC,EAAGC,CAAC,EAAK,EACrCK,EAAID,EACJA,EAAID,EACJA,EAAID,EACJA,EAAKD,EAAIe,EAAM,EACff,EAAID,EACJA,EAAID,EACJA,EAAID,EACJA,EAAKkB,EAAKE,EAAM,CACpB,CAEApB,EAAKA,EAAI,KAAK,EAAK,EACnBC,EAAKA,EAAI,KAAK,EAAK,EACnBC,EAAKA,EAAI,KAAK,EAAK,EACnBC,EAAKA,EAAI,KAAK,EAAK,EACnBC,EAAKA,EAAI,KAAK,EAAK,EACnBC,EAAKA,EAAI,KAAK,EAAK,EACnBC,EAAKA,EAAI,KAAK,EAAK,EACnBC,EAAKA,EAAI,KAAK,EAAK,EACnB,KAAK,IAAIP,EAAGC,EAAGC,EAAGC,EAAGC,EAAGC,EAAGC,EAAGC,CAAC,CACnC,CACA,YAAa,CACTe,GAAM3B,EAAQ,CAClB,CACA,SAAU,CAGN,KAAK,UAAY,GACjB,KAAK,IAAI,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,CAAC,EAC/B2B,GAAM,KAAK,MAAM,CACrB,CACJ,EAEaC,GAAN,cAAsB3B,EAAS,CAClC,aAAc,CACV,MAAM,GAAI4B,EAAS,CACvB,CACJ,EA4PO,IAAMC,GAAyBC,GAAa,IAAM,IAAIC,GAC7CC,GAAQ,CAAI,CAAC,ECnX7B,SAASC,GAAS,EAAGC,EAAQ,IAAK,CAE9B,GAAI,OAAO,GAAM,SACb,MAAM,IAAI,UAAU,sBAAsBA,CAAK,gCAAgC,OAAO,CAAC,EAAE,EAC7F,GAAI,CAAC,OAAO,cAAc,CAAC,GAAK,EAAI,GAAK,EAAI,WACzC,MAAM,IAAI,WAAW,sBAAsBA,CAAK,kCAAkC,CAAC,EAAE,EACzF,OAAO,CACX,CAYO,SAASC,GAAaC,EAAG,CAC5B,OAAAH,GAASG,EAAG,GAAG,GACPA,EAAKA,EAAI,KAAQ,GAAKA,IAAM,CACxC,CAkCO,SAASC,GAAY,EAAGC,EAAM,CAEjC,GADAC,GAAS,CAAC,EACN,OAAOD,GAAS,SAChB,MAAM,IAAI,UAAU,oCAAsC,OAAOA,CAAI,EACzE,GAAI,CAAC,OAAO,cAAcA,CAAI,GAAKA,EAAO,GAAKA,EAAO,GAClD,MAAM,IAAI,MAAM,yCAAyCA,CAAI,EAAE,EACnE,IAAIE,EAAW,EACf,QAASC,EAAI,EAAGA,EAAIH,EAAMG,IAAK,KAAO,EAClCD,EAAYA,GAAY,EAAM,EAAI,EAEtC,OAAOA,IAAa,CACxB,CAYO,SAASE,GAAK,EAAG,CACpB,OAAAH,GAAS,CAAC,EACH,GAAK,KAAK,MAAM,CAAC,CAC5B,CAgBO,SAASI,GAAmBC,EAAQ,CACvC,GAAI,CAACA,GACD,OAAOA,GAAW,UAClB,OAAOA,EAAO,QAAW,SACzB,MAAM,IAAI,UAAU,0CAA4C,OAAOA,CAAM,EACjF,IAAMC,EAAID,EAAO,OAEjB,GAAI,CAACE,GAAaD,CAAC,EACf,MAAM,IAAI,MAAM,8CAAgDA,CAAC,EACrE,IAAMP,EAAOI,GAAKG,CAAC,EACnB,QAASJ,EAAI,EAAGA,EAAII,EAAGJ,IAAK,CACxB,IAAMM,EAAIV,GAAYI,EAAGH,CAAI,EAC7B,GAAIG,EAAIM,EAAG,CACP,IAAMC,EAAMJ,EAAOH,CAAC,EACpBG,EAAOH,CAAC,EAAIG,EAAOG,CAAC,EACpBH,EAAOG,CAAC,EAAIC,CAChB,CACJ,CACA,OAAOJ,CACX,CA0KO,IAAMK,GAAU,CAACC,EAAGC,IAAa,CACpCC,GAAeD,EAAU,CAAE,EAAG,SAAU,MAAO,SAAU,IAAK,SAAU,EAAG,CAAE,kBAAmB,UAAW,WAAY,SAAU,IAAK,SAAU,EAAG,UAAU,EAC7J,GAAM,CAAE,EAAAE,EAAG,MAAAC,EAAO,IAAAC,EAAK,kBAAAC,EAAoB,GAAO,WAAAC,EAAa,EAAG,IAAAC,EAAM,EAAK,EAAIP,EACjFQ,GAASN,EAAG,YAAY,EACxB,IAAMO,EAAOC,GAAKR,CAAC,EACnB,GAAI,CAACS,GAAaT,CAAC,EACf,MAAM,IAAI,MAAM,6CAA6C,EACjEM,GAASF,EAAY,qBAAqB,EAC1C,IAAMM,EAAgBH,IAAS,EAAI,EAAIA,EAAO,EAE9C,GAAIH,EAAaM,EACb,MAAM,IAAI,MAAM,sDAAsDA,CAAa,EAAE,EAEzF,GAAIT,EAAM,SAAWD,EACjB,MAAM,IAAI,MAAM,qCAAqCA,CAAC,SAASC,EAAM,MAAM,EAAE,EACjF,IAAMU,EAAQT,IAAQC,EACtB,OAAQS,GAAW,CACf,GAAIA,EAAO,SAAWZ,EAClB,MAAM,IAAI,MAAM,8BAA8B,EAC9CE,GAAOG,GACPQ,GAAmBD,CAAM,EAC7B,QAASE,EAAI,EAAGC,EAAI,EAAGD,EAAIP,EAAOH,EAAYU,IAAK,CAE/C,IAAME,EAAId,EAAMY,EAAI,EAAIV,EAAaG,EAAOO,EACtCG,EAAI,GAAKD,EACTE,EAAKD,GAAK,EACVE,EAASnB,GAAKgB,EAEpB,QAASI,EAAI,EAAGA,EAAIpB,EAAGoB,GAAKH,EAExB,QAASI,EAAI,EAAGC,EAAMP,IAAKM,EAAIH,EAAIG,IAAK,CACpC,IAAME,EAAUpB,EAAqBD,EAAMF,EAAIsB,EAAMA,EAAOD,EAAIF,EAC1DK,EAAKJ,EAAIC,EACTI,EAAKL,EAAIC,EAAIH,EACbQ,EAAQzB,EAAMsB,CAAO,EACrBI,EAAIf,EAAOa,CAAE,EACbG,EAAIhB,EAAOY,CAAE,EAEnB,GAAIb,EAAO,CACP,IAAMkB,EAAIhC,EAAE,IAAI8B,EAAGD,CAAK,EACxBd,EAAOY,CAAE,EAAI3B,EAAE,IAAI+B,EAAGC,CAAC,EACvBjB,EAAOa,CAAE,EAAI5B,EAAE,IAAI+B,EAAGC,CAAC,CAC3B,MACS1B,GACLS,EAAOY,CAAE,EAAI3B,EAAE,IAAI8B,EAAGC,CAAC,EACvBhB,EAAOa,CAAE,EAAI5B,EAAE,IAAIA,EAAE,IAAI8B,EAAGC,CAAC,EAAGF,CAAK,IAGrCd,EAAOY,CAAE,EAAI3B,EAAE,IAAI+B,EAAGD,CAAC,EACvBf,EAAOa,CAAE,EAAI5B,EAAE,IAAIA,EAAE,IAAI+B,EAAGD,CAAC,EAAGD,CAAK,EAE7C,CAER,CACA,MAAI,CAACxB,GAAOG,GACRQ,GAAmBD,CAAM,EACtBA,CACX,CACJ,EClVA,IAAMkB,GAAqC,CACvC,EAAG,OAAO,oEAAoE,EAC9E,EAAG,OAAO,oEAAoE,EAC9E,EAAG,OAAO,CAAC,EACX,EAAG,OAAO,oEAAoE,EAC9E,EAAG,OAAO,oEAAoE,EAC9E,GAAI,OAAO,oEAAoE,EAC/E,GAAI,OAAO,oEAAoE,CACnF,EA6BA,IAAMC,GAA6BC,GAAYC,EAAU,EAoB5CC,GAAuBC,GAAMJ,GAAYK,EAAM,ECpD5D,IAAMC,GAAM,OAAO,CAAC,EACdC,GAAM,OAAO,CAAC,EACdC,GAAM,OAAO,CAAC,EACdC,GAAM,OAAO,CAAC,EACdC,GAAQ,OAAO,GAAG,EAGlBC,GAAS,OAAO,GAAI,EACpBC,GAAU,CAAC,EACXC,GAAY,CAAC,EACbC,GAAa,CAAC,EACpB,QAASC,EAAQ,EAAGC,EAAIT,GAAKU,EAAI,EAAGC,EAAI,EAAGH,EAAQ,GAAIA,IAAS,CAE5D,CAACE,EAAGC,CAAC,EAAI,CAACA,GAAI,EAAID,EAAI,EAAIC,GAAK,CAAC,EAChCN,GAAQ,KAAK,GAAK,EAAIM,EAAID,EAAE,EAE5BJ,GAAU,MAAQE,EAAQ,IAAMA,EAAQ,GAAM,EAAK,EAAE,EAErD,IAAII,EAAIb,GACR,QAASc,EAAI,EAAGA,EAAI,EAAGA,IACnBJ,GAAMA,GAAKT,IAASS,GAAKP,IAAOE,IAAWD,GACvCM,EAAIR,KACJW,GAAKZ,KAASA,IAAO,OAAOa,CAAC,GAAKb,IAE1CO,GAAW,KAAKK,CAAC,CACrB,CACA,IAAME,GAAQC,GAAMR,GAAY,EAAI,EAI9BS,GAAcF,GAAM,CAAC,EACrBG,GAAcH,GAAM,CAAC,EAKrBI,GAAS,CAACC,EAAGC,EAAGC,IAAOF,GAAKE,EAAMD,IAAO,GAAKC,EAC9CC,GAAS,CAACH,EAAGC,EAAGC,IAAOD,GAAKC,EAAMF,IAAO,GAAKE,EAC9CE,GAAS,CAACJ,EAAGC,EAAGC,IAAOD,GAAMC,EAAI,GAAQF,IAAO,GAAKE,EACrDG,GAAS,CAACL,EAAGC,EAAGC,IAAOF,GAAME,EAAI,GAAQD,IAAO,GAAKC,EACrDI,GAAQ,CAACN,EAAGC,EAAGC,IAAOA,EAAI,GAAKE,GAAOJ,EAAGC,EAAGC,CAAC,EAAIH,GAAOC,EAAGC,EAAGC,CAAC,EAC/DK,GAAQ,CAACP,EAAGC,EAAGC,IAAOA,EAAI,GAAKG,GAAOL,EAAGC,EAAGC,CAAC,EAAIC,GAAOH,EAAGC,EAAGC,CAAC,EAG/DM,GAAI,IAAI,YAAY,EAAK,EAexB,SAASC,GAAQP,EAAGQ,EAAS,GAAI,CACpC,GAAI,EAAER,aAAa,aACf,MAAM,IAAI,UAAU,0CAA4C,OAAOA,CAAC,EAC5E,GAAIA,EAAE,SAAW,GACb,MAAM,IAAI,WAAW,4CAA8CA,EAAE,MAAM,EAG/E,GAFAS,GAAQD,EAAQ,QAAQ,EAEpBA,EAAS,GAAKA,EAAS,GACvB,MAAM,IAAI,MAAM,iCAAiC,EAErD,QAASrB,EAAQ,GAAKqB,EAAQrB,EAAQ,GAAIA,IAAS,CAE/C,QAASE,EAAI,EAAGA,EAAI,GAAIA,IACpBiB,GAAEjB,CAAC,EAAIW,EAAEX,CAAC,EAAIW,EAAEX,EAAI,EAAE,EAAIW,EAAEX,EAAI,EAAE,EAAIW,EAAEX,EAAI,EAAE,EAAIW,EAAEX,EAAI,EAAE,EAC9D,QAASA,EAAI,EAAGA,EAAI,GAAIA,GAAK,EAAG,CAC5B,IAAMqB,GAAQrB,EAAI,GAAK,GACjBsB,GAAQtB,EAAI,GAAK,GACjBuB,EAAKN,GAAEK,CAAI,EACXE,EAAKP,GAAEK,EAAO,CAAC,EACfG,EAAKV,GAAMQ,EAAIC,EAAI,CAAC,EAAIP,GAAEI,CAAI,EAC9BK,EAAKV,GAAMO,EAAIC,EAAI,CAAC,EAAIP,GAAEI,EAAO,CAAC,EACxC,QAASpB,EAAI,EAAGA,EAAI,GAAIA,GAAK,GACzBU,EAAEX,EAAIC,CAAC,GAAKwB,EACZd,EAAEX,EAAIC,EAAI,CAAC,GAAKyB,CAExB,CAEA,IAAIC,EAAOhB,EAAE,CAAC,EACViB,EAAOjB,EAAE,CAAC,EACd,QAAST,EAAI,EAAGA,EAAI,GAAIA,IAAK,CACzB,IAAM2B,EAAQjC,GAAUM,CAAC,EACnBuB,EAAKV,GAAMY,EAAMC,EAAMC,CAAK,EAC5BH,EAAKV,GAAMW,EAAMC,EAAMC,CAAK,EAC5BC,EAAKnC,GAAQO,CAAC,EACpByB,EAAOhB,EAAEmB,CAAE,EACXF,EAAOjB,EAAEmB,EAAK,CAAC,EACfnB,EAAEmB,CAAE,EAAIL,EACRd,EAAEmB,EAAK,CAAC,EAAIJ,CAChB,CAKA,QAASzB,EAAI,EAAGA,EAAI,GAAIA,GAAK,GAAI,CAC7B,IAAM8B,EAAKpB,EAAEV,CAAC,EAAG+B,EAAKrB,EAAEV,EAAI,CAAC,EAAGgC,EAAKtB,EAAEV,EAAI,CAAC,EAAGiC,EAAKvB,EAAEV,EAAI,CAAC,EAC3DU,EAAEV,CAAC,GAAK,CAACU,EAAEV,EAAI,CAAC,EAAIU,EAAEV,EAAI,CAAC,EAC3BU,EAAEV,EAAI,CAAC,GAAK,CAACU,EAAEV,EAAI,CAAC,EAAIU,EAAEV,EAAI,CAAC,EAC/BU,EAAEV,EAAI,CAAC,GAAK,CAACU,EAAEV,EAAI,CAAC,EAAIU,EAAEV,EAAI,CAAC,EAC/BU,EAAEV,EAAI,CAAC,GAAK,CAACU,EAAEV,EAAI,CAAC,EAAIU,EAAEV,EAAI,CAAC,EAC/BU,EAAEV,EAAI,CAAC,GAAK,CAACU,EAAEV,EAAI,CAAC,EAAIU,EAAEV,EAAI,CAAC,EAC/BU,EAAEV,EAAI,CAAC,GAAK,CAACU,EAAEV,EAAI,CAAC,EAAIU,EAAEV,EAAI,CAAC,EAC/BU,EAAEV,EAAI,CAAC,GAAK,CAACU,EAAEV,EAAI,CAAC,EAAI8B,EACxBpB,EAAEV,EAAI,CAAC,GAAK,CAACU,EAAEV,EAAI,CAAC,EAAI+B,EACxBrB,EAAEV,EAAI,CAAC,GAAK,CAAC8B,EAAKE,EAClBtB,EAAEV,EAAI,CAAC,GAAK,CAAC+B,EAAKE,CACtB,CAEAvB,EAAE,CAAC,GAAKL,GAAYR,CAAK,EACzBa,EAAE,CAAC,GAAKJ,GAAYT,CAAK,CAC7B,CACAqC,GAAMlB,EAAC,CACX,CAkBO,IAAMmB,GAAN,MAAMC,CAAO,CAChB,MACA,IAAM,EACN,OAAS,EACT,SAAW,GACX,QACA,UAAY,GACZ,SACA,OACA,UACA,OACA,UAAY,GACZ,OAEA,YAAYC,EAAUC,EAAQC,EAAWC,EAAY,GAAOtB,EAAS,GAAI,CAcrE,GAbAC,GAAQkB,EAAU,UAAU,EAC5BlB,GAAQmB,EAAQ,QAAQ,EACxBnB,GAAQD,EAAQ,QAAQ,EACxBuB,GAAMD,EAAW,WAAW,EAC5B,KAAK,SAAWH,EAChB,KAAK,OAASC,EACd,KAAK,UAAYC,EACjB,KAAK,UAAYC,EACjB,KAAK,OAASA,EACd,KAAK,OAAStB,EAEdC,GAAQoB,EAAW,WAAW,EAE1B,EAAE,EAAIF,GAAYA,EAAW,KAC7B,MAAM,IAAI,MAAM,2BAA2B,EAC/C,KAAK,MAAQ,IAAI,WAAW,GAAG,EAC/B,KAAK,QAAUK,GAAI,KAAK,KAAK,CACjC,CACA,OAAQ,CACJ,OAAO,KAAK,WAAW,CAC3B,CACA,QAAS,CACLC,GAAW,KAAK,OAAO,EACvB1B,GAAQ,KAAK,QAAS,KAAK,MAAM,EACjC0B,GAAW,KAAK,OAAO,EACvB,KAAK,OAAS,EACd,KAAK,IAAM,CACf,CACA,OAAOC,EAAM,CACTC,GAAQ,IAAI,EACZC,EAAOF,CAAI,EACX,GAAM,CAAE,SAAAP,EAAU,MAAAU,EAAO,QAAAC,CAAQ,EAAI,KAC/BC,EAAML,EAAK,OAGXM,EAAYb,EAAW,IAAM,GAAKO,EAAK,WAAa,IAAM,EAC1DO,EAAad,EAAW,EACxBe,EAASF,GAAaD,GAAOZ,EAAWK,GAAIE,CAAI,EAAI,OAC1D,QAASS,EAAM,EAAGA,EAAMJ,GAAM,CAC1B,GAAIG,IAAW,QAAa,KAAK,MAAQ,GAAKC,EAAM,IAAM,GAAKJ,EAAMI,GAAOhB,EAAU,CAClF,QAASiB,EAAI,EAAGC,EAAIF,EAAM,EAAGC,EAAIH,EAAYG,IACzCN,EAAQM,CAAC,GAAKF,EAAOG,EAAID,CAAC,EAC9BD,GAAOhB,EAGP,KAAK,IAAMA,EACX,KAAK,OAAO,EACZ,QACJ,CACA,IAAMmB,EAAO,KAAK,IAAInB,EAAW,KAAK,IAAKY,EAAMI,CAAG,EACpD,QAASC,EAAI,EAAGA,EAAIE,EAAMF,IACtBP,EAAM,KAAK,KAAK,GAAKH,EAAKS,GAAK,EAC/B,KAAK,MAAQhB,GACb,KAAK,OAAO,CACpB,CACA,OAAO,IACX,CACA,QAAS,CACL,GAAI,KAAK,SACL,OACJ,KAAK,SAAW,GAChB,GAAM,CAAE,MAAAU,EAAO,OAAAT,EAAQ,IAAAe,EAAK,SAAAhB,CAAS,EAAI,KAIzCU,EAAMM,CAAG,GAAKf,GAITA,EAAS,OAAU,GAAKe,IAAQhB,EAAW,GAC5C,KAAK,OAAO,EAChBU,EAAMV,EAAW,CAAC,GAAK,IACvB,KAAK,OAAO,CAChB,CACA,UAAUoB,EAAK,CACXZ,GAAQ,KAAM,EAAK,EACnBC,EAAOW,CAAG,EACV,KAAK,OAAO,EACZ,IAAMC,EAAY,KAAK,MACjB,CAAE,SAAArB,CAAS,EAAI,KACrB,QAASgB,EAAM,EAAGJ,EAAMQ,EAAI,OAAQJ,EAAMJ,GAAM,CACxC,KAAK,QAAUZ,GACf,KAAK,OAAO,EAChB,IAAMmB,EAAO,KAAK,IAAInB,EAAW,KAAK,OAAQY,EAAMI,CAAG,EACvDI,EAAI,IAAIC,EAAU,SAAS,KAAK,OAAQ,KAAK,OAASF,CAAI,EAAGH,CAAG,EAChE,KAAK,QAAUG,EACfH,GAAOG,CACX,CACA,OAAOC,CACX,CACA,QAAQA,EAAK,CAIT,GAAI,CAAC,KAAK,UACN,MAAM,IAAI,MAAM,oBAAoB,EACxC,OAAO,KAAK,UAAUA,CAAG,CAC7B,CACA,IAAIE,EAAO,CACP,OAAAxC,GAAQwC,CAAK,EACN,KAAK,QAAQ,IAAI,WAAWA,CAAK,CAAC,CAC7C,CACA,WAAWF,EAAK,CAEZ,GADAG,GAAQH,EAAK,IAAI,EACb,KAAK,SACL,MAAM,IAAI,MAAM,6BAA6B,EAEjD,KAAK,UAAUA,EAAI,SAAW,KAAK,UAAYA,EAAMA,EAAI,SAAS,EAAG,KAAK,SAAS,CAAC,EACpF,KAAK,QAAQ,CACjB,CACA,QAAS,CACL,IAAMA,EAAM,IAAI,WAAW,KAAK,SAAS,EACzC,YAAK,WAAWA,CAAG,EACZA,CACX,CACA,SAAU,CACN,KAAK,UAAY,GACjBvB,GAAM,KAAK,KAAK,CACpB,CACA,WAAW2B,EAAI,CACX,GAAM,CAAE,SAAAxB,EAAU,OAAAC,EAAQ,UAAAC,EAAW,OAAArB,EAAQ,UAAAsB,CAAU,EAAI,KAC3D,OAAAqB,IAAO,IAAIzB,EAAOC,EAAUC,EAAQC,EAAWC,EAAWtB,CAAM,EAGhE2C,EAAG,SAAWxB,EACdwB,EAAG,QAAQ,IAAI,KAAK,OAAO,EAE3BA,EAAG,IAAM,KAAK,IACdA,EAAG,OAAS,KAAK,OACjBA,EAAG,SAAW,KAAK,SACnBA,EAAG,OAAS3C,EAEZ2C,EAAG,OAASvB,EACZuB,EAAG,UAAYtB,EACfsB,EAAG,UAAYrB,EAGfqB,EAAG,OAAS,KAAK,OACjBA,EAAG,UAAY,KAAK,UACbA,CACX,CACJ,EACMC,GAAY,CAACxB,EAAQD,EAAUE,EAAWwB,EAAO,CAAC,IAAMC,GAAa,IAAM,IAAI7B,GAAOE,EAAUC,EAAQC,CAAS,EAAGwB,CAAI,EAyBvH,IAAME,GAA2BC,GAAU,EAAM,IAAK,GAC7CC,GAAQ,CAAI,CAAC,EAyBtB,IAAMC,GAA2BC,GAAU,EAAM,GAAI,GAC5CC,GAAQ,EAAI,CAAC,EAiD7B,IAAMC,GAAW,CAACC,EAAQC,EAAUC,EAAWC,EAAO,CAAC,IAAMC,GAAa,CAACC,EAAO,CAAC,KAC/EA,EAAOC,GAAU,CAAC,EAAGD,CAAI,EAClB,IAAIE,GAAON,EAAUD,EAAQK,EAAK,QAAU,OAAYH,EAAYG,EAAK,MAAO,EAAI,GAC5FF,CAAI,EAYMK,GAEbT,GAAS,GAAM,IAAK,GAAoBU,GAAQ,EAAI,CAAC,EAYxCC,GAEbX,GAAS,GAAM,IAAK,GAAoBU,GAAQ,EAAI,CAAC,EC5arD,IAAME,GAAYC,EA2BX,IAAMC,GAAcA,GACpB,SAASC,GAAOC,EAAMC,EAAOC,EAAQ,IAAM,CAAE,EAAG,CACnD,GAAI,CAAC,MAAM,QAAQF,CAAI,EACnB,MAAM,IAAI,UAAU,IAAIC,CAAK,8BAA8B,OAAOD,CAAI,EAAE,EAC5E,QAASG,EAAI,EAAGA,EAAIH,EAAK,OAAQG,IAC7BD,EAAMF,EAAKG,CAAC,EAAG,GAAGF,CAAK,IAAIE,CAAC,GAAG,EACnC,OAAOH,CACX,CACO,SAASI,GAAQC,EAAOJ,EAAQ,SAAU,CAC7C,GAAII,IAAU,MAAQ,OAAOA,GAAU,UAAY,MAAM,QAAQA,CAAK,EAClE,MAAM,IAAI,UAAUJ,IAAU,SACxB,gCACA,IAAIA,CAAK,+BAA+B,OAAOI,CAAK,EAAE,EAChE,OAAOA,CACX,CAaO,SAASC,GAAWC,EAAGC,EAAG,CAG7B,GAFAD,EAAIE,EAAOF,CAAC,EACZC,EAAIC,EAAOD,CAAC,EACRD,EAAE,SAAWC,EAAE,OACf,MAAO,GACX,IAAIE,EAAO,EACX,QAASP,EAAI,EAAGA,EAAII,EAAE,OAAQJ,IAC1BO,GAAQH,EAAEJ,CAAC,EAAIK,EAAEL,CAAC,EACtB,OAAOO,IAAS,CACpB,CAYO,SAASC,GAAUC,EAAO,CAI7B,OAAO,IAAI,WAAWH,EAAOG,CAAK,CAAC,CACvC,CAkLO,SAASC,GAAWC,KAAUC,EAAS,CAC1C,IAAMC,EAAaC,GAAM,OAAOA,GAAM,SAAWA,EAAIA,EAAE,SACjDC,EAAWH,EAAQ,OAAO,CAACI,EAAKC,IAAMD,EAAMH,EAAUI,CAAC,EAAG,CAAC,EACjE,MAAO,CACH,SAAAF,EACA,OAASG,GAAS,CACd,IAAMC,EAAM,IAAI,WAAWJ,CAAQ,EACnC,QAAS,EAAI,EAAGK,EAAM,EAAG,EAAIR,EAAQ,OAAQ,IAAK,CAC9C,IAAM,EAAIA,EAAQ,CAAC,EACb,EAAIC,EAAU,CAAC,EACfQ,EAAI,OAAO,GAAM,SAAWH,EAAK,CAAC,EAAI,EAAE,OAAOA,EAAK,CAAC,CAAC,EAC5DI,EAAQD,EAAG,EAAGV,CAAK,EACnBQ,EAAI,IAAIE,EAAGD,CAAG,EACV,OAAO,GAAM,UACbC,EAAE,KAAK,CAAC,EACZD,GAAO,CACX,CACA,OAAOD,CACX,EACA,OAASI,GAAQ,CACbD,EAAQC,EAAKR,EAAUJ,CAAK,EAC5B,IAAMQ,EAAM,CAAC,EACb,QAAWL,KAAKF,EAAS,CACrB,IAAMY,EAAIX,EAAUC,CAAC,EACfO,EAAIE,EAAI,SAAS,EAAGC,CAAC,EAC3BL,EAAI,KAAK,OAAOL,GAAM,SAAWO,EAAIP,EAAE,OAAOO,CAAC,CAAC,EAChDE,EAAMA,EAAI,SAASC,CAAC,CACxB,CACA,OAAOL,CACX,CACJ,CACJ,CAqBO,SAASM,GAASX,EAAGY,EAAQ,CAChC,IAAMC,EAAQb,EACRC,EAAWW,EAASC,EAAM,SAChC,MAAO,CACH,SAAAZ,EACA,OAASa,GAAM,CACX,IAAMC,EAAOC,GAAOF,EAAG,GAAG,EAC1B,GAAIC,EAAK,SAAWH,EAChB,MAAM,IAAI,WAAW,iCAAiCG,EAAK,MAAM,eAAeH,CAAM,EAAE,EAC5F,IAAMP,EAAM,IAAI,WAAWJ,CAAQ,EACnC,QAASgB,EAAI,EAAGX,EAAM,EAAGW,EAAIF,EAAK,OAAQE,IAAK,CAC3C,IAAMV,EAAIM,EAAM,OAAOE,EAAKE,CAAC,CAAC,EAC9BZ,EAAI,IAAIE,EAAGD,CAAG,EACdC,EAAE,KAAK,CAAC,EACRD,GAAOC,EAAE,MACb,CACA,OAAOF,CACX,EACA,OAASF,GAAM,CACXK,EAAQL,EAAGF,CAAQ,EACnB,IAAMiB,EAAI,CAAC,EACX,QAAS,EAAI,EAAG,EAAIf,EAAE,OAAQ,GAAKU,EAAM,SACrCK,EAAE,KAAKL,EAAM,OAAOV,EAAE,SAAS,EAAG,EAAIU,EAAM,QAAQ,CAAC,CAAC,EAC1D,OAAOK,CACX,CACJ,CACJ,CAYO,SAASC,KAAcC,EAAM,CAChC,QAAWC,KAAKD,EACZ,GAAI,MAAM,QAAQC,CAAC,EACf,QAAWd,KAAKc,EACZd,EAAE,KAAK,CAAC,OAEZc,EAAE,KAAK,CAAC,CAEpB,CAaO,SAASC,GAAQC,EAAM,CAE1B,GADAC,GAAQD,EAAM,MAAM,EAChBA,EAAO,GACP,MAAM,IAAI,WAAW,8BAAgCA,CAAI,EAE7D,OAAOA,IAAS,GAAK,WAAa,EAAE,IAAMA,KAAU,CACxD,CC9WO,IAAME,GAAeC,GAAS,CAEjC,GAAM,CAAE,QAAAC,EAAS,EAAAC,EAAG,EAAAC,EAAG,EAAAC,EAAG,cAAAC,EAAe,QAAAC,EAAS,QAAAC,CAAQ,EAAIP,EAGxDQ,EAAM,CAACC,EAAGC,EAASP,IAAM,CAC3B,IAAMQ,EAAUF,EAAIC,EAAU,EAC9B,OAAQC,GAAU,EAAIA,EAAS,EAAKD,EAASC,EAAU,GAAK,CAChE,EAIMC,EAAO,CAACH,EAAGC,EAASP,IAAM,CAC5B,IAAMU,EAAIL,EAAIC,EAAGC,CAAM,EAAI,EAC3B,OAAQG,EAAIH,GAAU,EAAKG,EAAIH,EAAU,EAAIG,GAAK,CACtD,EAGA,SAASC,GAAY,CACjB,IAAMC,EAAMd,EAAQC,CAAC,EACrB,QAASc,EAAI,EAAGA,EAAId,EAAGc,IAAK,CACxB,IAAMC,EAAIC,GAAYF,EAAGV,CAAO,EAC1Ba,EAAI,OAAOd,CAAa,GAAK,OAAOY,CAAC,EAAI,OAAOd,CAAC,EACvDY,EAAIC,CAAC,EAAI,OAAOG,CAAC,EAAI,CACzB,CACA,OAAOJ,CACX,CACA,IAAMK,EAAWN,EAAU,EAKrBO,EAAOC,GAAO,CAChB,MAAM,IAAI,MAAM,iBAAiB,CACrC,EAOMC,EAAQhB,EACR,CACE,IAAK,CAACE,EAAGQ,IAAM,CACX,IAAMJ,EAAKJ,EAAIQ,EAAK,EACpB,OAAOJ,GAAKV,EAAKU,EAAIV,EAAK,EAAIU,CAClC,EACA,IAAK,CAACJ,EAAGQ,IAAM,CACX,IAAMJ,EAAKJ,EAAIQ,EAAK,EACpB,OAAOJ,EAAI,EAAKA,EAAIV,EAAK,EAAIU,CACjC,EACA,IAAK,CAACJ,EAAGQ,IAAMT,GAAKC,EAAI,IAAMQ,EAAI,EAAE,EAAI,EACxC,IAAAI,CACJ,EACE,CACE,IAAK,CAACZ,EAAGQ,IAAMT,GAAKC,EAAI,IAAMQ,EAAI,EAAE,EAAI,EACxC,IAAK,CAACR,EAAGQ,IAAMT,GAAKC,EAAI,IAAMQ,EAAI,EAAE,EAAI,EACxC,IAAK,CAACR,EAAGQ,IAAMT,GAAKC,EAAI,IAAMQ,EAAI,EAAE,EAAI,EACxC,IAAAI,CACJ,EACEG,EAAU,CACZ,EAAAtB,EACA,MAAOkB,EACP,kBAAmB,GACnB,WAAYb,EAAU,EAAI,EAC1B,IAAK,EACT,EACMkB,EAAMC,GAAQH,EAAO,CAAE,IAAK,GAAO,GAAGC,CAAQ,CAAC,EAC/CG,EAAMD,GAAQH,EAAO,CAAE,IAAK,GAAM,GAAGC,CAAQ,CAAC,EAC9CI,EAAM,CACR,OAASf,GACEY,EAAIZ,CAAC,EAEhB,OAASA,GAAM,CACXc,EAAId,CAAC,EAIL,QAASG,EAAI,EAAGA,EAAIH,EAAE,OAAQG,IAC1BH,EAAEG,CAAC,EAAIR,EAAIJ,EAAIS,EAAEG,CAAC,CAAC,EACvB,OAAOH,CACX,CACJ,EAwCA,MAAO,CACH,IAAAL,EACA,KAAAI,EACA,SAAUQ,EACV,IAAK,CACD,OAASP,GAAMe,EAAI,OAAOf,CAAC,EAC3B,OAASA,GAAMe,EAAI,OAAOf,CAAC,CAC/B,EACA,UA7Cc,CAACgB,EAAGC,IAAM,CAExB,QAASd,EAAI,EAAGe,EAAS,EAAGf,EAAId,EAAGc,IAC/Be,GAAUF,EACNE,EAAS,IACTC,GAAQD,CAAM,EAClBA,GAAU,EAEd,IAAME,EAAOD,GAAQH,CAAC,EAChBK,EAAWL,GAAK3B,EAAI,GAC1B,MAAO,CACH,SAAAgC,EACA,OAASC,GAAU,CACf,IAAMC,EAAOD,EACPtB,EAAI,IAAI,WAAWqB,CAAQ,EACjC,QAASlB,EAAI,EAAGqB,EAAM,EAAGN,EAAS,EAAGO,EAAM,EAAGtB,EAAIoB,EAAK,OAAQpB,IAK3D,IAJAqB,IAAQP,EAAE,OAAOM,EAAKpB,CAAC,CAAC,EAAIiB,IAASF,EACrCA,GAAUF,EAGHE,GAAU,EAAGA,GAAU,EAAGM,IAAQ,EACrCxB,EAAEyB,GAAK,EAAID,EAAM,IAEzB,OAAOxB,CACX,EACA,OAAS0B,GAAU,CACf,IAAM1B,EAAIZ,EAAQC,CAAC,EACnB,QAASc,EAAI,EAAGqB,EAAM,EAAGN,EAAS,EAAGO,EAAM,EAAGtB,EAAIuB,EAAM,OAAQvB,IAG5D,IAFAqB,GAAOE,EAAMvB,CAAC,GAAKe,EACnBA,GAAU,EACHA,GAAUF,EAAGE,GAAUF,EAAGQ,IAAQR,EACrChB,EAAEyB,GAAK,EAAIR,EAAE,OAAOO,EAAMJ,CAAI,EAEtC,OAAOpB,CACX,CACJ,CACJ,CAUA,CACJ,EACM2B,GAAkBC,GAAU,CAACC,EAAMC,IAAa,CAC7CA,IACDA,EAAWF,EAAM,UAKrB,IAAMG,EAAQ,IAAI,WAAWF,EAAK,OAAS,CAAC,EAC5CE,EAAM,IAAIF,CAAI,EACd,IAAMG,EAAUH,EAAK,OACfL,EAAM,IAAI,WAAWM,CAAQ,EAC/BG,EAAIL,EAAM,OAAO,CAAC,CAAC,EACnBM,EAAQ,EACRC,EAAO,EACX,MAAO,CACH,MAAO,KAAO,CAAE,MAAAD,EAAO,KAAAC,CAAK,GAC5B,IAAK,CAACC,EAAGC,KAGLN,EAAMC,EAAU,CAAC,EAAII,EACrBL,EAAMC,EAAU,CAAC,EAAIK,EACrBJ,EAAE,QAAQ,EACVA,EAAIL,EAAM,OAAO,CAAC,CAAC,EAAE,OAAOG,CAAK,EACjCG,IACO,KACHC,IACOF,EAAE,QAAQT,CAAG,IAG5B,MAAO,IAAM,CACTS,EAAE,QAAQ,EACVK,EAAWd,EAAKO,CAAK,CACzB,CACJ,CACJ,EAiBaQ,GAAyBZ,GAAea,EAAQ,ECxL7D,IAAMC,GAAI,IACJC,GAAI,KACJC,GAAI,KACJC,GAAgB,GAIhBC,GAA2BC,GAAY,CACzC,EAAAL,GACA,EAAAC,GACA,EAAAC,GACA,cAAAC,GACA,QAAU,GAAM,IAAI,YAAY,CAAC,EACjC,QAAS,EACT,QAAS,EACb,CAAC,EAQYG,GAAgC,OAAO,OAAO,CACvD,IAAK,OAAO,OAAO,CAAE,EAAAN,GAAG,EAAAC,GAAG,EAAG,EAAG,KAAM,EAAG,KAAM,EAAG,GAAI,GAAI,GAAI,EAAG,YAAa,GAAI,CAAC,EACpF,IAAK,OAAO,OAAO,CAAE,EAAAD,GAAG,EAAAC,GAAG,EAAG,EAAG,KAAM,EAAG,KAAM,EAAG,GAAI,GAAI,GAAI,EAAG,YAAa,GAAI,CAAC,EACpF,KAAM,OAAO,OAAO,CAAE,EAAAD,GAAG,EAAAC,GAAG,EAAG,EAAG,KAAM,EAAG,KAAM,EAAG,GAAI,GAAI,GAAI,EAAG,YAAa,GAAI,CAAC,CACzF,CAAC,EAEKM,GAAYC,GAAM,CAIpB,GAAIA,GAAK,GACL,MAAO,CAAE,OAASC,GAAMA,EAAG,OAASA,GAAOA,GAAKR,GAAIQ,EAAIR,GAAIQ,CAAG,EAGnE,IAAMC,EAAI,IAAMF,EAAI,GACpB,MAAO,CAEH,OAASC,KAAQA,GAAKD,GAAKP,GAAI,GAAKA,GAEpC,OAASQ,GAAOA,EAAIR,GAAIS,IAAOF,CACnC,CACJ,EAKMG,GAAaH,GAAMJ,GAAS,UAAUI,EAAGA,IAAM,GAC/C,CAAE,OAASC,GAAMA,EAAG,OAASA,GAAOA,GAAKR,GAAIQ,EAAIR,GAAIQ,CAAG,EACxD,CAAE,OAASA,GAAMA,EAAG,OAASA,GAAMA,CAAE,CAAC,EAOtCG,GAAaJ,GAAOA,IAAM,GAAKG,GAAU,EAAE,EAAIP,GAAS,UAAUI,EAAGD,GAASC,CAAC,CAAC,EAGtF,SAASK,GAAQC,EAAIC,EAAI,CACrB,IAAML,EAAII,EACJE,EAAID,EAEV,QAASN,EAAI,EAAGA,EAAIT,GAAGS,IAAK,CACxB,IAAMQ,EAAIP,EAAED,CAAC,EAAIO,EAAEP,CAAC,EACpBC,EAAED,CAAC,EAAIQ,GAAKhB,GAAIgB,EAAIhB,GAAIgB,CAC5B,CACJ,CACA,SAASC,GAAQJ,EAAIC,EAAI,CACrB,IAAML,EAAII,EACJE,EAAID,EAEV,QAASN,EAAI,EAAGA,EAAIT,GAAGS,IAAK,CACxB,IAAMQ,EAAIP,EAAED,CAAC,EAAIO,EAAEP,CAAC,EACpBC,EAAED,CAAC,EAAIQ,EAAI,EAAIA,EAAIhB,GAAIgB,CAC3B,CACJ,CAEA,SAASE,GAAiBC,EAAIC,EAAIC,EAAIC,EAAIC,EAAM,CAI5C,IAAMC,EAAKrB,GAAS,IAAIA,GAAS,IAAIiB,EAAKE,CAAE,EAAIC,EAAOJ,EAAKE,CAAE,EACxDI,EAAKtB,GAAS,IAAIgB,EAAKG,EAAKF,EAAKC,CAAE,EACzC,MAAO,CAAE,GAAAG,EAAI,GAAAC,CAAG,CACpB,CAGA,SAASC,GAAaC,EAAIC,EAAI,CAC1B,IAAMC,EAAIF,EACJG,EAAIF,EACV,QAASpB,EAAI,EAAGA,EAAIT,GAAI,EAAGS,IAAK,CAC5B,IAAIuB,EAAI5B,GAAS,SAAS,IAAMK,GAAK,EAAE,EACnCA,EAAI,IACJuB,EAAI,CAACA,GACT,GAAM,CAAE,GAAAP,EAAI,GAAAC,CAAG,EAAIP,GAAiBW,EAAE,EAAIrB,EAAI,CAAC,EAAGqB,EAAE,EAAIrB,EAAI,CAAC,EAAGsB,EAAE,EAAItB,EAAI,CAAC,EAAGsB,EAAE,EAAItB,EAAI,CAAC,EAAGuB,CAAC,EAC7FF,EAAE,EAAIrB,EAAI,CAAC,EAAIgB,EACfK,EAAE,EAAIrB,EAAI,CAAC,EAAIiB,CACnB,CACA,OAAOI,CACX,CAEA,SAASG,GAAUC,EAAM,CACrB,IAAMC,EAAMD,EAGNjB,EAAI,IAAI,YAAYjB,EAAC,EAC3B,QAASoC,EAAI,EAAGA,EAAIpC,IAAI,CACpB,IAAMgB,EAAImB,EAAI,EACd,GAAInB,EAAE,OAAS,EACX,MAAM,IAAI,MAAM,4BAA4B,EAChD,QAASP,EAAI,EAAG2B,EAAIpC,IAAKS,EAAI,GAAKO,EAAE,OAAQP,GAAK,EAAG,CAChD,IAAM4B,GAAOrB,EAAEP,EAAI,CAAC,GAAK,EAAMO,EAAEP,EAAI,CAAC,GAAK,GAAM,KAC3C6B,GAAOtB,EAAEP,EAAI,CAAC,GAAK,EAAMO,EAAEP,EAAI,CAAC,GAAK,GAAM,KAC7C4B,EAAKpC,KACLgB,EAAEmB,GAAG,EAAIC,GACTD,EAAIpC,IAAKsC,EAAKrC,KACdgB,EAAEmB,GAAG,EAAIE,EACjB,CACJ,CACA,OAAOrB,CACX,CAIA,IAAMsB,GAAiB,CAACC,EAAKC,IAAQ,CACjC,IAAMxB,EAAI,IAAI,YAAYjB,EAAC,EAGrB0C,EAAMC,GAAIH,CAAG,EACnBI,GAAWF,CAAG,EACd,IAAIG,EAAM,EACV,QAASpC,EAAI,EAAGqC,EAAI,EAAGC,EAAK,EAAGC,EAAK,EAAGvC,EAAIiC,EAAI,OAAQjC,IAAK,CACxD,IAAIO,EAAI0B,EAAIjC,CAAC,EACb,QAAS2B,EAAI,EAAGA,EAAI,GAAIA,IACpBW,GAAM/B,EAAI,EACVA,IAAM,EACN6B,GAAO,EACHA,IAAQJ,GACRO,EAAKD,EACLA,EAAK,GAEAF,IAAQ,EAAIJ,IACjBxB,EAAE6B,GAAG,EAAI1C,GAAS,IAAI4C,EAAKD,CAAE,EAC7BA,EAAK,EACLF,EAAM,EAGlB,CAEA,GADAD,GAAWF,CAAG,EACVG,EACA,MAAM,IAAI,MAAM,6BAA6BA,CAAG,EAAE,EACtD,OAAO5B,CACX,EACA,SAASgC,GAAUC,EAAMC,EAAMC,EAAOX,EAAK,CAEvC,OAAOF,GADKW,EACeT,EAAMzC,GAAK,EAAGmD,EAAMC,CAAK,EAAGX,CAAG,CAC9D,CAKA,IAAMY,GAAWC,GAAU,CACvB,IAAMC,EAAOD,EACP,CAAE,EAAAE,EAAG,IAAAC,EAAK,IAAAC,EAAK,QAAAC,EAAS,KAAAC,EAAM,KAAAC,EAAM,GAAAC,EAAI,GAAAC,CAAG,EAAIR,EAC/CS,EAAQpD,GAAU,CAAC,EACnBqD,EAAQrD,GAAUmD,CAAE,EACpBG,EAAQtD,GAAUkD,CAAE,EACpBK,EAAcC,GAAW,YAAaC,GAASzD,GAAU,EAAE,EAAG4C,CAAC,EAAG,EAAE,EACpEc,EAAcD,GAASzD,GAAU,EAAE,EAAG4C,CAAC,EACvCe,EAAcH,GAAW,aAAcC,GAASH,EAAOV,CAAC,EAAGS,CAAK,EAChEO,EAAYJ,GAAW,OAAQ,GAAI,EAAE,EAIrCK,EAAc,CAACC,EAAMC,EAAMC,EAAKzB,IAAS,CAC3C,IAAM0B,EAAO,CAAC,EACd,QAASpE,EAAI,EAAGA,EAAI+C,EAAG/C,IACnBoE,EAAK,KAAKzE,GAAS,IAAI,OAAO6C,GAAUQ,EAAKN,EAAM1C,EAAGmD,CAAI,CAAC,CAAC,EAChE,IAAMkB,EAAO,IAAI,YAAY9E,EAAC,EACxB+E,EAAI,CAAC,EACX,QAAStE,EAAI,EAAGA,EAAI+C,EAAG/C,IAAK,CACxB,IAAMuE,EAAK/B,GAAUQ,EAAKN,EAAMK,EAAI/C,EAAGoD,CAAI,EACrCoB,EAAM,IAAI,YAAYjF,EAAC,EAC7B,QAASoC,EAAI,EAAGA,EAAIoB,EAAGpB,IAAK,CACxB,IAAM8C,GAAMP,EAAKlE,EAAG2B,CAAC,EACrBvB,GAAQoE,EAAKtD,GAAauD,GAAKL,EAAKzC,CAAC,CAAC,CAAC,CAC3C,CACAvB,GAAQmE,EAAI5E,GAAS,IAAI,OAAO6E,CAAG,CAAC,EACpCF,EAAE,KAAKC,CAAE,EACTnE,GAAQiE,EAAMnD,GAAa+C,EAAKjE,CAAC,EAAGoE,EAAKpE,CAAC,CAAC,CAAC,EAC5C0E,EAAWF,CAAG,CAClB,CACA,IAAMG,EAAKnC,GAAUQ,EAAKN,EAAM,EAAIK,EAAGK,CAAI,EAC3ChD,GAAQuE,EAAIhF,GAAS,IAAI,OAAO0E,CAAI,CAAC,EACrC,IAAMO,EAAIrB,EAAM,OAAOY,CAAG,EAC1B,OAAA/D,GAAQwE,EAAGD,CAAE,EACbD,EAAWT,EAAMG,EAAMC,EAAMM,CAAE,EACxBb,EAAY,OAAO,CAACQ,EAAGM,CAAC,CAAC,CACpC,EACA,MAAO,CACH,YAAAf,EACA,QAAS,CACL,UAAWA,EAAY,SACvB,UAAWH,EAAY,SACvB,WAAYI,EAAY,QAC5B,EACA,OAASpB,GAAS,CACdmC,GAAOnC,EAAM,GAAI,MAAM,EACvB,IAAMoC,EAAU,IAAI,WAAW,EAAE,EACjCA,EAAQ,IAAIpC,CAAI,EAIhBoC,EAAQ,EAAE,EAAI/B,EACd,IAAMgC,EAAW7B,EAAQ4B,CAAO,EAC1B,CAACE,EAAKC,CAAK,EAAIlB,EAAU,OAAOgB,CAAQ,EACxCG,EAAO,CAAC,EACRjB,EAAO,CAAC,EACd,QAASjE,EAAI,EAAGA,EAAI+C,EAAG/C,IACnBkF,EAAK,KAAKvF,GAAS,IAAI,OAAO6C,GAAUQ,EAAKiC,EAAOjF,EAAGmD,CAAI,CAAC,CAAC,EACjE,IAAMgC,EAAIlC,EAAI+B,CAAG,EACjB,QAAShF,EAAI,EAAGA,EAAI+C,EAAG/C,IAAK,CACxB,IAAMoF,EAAIzF,GAAS,IAAI,OAAO6C,GAAUQ,EAAKiC,EAAOlC,EAAI/C,EAAGmD,CAAI,CAAC,EAChE,QAASxB,EAAI,EAAGA,EAAIoB,EAAGpB,IAAK,CACxB,IAAM0D,EAAM7D,GAAU2D,EAAE,IAAIxD,EAAG3B,CAAC,CAAC,EACjCI,GAAQgF,EAAGlE,GAAamE,EAAKH,EAAKvD,CAAC,CAAC,CAAC,CACzC,CACAsC,EAAK,KAAKmB,CAAC,CACf,CACAD,EAAE,MAAM,EACR,IAAMG,EAAM,CACR,UAAW5B,EAAY,OAAO,CAACO,EAAMe,CAAG,CAAC,EACzC,UAAWnB,EAAY,OAAOqB,CAAI,CACtC,EACA,OAAAR,EAAWM,EAAKC,EAAOC,EAAMjB,EAAMa,EAASC,CAAQ,EAC7CO,CACX,EACA,QAAS,CAACC,EAAWpB,EAAKzB,IAAS,CAC/B,GAAM,CAACuB,EAAMe,CAAG,EAAItB,EAAY,OAAO6B,CAAS,EAC1CJ,EAAIlC,EAAI+B,CAAG,EACXM,EAAMtB,EAAYC,EAAM,CAACjE,EAAG2B,IAAMH,GAAU2D,EAAE,IAAInF,EAAG2B,CAAC,CAAC,EAAGwC,EAAKzB,CAAI,EACzE,OAAAyC,EAAE,MAAM,EACDG,CACX,EAIA,QAAUC,GAAc,CACpB,GAAM,CAACtB,EAAMe,CAAG,EAAItB,EAAY,OAAO6B,CAAS,EAC1CJ,EAAIlC,EAAI+B,CAAG,EACXQ,EAAI,CAAC,EACX,QAASxF,EAAI,EAAGA,EAAI+C,EAAG/C,IACnB,QAAS2B,EAAI,EAAGA,EAAIoB,EAAGpB,IACnB6D,EAAE,KAAKhE,GAAU2D,EAAE,IAAInF,EAAG2B,CAAC,CAAC,CAAC,EACrC,OAAAwD,EAAE,MAAM,EACD,CACH,QAAS,CAAChB,EAAKzB,IAASsB,EAAYC,EAAK,IAAK5B,GAAMA,EAAE,MAAM,CAAC,EAAG,CAACrC,EAAG2B,IAAM6D,EAAExF,EAAI+C,EAAIpB,CAAC,EAAE,MAAM,EAAGwC,EAAKzB,CAAI,EACzG,MAAO,IAAMgC,EAAWT,EAAMuB,CAAC,CACnC,CACJ,EACA,QAAS,CAACC,EAAYC,IAAe,CACjC,GAAM,CAACpB,EAAGM,CAAC,EAAId,EAAY,OAAO2B,CAAU,EACtCE,EAAK9B,EAAY,OAAO6B,CAAU,EAClClB,EAAM,IAAI,YAAYjF,EAAC,EAE7B,QAASS,EAAI,EAAGA,EAAI+C,EAAG/C,IACnBI,GAAQoE,EAAKtD,GAAayE,EAAG3F,CAAC,EAAGL,GAAS,IAAI,OAAO2E,EAAEtE,CAAC,CAAC,CAAC,CAAC,EAC/DS,GAAQmE,EAAGjF,GAAS,IAAI,OAAO6E,CAAG,CAAC,EAGnC,IAAMc,EAAM/B,EAAM,OAAOqB,CAAC,EAC1B,OAAAF,EAAWF,EAAKmB,EAAIrB,EAAGM,CAAC,EACjBU,CACX,CACJ,CACJ,EAeA,SAASM,GAAY9C,EAAM,CACvB,IAAM+C,EAAU/C,EACVgD,EAAOlD,GAAQiD,CAAO,EACtB,CAAE,QAAAE,EAAS,QAAA7C,EAAS,IAAA8C,CAAI,EAAIH,EAC5B,CAAE,YAAaI,EAAiB,QAAAC,CAAQ,EAAIJ,EAC5CjC,EAAcF,GAAW,YAAauC,EAAQ,UAAWA,EAAQ,UAAW,GAAI,EAAE,EAClFC,EAAS,GACTC,EAAU,GAEVC,EAAkB,CAACd,EAAWe,IAAO,CACvC,IAAMC,EAAMhB,EAAU,SAAS,EAAG,IAAMM,EAAQ,CAAC,EAE3CW,EAAKP,EAAgB,OAAOA,EAAgB,OAAOQ,GAAUF,CAAG,CAAC,CAAC,EAGlEG,EAAKC,GAAWH,EAAID,CAAG,EAE7B,GADA7B,EAAW8B,CAAE,EACT,CAACE,EACD,MAAM,IAAI,MAAM,UAAUJ,CAAE,2BAA2B,CAC/D,EACMM,EAAa,OAAO,OAAO,CAC7B,GAAGV,EACH,KAAM,GACN,IAAKC,EACL,QAASA,EACT,UAAWtC,EAAY,QAC3B,CAAC,EACD,OAAO,OAAO,OAAO,CACjB,KAAM,OAAO,OAAO,CAAE,KAAM,QAAS,CAAC,EACtC,QAAS+C,EACT,OAASlE,GAAS,CAId,IAAMmE,EAAUnE,IAAS,OACnBoE,EAAID,EAAUE,GAAYX,CAAO,EAAI1D,EACvCiD,EACAqB,EACJ,GAAI,CACAnC,GAAOiC,EAAGV,EAAS,MAAM,EACzB,IAAMa,EAAOnB,EAAK,OAAOgB,EAAE,SAAS,EAAG,EAAE,CAAC,EACpCvB,EAAY0B,EAAK,UACvBtB,EAAKsB,EAAK,UACVD,EAAgBjB,EAAQR,CAAS,EAEjC,IAAM2B,EAAYrD,EAAY,OAAO,CAAC8B,EAAIJ,EAAWyB,EAAeF,EAAE,SAAS,EAAE,CAAC,CAAC,EACnF,MAAO,CACH,UAAWvB,EACX,UAAW2B,CACf,CACJ,QACA,CACQvB,IAAO,QACPjB,EAAWiB,CAAE,EACbqB,IAAkB,QAClBtC,EAAWsC,CAAa,EACxBH,GACAnC,EAAWoC,CAAC,CACpB,CACJ,EACA,aAAeI,GAAc,CACzB,GAAM,CAACC,EAAK5B,EAAW6B,EAAgBC,CAAE,EAAIxD,EAAY,OAAOqD,CAAS,EACzE,OAAO,WAAW,KAAK3B,CAAS,CACpC,EACA,YAAa,CAACA,EAAWpB,IAAQ,CAI7B,IAAMmD,EAASnD,IAAQ,OACjBoD,EAAID,EAASP,GAAYZ,CAAM,EAAIhC,EACrCqD,EACJ,GAAI,CACA,OAAA3C,GAAOU,EAAWW,EAAQ,UAAW,WAAW,EAChDrB,GAAO0C,EAAGpB,EAAQ,SAAS,EAC3BE,EAAgBd,EAAW,aAAa,EAExCiC,EAAKtE,EAAQ,OAAO,EAAE,OAAOqE,CAAC,EAAE,OAAOxB,EAAQR,CAAS,CAAC,EAAE,OAAO,EAE3D,CACH,WAFeO,EAAK,QAAQP,EAAWgC,EAAGC,EAAG,SAAS,GAAI,EAAE,CAAC,EAG7D,aAAcA,EAAG,SAAS,EAAG,EAAE,CACnC,CACJ,QACA,CACQA,IAAO,QACP9C,EAAW8C,EAAG,SAAS,EAAE,CAAC,EAC1BF,GACA5C,EAAW6C,CAAC,CACpB,CACJ,EACA,YAAa,CAAC9B,EAAYyB,IAAc,CACpCrC,GAAOqC,EAAWrD,EAAY,SAAU,WAAW,EACnDgB,GAAOY,EAAYS,EAAQ,WAAY,YAAY,EAEnD,IAAMuB,EAAO5D,EAAY,SAAW,GAC9B6D,EAAQD,EAAO,GACfE,EAAO5B,EAAQmB,EAAU,SAASO,EAAO,EAAGC,CAAK,CAAC,EAExD,GAAI,CAACf,GAAWgB,EAAMT,EAAU,SAASQ,EAAOA,EAAQ,EAAE,CAAC,EACvD,MAAM,IAAI,MAAM,sCAAsC,EAC1D,GAAM,CAAC/B,EAAIJ,EAAWyB,EAAezF,CAAC,EAAIsC,EAAY,OAAOqD,CAAS,EAChE/C,EAAM2B,EAAK,QAAQL,EAAYE,CAAE,EAEjC6B,EAAKtE,EAAQ,OAAO,EAAE,OAAOiB,CAAG,EAAE,OAAO6C,CAAa,EAAE,OAAO,EAC/DY,EAAOJ,EAAG,SAAS,EAAG,EAAE,EAExBK,EAAc/B,EAAK,QAAQP,EAAWpB,EAAKqD,EAAG,SAAS,GAAI,EAAE,CAAC,EAE9DM,EAAUnB,GAAWlB,EAAYoC,CAAW,EAC5CE,EAAO/B,EAAI,OAAO,CAAE,MAAO,EAAG,CAAC,EAAE,OAAOzE,CAAC,EAAE,OAAOkE,CAAU,EAAE,OAAO,EAE3E,OAAAf,EAAWP,EAAK0D,EAAaL,EAAG,SAAS,EAAE,EAAIM,EAAiBC,EAAPH,CAAW,EAC5DE,EAAUF,EAAOG,CAC7B,EAMA,QAAUxC,GAAc,CACpBV,GAAOU,EAAWW,EAAQ,UAAW,WAAW,EAChDG,EAAgBd,EAAW,SAAS,EACpC,IAAMiB,EAAKC,GAAUlB,CAAS,EACxByB,EAAgBjB,EAAQS,CAAE,EAC1BwB,EAASlC,EAAK,QAAQU,CAAE,EAC9B,OAAO,OAAO,OAAO,CACjB,UAAWA,EACX,YAAcrC,GAAQ,CAGlB,IAAMmD,EAASnD,IAAQ,OACjBoD,EAAID,EAASP,GAAYZ,CAAM,EAAIhC,EACrCqD,EACJ,GAAI,CACA,OAAA3C,GAAO0C,EAAGpB,EAAQ,SAAS,EAC3BqB,EAAKtE,EAAQ,OAAO,EAAE,OAAOqE,CAAC,EAAE,OAAOP,CAAa,EAAE,OAAO,EAEtD,CACH,WAFegB,EAAO,QAAQT,EAAGC,EAAG,SAAS,GAAI,EAAE,CAAC,EAGpD,aAAcA,EAAG,SAAS,EAAG,EAAE,CACnC,CACJ,QACA,CACQA,IAAO,QACP9C,EAAW8C,EAAG,SAAS,EAAE,CAAC,EAC1BF,GACA5C,EAAW6C,CAAC,CACpB,CACJ,EACA,YAAa,CAAC9B,EAAYyB,IAAc,CACpCrC,GAAOqC,EAAWrD,EAAY,SAAU,WAAW,EACnDgB,GAAOY,EAAYS,EAAQ,WAAY,YAAY,EACnD,GAAM,CAACP,EAAIsC,EAAYC,EAAY3G,CAAC,EAAIsC,EAAY,OAAOqD,CAAS,EAIpE,GAAI,CAACP,GAAWsB,EAAYzB,CAAE,GAAK,CAACG,GAAWuB,EAAYlB,CAAa,EACpE,MAAM,IAAI,MAAM,iEAAiE,EACrF,IAAM7C,EAAM2B,EAAK,QAAQL,EAAYE,CAAE,EAEjC6B,EAAKtE,EAAQ,OAAO,EAAE,OAAOiB,CAAG,EAAE,OAAO6C,CAAa,EAAE,OAAO,EAC/DY,EAAOJ,EAAG,SAAS,EAAG,EAAE,EAExBK,EAAcG,EAAO,QAAQ7D,EAAKqD,EAAG,SAAS,GAAI,EAAE,CAAC,EAErDM,EAAUnB,GAAWlB,EAAYoC,CAAW,EAC5CE,EAAO/B,EAAI,OAAO,CAAE,MAAO,EAAG,CAAC,EAAE,OAAOzE,CAAC,EAAE,OAAOkE,CAAU,EAAE,OAAO,EAC3E,OAAAf,EAAWP,EAAK0D,EAAaL,EAAG,SAAS,EAAE,EAAIM,EAAiBC,EAAPH,CAAW,EAC5DE,EAAUF,EAAOG,CAC7B,EACA,MAAOC,EAAO,KAClB,CAAC,CACL,CACJ,CAAC,CACL,CAGA,SAASG,GAASC,EAAOC,EAAK1F,EAAO,CACjC,OAAO2F,GACF,OAAO,CAAE,MAAAF,CAAM,CAAC,EAChB,OAAOC,CAAG,EACV,OAAO,IAAI,WAAW,CAAC1F,CAAK,CAAC,CAAC,EAC9B,OAAO,CAChB,CAGA,IAAMG,GAA+B,CACjC,QAASyF,GACT,QAASC,GACT,IAAKF,GACL,IAAKG,GACL,IAAKN,EACT,EAGMO,GAAMC,GAAW/C,GAAY,CAC/B,GAAG9C,GACH,GAAG6F,CACP,CAAC,EAqBM,IAAMC,GAAmCC,GAAGC,GAAO,GAAG,CAAC,ECnc9D,IAAMC,GAAc,CAACC,EAAKC,IAAU,CAChC,IAAMC,EAAIC,GAAQH,EAAKC,CAAK,EAC5B,OAAAE,GAAQD,EAAE,QAAS,GAAGD,CAAK,UAAU,EACrCG,GAAUF,EAAE,OAAQ,GAAGD,CAAK,SAAS,EACrCG,GAAUF,EAAE,aAAc,GAAGD,CAAK,eAAe,EACjDG,GAAUF,EAAE,YAAa,GAAGD,CAAK,cAAc,EAC/CG,GAAUF,EAAE,YAAa,GAAGD,CAAK,cAAc,EACxCC,CACX,EAuKA,SAASG,GAAeC,EAAOC,EAAO,CAClC,IAAMC,EAASC,GAAQH,EAAOC,CAAK,EACnC,GAAIC,IAAW,EACX,MAAM,IAAI,WAAW,IAAID,CAAK,0CAA0C,EAC5E,OAAOC,CACX,CACA,SAASE,GAAaC,EAAKC,EAAM,CAE7B,IAAMC,EAAQC,GAAWF,EAAM,GAAGD,EAAI,IAAKI,GAAM,CAC7C,GAAI,OAAOA,EAAE,QAAQH,CAAI,GAAM,SAC3B,MAAM,IAAI,MAAM,iBAAmBA,CAAI,EAC3C,OAAOP,GAAeU,EAAE,QAAQH,CAAI,EAAGA,CAAI,CAC/C,CAAC,CAAC,EACF,OAAAP,GAAeQ,EAAM,SAAUD,CAAI,EAC5BC,CACX,CAqBA,SAASG,GAAYC,EACrBC,KAAgBC,EAAK,CACjB,IAAMC,EAAaF,EACbG,EAAKF,EACLG,EAAYC,GAAaF,EAAI,MAAM,EACnCG,EAAUD,GAAaF,EAAI,WAAW,EAEtCI,EAAcC,GAAeT,IAAgB,OAAYK,EAAU,SAAWL,EAAa,aAAa,EAC9G,SAASU,EAAuBC,EAAM,CAClCC,EAAOD,EAAMH,CAAW,EACxB,IAAMK,EAAcV,EAAWQ,EAAMN,EAAU,QAAQ,EAGjDS,EAAeD,EAAY,SAAWF,EAAK,OAASI,GAAUF,CAAW,EAAIA,EAC7EG,EAAW,CAAC,EACZC,EAAY,CAAC,EACbC,EAAY,CAAC,EACbC,EAAY,CAAC,EACfC,EAAK,GACT,GAAI,CAIA,QAAWC,KAAQhB,EAAU,OAAOS,CAAY,EAC5CE,EAAS,KAAKD,GAAUM,CAAI,CAAC,EACjC,QAASC,EAAI,EAAGA,EAAIlB,EAAG,OAAQkB,IAAK,CAChC,IAAMC,EAAOnB,EAAGkB,CAAC,EAAE,OAAON,EAASM,CAAC,CAAC,EACrCL,EAAU,KAAKM,EAAK,SAAS,EAC7BL,EAAU,KAAKH,GAAUQ,EAAK,SAAS,CAAC,EACxCJ,EAAU,KAAKI,EAAK,SAAS,CACjC,CACA,OAAAH,EAAK,GACE,CAAE,UAAAF,EAAW,UAAAC,CAAU,CAClC,QACA,CAIIK,EAAWV,EAAcE,EAAUC,CAAS,EACvCG,GACDI,EAAWN,CAAS,CAC5B,CACJ,CAEA,IAAMO,EAAUd,GAAS,CAGrB,IAAMe,EAAOf,IAAS,OAAYgB,GAAYnB,CAAW,EAAIO,GAAUJ,CAAI,EACvEiB,EACJ,GAAI,CACA,GAAM,CAAE,UAAWC,EAAI,UAAAX,CAAU,EAAIR,EAAuBgB,CAAI,EAChE,GAAI,CACAE,EAAM,CACF,UAAWF,EACX,UAAWnB,EAAQ,OAAOsB,CAAE,CAChC,CACJ,QACA,CAGIL,EAAWK,EAAIX,CAAS,CAC5B,CACA,OAAOU,CACX,QACA,CACSA,GACDJ,EAAWE,CAAI,CACvB,CACJ,EACA,MAAO,CACH,KAAM,CAAE,QAAS,CAAE,KAAMlB,EAAa,UAAWD,EAAQ,SAAU,UAAWC,CAAY,CAAE,EAG5F,aAAeU,GAAc,CACzB,IAAMK,EAAOE,EAAOP,CAAS,EAE7B,OAAAM,EAAWD,EAAK,SAAS,EAClBA,EAAK,SAChB,EACA,OAAAE,EACA,uBAAAf,EACA,YAAaF,CACjB,CACJ,CAiCO,SAASsB,GAAY9B,EAC5B+B,EACA5B,EAAY6B,KAAaC,EAAM,CAK3B,GAJIjC,IAAgB,QAChBS,GAAeT,EAAa,aAAa,EACzC+B,IAAe,QACftB,GAAesB,EAAY,YAAY,EACvC,OAAO5B,GAAe,WACtB,MAAM,IAAI,UAAU,4CAA8C,OAAOA,CAAU,EACvF,GAAI,OAAO6B,GAAa,WACpB,MAAM,IAAI,UAAU,0CAA4C,OAAOA,CAAQ,EACnF,IAAME,EAAcF,EACdG,EAAUF,EAChB,GAAIE,EAAQ,SAAW,EACnB,MAAM,IAAI,WAAW,uCAAuC,EAChE,QAASb,EAAI,EAAGA,EAAIa,EAAQ,OAAQb,IAChCc,GAAYD,EAAQb,CAAC,EAAG,QAAQA,CAAC,GAAG,EACxC,IAAMC,EAAOxB,GAAYC,EAAaG,EAAY,GAAGgC,CAAO,EACtDE,EAAU/B,GAAa6B,EAAS,YAAY,EAC5C5B,EAAUD,GAAa6B,EAAS,WAAW,EAC3CG,EAAWhC,GAAa6B,EAAS,KAAK,EACtCI,EAAkB9B,GAAesB,IAAe,OAAYO,EAAS,SAAWP,EAAY,YAAY,EACxGS,EAAU,OAAO,OAAO,CAC1B,GAAGjB,EAAK,KAAK,QACb,IAAKgB,EACL,QAASD,EAAS,SAClB,WAAYD,EAAQ,QACxB,CAAC,EACKI,EAAU,CAACC,EAAYC,EAAaC,IAAkB,CACxD,IAAMC,EAAWX,EAAYQ,EAAYC,EAAaC,CAAa,EACnE,GAAI,CACA,OAAO7B,GAAUH,EAAOiC,EAAUN,EAAiB,cAAc,CAAC,CACtE,OACOO,EAAO,CACV,GAAIC,GAAQF,CAAQ,EAAG,CAKnB,IAAMG,EAAYC,GAAUJ,EAAS,SAAWI,EAAM,QAClDJ,EAAS,WAAaI,EAAM,WAAaA,EAAM,YAC/CA,EAAM,WAAaJ,EAAS,WAAaA,EAAS,WAC3BH,EAAW,KAAKM,CAAQ,GAC/CL,EAAY,KAAKK,CAAQ,GAEzBxB,EAAWqB,CAAQ,CAC3B,CACA,MAAMC,CACV,CACJ,EACA,OAAO,OAAO,OAAO,CACjB,QAAAN,EACA,aAAcjB,EAAK,aACnB,OAAQA,EAAK,OACb,YAAYM,EAAIqB,EAAavB,GAAYW,EAAS,QAAQ,EAAG,CACzD,IAAMa,EAAM5C,EAAQ,OAAOsB,CAAE,EACvBuB,EAAOd,EAAS,OAAOY,CAAU,EACjCG,EAAe,CAAC,EAChBC,EAAa,CAAC,EACpB,GAAI,CACA,QAAShC,EAAI,EAAGA,EAAIa,EAAQ,OAAQb,IAAK,CACrC,IAAMiC,EAAMpB,EAAQb,CAAC,EAAE,YAAY6B,EAAI7B,CAAC,EAAG8B,EAAK9B,CAAC,CAAC,EAClD+B,EAAa,KAAKE,EAAI,YAAY,EAClCD,EAAW,KAAKC,EAAI,UAAU,CAClC,CAGA,IAAMC,EAAoBnB,EAAQ,OAAOiB,CAAU,EACnD,MAAO,CAGH,aAAcb,EAAQU,EAAKG,EAAYD,CAAY,EACnD,WAAYG,CAChB,CACJ,QACA,CAGIhC,EAAW6B,EAAcC,CAAU,CACvC,CACJ,EACA,YAAYG,EAAI9C,EAAM,CAClB,IAAM+C,EAAMrB,EAAQ,OAAOoB,CAAE,EACvB,CAAE,UAAAtC,EAAW,UAAAD,CAAU,EAAIK,EAAK,uBAAuBZ,CAAI,EAC3D0C,EAAe,CAAC,EACtB,GAAI,CAIA,QAAS/B,EAAI,EAAGA,EAAIa,EAAQ,OAAQb,IAChC+B,EAAa,KAAKlB,EAAQb,CAAC,EAAE,YAAYoC,EAAIpC,CAAC,EAAGJ,EAAUI,CAAC,CAAC,CAAC,EAGlE,OAAOmB,EAAQtB,EAAWuC,EAAKL,CAAY,CAC/C,QACA,CAGI7B,EAAWN,EAAWmC,CAAY,CACtC,CACJ,CACJ,CAAC,CACL,CA4MA,SAASM,GAAaC,EAAOC,EAAWC,EAASC,EAAO,CACpD,IAAMC,EAAKJ,EAAM,MAAM,GACvB,GAAI,CAACI,EACD,MAAM,IAAI,MAAM,aAAa,EAGjC,SAASC,EAAkBC,EAAM,CAC7B,IAAIC,EACJ,QAASC,EAAQ,EAAGC,EAAMR,GAAYO,EAAQC,EAAKA,GAAOR,EAAW,CACjE,GAAIQ,EAAMH,EAAK,OACX,MAAM,IAAI,MAAM,2BAA2B,EAE/C,GADAC,EAAKH,EAAG,UAAUE,EAAK,SAASE,EAAOC,CAAG,EAAG,EAAI,EAC7CL,EAAG,YAAYG,CAAE,EACjB,KACR,CACA,IAAMG,EAAYN,EAAG,QAAQA,EAAG,OAAOG,CAAE,CAAC,EACpCI,EAAYX,EAAM,aAAaU,EAAW,EAAK,EACrD,MAAO,CAAE,UAAAA,EAAW,UAAAC,CAAU,CAClC,CAEA,IAAMC,EAAc,CAACC,EAAYH,IAChBV,EAAM,gBAAgBU,EAAWG,CAAU,EAC5C,SAAS,CAAC,EAE1B,MAAO,CACH,QAAS,CACL,UAAWZ,EACX,UAAWC,EACX,KAAMC,EACN,IAAKA,EACL,WAAYD,CAChB,EACA,OAAOI,EAAOQ,GAAYX,CAAK,EAAG,CAC9B,OAAAY,EAAOT,EAAMH,EAAO,MAAM,EACnBE,EAAkBC,CAAI,CACjC,EACA,aAAaI,EAAW,CACpB,OAAOV,EAAM,aAAaU,EAAW,EAAK,CAC9C,EACA,YAAYC,EAAWK,EAAOF,GAAYX,CAAK,EAAG,CAC9CY,EAAOC,EAAMb,EAAO,MAAM,EAC1B,IAAIc,EACJ,GAAI,CACAA,EAAKZ,EAAkBW,CAAI,EAAE,UAC7B,IAAME,EAAeN,EAAYD,EAAWM,CAAE,EACxCJ,EAAab,EAAM,aAAaiB,EAAI,EAAK,EAC/C,MAAO,CAAE,aAAAC,EAAc,WAAAL,CAAW,CACtC,QACA,CAGQI,GACAE,EAAWF,CAAE,CACrB,CACJ,EACA,YAAAL,CACJ,CACJ,CASA,SAASQ,GAAkBC,EAAOC,EAAOtB,EAAOG,EAAO,CACnD,GAAM,CAAE,UAAWF,EAAW,sBAAuBC,CAAQ,EAAIF,EAAM,QACvE,GAAI,CAACC,GAAa,CAACC,EACf,MAAM,IAAI,MAAM,aAAa,EACjC,IAAMqB,EAAWxB,GAAaC,EAAOC,EAAWC,EAASC,CAAK,EAExDqB,EADe,GACerB,EACpC,OAAOsB,GAAY,GAAI,GAAKnB,IACxBS,EAAOT,EAAM,EAAE,EAKRoB,GAASpB,EAAM,CAAE,MAAOkB,CAAa,CAAC,GAC9C,CAACG,EAAIC,EAAIC,IAAOC,GAASC,GAAYF,EAAG,CAAC,EAAGA,EAAG,CAAC,EAAGD,EAAG,CAAC,EAAGD,EAAG,CAAC,EAAGK,GAAaX,CAAK,CAAC,CAAC,EAAGC,EAAOC,CAAQ,CAC9G,CAEO,IAAMU,GAAwCb,GAAkB,gBAAiBc,GAAWC,GAAM,GAAG,EC/uBrG,SAASC,GAASC,EAAyC,CAChE,IAAMC,EAAKD,aAAiB,WAAaA,EAAQ,IAAI,WAAWA,CAAK,EACrE,GAAI,OAAO,OAAW,IACpB,OAAO,OAAO,KAAKC,CAAE,EAAE,SAAS,QAAQ,EAE1C,IAAIC,EAAS,GACb,QAASC,EAAI,EAAGA,EAAIF,EAAG,OAAQE,IAC7BD,GAAU,OAAO,aAAaD,EAAGE,CAAC,CAAC,EAErC,OAAO,KAAKD,CAAM,CACpB,CAKO,SAASE,GAAWC,EAA4B,CACrD,GAAI,OAAO,OAAW,IACpB,OAAO,IAAI,WAAW,OAAO,KAAKA,EAAQ,QAAQ,CAAC,EAErD,IAAMH,EAAS,KAAKG,CAAM,EACpBJ,EAAK,IAAI,WAAWC,EAAO,MAAM,EACvC,QAASC,EAAI,EAAGA,EAAID,EAAO,OAAQC,IACjCF,EAAGE,CAAC,EAAID,EAAO,WAAWC,CAAC,EAE7B,OAAOF,CACT,CA6BA,eAAsBK,GACpBC,EACAC,EACiB,CACjB,IAAMC,EAAS,OAAO,OAGhBC,EAAgB,MAAMD,EAAO,YACjC,CAAE,KAAM,OAAQ,WAAY,OAAQ,EACpC,GACA,CAAC,WAAW,CACd,EAGME,EAAuB,MAAMF,EAAO,UACxC,MACAF,EACA,CAAE,KAAM,OAAQ,WAAY,OAAQ,EACpC,GACA,CAAC,CACH,EAGMK,EAAS,MAAMH,EAAO,UAC1B,CAAE,KAAM,OAAQ,OAAQE,EAAsB,QAASA,CAAqB,EAC5ED,EAAa,WACb,CAAE,KAAM,UAAW,OAAQ,GAAI,EAC/B,GACA,CAAC,SAAS,CACZ,EAGMG,EAAK,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC,EAC9CC,EAAiB,IAAI,YAAY,EAAE,OAAO,KAAK,UAAUN,CAAa,CAAC,EACvEO,EAAmB,MAAMN,EAAO,QACpC,CAAE,KAAM,UAAW,GAAAI,CAAG,EACtBD,EACAE,CACF,EAKME,EAA4B,CAChC,SAHkB,MAAMP,EAAO,UAAU,MAAOC,EAAa,SAAS,EAItE,GAAIO,GAASJ,CAAE,EACf,WAAYI,GAASF,CAAgB,CACvC,EAEA,OAAO,KAAK,UAAUC,CAAO,CAC/B,CAyPO,SAASE,GAAkBC,EAGhC,CACA,IAAMC,EAAMC,GAAe,YAAYF,CAAc,EACrD,MAAO,CACL,YAAaG,GAASF,EAAI,UAAU,EACpC,aAAcA,EAAI,YACpB,CACF,CAsBA,eAAsBG,GAA0BC,EAA8C,CAC5F,IAAMC,EAAS,OAAO,OAChBC,EAAU,MAAMD,EAAO,UAC3B,MACAD,EACA,CAAE,KAAM,MAAO,EACf,GACA,CAAC,WAAW,CACd,EAEA,OAAO,MAAMC,EAAO,UAClB,CACE,KAAM,OACN,KAAM,UACN,KAAM,IAAI,WAAW,CAAC,EACtB,KAAM,IAAI,YAAY,EAAE,OAAO,iBAAiB,CAClD,EACAC,EACA,CAAE,KAAM,UAAW,OAAQ,GAAI,EAC/B,GACA,CAAC,UAAW,SAAS,CACvB,CACF,CAUA,eAAsBC,GACpBC,EACAC,EACiB,CACjB,IAAMC,EAAK,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC,EAC9CC,EAAiB,IAAI,YAAY,EAAE,OAAO,KAAK,UAAUF,CAAa,CAAC,EACvEG,EAAmB,MAAM,OAAO,OAAO,QAC3C,CAAE,KAAM,UAAW,GAAAF,CAAG,EACtBF,EACAG,CACF,EAEME,EAAsC,CAC1C,GAAIC,GAASJ,CAAE,EACf,WAAYI,GAASF,CAAgB,CACvC,EAEA,OAAO,KAAK,UAAUC,CAAO,CAC/B,CC1cO,IAAME,GAAkB,KAAO,IAGhCC,GAAe,IAAI,IAGnBC,GAAsB,IAAI,IAUzB,SAASC,GAAcC,EAAqC,CACjE,OAAOH,GAAa,IAAIG,CAAS,GAAK,IACxC,CAiBA,eAAsBC,GACpBC,EACAF,EACAG,EACoB,CACpB,IAAMC,EAAM,KAAK,IAAI,EACfC,EAAWR,GAAa,IAAIG,CAAS,EAG3C,GAAIK,GAAYA,EAAS,UAAYD,EACnC,OAAOC,EAIT,IAAMC,EAAWR,GAAoB,IAAIE,CAAS,EAClD,GAAIM,EACF,OAAO,MAAMA,EAGf,IAAMC,GAAW,SAAY,CAC3B,GAAI,CACF,OAAO,MAAMC,GAAUN,EAAIF,EAAWG,CAAkB,CAC1D,QAAE,CACAL,GAAoB,OAAOE,CAAS,CACtC,CACF,GAAG,EAEH,OAAAF,GAAoB,IAAIE,EAAWO,CAAO,EACnC,MAAMA,CACf,CAYA,eAAsBC,GACpBN,EACAF,EACAG,EACoB,CACpB,IAAMC,EAAM,KAAK,IAAI,EACfK,EAAS,KAAK,MAAML,EAAM,GAAI,EAC9BM,EAAeD,EAAS,KAGxBE,EAAUC,GAAWT,CAAkB,EACvC,CAAE,YAAAU,EAAa,aAAAC,CAAa,EAAIC,GAAkBJ,CAAO,EAGzDK,EAAM,MAAMC,GAA0BH,CAAY,EAGlDI,EAAW,OAAOC,GAAc,CAAC,GAGvC,GAAI,CACF,MAAMjB,EACH,QACC,8FACF,EACC,KAAKgB,EAAUlB,EAAWa,EAAaJ,EAAQC,CAAY,EAC3D,IAAI,CACT,OAASU,EAAK,CACZ,QAAQ,MAAM,0CAA0CF,CAAQ,UAAWE,CAAG,CAChF,CAGA,IAAMC,EAAuB,CAC3B,SAAAH,EACA,IAAAF,EACA,UAAAhB,EACA,UAAWI,EACX,UAAWA,EAAMR,EACnB,EAEA,OAAAC,GAAa,IAAIG,EAAWqB,CAAS,EAC9BA,CACT,CAKO,SAASC,GAAoBtB,EAAyB,CAC3DH,GAAa,OAAOG,CAAS,EAC7BF,GAAoB,OAAOE,CAAS,CACtC,CnBxIA,IAAMuB,GAAkB,IAAI,IAK5B,eAAsBC,GACpBC,EACAC,EACqC,CACrC,IAAMC,EAAM,KAAK,IAAI,EACfC,EAASL,GAAgB,IAAIG,CAAS,EAC5C,GAAIE,GAAUA,EAAO,UAAYD,EAC/B,OAAOC,EAAO,IAGhB,GAAI,CACF,IAAIC,EAAqC,KACzC,GAAI,CACFA,EAAM,MAAMJ,EACT,QAAQ,oGAAoG,EAC5G,KAAKC,CAAS,EACd,MAA8B,CACnC,OAASI,EAAgB,CACvB,GAAI,OAAOA,GAAW,SAAWA,CAAS,EAAE,SAAS,2BAA2B,EAC9ED,EAAM,MAAMJ,EACT,QAAQ,2DAA2D,EACnE,KAAKC,CAAS,EACd,MAA8B,MAEjC,OAAMI,CAEV,CAEA,IAAMC,EAAMF,GAAK,WAAc,KAAK,MAAMA,EAAI,UAAU,EAA4B,KAE9EG,EAAQD,EAAM,IAAS,IAAO,GAAK,IACzC,OAAAR,GAAgB,IAAIG,EAAW,CAAE,IAAAK,EAAK,UAAWJ,EAAMK,CAAM,CAAC,EACvDD,CACT,OAASE,EAAK,CACZ,eAAQ,MAAM,qDAAqDP,CAAS,IAAKO,CAAG,EAC7E,IACT,CACF,CAKO,SAASC,GAA6BR,EAAyB,CACpEH,GAAgB,OAAOG,CAAS,EAChCS,GAAoBT,CAAS,CAC/B,CAGA,IAAMU,GAAiC,CAAC,EAGlCC,GAAiB,GAGjBC,GAAoB,KAGpBC,GAA+B,KAGjCC,GAA4B,EAG5BC,GAAmB,GAGnBC,GAAgB,KAAK,IAAI,EAQ7B,eAAsBC,GAAoBC,EAA+B,CACvE,IAAMjB,EAAM,KAAK,IAAI,EACrB,GAAIa,GAA4Bb,EAC9B,MAAO,GAGT,GAAIiB,EACF,GAAI,CACF,IAAMC,EAAe,MAAMC,EAAW,IAAYF,EAAO,wBAAwB,EACjF,GAAIC,GAAgBA,EAAe,KAAK,MAAMlB,EAAM,GAAI,EACtD,OAAAa,GAA4BK,EAAe,IACpC,EAEX,MAAQ,CAER,CAGF,MAAO,EACT,CAOA,eAAsBE,GAAwBH,EAA4B,CAExE,IAAMI,EADS,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACVT,GAWjC,GAVAC,GAA4BQ,EAAkB,IAG9CZ,GAAc,OAAS,EACvBK,GAAmB,GAEnB,QAAQ,KACN,uEAAuEF,EAA4B,IACrG,EAEIK,EACF,GAAI,CACF,MAAME,EAAW,IAAIF,EAAO,yBAA0BI,EAAiBT,EAA4B,CACrG,MAAQ,CAER,CAEJ,CAOA,eAAsBU,GAAcC,EAAyB,CAI3D,GAHAT,GAAmB,GACnBC,GAAgB,KAAK,IAAI,EAErBN,GAAc,SAAW,EAC3B,OAGF,IAAMQ,EAAS,OAAe,QAC9B,GAAI,MAAMD,GAAoBC,CAAK,EAAG,CACpCR,GAAc,OAAS,EACvB,MACF,CAGA,IAAMe,EAAcf,GAAc,OAAO,EAAGC,EAAc,EAE1D,GAAIc,EAAY,SAAW,EACzB,OAGF,IAAMC,EAAW,IAAIC,GAASH,EAAI,EAAE,EAC9BI,EAAoC,CAAC,EAGrCC,EAAa,MAAM,KAAK,IAAI,IAAIJ,EAAY,IAAKK,GAAMA,EAAE,UAAU,CAAC,CAAC,EACrEC,EAAS,IAAI,IACbC,EAAS,IAAI,IAEnB,MAAM,QAAQ,IACZH,EAAW,IAAI,MAAOI,GAAQ,CAC5B,IAAMC,EAAS,MAAMpC,GAAuB0B,EAAI,GAAIS,CAAG,EACvD,GAAIC,IACFH,EAAO,IAAIE,EAAKC,CAAM,EAClB,WAAYA,GAAQ,CAEtB,IAAIC,EAAYC,GAAcH,CAAG,EAC5BE,IACHA,EAAY,MAAME,GAAgBb,EAAI,GAAIS,EAAKC,EAAO,MAAM,GAE1DC,GACFH,EAAO,IAAIC,EAAKE,CAAS,CAE7B,CAEJ,CAAC,CACH,EAEA,QAAWG,KAAOb,EAAa,CAC7B,IAAMS,EAASH,EAAO,IAAIO,EAAI,UAAU,EACxC,GAAIJ,EACF,GAAI,CACF,IAAMK,EAAgB,CACpB,OAAQD,EAAI,OACZ,UAAWA,EAAI,UACf,YAAaA,EAAI,YACjB,OAAQA,EAAI,OACZ,WAAYA,EAAI,WAChB,kBAAmBA,EAAI,kBACvB,aAAcA,EAAI,aAClB,SAAUA,EAAI,SACd,IAAKA,EAAI,IACT,SAAUA,EAAI,SACd,OAAQA,EAAI,MACd,EAEA,GAAI,WAAYJ,EAAQ,CAGtB,IAAMC,EAAYH,EAAO,IAAIM,EAAI,UAAU,EAC3C,GAAIH,EAAW,CACb,IAAMK,EAAY,MAAMC,GAA+BN,EAAU,IAAKI,CAAa,EACnFD,EAAI,gBAAkB,EACtBA,EAAI,WAAaH,EAAU,SAC3BG,EAAI,kBAAoBE,CAC1B,CACF,SAAW,QAASN,EAAQ,CAE1B,IAAMM,EAAY,MAAME,GAAwBR,EAAQK,CAAa,EACrED,EAAI,gBAAkB,EACtBA,EAAI,WAAa,KACjBA,EAAI,kBAAoBE,CAC1B,CAEIF,EAAI,iBAAmBA,EAAI,gBAAkB,IAC/CA,EAAI,OAAS,GACbA,EAAI,UAAY,GAChBA,EAAI,YAAc,GAClBA,EAAI,OAAS,OACbA,EAAI,WAAa,OACjBA,EAAI,kBAAoB,KACxBA,EAAI,aAAe,KACnBA,EAAI,SAAW,KACfA,EAAI,IAAM,OACVA,EAAI,SAAW,OACfA,EAAI,OAAS,OAEjB,OAASK,EAAQ,CACf,QAAQ,MAAM,8CAA8CL,EAAI,UAAU,IAAKK,CAAM,CACvF,MAEAL,EAAI,gBAAkB,EACtBA,EAAI,WAAa,KAEnBV,EAAW,KAAKF,EAAS,sBAAsBY,CAAG,CAAC,CACrD,CAEA,GAAI,CACF,MAAMd,EAAI,GAAG,MAAMI,CAAU,CAC/B,OAASrB,EAAU,CACjB,IAAMqC,EAAW,OAAOrC,GAAK,SAAWA,CAAG,EAIzCqC,EAAS,SAAS,+CAA+C,GACjEA,EAAS,SAAS,iBAAiB,GACnCA,EAAS,SAAS,mBAAmB,EAErC,MAAMvB,GAAwBH,CAAK,EAEnC,QAAQ,KAAK,oCAAoCU,EAAW,MAAM,WAAYgB,CAAQ,CAE1F,CAGIlC,GAAc,OAAS,GACzBmC,GAAsBrB,CAAG,CAE7B,CAOA,SAASqB,GAAsBrB,EAAgB,CACzCT,KAGJA,GAAmB,GAGnB,IAAI,QAAS+B,GAAY,WAAWA,EAASlC,EAAiB,CAAC,EAC5D,KAAK,IAAMW,GAAcC,CAAG,CAAC,EAC7B,MAAO,GAAM,CACZT,GAAmB,GACnB,QAAQ,MAAM,qCAAsC,CAAC,CACvD,CAAC,EACL,CAUO,SAASgC,GACdT,EACAU,EACAxB,EACAyB,EACM,CAEN,GAAID,GAAY,OAAOA,EAAS,kBAAkB,IAAM,EACtD,OAIF,IAAME,GAAUZ,EAAI,QAAU,QAAQ,YAAY,EAMlD,GALIU,GAAU,kBAAoBE,IAAW,QAKzCpC,GAA4B,KAAK,IAAI,EACvC,OAIGwB,EAAI,KACPA,EAAI,GAAKa,GAAc,GAIzBzC,GAAc,KAAK4B,CAAG,EAGtB,IAAMrC,EAAM,KAAK,IAAI,EAEnBS,GAAc,QAAUC,IACxBV,EAAMe,IAAiBJ,GAEvBqC,EAAI,UAAU1B,GAAcC,CAAG,CAAC,EAEhCqB,GAAsBrB,CAAG,CAE7B,CoBhTA,eAAsB4B,GACpBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EAC2B,CAC3B,IAAMC,EAAWT,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACxDU,EACAC,EAAgBL,GAAgB,GAEpC,GAAIF,IAAW,YAAcE,EAC3B,GACEC,IAAiB,UAChBN,EAAM,OAAS,KAAOA,EAAM,OAAS,SACtCO,EAEA,GAAI,CAEF,IAAMI,EAAiBC,GAAcP,EAAcL,EAAM,IAAI,EACvDa,EAAwB,CAC5B,KAAMR,EACN,KAAML,EAAM,KACZ,IAAKW,CACP,EACMG,EAAc,MAAMP,EACxBR,EACAc,EACAZ,EACAC,EACA,MACF,EAEA,GAAIY,EAAY,QAAUA,EAAY,OAAO,OAAS,EAAG,CACvD,IAAMC,EAAsBC,GAAeF,EAAY,MAAM,EACvDG,EAAuB,CAC3B,CAAE,KAAM,QAAS,MAAOZ,EAAc,IAAK,EAAG,CAChD,EAEA,QAAWa,KAAKH,GACVG,EAAE,OAASlB,EAAM,MAAQkB,EAAE,OAAS,UACtCD,EAAQ,KAAK,CACX,KAAMC,EAAE,KACR,KAAMA,EAAE,KACR,MAAOA,EAAE,KACT,IAAKA,EAAE,GACT,CAAC,EAGLT,EAASU,GAAmBnB,EAAM,IAAKiB,EAAS,CAAC,CACnD,MACER,EAASW,GAAcpB,EAAM,IAAKM,EAAcD,CAAY,CAEhE,OAASgB,EAAG,CACV,QAAQ,MAAM,2BAA4BA,CAAC,EAC3CZ,EAASW,GAAcpB,EAAM,IAAKM,EAAcD,CAAY,CAC9D,MAEAI,EAASW,GACPpB,EAAM,IACNM,GAAgBN,EAAM,KACtBK,CACF,MAEG,CAEL,IAAMiB,EAAOpB,EAAS,YAAc,UAEpC,GAAIoB,IAAS,WACXb,EAASW,GAAcpB,EAAM,IAAKA,EAAM,KAAM,GAAI,KAAM,CAAC,EACzDU,EAAgB,mBACPY,IAAS,SAClBb,EAASW,GAAcpB,EAAM,IAAKA,EAAM,KAAM,GAAI,KAAM,CAAC,EACzDU,EAAgB,iBACPY,IAAS,YAAa,CAC/B,IAAMC,EACJvB,EAAM,OAAS,OACXE,EAAS,mBAAqB,KAC9BA,EAAS,mBAAqB,UACpCO,EAASW,GAAcpB,EAAM,IAAKA,EAAM,KAAMuB,CAAQ,EACtDb,EAAgBa,CAClB,KAAO,CACL,IAAMC,EAASxB,EAAM,OAAS,OAAS,KAAO,UAC9CS,EAASW,GAAcpB,EAAM,IAAKA,EAAM,KAAMwB,CAAM,EACpDd,EAAgBc,CAClB,CACF,CAEA,IAAMC,EAAU,KAAK,IAAI,EAAIxB,EAAQ,UAC/ByB,EACH3B,EAAqD,IAAI,SAC1DA,EAAQ,QAAQ,IAAI,cAAc,GAClC,KAEF,OAAAE,EAAQ,IAAI,WACT,SAAY,CACX,GAAI,CACF0B,GACE,CACE,WAAY1B,EAAQ,UACpB,gBAAiBA,EAAQ,cACzB,UAAW,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACvC,UAAWO,EACX,YAAakB,EACb,OAAQ1B,EAAM,KACd,YAAaA,EAAM,KACnB,OAAAG,EACA,OAAAC,EACA,OAAQM,EACR,QAAAe,CACF,EACAvB,EACAD,EAAQ,IACRA,EAAQ,GACV,CACF,MAAQ,CAER,CACF,GAAG,CACL,EAEO,CAAE,OAAAQ,EAAQ,IAAK,KAAM,OAAAN,EAAQ,OAAAC,EAAQ,QAAAqB,CAAQ,CACtD,CC3KAG,KAcA,IAAMC,GAAc,IAAI,IAClBC,GAAmB,IAAU,GAAK,GAAK,IAE7C,eAAsBC,GAAWC,EAAmC,CAClE,GAAI,CAACC,GAAmBD,CAAE,EACxB,OAAO,KAGT,IAAME,EAAW,SAASF,CAAE,GACtBG,EAAM,KAAK,IAAI,EAGfC,EAAYP,GAAY,IAAIK,CAAQ,EAC1C,GAAIE,EAAW,CACb,GAAID,EAAMC,EAAU,UAClB,OAAOA,EAAU,KAEjBP,GAAY,OAAOK,CAAQ,CAE/B,CAGA,IAAIG,EAAa,KAKjB,GAJI,OAAO,OAAW,KAAgB,OAAe,UACnDA,EAAS,OAAe,SAGtBA,EACF,GAAI,CACF,IAAMC,EAAS,MAAMC,EAAW,IAAWF,EAAOH,CAAQ,EAC1D,GAAII,EAEF,OAAAT,GAAY,IAAIK,EAAU,CAAE,KAAMI,EAAQ,UAAWH,EAAML,EAAiB,CAAC,EACtEQ,CAEX,OAASE,EAAK,CACZ,QAAQ,KAAK,mEAAoEA,CAAG,CACtF,CAGF,IAAMC,EAAe,MAAOC,GAAe,CACzC,GAAIb,GAAY,MAAQ,IAAQ,CAC9B,IAAMc,EAAWd,GAAY,KAAK,EAAE,KAAK,EAAE,MACvCc,IAAa,QACfd,GAAY,OAAOc,CAAQ,CAE/B,CAGA,GAFAd,GAAY,IAAIK,EAAU,CAAE,KAAMQ,EAAK,UAAWP,EAAML,EAAiB,CAAC,EAEtEO,EACF,GAAI,CACF,MAAME,EAAW,IAAIF,EAAOH,EAAUQ,EAAK,MAAQ,EAAE,CACvD,OAASF,EAAK,CACZ,QAAQ,KAAK,oCAAqCA,CAAG,CACvD,CAEJ,EAGA,GAAI,CACF,IAAMI,EAAW,MAAM,MACrB,0BAA0BZ,CAAE,gEAC5B,CACE,GAAI,CACF,iBAAkB,CAChB,UAAW,QACX,UAAW,EACX,UAAW,CACb,EACA,gBAAiB,EACnB,CACF,CACF,EACA,GAAIY,EAAS,GAAI,CACf,IAAMC,EAAQ,MAAMD,EAAS,KAAK,EAClC,GAAIC,EAAK,SAAW,UAAW,CAC7B,IAAMH,EAAa,CACjB,QAASG,EAAK,QACd,aAAcA,EAAK,YACnB,OAAQA,EAAK,WACb,KAAMA,EAAK,KACX,IAAKA,EAAK,IACV,IAAKA,EAAK,IACV,GAAIA,EAAK,EACX,EACA,aAAMJ,EAAaC,CAAG,EACfA,CACT,CACF,CACF,MAAQ,CAER,CAGA,GAAI,CACF,IAAMI,EAAY,MAAM,MAAM,oBAAoBd,CAAE,EAAE,EACtD,GAAIc,EAAU,GAAI,CAChB,IAAMC,EAAS,MAAMD,EAAU,KAAK,EACpC,GAAIC,EAAM,QAAS,CACjB,IAAML,EAAa,CACjB,QAASK,EAAM,QACf,aAAcA,EAAM,aACpB,OAAQA,EAAM,OACd,KAAMA,EAAM,KACZ,IAAKA,EAAM,YAAY,IACvB,IAAKA,EAAM,YAAY,IACvB,GAAIA,EAAM,YAAY,IAClB,KAAKA,EAAM,WAAW,GAAG,IAAIA,EAAM,YAAY,KAAO,EAAE,GAAG,KAAK,EAChE,MACN,EACA,aAAMN,EAAaC,CAAG,EACfA,CACT,CACF,CACF,OAASM,EAAI,CACX,QAAQ,MAAM,qBAAsBA,CAAE,CACxC,CAEA,OAAO,IACT,CC/GO,SAASC,GACdC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACM,CACN,IAAMC,EAAWX,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDY,EACHZ,EAAqD,IAAI,SAC1DA,EAAQ,QAAQ,IAAI,cAAc,GAClC,KAEFE,EAAQ,IAAI,WACT,SAAY,CACX,GAAI,CACF,IAAMW,EAAUP,EAAc,KAC3BQ,GAAMA,EAAE,OAAS,KAAOA,EAAE,OAAS,MACtC,GAAG,KACCC,EAAc,GACdC,EAAiC,KACjCC,EAA6B,KAC7BC,EAAyB,KAE7B,GAAIL,EAAS,CACX,IAAMM,EAAM,MAAMC,GAAWP,CAAO,EAChCM,IACFJ,EAAc,KAAK,UAAUI,CAAG,EAChCH,EAAkBG,EAAI,aAAeA,EAAI,aAAa,YAAY,EAAI,KACtEF,EAAcE,EAAI,SAAW,KAC7BD,EAAUC,EAAI,KAAO,KAEzB,CAEA,IAAME,EAAU,KAAK,IAAI,EAAInB,EAAQ,UACrCoB,GACE,CACE,WAAYpB,EAAQ,UACpB,gBAAiBA,EAAQ,cACzB,UAAW,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACvC,UAAWS,EACX,YAAaC,EACb,OAAQX,EAAM,KACd,YAAaA,EAAM,KACnB,OAAAG,EACA,OAAQC,EACR,OAAQC,EAAc,IAAKQ,GAAMA,EAAE,IAAI,EAAE,KAAK,IAAI,EAClD,WAAYC,EACZ,kBAAmBC,EACnB,aAAcC,EACd,SAAUC,EACV,SAAUT,EACV,QAAAY,EACA,IAAAX,CACF,EACAP,EACAD,EAAQ,IACRA,EAAQ,GACV,EAEIK,EAAO,OAAS,GAClBgB,GAAS,IAAI,GAAGrB,EAAQ,SAAS,IAAID,EAAM,IAAI,IAAIA,EAAM,IAAI,GAAI,CAC/D,OAAAM,EACA,IAAKC,EACL,OAAAJ,EACA,OAAQC,EACR,UAAW,KAAK,IAAI,EAAIG,EAAS,GACnC,CAAC,CAEL,MAAQ,CAER,CACF,GAAG,CACL,CACF,CAiBO,SAASgB,GACdxB,EACAC,EACAC,EACAC,EACAM,EACAgB,EACAf,EACAgB,EACAC,EACkB,CAClB,IAAIC,EAAS,EACTC,EACAC,EACFL,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACnDM,EACAC,EAEJ,GAAIP,aAAiBQ,GACnBL,EAASH,EAAM,OACfI,EAAaJ,EAAM,YAAc,OACjCM,EAAsBN,EAAM,qBAAuB,OACnDO,EAAQP,EAAM,cAEdA,GACA,OAAOA,GAAU,UACjB,UAAWA,GACXA,EAAM,MACN,CACA,IAAMS,EAAQT,EAAM,MACdU,EACJ,OAAOD,GAAU,UAAYA,GAAS,YAAaA,EAC/C,OAAQA,EAA+B,OAAO,EAC9C,OAAOA,CAAK,EAClBJ,GAAe,YAAYK,CAAQ,GACrC,CAEA,IAAMC,EACJR,EAAS,EACL,iBAAiBA,CAAM,GAAGC,EAAa,IAAIA,CAAU,GAAK,EAAE,GAC5D,mBAAmBC,CAAW,GAE9BnB,EAAWX,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDY,EACHZ,EAAqD,IAAI,SAC1DA,EAAQ,QAAQ,IAAI,cAAc,GAClC,KAGFE,EAAQ,IAAI,WACT,SAAY,CACX,GAAI,CACFoB,GACE,CACE,WAAYpB,EAAQ,UACpB,gBAAiBA,EAAQ,cACzB,UAAW,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACvC,UAAWS,EACX,YAAaC,EACb,OAAQX,EAAM,KACd,YAAaA,EAAM,KACnB,OAAQ,OACR,OAAQmC,EACR,OAAQ,GACR,WAAY,GACZ,kBAAmB,KACnB,aAAc,KACd,SAAU,KACV,SAAU3B,EACV,QAAS,KAAK,IAAI,EAAIP,EAAQ,UAC9B,IAAAQ,CACF,EACAP,EACAD,EAAQ,IACRA,EAAQ,GACV,CACF,MAAQ,CAER,CACF,GAAG,CACL,EAEA,IAAMmC,EAAsBX,GAAcjB,EACpC6B,EAAsBX,GAAc,OAE1C,MAAO,CACL,OAAQ,IAAI,WACZ,IAAK,EACL,OAAQ,OACR,OAAQS,EACR,QAAS,KAAK,IAAI,EAAIlC,EAAQ,UAC9B,YAAa,CACX,aAAcO,EAAe,WAAW,SAAS,EAC7C,GAAGA,CAAc,KAAK4B,CAAmB,IACzCA,EACJ,OAAQC,EACR,OAAAV,EACA,YAAaC,EACb,aAAcC,EACd,cAAeC,EACf,OAAQC,CACV,CACF,CACF,CC/LO,IAAMO,GAAmB,CAY9B,MAAM,QACJC,EACAC,EACAC,EACAC,EACAC,EACAC,EAC2B,CAC3B,IAAMC,EACJH,EAAS,SAAS,CAAC,GAAK,gDAE1B,GAAI,CAACI,GAAUD,CAAc,EAC3B,MAAO,CACL,OAAQ,IAAI,WACZ,IAAK,EACL,OAAQ,OACR,OAAQ,sBACR,YAAa,CACX,aAAcA,EACd,OAAQ,UACR,OAAQ,CACV,EACA,QAAS,KAAK,IAAI,EAAIJ,EAAQ,SAChC,EAGF,GAAM,CAAE,SAAAM,EAAU,IAAAC,CAAI,EAAIC,GAASV,EAASC,EAAOE,CAAQ,EAEvDQ,EACJ,GAAI,CACFA,EAAWC,GAAwBN,CAAc,EACjD,IAAMO,EAAe,MAAMC,GAAkBH,EAAUH,CAAQ,EAEzDO,EAAuBC,GAAeH,EAAa,MAAM,EAEzDI,EAAS,MAAMC,GACnBjB,EACAc,EACAF,EAAa,OACbX,EACAC,EACAH,EACAD,GAAiB,QACjBM,CACF,EAEMc,EAASF,EAAO,OAChBG,EAAgBH,EAAO,cACvBI,EAAkBJ,EAAO,gBAEzBK,EACJF,EAAc,OAAS,EACnB,KAAK,IAAI,GAAI,KAAK,IAAI,GAAGA,EAAc,IAAKG,GAAMA,EAAE,GAAG,CAAC,CAAC,EACzD,GAEN,OAAAC,GACExB,EACAC,EACAC,EACAC,EACAC,EACAiB,EACAD,EACAD,EACAG,EACAhB,EACAG,CACF,EAEO,CACL,OAAAU,EACA,IAAKG,EACL,OAAAlB,EACA,OAAQiB,EACR,QAAS,KAAK,IAAI,EAAInB,EAAQ,UAC9B,QAAS,CAAE,eAAgBW,EAAa,OAAQ,EAChD,YAAa,CACX,aAAcP,EAAe,WAAW,SAAS,EAC7C,GAAGA,CAAc,KAAKO,EAAa,UAAU,IAC7CA,EAAa,WACjB,OAAQA,EAAa,WACrB,OAAQ,IACR,YAAa,IACf,CACF,CACF,OAASY,EAAY,CACnB,OAAOC,GACL1B,EACAC,EACAC,EACAC,EACAG,EACAmB,EACAhB,EACAE,GAAU,WACVA,GAAU,UACZ,CACF,CACF,EAeA,MAAM,MACJX,EACAC,EACAC,EACAC,EACAC,EACAC,EACAsB,EACAC,EAC2B,CAC3B,OAAOC,GACL7B,EACAC,EACAC,EACAC,EACAC,EACAC,EACAsB,EACAC,EACA7B,GAAiB,OACnB,CACF,CACF,EChLO,IAAM+B,GAAN,KAAgB,CAKrB,OAAO,QAAQC,EAAgBC,EAA0B,CACvD,IAAMC,EAAIF,EAAO,YAAY,EACvBG,EAAIF,EAAQ,YAAY,EAC9B,OAAOC,IAAMC,GAAKD,EAAE,SAAS,IAAIC,CAAC,EAAE,CACtC,CAMA,OAAO,UAAUH,EAAgBI,EAA4B,CAE3D,QAAWC,KAAQD,EACjB,GAAI,KAAK,QAAQJ,EAAQK,EAAK,OAAO,EACnC,OAAOA,EAGX,OAAO,IACT,CACF,ECnBO,IAAMC,GAAiB,CAC5B,MAAM,MACJC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACkC,CAClC,IAAMC,EAAcN,EAAM,KAAK,YAAY,EAGrCO,EAAYJ,EAAM,OAAOK,GAAKA,EAAE,OAAS,OAAO,EACtD,GAAIC,GAAU,UAAUT,EAAM,KAAMO,CAAS,EAC3C,OAAAF,EAAM,aAAa,EACZK,GAAiB,QAAQX,EAASC,EAAOC,EAASC,EAAU,OAAQ,WAAW,EAIxF,IAAMS,EAAeR,EAAM,OAAOK,GAAKA,EAAE,OAAS,UAAU,EACtDI,EAAeH,GAAU,UAAUT,EAAM,KAAMW,CAAY,EACjE,GAAIC,EAAc,CAChB,IAAIC,EACAC,EAAWd,EAAM,KAoBrB,GAlBIA,EAAM,OAAS,KACjBa,EAAMD,EAAa,IACf,CAACC,GAAOD,EAAa,UACvBC,EAAMD,EAAa,QACnBE,EAAW,UAEJd,EAAM,OAAS,QACxBa,EAAMD,EAAa,OACf,CAACC,GAAOD,EAAa,UACvBC,EAAMD,EAAa,QACnBE,EAAW,UAEJd,EAAM,OAAS,MACxBa,EAAMD,EAAa,MACVZ,EAAM,OAAS,UACxBa,EAAMD,EAAa,SAGjBC,EACF,OAAAR,EAAM,aAAa,EACJ,MAAMK,GAAiB,MAAMX,EAASC,EAAOC,EAASC,EAAU,WAAY,SAASU,EAAa,OAAO,GAAIC,EAAKC,CAAQ,CAG7I,CAGA,IAAMC,EAAYZ,EAAM,OAAOK,GAAKA,EAAE,OAAS,OAAO,EAChDQ,EAAYP,GAAU,UAAUT,EAAM,KAAMe,CAAS,EAC3D,GAAIC,EACF,OAAAX,EAAM,aAAa,EACZK,GAAiB,MAAMX,EAASC,EAAOC,EAASC,EAAU,QAAS,cAAcc,EAAU,OAAO,EAAE,EAK7G,GAHAX,EAAM,aAAa,EAGfD,EAAO,CACT,IAAIa,EAAgBX,EACpB,KAAOW,GAAe,CACpB,GAAIb,EAAM,KAAKa,CAAa,EAC1B,OAAAZ,EAAM,aAAa,EACZK,GAAiB,MAAMX,EAASC,EAAOC,EAASC,EAAU,QAAS,kBAAkBe,CAAa,EAAE,EAE7G,IAAMC,EAAWD,EAAc,QAAQ,GAAG,EAC1C,GAAIC,IAAa,GAAI,MACrBD,EAAgBA,EAAc,UAAUC,EAAW,CAAC,CACtD,CACAb,EAAM,aAAa,CACrB,CAEA,OAAO,IACT,CACF,EC7EO,IAAMc,GAAW,CACtB,WAAYC,GAAc,MAE1B,MAAM,QAAQC,EAAkBC,EAAiBC,EAA6C,CAC5F,IAAMC,EAAkC,CAAC,EACrCC,EAAOF,EAAQ,UACbG,EAASC,GAAiB,CAC9B,IAAMC,EAAM,KAAK,IAAI,EACrBJ,EAAQG,CAAI,GAAKH,EAAQG,CAAI,GAAK,IAAMC,EAAMH,GAC9CA,EAAOG,CACT,EAGA,GAAIN,EAAM,KAAK,YAAY,IAAM,QAAUA,EAAM,OAAS,MAExD,MAAO,CAAE,OADMO,GAAcP,EAAM,IAAK,MAAOC,EAAQ,WAAa,MAAM,EACzD,IAAK,GAAI,OAAQ,OAAQ,OAAQ,wBAAyB,QAAS,KAAK,IAAI,EAAIA,EAAQ,UAAW,QAAS,CAAE,MAAO,KAAK,IAAI,EAAIA,EAAQ,SAAU,CAAE,EAGzK,IAAMO,EAASC,GAAS,IAAI,GAAGR,EAAQ,SAAS,IAAID,EAAM,IAAI,IAAIA,EAAM,IAAI,EAAE,EAC9E,GAAIQ,GAAU,KAAK,IAAI,EAAIA,EAAO,WAAaA,EAAO,kBAAkB,WAAY,CAGlF,IAAME,EAAW,IAAI,WAAWF,EAAO,MAAM,EACzCE,EAAS,QAAU,GAAKV,EAAM,IAAI,QAAU,IAC9CU,EAAS,CAAC,EAAIV,EAAM,IAAI,CAAC,EACzBU,EAAS,CAAC,EAAIV,EAAM,IAAI,CAAC,GAG3B,IAAMW,EAAU,KAAK,IAAI,EAAIV,EAAQ,UACrC,MAAO,CACL,OAAQS,EACR,IAAK,KAAK,MAAMF,EAAO,UAAY,KAAK,IAAI,GAAK,GAAI,EACrD,OAAQA,EAAO,OACf,OAAQA,EAAO,OACf,QAAAG,EACA,QAAS,CAAE,cAAeA,CAAQ,CACpC,CACF,CAGA,IAAMC,EAAS,MAAMC,GAAe,KAAKZ,EAASG,CAAK,EACvD,GAAI,CAACQ,EAAQ,MAAO,CAAE,OAAQ,IAAI,WAAc,IAAK,EAAG,OAAQ,OAAQ,OAAQ,mBAAoB,EAGpG,IAAME,EAAc,MAAMC,GAAe,MAAMhB,EAASC,EAAOC,EAASW,EAAO,SAAUA,EAAO,MAAOA,EAAO,MAAOR,CAAK,EAC1H,GAAIU,EAAa,MAAO,CAAE,GAAGA,EAAa,QAAS,CAAE,GAAGZ,EAAS,GAAGY,EAAY,OAAQ,CAAE,EAG1F,GAAIF,EAAO,SAAS,iBAAmB,QAAS,CAC9C,IAAMI,EAAQ,MAAMC,GAAiB,MAAMlB,EAASC,EAAOC,EAASW,EAAO,SAAU,QAAS,gBAAgB,EAC9G,MAAO,CAAE,GAAGI,EAAO,QAAS,CAAE,GAAGd,EAAS,GAAGc,EAAM,OAAQ,CAAE,CAC/D,CAEA,IAAME,EAAQ,MAAMD,GAAiB,QAAQlB,EAASC,EAAOC,EAASW,EAAO,SAAU,MAAM,EAC7F,MAAO,CAAE,GAAGM,EAAO,QAAS,CAAE,GAAGhB,EAAS,GAAGgB,EAAM,OAAQ,CAAE,CAC/D,CACF,ECvDO,SAASC,GAAoBC,EAAyB,CAC3D,GAAI,CAACA,GAAO,OAAOA,GAAQ,SAAU,OAAO,KAC5C,IAAIC,EAAS,EACb,QAAWC,KAAO,OAAO,KAAKF,CAAG,EAAG,CAClC,IAAMG,EAAQD,EAAI,MAAM,iBAAiB,EACzC,GAAIC,EAAO,CACT,IAAMC,EAAMJ,EAAIE,CAAG,EACnB,GAAyBE,GAAQ,MAAQA,IAAQ,GAAI,CACnD,IAAMC,EAAM,SAASF,EAAM,CAAC,EAAG,EAAE,EAC7B,CAAC,MAAME,CAAG,GAAKA,EAAMJ,IACvBA,EAASI,EAEb,CACF,CACF,CACA,OAAIJ,EAAS,EAAU,IAAIA,CAAM,GAC1B,IACT,CAWO,SAASK,GAAaC,EAAiBP,EAAyB,CACrE,GAAI,CAACA,EAAK,OAAO,KACjB,IAAMI,EAAMJ,EAAI,OAAOO,CAAO,EAAE,GAAKP,EAAI,OAAOO,EAAQ,YAAY,CAAC,EAAE,EACvE,OAAIH,GAAO,OAAOA,GAAQ,UAAYA,EAAI,KAAK,IAAM,GAC5CA,GAGJG,IAAY,MAAQA,IAAY,YAAcP,EAAI,YAAc,OAAOA,EAAI,YAAe,UAAYA,EAAI,WAAW,KAAK,IAAM,GAC5HA,EAAI,WAEN,IACT,CAEA,SAASQ,GAASC,EAA2B,CAC3C,IAAIC,EAAS,GACPC,EAAMF,EAAM,WAClB,QAASG,EAAI,EAAGA,EAAID,EAAKC,IACvBF,GAAU,OAAO,aAAaD,EAAMG,CAAC,CAAC,EAExC,OAAO,KAAKF,CAAM,CACpB,CAEA,SAASG,GAAWC,EAA4B,CAC9C,IAAMC,EAAe,KAAKD,CAAM,EAC1BH,EAAMI,EAAa,OACnBN,EAAQ,IAAI,WAAWE,CAAG,EAChC,QAASC,EAAI,EAAGA,EAAID,EAAKC,IACvBH,EAAMG,CAAC,EAAIG,EAAa,WAAWH,CAAC,EAEtC,OAAOH,CACT,CAEA,eAAeO,GAAUC,EAAoC,CAE3D,IAAMC,EADU,IAAI,YAAY,EACL,OAAOD,EAAO,KAAK,CAAC,EACzCE,EAAa,MAAM,OAAO,OAAO,OAAO,UAAWD,CAAU,EACnE,OAAO,MAAM,OAAO,OAAO,UACzB,MACAC,EACA,CAAE,KAAM,SAAU,EAClB,GACA,CAAC,UAAW,SAAS,CACvB,CACF,CAEA,eAAeC,GAAUC,EAA0C,CACjE,OAAO,MAAM,OAAO,OAAO,UACzB,MACAA,EACA,CAAE,KAAM,SAAU,EAClB,GACA,CAAC,UAAW,SAAS,CACvB,CACF,CAiBA,eAAsBC,GACpBC,EACAvB,EACiE,CACjE,IAAMwB,EAAgBzB,GAAoBC,CAAG,EAC7C,GAAI,CAACwB,EAEH,OAAO,KAGT,IAAMC,EAAYnB,GAAakB,EAAexB,CAAG,EACjD,GAAI,CAACyB,GAAa,OAAOA,GAAc,UAAYA,EAAU,KAAK,IAAM,GACtE,MAAM,IAAI,MAAM,eAAeD,CAAa,iDAAiD,EAG/F,IAAME,EAAS,MAAMV,GAAUS,CAAS,EAGlCJ,EAAW,IAAI,WAAW,EAAE,EAClC,OAAO,gBAAgBA,CAAQ,EAC/B,IAAMM,EAAS,MAAMP,GAAUC,CAAQ,EAGjCO,EAAS,IAAI,WAAW,EAAE,EAChC,OAAO,gBAAgBA,CAAM,EAE7B,IAAMC,EADU,IAAI,YAAY,EACL,OAAON,CAAS,EACrCO,EAAsB,MAAM,OAAO,OAAO,QAC9C,CAAE,KAAM,UAAW,GAAIF,CAAO,EAC9BD,EACAE,CACF,EAEME,EAAuC,CAC3C,WAAYvB,GAAS,IAAI,WAAWsB,CAAmB,CAAC,EACxD,GAAItB,GAASoB,CAAM,CACrB,EAGMI,EAAQ,IAAI,WAAW,EAAE,EAC/B,OAAO,gBAAgBA,CAAK,EAC5B,IAAMC,EAAqB,MAAM,OAAO,OAAO,QAC7C,CAAE,KAAM,UAAW,GAAID,CAAM,EAC7BN,EACAL,CACF,EAEMa,EAAqC,CACzC,WAAY1B,GAAS,IAAI,WAAWyB,CAAkB,CAAC,EACvD,GAAIzB,GAASwB,CAAK,EAClB,YAAaR,CACf,EAEA,MAAO,CACL,cAAe,KAAK,UAAUO,CAAa,EAC3C,aAAc,KAAK,UAAUG,CAAY,CAC3C,CACF,CAOA,eAAsBC,GACpBC,EACAC,EACArC,EACiB,CACjB,IAAI+B,EACAG,EACJ,GAAI,CACFH,EAAgB,KAAK,MAAMK,CAAgB,EAC3CF,EAAe,KAAK,MAAMG,CAAe,CAC3C,OAASC,EAAU,CACjB,MAAM,IAAI,MAAM,qCAAqCA,aAAoB,MAAQA,EAAS,QAAU,OAAOA,CAAQ,CAAC,EAAE,CACxH,CAEA,IAAMC,EAAiB,MAAOC,GAAwC,CACpE,IAAMf,EAAYnB,GAAakC,EAAYxC,CAAG,EAC9C,GAAI,CAACyB,GAAa,OAAOA,GAAc,UAAYA,EAAU,KAAK,IAAM,GACtE,MAAM,IAAI,MAAM,wBAAwBe,CAAU,iDAAiD,EAGrG,IAAMd,EAAS,MAAMV,GAAUS,CAAS,EAGlCgB,EAAoB5B,GAAWqB,EAAa,UAAU,EACtDF,EAAQnB,GAAWqB,EAAa,EAAE,EAClCQ,EAAqB,MAAM,OAAO,OAAO,QAC7C,CAAE,KAAM,UAAW,GAAIV,CAAM,EAC7BN,EACAe,CACF,EAEMpB,EAAW,IAAI,WAAWqB,CAAkB,EAC5Cf,EAAS,MAAMP,GAAUC,CAAQ,EAGjCsB,EAAqB9B,GAAWkB,EAAc,UAAU,EACxDH,EAASf,GAAWkB,EAAc,EAAE,EACpCa,EAAsB,MAAM,OAAO,OAAO,QAC9C,CAAE,KAAM,UAAW,GAAIhB,CAAO,EAC9BD,EACAgB,CACF,EAGA,OADgB,IAAI,YAAY,EACjB,OAAOC,CAAmB,CAC3C,EAEA,GAAI,CACF,OAAO,MAAML,EAAeL,EAAa,WAAW,CACtD,OAASW,EAAY,CAGnB,GAAIX,EAAa,cAAgB,MAAQlC,GAAK,YAAc,OAAOA,EAAI,YAAe,UAAYA,EAAI,WAAW,KAAK,IAAM,GAC1H,GAAI,CACF,OAAO,MAAMuC,EAAe,IAAI,CAClC,MAAsB,CAEpB,MAAMM,CACR,CAEF,MAAMA,CACR,CACF,CAOA,eAAsBC,GACpBT,EACArC,EACwB,CACxB,GAAI,CAACA,EAAK,OAAO,KAEjB,IAAIkC,EACJ,GAAI,CACFA,EAAe,KAAK,MAAMG,CAAe,CAC3C,MAAQ,CACN,OAAO,IACT,CAEA,IAAMU,EAAoBb,EAAa,YACjCc,EAAuB,SAASD,EAAkB,QAAQ,KAAM,EAAE,EAAG,EAAE,EAC7E,GAAI,MAAMC,CAAoB,EAC5B,OAAO,KAIT,IAAMC,EAAiB,IADGD,EAAuB,CACL,GACtCE,EAAgB5C,GAAa2C,EAAgBjD,CAAG,EAEtD,GAAI,CAACkD,GAAiB,OAAOA,GAAkB,UAAYA,EAAc,KAAK,IAAM,GAElF,OAAO,KAIT,IAAMC,EAAmB7C,GAAayC,EAAmB/C,CAAG,EAC5D,GAAI,CAACmD,GAAoB,OAAOA,GAAqB,UAAYA,EAAiB,KAAK,IAAM,GAC3F,MAAM,IAAI,MAAM,uBAAuBJ,CAAiB,6DAA6D,EAGvH,IAAMK,EAAgB,MAAMpC,GAAUmC,CAAgB,EAChDV,EAAoB5B,GAAWqB,EAAa,UAAU,EACtDF,EAAQnB,GAAWqB,EAAa,EAAE,EAClCQ,EAAqB,MAAM,OAAO,OAAO,QAC7C,CAAE,KAAM,UAAW,GAAIV,CAAM,EAC7BoB,EACAX,CACF,EAEMpB,EAAW,IAAI,WAAWqB,CAAkB,EAG5CW,EAAa,MAAMrC,GAAUkC,CAAa,EAC1CI,EAAW,IAAI,WAAW,EAAE,EAClC,OAAO,gBAAgBA,CAAQ,EAC/B,IAAMC,EAAwB,MAAM,OAAO,OAAO,QAChD,CAAE,KAAM,UAAW,GAAID,CAAS,EAChCD,EACAhC,CACF,EAEMmC,EAA4C,CAChD,WAAYhD,GAAS,IAAI,WAAW+C,CAAqB,CAAC,EAC1D,GAAI/C,GAAS8C,CAAQ,EACrB,YAAaL,CACf,EAEA,OAAO,KAAK,UAAUO,CAAmB,CAC3C,CCnSO,IAAMC,GAAN,KAA0B,CAC/B,YAAoBC,EAAwBC,EAAe,CAAvC,QAAAD,EAAwB,SAAAC,CAAgB,CAAxC,GAAwB,IAY5C,MAAM,mBAAmBC,EAAqD,CAC5E,GAAI,CAACA,EAAM,OAAO,KAElB,IAAMC,EAAyC,CAAC,EAC5CC,EAAiB,GAGjBC,EAAaH,EAAK,YACtB,GAAIA,EAAK,uBAAyBA,EAAK,gBACrC,GAAI,CACFG,EAAa,MAAMC,GAAgBJ,EAAK,sBAAuBA,EAAK,gBAAiB,KAAK,GAAG,EAG7F,IAAMK,EAAa,MAAMC,GAAkBN,EAAK,gBAAiB,KAAK,GAAG,EACrEK,IACFJ,EAAc,gBAAkBI,EAChCH,EAAiB,GAErB,OAASK,EAAY,CACnB,IAAMC,EAAMD,aAAa,MAAQA,EAAE,OAASA,EAAE,QAAU,OAAOA,CAAC,EAChE,QAAQ,MAAM,uEAAuEP,EAAK,EAAE,IAAKQ,CAAG,CACtG,SACSR,EAAK,YAEd,GAAI,CACF,IAAMS,EAAY,MAAMC,GAAgBV,EAAK,YAAa,KAAK,GAAG,EAC9DS,IACFR,EAAc,sBAAwBQ,EAAU,cAChDR,EAAc,gBAAkBQ,EAAU,aAC1CR,EAAc,YAAc,KAC5BC,EAAiB,GAErB,OAASK,EAAY,CACnB,QAAQ,MAAM,kEAAkEP,EAAK,EAAE,IAAKO,CAAC,CAC/F,CAIF,IAAII,EAAmBX,EAAK,mBAC5B,GAAIA,EAAK,8BAAgCA,EAAK,uBAC5C,GAAI,CACFW,EAAmB,MAAMP,GAAgBJ,EAAK,6BAA8BA,EAAK,uBAAwB,KAAK,GAAG,EAGjH,IAAMK,EAAa,MAAMC,GAAkBN,EAAK,uBAAwB,KAAK,GAAG,EAC5EK,IACFJ,EAAc,uBAAyBI,EACvCH,EAAiB,GAErB,OAASK,EAAY,CACnB,IAAMC,EAAMD,aAAa,MAAQA,EAAE,OAASA,EAAE,QAAU,OAAOA,CAAC,EAChE,QAAQ,MAAM,8EAA8EP,EAAK,EAAE,IAAKQ,CAAG,CAC7G,SACSR,EAAK,mBAEd,GAAI,CACF,IAAMS,EAAY,MAAMC,GAAgBV,EAAK,mBAAoB,KAAK,GAAG,EACrES,IACFR,EAAc,6BAA+BQ,EAAU,cACvDR,EAAc,uBAAyBQ,EAAU,aACjDR,EAAc,mBAAqB,KACnCC,EAAiB,GAErB,OAASK,EAAY,CACnB,QAAQ,MAAM,yEAAyEP,EAAK,EAAE,IAAKO,CAAC,CACtG,CAIF,GAAIL,EACF,GAAI,CACF,IAAMU,EAAwB,CAAC,EACzBC,EAAsB,CAAC,EAC7B,OAAW,CAACC,EAAKC,CAAG,IAAK,OAAO,QAAQd,CAAa,EACnDW,EAAY,KAAK,GAAGE,CAAG,MAAM,EAC7BD,EAAS,KAAKE,CAAG,EAEnBF,EAAS,KAAKb,EAAK,EAAE,EAErB,MAAM,KAAK,GACR,QAAQ,oBAAoBY,EAAY,KAAK,IAAI,CAAC,eAAe,EACjE,KAAK,GAAGC,CAAQ,EAChB,IAAI,EAGP,OAAO,OAAOb,EAAMC,CAAa,CACnC,OAASM,EAAY,CACnB,QAAQ,MAAM,0EAA0EP,EAAK,EAAE,IAAKO,CAAC,CACvG,CAIF,MAAO,CACL,GAAGP,EACH,YAAaG,EACb,mBAAoBQ,CACtB,CACF,CACF,EC/GO,IAAMK,GAAN,KAAoB,CACzB,YACUC,EACAC,EACR,CAFQ,QAAAD,EACA,qBAAAC,CACP,CAFO,GACA,gBAUV,MAAM,QAAQC,EAAwC,CACpD,IAAMC,EAAO,MAAM,KAAK,GAAG,QAAQ,kCAAkC,EAAE,KAAKD,CAAE,EAAE,MAAkB,EAClG,OAAO,MAAM,KAAK,gBAAgB,mBAAmBC,CAAI,CAC3D,CASA,MAAM,cAAcC,EAA8C,CAChE,GAAI,CACF,IAAMD,EAAO,MAAM,KAAK,GAAG,QAAQ,wCAAwC,EAAE,KAAKC,CAAQ,EAAE,MAAkB,EAC9G,OAAO,MAAM,KAAK,gBAAgB,mBAAmBD,CAAI,CAC3D,OAASE,EAAY,CAEnB,IADiBA,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,GAC7C,SAAS,eAAe,EACnC,OAAO,KAET,MAAMA,CACR,CACF,CAOA,MAAM,SAA2B,CAC/B,GAAM,CAAE,QAAAC,CAAQ,EAAI,MAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAazC,EAAE,IAAU,EACb,OAAOA,CACT,CAQA,MAAM,OAAOH,EAA2C,CACtD,IAAMI,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAaxC,OAZe,MAAM,KAAK,GAAG,QAC3B,yIACF,EAAE,KACAJ,EAAK,GACLA,EAAK,SACLA,EAAK,aACLA,EAAK,KACLI,EACAJ,EAAK,UAAY,KACjBA,EAAK,QAAU,QACfA,EAAK,iBAAmB,CAC1B,EAAE,IAAI,GACQ,OAChB,CASA,MAAM,eAAeD,EAAYE,EAAoC,CAEnE,OADe,MAAM,KAAK,GAAG,QAAQ,4CAA4C,EAAE,KAAKA,EAAUF,CAAE,EAAE,IAAI,GAC5F,OAChB,CASA,MAAM,eAAeA,EAAYM,EAA2C,CAE1E,OADe,MAAM,KAAK,GAAG,QAAQ,4CAA4C,EAAE,KAAKA,EAAUN,CAAE,EAAE,IAAI,GAC5F,OAChB,CASA,MAAM,aAAaA,EAAYO,EAAyC,CAEtE,OADe,MAAM,KAAK,GAAG,QAAQ,0CAA0C,EAAE,KAAKA,GAAU,QAASP,CAAE,EAAE,IAAI,GACnG,OAChB,CAQA,MAAM,OAAOA,EAA8B,CAEzC,OADe,MAAM,KAAK,GAAG,QAAQ,gCAAgC,EAAE,KAAKA,CAAE,EAAE,IAAI,GACtE,OAChB,CAWA,MAAM,SAA4B,CAChC,GAAI,CAEF,OADc,MAAM,KAAK,GAAG,QAAQ,qCAAqC,EAAE,MAAc,OAAO,GAC/E,KAAO,CAC1B,OAASQ,EAAK,CACZ,eAAQ,MAAM,4EAA6EA,CAAG,EACvF,EACT,CACF,CACF,ECxJO,IAAMC,GAAN,KAAwB,CAC7B,YAAoBC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAUpB,MAAM,eAAeC,EAAYC,EAAsBC,EAA0B,EAAqB,CAKpG,OAJe,MAAM,KAAK,GACvB,QAAQ,yEAAyE,EACjF,KAAKD,EAAcC,EAAiBF,CAAE,EACtC,IAAI,GACO,OAChB,CASA,MAAM,cAAcA,EAAYG,EAA0C,CAKxE,OAJe,MAAM,KAAK,GACvB,QAAQ,4CAA4C,EACpD,KAAKA,EAASH,CAAE,EAChB,IAAI,GACO,OAChB,CASA,MAAM,yBAAyBA,EAAYI,EAAmC,CAK5E,OAJe,MAAM,KAAK,GACvB,QAAQ,wDAAwD,EAChE,KAAKA,EAASJ,CAAE,EAChB,IAAI,GACO,OAChB,CACF,EC9CO,IAAMK,GAAN,KAAmB,CACxB,YAAoBC,EAAwBC,EAAe,CAAvC,QAAAD,EAAwB,SAAAC,CAAgB,CAAxC,GAAwB,IAW5C,MAAM,WAAWC,EAAYC,EAAgBC,EAAqD,CAChG,IAAMC,EAAkB,KAAK,UAAUD,CAAY,EAC7CE,EAAkB,MAAMC,GAAgBJ,EAAQ,KAAK,GAAG,EACxDK,EAAgB,MAAMD,GAAgBF,EAAiB,KAAK,GAAG,EAErE,OAAIC,GAAmBE,GACN,MAAM,KAAK,GACvB,QACC,qOACF,EACC,KACCF,EAAgB,cAChBA,EAAgB,aAChBE,EAAc,cACdA,EAAc,aACdN,CACF,EACC,IAAI,GACO,SAGC,MAAM,KAAK,GACvB,QACC,2OACF,EACC,KAAKC,EAAQE,EAAiBH,CAAE,EAChC,IAAI,GACO,OAElB,CAUA,MAAM,WAAWA,EAAYO,EAAwB,GAAyB,CAC5E,OAAIA,GACa,MAAM,KAAK,GACvB,QAAQ,0HAA0H,EAClI,KAAKP,CAAE,EACP,IAAI,GACO,SAED,MAAM,KAAK,GACvB,QAAQ,iPAAiP,EACzP,KAAKA,CAAE,EACP,IAAI,GACO,OAChB,CASA,MAAM,mBAAmBA,EAAYE,EAAqD,CACxF,IAAMC,EAAkB,KAAK,UAAUD,CAAY,EAC7CI,EAAgB,MAAMD,GAAgBF,EAAiB,KAAK,GAAG,EAErE,OAAIG,GACa,MAAM,KAAK,GACvB,QACC,uHACF,EACC,KACCA,EAAc,cACdA,EAAc,aACdN,CACF,EACC,IAAI,GACO,SAEC,MAAM,KAAK,GACvB,QACC,0HACF,EACC,KAAKG,EAAiBH,CAAE,EACxB,IAAI,GACO,OAElB,CASA,MAAM,mBAAmBA,EAAYQ,EAAyC,CAK5E,OAJe,MAAM,KAAK,GACvB,QAAQ,sDAAsD,EAC9D,KAAKA,EAAe,EAAI,EAAGR,CAAE,EAC7B,IAAI,GACO,OAChB,CAUA,MAAM,mBAAmBA,EAAYS,EAAmBC,EAAsD,CAC5G,IAAMC,EAAUD,EAAc,OAAO,CAACE,EAAGC,IAAMA,IAAMJ,CAAS,EACxDK,EAAa,KAAK,UAAUH,CAAO,EAEnCL,EAAgB,MAAMD,GAAgBS,EAAY,KAAK,GAAG,EAChE,OAAIR,GACa,MAAM,KAAK,GACvB,QAAQ,uHAAuH,EAC/H,KAAKA,EAAc,cAAeA,EAAc,aAAcN,CAAE,EAChE,IAAI,GACO,SAEC,MAAM,KAAK,GACvB,QAAQ,0HAA0H,EAClI,KAAKc,EAAYd,CAAE,EACnB,IAAI,GACO,OAElB,CACF,EC/IO,IAAMe,GAAN,KAAyB,CAC9B,YAAoBC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAUpB,MAAM,0BAA0BC,EAAmBC,EAA+B,CAKhF,OAJe,MAAM,KAAK,GACvB,QAAQ,6FAA6F,EACrG,KAAKA,EAAKD,CAAS,EACnB,IAAI,GACO,OAChB,CAUA,MAAM,sBAAsBC,EAA8C,CACxE,IAAMC,EAAgBD,EAAM,OACtBE,EAAgBF,EAAM,KAAU,KAGhCG,EAAiB,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAUtC,EAAE,KAAKF,EAAeA,CAAa,EAE9BG,EAAqB,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAO1C,EAAE,KAAKH,EAAeA,CAAa,EAG9BI,EAAkB,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAQvC,EAAE,KAAKH,CAAa,EAEfI,EAAU,MAAM,KAAK,GAAG,MAAM,CAClCH,EACAC,EACAC,CACF,CAAC,EAED,MAAO,CACL,gBAAiBC,EAAQ,CAAC,EAAE,KAAK,SAAW,EAC5C,aAAcA,EAAQ,CAAC,EAAE,KAAK,SAAW,CAC3C,CACF,CACF,EC7DO,IAAMC,EAAN,KAAgB,CACL,QACA,KACA,SACA,IACA,UAEhB,YAAYC,EAAgBC,EAAe,CACzC,KAAK,QAAU,IAAIC,GAAoBF,EAAIC,CAAG,EAC9C,KAAK,KAAO,IAAIE,GAAcH,EAAI,KAAK,OAAO,EAC9C,KAAK,SAAW,IAAII,GAAkBJ,CAAE,EACxC,KAAK,IAAM,IAAIK,GAAaL,EAAIC,CAAG,EACnC,KAAK,UAAY,IAAIK,GAAmBN,CAAE,CAC5C,CAKA,MAAM,QAAQO,EAAwC,CACpD,OAAO,KAAK,KAAK,QAAQA,CAAE,CAC7B,CAKA,MAAM,cAAcC,EAA8C,CAChE,OAAO,KAAK,KAAK,cAAcA,CAAQ,CACzC,CAKA,MAAM,SAA2B,CAC/B,OAAO,KAAK,KAAK,QAAQ,CAC3B,CAKA,MAAM,OAAOC,EAA2C,CACtD,OAAO,KAAK,KAAK,OAAOA,CAAI,CAC9B,CAKA,MAAM,eAAeF,EAAYC,EAAoC,CACnE,OAAO,KAAK,KAAK,eAAeD,EAAIC,CAAQ,CAC9C,CAKA,MAAM,eAAeD,EAAYG,EAA2C,CAC1E,OAAO,KAAK,KAAK,eAAeH,EAAIG,CAAQ,CAC9C,CAKA,MAAM,aAAaH,EAAYI,EAAyC,CACtE,OAAO,KAAK,KAAK,aAAaJ,EAAII,CAAM,CAC1C,CAKA,MAAM,OAAOJ,EAA8B,CACzC,OAAO,KAAK,KAAK,OAAOA,CAAE,CAC5B,CAKA,MAAM,SAA4B,CAChC,OAAO,KAAK,KAAK,QAAQ,CAC3B,CAKA,MAAM,eAAeA,EAAYK,EAAsBC,EAA0B,EAAqB,CACpG,OAAO,KAAK,SAAS,eAAeN,EAAIK,EAAcC,CAAe,CACvE,CAKA,MAAM,cAAcN,EAAYO,EAA0C,CACxE,OAAO,KAAK,SAAS,cAAcP,EAAIO,CAAO,CAChD,CAKA,MAAM,yBAAyBP,EAAYQ,EAAmC,CAC5E,OAAO,KAAK,SAAS,yBAAyBR,EAAIQ,CAAO,CAC3D,CAKA,MAAM,WAAWR,EAAYS,EAAgBC,EAAqD,CAChG,OAAO,KAAK,IAAI,WAAWV,EAAIS,EAAQC,CAAY,CACrD,CAKA,MAAM,WAAWV,EAAYW,EAAwB,GAAyB,CAC5E,OAAO,KAAK,IAAI,WAAWX,EAAIW,CAAY,CAC7C,CAKA,MAAM,mBAAmBX,EAAYU,EAAqD,CACxF,OAAO,KAAK,IAAI,mBAAmBV,EAAIU,CAAY,CACrD,CAKA,MAAM,mBAAmBV,EAAYY,EAAyC,CAC5E,OAAO,KAAK,IAAI,mBAAmBZ,EAAIY,CAAY,CACrD,CAKA,MAAM,mBAAmBZ,EAAYa,EAAmBC,EAAsD,CAC5G,OAAO,KAAK,IAAI,mBAAmBd,EAAIa,EAAWC,CAAa,CACjE,CAKA,MAAM,0BAA0BC,EAAmBC,EAA+B,CAChF,OAAO,KAAK,UAAU,0BAA0BD,EAAWC,CAAG,CAChE,CAKA,MAAM,sBAAsBA,EAA8C,CACxE,OAAO,KAAK,UAAU,sBAAsBA,CAAG,CACjD,CACF,ECpKAC,KAOA,eAAeC,GAAgBC,EAA8C,CAC3E,GAAI,CACF,GAAIA,EAAQ,SAAW,MAAO,CAE5B,IAAMC,EADM,IAAI,IAAID,EAAQ,GAAG,EACV,aAAa,IAAI,KAAK,EAC3C,GAAI,CAACC,EAAU,OAAO,KACtB,IAAIC,EAASD,EAAS,QAAQ,KAAM,GAAG,EAAE,QAAQ,KAAM,GAAG,EAC1D,KAAOC,EAAO,OAAS,GAAGA,GAAU,IACpC,IAAMC,EAAS,KAAKD,CAAM,EACpBE,EAAM,IAAI,WAAWD,EAAO,MAAM,EACxC,QAAS,EAAI,EAAG,EAAIA,EAAO,OAAQ,IACjCC,EAAI,CAAC,EAAID,EAAO,WAAW,CAAC,EAE9B,OAAOC,CACT,SAAWJ,EAAQ,SAAW,OAAQ,CACpC,IAAMK,EAAS,MAAML,EAAQ,MAAM,EAAE,YAAY,EACjD,OAAO,IAAI,WAAWK,CAAM,CAC9B,CACF,MAAQ,CACN,OAAO,IACT,CACA,OAAO,IACT,CAKA,eAAsBC,GACpBC,EACAC,EACAC,EAC+F,CAC/F,IAAMC,EAAS,OAAe,QACxBC,EAAM,KAAK,IAAI,EAGfC,EAAQC,GAAoB,IAAIN,CAAU,EAChD,GAAIK,GAASD,EAAMC,EAAM,GAAK,IAC5B,OAAOA,EAAM,KAIf,IAAME,EAAW,cAAcP,CAAU,GACzC,GAAI,CACF,IAAMQ,EAAS,MAAMC,EAAW,IAASN,EAAOI,CAAQ,EACxD,GAAIC,EACF,OAAAF,GAAoB,IAAIN,EAAY,CAAE,KAAMQ,EAAQ,GAAIJ,CAAI,CAAC,EACtDI,CAEX,MAAQ,CAER,CAGA,GAAI,CAEF,IAAME,EAAU,MADK,IAAIC,EAAaV,EAAI,EAAE,EACT,UAAUD,CAAU,EAEvD,GAAIU,EACF,OAAAJ,GAAoB,IAAIN,EAAY,CAAE,KAAMU,EAAS,GAAIN,CAAI,CAAC,EAC9DF,EAAI,UAAUO,EAAW,IAAIN,EAAOI,EAAUG,EAAS,IAAI,CAAC,EACrDA,CAEX,OAASE,EAAQ,CAGf,GAFA,QAAQ,KAAK,0CAA0CZ,CAAU,IAAKY,EAAE,SAAWA,CAAC,EAEhFP,GAAO,KACT,OAAOA,EAAM,KAKf,IAAMQ,EAAmBZ,EAAI,oBAAsB,mDACnD,eAAQ,KAAK,sEAAsED,CAAU,OAAOa,CAAgB,EAAE,EAC/G,CACL,GAAIb,EACJ,KAAM,qBACN,SAAU,KAAK,UAAU,CACvB,SAAU,CAACa,CAAgB,EAC3B,eAAgB,QAChB,mBAAoB,EACpB,IAAK,CAAE,QAAS,GAAM,cAAe,EAAK,CAC5C,CAAC,EACD,SAAU,SACV,WAAY,KAAK,MAAMT,EAAM,GAAI,EACjC,WAAY,KAAK,MAAMA,EAAM,GAAI,CACnC,CACF,CAEA,OAAO,IACT,CAKA,eAAsBU,GACpBb,EACAC,EAC+F,CAC/F,IAAMa,EAAad,EAAI,gCAAkCA,EAAI,oBAC7D,GAAIc,EAAY,CACd,IAAML,EAAU,MAAMX,GAAoBgB,EAAYd,EAAKC,CAAG,EAC9D,GAAIQ,EAAS,OAAOA,CACtB,CAEA,GAAI,CACF,IAAMM,EAAe,IAAIL,EAAaV,EAAI,EAAE,EACtCgB,EAAW,MAAMD,EAAa,KAAK,kCAAmC,CAAC,CAAC,EAC9E,GAAIC,GAAYA,EAAS,OAAS,EAAG,CACnC,IAAMC,EAAc,MAAMF,EAAa,QAAQC,EAAS,CAAC,EAAE,EAAE,EAC7D,GAAIC,EAAa,OAAOA,CAC1B,CACF,OAASN,EAAQ,CACf,QAAQ,KAAK,sDAAuDA,EAAE,SAAWA,CAAC,CACpF,CAEA,MAAO,CACL,GAAI,UACJ,KAAM,kBACN,SAAU,KAAK,UAAU,CACvB,SAAU,CAACX,EAAI,oBAAsB,+CAA+C,EACpF,eAAgB,QAChB,mBAAoB,EACpB,IAAK,CAAE,QAAS,GAAM,cAAe,EAAK,CAC5C,CAAC,EACD,SAAU,SACV,WAAY,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACxC,WAAY,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,CAC1C,CACF,CAMA,eAAsBkB,GACpB1B,EACAQ,EACAC,EACAF,EACmB,CACnB,IAAMG,EAAS,OAAe,QAC1BiB,EAA8B,KAElC,GAAI,CACF,IAAMV,EAAU,MAAMX,GAAoBC,EAAYC,EAAKC,CAAG,EAC9D,GAAI,CAACQ,EACH,OAAO,IAAI,SAAS,sBAAuB,CAAE,OAAQ,GAAI,CAAC,EAE5D,IAAMW,EAAYX,EAAQ,GACpBY,EAAQ,MAAMC,GAAc9B,CAAO,EACzC,GAAI,CAAC6B,EACH,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAE1DF,EAAWE,EAAM,IAEjB,IAAME,EAAmB,CACvB,UAAAH,EACA,cAAeX,EAAQ,gBACvB,gBAAiBA,EAAQ,kBACzB,UAAW,KAAK,IAAI,EACpB,IAAAT,EACA,IAAAC,CACF,EACMuB,EAAS,MAAMC,GAAS,QAAQjC,EAAS6B,EAAOE,CAAO,EAG7D,OAAAtB,EAAI,WAAW,SAAY,CACzB,GAAI,CACF,IAAMyB,EAAWlC,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YAGtDmC,EAAe,OAAO3B,EAAI,oBAAoB,GAAK,GACzD,MAAMQ,EAAW,IAAIN,EAAO,cAAcwB,CAAQ,GAAIN,EAAWO,CAAY,EAG7E,IAAMC,EAAS,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACrCC,EAAgB,mBAAmBT,CAAS,GAC5CU,EAAsB,MAAMtB,EAAW,IAAYN,EAAO2B,CAAa,EAEvEE,EAAc,OAAO/B,EAAI,mBAAmB,GAAK,KACvD,GAAI,CAAC8B,GAAuBF,EAASE,EAAsBC,EAAa,CACtE,GAAI,CAEF,MADqB,IAAIrB,EAAaV,EAAI,EAAE,EACzB,iBAAiBoB,EAAWQ,CAAM,EAGrD,MADkB,IAAII,EAAUhC,EAAI,GAAIA,CAAG,EAC3B,0BAA0BoB,EAAWQ,CAAM,CAC7D,MAAQ,CAER,CAEA,MAAMpB,EAAW,IAAIN,EAAO2B,EAAeD,EAAQG,CAAW,CAChE,CACF,OAASpB,EAAG,CACV,QAAQ,MAAM,+BAA+BS,CAAS,IAAKT,CAAC,CAC9D,CACF,GAAG,CAAC,EAEG,IAAI,SAASa,EAAO,OAAe,CACxC,QAAS,CACP,eAAgB,0BAChB,gBAAiB,WAAWA,EAAO,GAAG,EACxC,CACF,CAAC,CACH,OAASb,EAAQ,CACf,QAAQ,MAAM,iCAAkCA,CAAC,EACjD,GAAI,CAGF,IAAMC,EAAmBZ,EAAI,oBAAsB,mDAC7CiC,EAAWd,GAAa,MAAM5B,GAAgBC,CAAO,EAE3D,GAAIyC,GAAYA,EAAS,OAAS,EAAG,CACnC,IAAMC,EAAWC,GAAwBvB,CAAgB,EACnDwB,EAAe,MAAMC,GAAkBH,EAAUD,CAAQ,EAC/D,OAAO,IAAI,SAASG,EAAa,OAAe,CAC9C,QAAS,CACP,eAAgB,0BAChB,gBAAiB,YACnB,CACF,CAAC,CACH,CAGA,GAAIxB,EAAiB,WAAW,SAAS,GAAKA,EAAiB,WAAW,UAAU,EAAG,CACrF,IAAM0B,EAAc,IAAI,IAAI1B,CAAgB,EACtC2B,EAAS,IAAI,IAAI/C,EAAQ,GAAG,EAClC,OAAA8C,EAAY,OAASC,EAAO,OACR,MAAM,MAAMD,EAAY,SAAS,EAAG,CACtD,OAAQ9C,EAAQ,OAChB,QAAS,CACP,OAAUA,EAAQ,QAAQ,IAAI,QAAQ,GAAK,0BAC3C,eAAgBA,EAAQ,QAAQ,IAAI,cAAc,GAAK,yBACzD,EACA,KAAMA,EAAQ,SAAW,OAASA,EAAQ,KAAO,MACnD,CAAC,CAEH,CAEA,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,CAC1D,OAASgD,EAAU,CACjB,eAAQ,MAAM,4CAA6CA,CAAQ,EAC5D,IAAI,SAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,CAC9D,CACF,CACF,CChOO,SAASC,GAAUC,EAAwBC,EAAiB,KAAc,CAC/E,OAAKD,EAIE;AAAA;AAAA,EAHO,MAAM,QAAQA,CAAG,EAC3BA,EAAI,IAAKE,GAAM,GAAGD,CAAM,GAAGC,CAAC,EAAE,EAAE,KAAK;AAAA,CAAI,EACzC,GAAGD,CAAM,GAAGD,CAAG,EACI;AAAA,EAJN,EAKnB,CAQO,SAASG,GAAeC,EAAgC,CAC7D,GAAM,CAAE,KAAAC,EAAM,YAAAC,EAAa,MAAAC,EAAO,SAAAC,EAAU,QAAAC,EAAS,SAAAC,EAAU,QAAAC,EAAS,IAAAX,CAAI,EAAII,EAC1EH,EAAS,KACTW,EAAM,EAGNC,EAAiB,CAAC,GAAIL,GAAY,CAAC,EAAI,GAAIC,GAAW,CAAC,CAAE,EACzDK,EAAiB,KAAK,IAAI,GAAGD,EAAe,IAAKE,GAASA,EAAK,MAAM,MAAM,EAAG,CAAC,EAE/EC,EAAgB,CAACC,EAAeC,EAAoBC,IAAiC,CACzF,GAAI,CAACD,GAASA,EAAM,SAAW,EAAG,MAAO,GACzC,IAAME,EAAQD,GAAe,KAAK,IAAI,GAAGD,EAAM,IAAKH,GAASA,EAAK,MAAM,MAAM,EAAG,CAAC,EAC5EM,EAAQH,EAAM,IACjBH,GAAS,GAAGd,CAAM,GAAGc,EAAK,MAAM,OAAOK,EAAQR,CAAG,CAAC,GAAGG,EAAK,IAAI,EAClE,EACA,MAAO;AAAA,EAAKE,CAAK;AAAA,EAAMI,EAAM,KAAK;AAAA,CAAI,CAAC,EACzC,EAEMC,EAAY,MAAM,QAAQf,CAAK,EACjCA,EAAM,IAAKgB,GAAM,GAAGtB,CAAM,GAAGsB,CAAC,EAAE,EAAE,KAAK;AAAA,CAAI,EAC3C,GAAGtB,CAAM,GAAGM,CAAK,GAEfiB,EAAUxB,EAAMD,GAAUC,EAAKC,CAAM,EAAE,QAAQ,EAAI,GAYzD,MAViB,CACf,GAAGI,CAAI,MAAMC,CAAW,GACxB;AAAA;AAAA,EAAagB,CAAS,GACtBN,EAAc,WAAYR,EAAUM,CAAc,EAClDE,EAAc,UAAWP,EAASK,CAAc,EAChDE,EAAc,wBAAyBN,CAAQ,EAC/CM,EAAc,wBAAyBL,CAAO,EAC9Ca,CACF,EAEgB,OAAO,OAAO,EAAE,KAAK;AAAA,CAAI,EAAI;AAAA,CAC/C,CA0BO,SAASC,GAAkBrB,EAAmC,CACnE,GAAM,CAAE,MAAAa,EAAO,MAAAC,EAAO,OAAAjB,EAAS,KAAM,IAAAW,EAAM,CAAE,EAAIR,EACjD,GAAI,CAACc,GAASA,EAAM,SAAW,EAAG,MAAO,GAEzC,IAAMQ,EAAc,KAAK,IAAI,GAAGR,EAAM,IAAKS,GAAMA,EAAE,QAAQ,MAAM,EAAG,CAAC,EAC/DN,EAAQH,EAAM,IACjBH,GAAS,GAAGd,CAAM,GAAGc,EAAK,QAAQ,OAAOW,EAAcd,CAAG,CAAC,KAAKG,EAAK,IAAI,EAC5E,EAEA,OAAIE,EACK,GAAGA,CAAK;AAAA,EAAKI,EAAM,KAAK;AAAA,CAAI,CAAC;AAAA,EAE/BA,EAAM,KAAK;AAAA,CAAI,EAAI;AAAA,CAC5B,CAmBO,SAASO,GAAaxB,EAA8B,CACzD,GAAM,CAAE,MAAAa,EAAO,QAAAY,EAAS,WAAAC,EAAa,IAAK,SAAAC,EAAW,GAAI,IAAA/B,CAAI,EAAII,EAC3D4B,EAAa,IAAIf,CAAK,IACtBgB,EAAe,KAAK,IAAIF,EAAUC,EAAW,OAAS,CAAC,EACvDE,EAASJ,EAAW,OAAOG,CAAY,EAEvCE,EAAU,EACVC,EAAW,KAAK,IAAI,EAAGH,EAAeD,EAAW,OAASG,EAAU,CAAC,EAGrEd,EAAkB,CAFT,GAAGS,EAAW,OAAOK,CAAO,CAAC,IAAIH,CAAU,IAAIF,EAAW,OAAOM,CAAQ,CAAC,GAIvFP,EACAK,CACF,EAEA,OAAIlC,GACFqB,EAAM,KAAK,QAAQrB,CAAG,EAAE,EAGnB;AAAA,EAAOqB,EAAM,KAAK;AAAA,CAAI,EAAI;AAAA,CACnC,CA2BO,SAASgB,GAAsBjC,EAAuC,CAC3E,GAAM,CAAE,MAAAa,EAAO,OAAAqB,EAAS,UAAM,MAAApB,EAAO,OAAAjB,EAAS,KAAM,IAAAW,EAAM,CAAE,EAAIR,EAChE,GAAI,CAACc,GAASA,EAAM,SAAW,EAAG,MAAO,GAEzC,IAAMqB,EAAgB,KAAK,IAAI,GAAGrB,EAAM,IAAKH,GAASA,EAAK,MAAM,MAAM,EAAG,CAAC,EACrEM,EAAQH,EAAM,IACjBH,GAAS,GAAGd,CAAM,GAAGqC,CAAM,GAAGvB,EAAK,MAAM,OAAOwB,EAAgB3B,CAAG,CAAC,MAAMG,EAAK,KAAK,EACvF,EAEA,OAAIE,EACK,GAAGA,CAAK;AAAA,EAAKI,EAAM,KAAK;AAAA,CAAI,CAAC,GAE/BA,EAAM,KAAK;AAAA,CAAI,CACxB,CA4BO,SAASmB,GAAapC,EAA8B,CACzD,GAAM,CAAE,MAAAa,EAAO,SAAAwB,EAAU,WAAAX,EAAa,IAAK,OAAAQ,EAAS,UAAM,SAAAP,EAAW,GAAI,MAAAb,CAAM,EAAId,EAG7EsC,EAAiB,CACrBzB,EAAM,OAAS,EACfwB,EAAWA,EAAS,OAAS,EAAI,EACjC,IAAIvB,GAAS,CAAC,GAAG,IAAKS,GAAMA,EAAE,MAAM,OAASA,EAAE,MAAM,OAAS,EAAE,CAClE,EACMgB,EAAkB,KAAK,IAAIZ,EAAU,GAAGW,CAAc,EACtDR,EAASJ,EAAW,OAAOa,CAAe,EAE1CC,EAAcC,GAAyB,CAC3C,IAAMC,EAAM,KAAK,IAAI,EAAG,KAAK,OAAOH,EAAkBE,EAAK,QAAU,CAAC,CAAC,EACvE,MAAO,IAAI,OAAOC,CAAG,EAAID,CAC3B,EAEMxB,EAAkB,CAACa,EAAQU,EAAW3B,CAAK,CAAC,EAQlD,GANIwB,GACFpB,EAAM,KAAKuB,EAAWH,CAAQ,CAAC,EAGjCpB,EAAM,KAAKa,CAAM,EAEbhB,GAASA,EAAM,OAAS,EAAG,CAC7B,IAAMqB,EAAgB,KAAK,IAAI,GAAGrB,EAAM,IAAKH,GAASA,EAAK,MAAM,MAAM,EAAG,CAAC,EACrEgC,EAAY7B,EAAM,IACrBH,GAAS,KAAKuB,CAAM,GAAGvB,EAAK,MAAM,OAAOwB,EAAgB,CAAC,CAAC,MAAMxB,EAAK,KAAK,EAC9E,EACAM,EAAM,KAAK,GAAG0B,CAAS,EACvB1B,EAAM,KAAKa,CAAM,CACnB,CAEA,OAAOb,EAAM,KAAK;AAAA,CAAI,CACxB,CAyBO,SAAS2B,EAAiB5C,EAAkC,CACjE,GAAM,CAAE,MAAAa,EAAO,QAAAgC,EAAS,QAAAC,EAAS,OAAAC,EAAQ,UAAAC,CAAU,EAAIhD,EACjDH,EAAS,KACToD,EAAY,OAEZC,EAAqB,CACzB,IAAIrC,CAAK,KAAKgC,CAAO,EACvB,EAMA,GAJIC,GAAWA,EAAQ,OAAS,GAC9BI,EAAS,KAAKJ,EAAQ,IAAKK,GAAM,GAAGtD,CAAM,GAAGsD,CAAC,EAAE,EAAE,KAAK;AAAA,CAAI,CAAC,EAG1DJ,GAAUA,EAAO,OAAS,EAAG,CAC/B,IAAMK,EAAe,GAAGvD,CAAM,mBACxBwD,EAAaN,EAAO,IAAKO,GAAM,GAAGL,CAAS,KAAKK,CAAC,EAAE,EAAE,KAAK;AAAA,CAAI,EACpEJ,EAAS,KAAK,GAAGE,CAAY;AAAA,EAAKC,CAAU,EAAE,CAChD,CAEA,GAAIL,GAAaA,EAAU,OAAS,EAAG,CACrC,IAAMO,EAAYP,EAAU,SAAW,EAAI,GAAGnD,CAAM,YAAc,GAAGA,CAAM,aACrE2D,EAAWR,EAAU,IAAKS,GAAM,GAAGR,CAAS,KAAKQ,CAAC,EAAE,EAAE,KAAK;AAAA,CAAI,EACrEP,EAAS,KAAK,GAAGK,CAAS;AAAA,EAAKC,CAAQ,EAAE,CAC3C,CAEA,MAAO;AAAA,EAAON,EAAS,OAAO,OAAO,EAAE,KAAK;AAAA,CAAI,EAAI;AAAA,CACtD,CAoBO,SAASQ,GAAgB1D,EAAiC,CAC/D,GAAM,CAAE,YAAA2D,EAAa,SAAAC,EAAU,KAAAC,EAAM,IAAAC,EAAK,gBAAAC,EAAiB,cAAAC,CAAc,EAAIhE,EACvEiE,EAAWH,GAA2B,KAE5C,GAAIG,IAAY,aAAc,CAC5B,IAAMlB,EAAmB,CAAC,EACtBa,IAAa,OAASC,IAAS,IACjCd,EAAO,KAAK,4FAA4F,EACxGA,EAAO,KAAK,+DAA+D,GAE3EA,EAAO,KAAK,0DAA0Da,CAAQ,SAASC,CAAI,GAAG,EAGhG,IAAMb,EAAY,CAChB,OAAOe,CAAe,iCACtBC,EAAgB,UAAUA,CAAa,8BAAgC,IACzE,EAAE,OAAO,OAAO,EAEhB,OAAOpB,EAAiB,CACtB,MAAO,QACP,MAAO,gBACP,QAAS,GAAGe,CAAW,SAASE,CAAI,sBACpC,OAAAd,EACA,UAAAC,CACF,CAAC,CACH,CAEA,GAAIiB,IAAY,SAAU,CACxB,IAAMlB,EAAS,CACb,sEACA,mFACF,EAEMC,EAAY,CAChB,2DACA,UAAUe,CAAe,gDAC3B,EAEA,OAAOnB,EAAiB,CACtB,MAAO,QACP,MAAO,oBACP,QAAS,gCAAgCe,CAAW,SAASE,CAAI,IACjE,OAAAd,EACA,UAAAC,CACF,CAAC,CACH,CAEA,IAAMH,EAAUiB,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAC/D,MAAO;AAAA,GAAMH,CAAW,oBAAoBd,CAAO;AAAA,CACrD,CvE5WO,IAAMqB,GAAN,KAAmB,CAIxB,YAAoBC,EAA2B,CAA3B,aAAAA,CAA4B,CAA5B,QAHZ,OAA8B,KAC9B,UAAqB,GAI7B,OAAuB,CACrB,OAAO,IAAI,QAAQ,CAACC,EAASC,IAAW,CACtC,GAAI,CACF,KAAK,OAASC,GAAM,aAAa,MAAM,EAEvC,IAAMC,EAAuBC,GAAa,CACxC,GAAI,CAAE,KAAK,QAAQ,MAAM,CAAG,MAAQ,CAAC,CACrC,KAAK,OAAS,KAEd,QAAQ,MAAMC,GAAgB,CAC5B,YAAa,UACb,SAAU,MACV,KAAM,KAAK,QAAQ,KACnB,IAAAD,EACA,gBAAiB,2CACjB,cAAe,eACjB,CAAC,CAAC,EAEFH,EAAOG,CAAG,CACZ,EAEA,KAAK,OAAO,KAAK,QAASD,CAAmB,EAE7C,KAAK,OAAO,GAAG,UAAW,MAAOG,EAAaC,IAA4B,CACxE,MAAM,KAAK,cAAcD,EAAKC,CAAK,CACrC,CAAC,EAED,KAAK,OAAO,KAAK,KAAK,QAAQ,KAAM,KAAK,QAAQ,KAAM,IAAM,CAC3D,KAAK,QAAQ,eAAe,QAASJ,CAAmB,EACxD,KAAK,QAAQ,GAAG,QAAUC,GAAe,CACvC,QAAQ,MAAM,kCAAmCA,CAAG,CACtD,CAAC,EAED,GAAI,CACF,KAAK,QAAQ,kBAAkB,EAAI,KAAO,IAAI,EAC9C,KAAK,QAAQ,kBAAkB,EAAI,KAAO,IAAI,CAChD,MAAQ,CAER,CACA,KAAK,UAAY,GACjB,QAAQ,IAAI,4CAA4C,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,EAAE,EAChGJ,EAAQ,CACV,CAAC,CACH,OAASI,EAAK,CACZH,EAAOG,CAAG,CACZ,CACF,CAAC,CACH,CAEA,MAAc,cAAcE,EAAaC,EAAwC,CAC/E,GAAI,CACF,IAAMC,EAAW,IAAI,WAAWF,EAAI,OAAQA,EAAI,WAAYA,EAAI,UAAU,EACpEG,EAAQC,GAAqBF,CAAQ,EAC3C,GAAI,CAACC,EACH,OAGF,IAAME,EAAM,KAAK,QAAQ,IACnBC,EAAwB,CAC5B,UAAUC,EAAuB,CAC/BA,EAAQ,MAAOT,GAAQ,QAAQ,MAAM,+BAAgCA,CAAG,CAAC,CAC3E,EACA,wBAAyB,CAAC,CAC5B,EAKIU,EAAU,KAOd,GANI,KAAK,QAAQ,oBACfA,EAAU,MAAMC,GAAoB,KAAK,QAAQ,kBAAmBJ,EAAKC,CAAG,GAEzEE,IACHA,EAAU,MAAME,GAAsBL,EAAKC,CAAG,GAE5C,CAACE,EACH,OAGF,IAAMG,EAAmB,CACvB,UAAWH,EAAQ,GACnB,cAAeA,EAAQ,gBACvB,gBAAiBA,EAAQ,kBACzB,UAAW,KAAK,IAAI,EACpB,IAAAH,EACA,IAAAC,CACF,EAEMM,EAAOX,EAAM,QAAQ,SAAS,GAAG,EAAI,IAAIA,EAAM,OAAO,IAAMA,EAAM,QAClEY,EAAU,IAAI,QAAQ,UAAUD,CAAI,aAAc,CACtD,QAAS,CACP,mBAAoBX,EAAM,QAC1B,OAAU,0BACV,eAAgB,yBAClB,CACF,CAAC,EAEKa,EAAS,MAAMC,GAAS,QAAQF,EAASV,EAAOQ,CAAO,EAC7D,GAAI,CAACG,GAAU,CAACA,EAAO,QAAUA,EAAO,OAAO,SAAW,EACxD,OAGF,IAAIE,EAASF,EAAO,OAGpB,GAAIE,EAAO,OAAS,IAAK,CACvB,IAAMC,EAAY,IAAI,WAAWD,CAAM,EAEvCC,EAAU,CAAC,GAAK,EAChBD,EAASC,EAAU,MAAM,EAAG,GAAG,CACjC,CAEA,KAAK,QAAQ,KAAKD,EAAQf,EAAM,KAAMA,EAAM,QAAUH,GAAsB,CACtEA,GACF,QAAQ,KAAK,sCAAsCG,EAAM,OAAO,IAAIA,EAAM,IAAI,IAAKH,EAAI,OAAO,CAElG,CAAC,CACH,OAASA,EAAK,CACZ,QAAQ,MAAM,wCAAyCA,CAAG,EAC1D,GAAI,CACF,IAAMoB,EAAmB,KAAK,QAAQ,IAAI,oBAAsB,mDAC1DhB,EAAW,IAAI,WAAWF,EAAI,OAAQA,EAAI,WAAYA,EAAI,UAAU,EACpEmB,EAAWC,GAAwBF,CAAgB,EAErDF,GADiB,MAAMK,GAAkBF,EAAUjB,CAAQ,GACrC,OAC1B,GAAIc,EAAO,OAAS,IAAK,CACvB,IAAMC,EAAY,IAAI,WAAWD,CAAM,EACvCC,EAAU,CAAC,GAAK,EAChBD,EAASC,EAAU,MAAM,EAAG,GAAG,CACjC,CACA,KAAK,QAAQ,KAAKD,EAAQf,EAAM,KAAMA,EAAM,OAAO,CACrD,OAASqB,EAAa,CACpB,QAAQ,MAAM,gDAAiDA,CAAW,CAC5E,CACF,CACF,CAEA,MAAsB,CACpB,OAAO,IAAI,QAAS5B,GAAY,CAC1B,KAAK,QAAU,KAAK,UACtB,KAAK,OAAO,MAAM,IAAM,CACtB,KAAK,UAAY,GACjB,QAAQ,IAAI,4BAA4B,EACxCA,EAAQ,CACV,CAAC,EAEDA,EAAQ,CAEZ,CAAC,CACH,CACF,EwE5KA,OAAO6B,OAAQ,UACf,OAAOC,OAAS,WAChB,OAAS,UAAAC,OAAc,cAkBhB,IAAMC,GAAN,KAAmB,CAIxB,YAAoBC,EAA2B,CAA3B,aAAAA,CAA4B,CAA5B,QAHZ,OAA4B,KAC5B,UAAqB,GAI7B,OAAuB,CACrB,OAAO,IAAI,QAAQ,CAACC,EAASC,IAAW,CACtC,GAAM,CAAE,WAAAC,EAAY,YAAAC,EAAa,KAAAC,EAAM,KAAAC,CAAK,EAAI,KAAK,QAErD,GAAI,CAACH,GAAc,CAACC,EAAa,CAC/B,QAAQ,KAAKG,EAAiB,CAC5B,MAAO,UACP,MAAO,2BACP,QAAS,yEACT,UAAW,CACT,wFACF,CACF,CAAC,CAAC,EACFN,EAAQ,EACR,MACF,CAEA,GAAI,CAACO,GAAG,WAAWL,CAAU,GAAK,CAACK,GAAG,WAAWJ,CAAW,EAAG,CAC7D,QAAQ,KAAKG,EAAiB,CAC5B,MAAO,UACP,MAAO,0BACP,QAAS,+FACT,QAAS,CACP,eAAeJ,CAAU,GACzB,eAAeC,CAAW,EAC5B,EACA,UAAW,CACT,yEACF,CACF,CAAC,CAAC,EACFH,EAAQ,EACR,MACF,CAEA,GAAI,CACF,IAAMQ,EAA6B,CACjC,IAAKD,GAAG,aAAaL,CAAU,EAC/B,KAAMK,GAAG,aAAaJ,CAAW,EACjC,cAAe,CAAC,KAAK,EACrB,iBAAkB,gBACpB,EAEA,KAAK,OAASM,GAAI,aAAaD,EAAaE,GAA0B,CACpE,KAAK,iBAAiBA,CAAM,CAC9B,CAAC,EAED,IAAMC,EAAuBC,GAAa,CACxC,KAAK,OAAS,KAEd,QAAQ,MAAMC,GAAgB,CAC5B,YAAa,qBACb,SAAU,MACV,KAAAT,EACA,IAAAQ,EACA,gBAAiB,2CACjB,cAAe,eACjB,CAAC,CAAC,EAEFX,EAAOW,CAAG,CACZ,EAEA,KAAK,OAAO,KAAK,QAASD,CAAmB,EAE7C,KAAK,OAAO,OAAOP,EAAMC,EAAM,IAAM,CACnC,KAAK,QAAQ,eAAe,QAASM,CAAmB,EACxD,KAAK,QAAQ,GAAG,QAAUC,GAAe,CACvC,QAAQ,MAAM,uBAAwBA,CAAG,CAC3C,CAAC,EACD,KAAK,UAAY,GACjB,QAAQ,IAAI,yCAAyCP,CAAI,IAAID,CAAI,EAAE,EACnEJ,EAAQ,CACV,CAAC,CACH,OAASY,EAAK,CACZ,QAAQ,MAAM,yCAA0CA,CAAG,EAC3DX,EAAOW,CAAG,CACZ,CACF,CAAC,CACH,CAEQ,iBAAiBF,EAA6B,CAEpDA,EAAO,WAAW,EAAI,EACtBA,EAAO,aAAa,GAAM,GAAK,EAE/B,IAAMI,EAAWJ,EAAO,eAAe,QAAQ,WAAY,EAAE,GAAK,YAC9DK,EAASC,GAAO,MAAM,CAAC,EAGrBC,EAAaP,EAAO,YAAc,GACpCQ,EAAe,GACnB,GAAID,EAAY,CACd,IAAME,EAAYF,EAAW,MAAM,GAAG,EAAE,CAAC,EACrCG,GAAiB,KAAKD,CAAS,IACjCD,EAAeC,EAEnB,CAEAT,EAAO,WAAW,IAAO,IAAM,CAC7BA,EAAO,QAAQ,CACjB,CAAC,EAEDA,EAAO,GAAG,QAAUE,GAAe,CAE5BA,EAAY,OAAS,cACxB,QAAQ,KAAK,2BAA2BE,CAAQ,IAAKF,EAAI,OAAO,CAEpE,CAAC,EAED,IAAMS,EAAiB,MACvBX,EAAO,GAAG,OAAQ,MAAOY,GAAkB,CACzC,GAAIP,EAAO,OAASO,EAAM,OAASD,EAAgB,CACjDX,EAAO,QAAQ,EACf,MACF,CAGA,IAFAK,EAASC,GAAO,OAAO,CAACD,EAAQO,CAAK,CAAC,EAE/BP,EAAO,QAAU,GAAG,CACzB,IAAMQ,EAASR,EAAO,aAAa,CAAC,EACpC,GAAIA,EAAO,OAAS,EAAIQ,EAEtB,MAGF,IAAMC,EAAaT,EAAO,SAAS,EAAG,EAAIQ,CAAM,EAChDR,EAASA,EAAO,SAAS,EAAIQ,CAAM,EAEnC,MAAM,KAAK,aAAab,EAAQc,EAAYV,EAAUI,CAAY,CACpE,CACF,CAAC,CACH,CAEA,MAAc,aACZR,EACAe,EACAX,EACAY,EACe,CACf,GAAI,CACF,IAAMC,EAAQC,GAAqBH,CAAM,EACzC,GAAI,CAACE,EAAO,OAEZ,IAAME,EAAM,KAAK,QAAQ,IACnBC,EAAwB,CAC5B,UAAUC,EAAuB,CAC/BA,EAAQ,MAAOnB,GAAQ,QAAQ,MAAM,+BAAgCA,CAAG,CAAC,CAC3E,EACA,wBAAyB,CAAC,CAC5B,EAMIoB,EAAU,KAUd,GATIN,IACFM,EAAU,MAAMC,GAAoBP,EAAQG,EAAKC,CAAG,GAElD,CAACE,GAAW,KAAK,QAAQ,oBAC3BA,EAAU,MAAMC,GAAoB,KAAK,QAAQ,kBAAmBJ,EAAKC,CAAG,GAEzEE,IACHA,EAAU,MAAME,GAAsBL,EAAKC,CAAG,GAE5C,CAACE,EAAS,OAEd,IAAMG,EAAmB,CACvB,UAAWH,EAAQ,GACnB,cAAeA,EAAQ,gBACvB,gBAAiBA,EAAQ,kBACzB,UAAW,KAAK,IAAI,EACpB,IAAAH,EACA,IAAAC,CACF,EAEMzB,EAAOS,EAAS,SAAS,GAAG,EAAI,IAAIA,CAAQ,IAAMA,EAClDsB,EAAU,IAAI,QAAQ,UAAU/B,CAAI,aAAc,CACtD,QAAS,CACP,mBAAoBS,EACpB,OAAU,0BACV,eAAgB,yBAClB,CACF,CAAC,EAEKuB,EAAS,MAAMC,GAAS,QAAQF,EAAST,EAAOQ,CAAO,EAC7D,GAAI,CAACE,GAAU,CAACA,EAAO,QAAUA,EAAO,OAAO,SAAW,EAAG,OAE7D,GAAI,CAAC3B,EAAO,WAAaA,EAAO,SAAU,CACxC,IAAM6B,EAASF,EAAO,OAChBG,EAAcxB,GAAO,YAAY,EAAIuB,EAAO,MAAM,EACxDC,EAAY,cAAcD,EAAO,OAAQ,CAAC,EAC1CC,EAAY,IAAID,EAAQ,CAAC,EACzB7B,EAAO,MAAM8B,CAAW,CAC1B,CACF,OAAS5B,EAAK,CACZ,QAAQ,MAAM,oCAAqCA,CAAG,EACtD,GAAI,CACF,IAAM6B,EAAmB,KAAK,QAAQ,IAAI,oBAAsB,mDAC1DC,EAAWC,GAAwBF,CAAgB,EACnDG,EAAe,MAAMC,GAAkBH,EAAUjB,CAAM,EAC7D,GAAI,CAACf,EAAO,WAAaA,EAAO,SAAU,CACxC,IAAM6B,EAASK,EAAa,OACtBJ,EAAcxB,GAAO,YAAY,EAAIuB,EAAO,MAAM,EACxDC,EAAY,cAAcD,EAAO,OAAQ,CAAC,EAC1CC,EAAY,IAAID,EAAQ,CAAC,EACzB7B,EAAO,MAAM8B,CAAW,CAC1B,CACF,OAASM,EAAa,CACpB,QAAQ,MAAM,4CAA6CA,CAAW,CACxE,CACF,CACF,CAEA,MAAsB,CACpB,OAAO,IAAI,QAAS9C,GAAY,CAC1B,KAAK,QAAU,KAAK,UACtB,KAAK,OAAO,MAAM,IAAM,CACtB,KAAK,UAAY,GACjB,QAAQ,IAAI,wBAAwB,EACpCA,EAAQ,CACV,CAAC,EAEDA,EAAQ,CAEZ,CAAC,CACH,CACF,EC3PA,OAAO+C,OAAU,YACjB,OAAOC,OAAQ,UACf,OAAOC,OAAU,YACjB,OAAS,UAAAC,OAAc,cCNvBC,KCFO,SAASC,GAAyBC,EAAgBC,EAAqB,CAC5E,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4DAUmDD,CAAM,IAAIC,CAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAWzE,CDfAC,KECA,eAAsBC,GAAwBC,EAAkBC,EAA6B,CAC3F,IAAMC,EAAM,IAAI,IAAIF,EAAQ,GAAG,EACzBG,EAAY,IAAIC,EAAUH,EAAI,GAAIA,CAAG,EAG3C,GAAIC,EAAI,WAAa,oBAAsBF,EAAQ,SAAW,MAAO,CACnE,IAAMK,EAAgB,IAAIC,GAAoBL,EAAI,EAAE,EAC9C,CAACM,EAASC,EAAeC,EAAcC,EAAUC,CAAmB,EAAI,MAAM,QAAQ,IAAI,CAC9FN,EAAc,IAAI,oBAAoB,EACtCA,EAAc,IAAI,0BAA0B,EAC5CA,EAAc,IAAI,yBAAyB,EAC3CF,EAAU,QAAQ,EAAE,KAAMS,GAAU,CAACA,CAAK,EAAE,MAAM,IAAM,EAAK,EAC7DP,EAAc,IAAI,sBAAsB,CAC1C,CAAC,EACD,OAAO,IAAI,SAAS,KAAK,UAAU,CACjC,mBAAoBE,EACpB,yBAA0BC,IAAkB,OAC5C,wBAAyBC,IAAiB,OAC1C,iCAAkC,OAAOR,EAAI,gCAAgC,GAAK,EAClF,UAAWS,EACX,qBAAsBC,IAAwB,OAChD,CAAC,EAAG,CACF,QAAS,CACP,eAAgB,mBAChB,gBAAiBD,EAAW,oCAAsC,oBACpE,CACF,CAAC,CACH,CAGA,GAAIR,EAAI,WAAa,4BAA8BF,EAAQ,SAAW,MAAO,CAC3E,IAAMa,EAAWX,EAAI,aAAa,IAAI,UAAU,GAAK,GACrD,GAAI,CAACY,GAAe,KAAKD,CAAQ,EAC/B,OAAO,IAAI,SAAS,KAAK,UAAU,CAAE,MAAO,kBAAmB,CAAC,EAAG,CAAE,OAAQ,IAAK,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,EAErI,IAAME,EAAS,MAAMZ,EAAU,cAAcU,CAAQ,EACrD,OAAO,IAAI,SAAS,KAAK,UAAU,CAAE,OAAQ,CAAC,CAACE,CAAO,CAAC,EAAG,CACxD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CCjDAC,KAQAC,KACAC,KCwBO,IAAMC,GAAN,KAAuB,CAC5B,YAAoBC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAUpB,MAAM,OACJC,EACAC,EACAC,EACAC,EACAC,EACAC,EACe,CACf,GAAI,CACF,IAAMC,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACxC,MAAM,KAAK,GACR,QACC,yIACF,EACC,KACCN,EACAC,EACAC,EACAC,GAAa,KACbG,EACAF,EAAQ,KAAK,UAAUA,CAAK,EAAI,KAChCC,GAAiB,IACnB,EACC,IAAI,CACT,OAASE,EAAU,CACjB,QAAQ,MAAM,mCAAmCN,CAAM,oBAAoBD,CAAM,IAAKO,GAAK,SAAWA,CAAG,CAC3G,CACF,CAQA,MAAM,WACJP,EACAQ,EAAgB,GAChBC,EAC8B,CAC9B,IAAMC,EAAe,KAAK,IAAIF,EAAO,EAAE,EAEvC,GAAIC,EAAQ,CACV,GAAM,CAAE,QAAAE,CAAQ,EAAI,MAAM,KAAK,GAC5B,QACC,qGACF,EACC,KAAKX,EAAQS,EAAQC,CAAY,EACjC,IAAuB,EAC1B,OAAOC,CACT,CAEA,GAAM,CAAE,QAAAA,CAAQ,EAAI,MAAM,KAAK,GAC5B,QACC,mFACF,EACC,KAAKX,EAAQU,CAAY,EACzB,IAAuB,EAC1B,OAAOC,CACT,CACF,EClGA,IAAMC,GAAkB,mCAKxB,SAASC,GAAaC,EAA2B,CAC/C,IAAIC,EAAS,GACTC,EAAS,EACTC,EAAW,EAEf,QAAWC,KAAQJ,EAGjB,IAFAE,EAAUA,GAAU,EAAKE,EACzBD,GAAY,EACLA,GAAY,GACjBA,GAAY,EACZF,GAAUH,GAAiBI,GAAUC,EAAY,EAAI,EAIzD,OAAIA,EAAW,IACbF,GAAUH,GAAiBI,GAAW,EAAIC,EAAa,EAAI,GAGtDF,CACT,CAKA,SAASI,GAAaC,EAA2B,CAC/C,IAAIC,EAAMD,EAAM,OAChB,KAAOC,EAAM,GAAKD,EAAMC,EAAM,CAAC,IAAM,KACnCA,IAEF,IAAMC,EAAaF,EAAM,MAAM,EAAGC,CAAG,EAAE,YAAY,EAC7CP,EAAkB,CAAC,EACrBE,EAAS,EACTC,EAAW,EAEf,QAAWM,KAAQD,EAAY,CAC7B,IAAME,EAAMZ,GAAgB,QAAQW,CAAI,EACpCC,IAAQ,KACZR,EAAUA,GAAU,EAAKQ,EACzBP,GAAY,EACRA,GAAY,IACdA,GAAY,EACZH,EAAM,KAAME,GAAUC,EAAY,GAAI,GAE1C,CAEA,OAAO,IAAI,WAAWH,CAAK,CAC7B,CAMO,SAASW,IAA6B,CAC3C,IAAMX,EAAQ,IAAI,WAAW,EAAE,EAC/B,cAAO,gBAAgBA,CAAK,EACrBD,GAAaC,CAAK,CAC3B,CAQA,eAAeY,GAAYC,EAAgBC,EAAkC,CAC3E,IAAMC,EAAWV,GAAaQ,CAAM,EAC9BG,EAAe,IAAI,WAAW,CAAC,EAE/BC,EAAO,IAAI,SAASD,EAAa,MAAM,EAC7CC,EAAK,UAAU,EAAGH,IAAY,EAAG,EAAK,EACtCG,EAAK,UAAU,EAAG,KAAK,MAAMH,EAAU,UAAW,IAAM,EAAG,EAAK,EAEhE,IAAMI,EAAY,MAAM,OAAO,OAAO,UACpC,MACAH,EACA,CAAE,KAAM,OAAQ,KAAM,OAAQ,EAC9B,GACA,CAAC,MAAM,CACT,EAEMI,EAAY,MAAM,OAAO,OAAO,KAAK,OAAQD,EAAWF,CAAY,EACpEI,EAAO,IAAI,WAAWD,CAAS,EAG/BE,EAASD,EAAKA,EAAK,OAAS,CAAC,EAAI,GAOvC,SALIA,EAAKC,CAAM,EAAI,MAAS,IACxBD,EAAKC,EAAS,CAAC,EAAI,MAAS,IAC5BD,EAAKC,EAAS,CAAC,EAAI,MAAS,EAC7BD,EAAKC,EAAS,CAAC,EAAI,KAEP,KAAW,SAAS,EAAE,SAAS,EAAG,GAAG,CACtD,CAqBA,eAAsBC,GACpBT,EACAU,EACAC,EACiC,CACjC,GAAI,CAACX,GAAU,CAACU,EAAO,MAAO,CAAE,MAAO,EAAM,EAC7C,GAAI,CAACC,GAAQ,CAACC,GAAiB,KAAKF,CAAK,EAAG,MAAO,CAAE,MAAO,EAAM,EAElE,GAAI,CACF,IAAMG,EAAW,KAAK,MAAM,KAAK,IAAI,EAAI,IAAO,EAAE,EAGlD,QAAWC,IAAS,CAAC,GAAI,EAAG,CAAC,EAAG,CAC9B,IAAMC,EAAOF,EAAWC,EAClBE,EAAW,MAAMjB,GAAYC,EAAQe,CAAI,EAE/C,GAAIJ,EAAM,CACR,IAAMM,EAAO,IAAI,YAAY,EAAE,OAAOD,EAAWL,CAAI,EAC/CO,EAAa,MAAM,OAAO,OAAO,OAAO,UAAWD,CAAI,EAI7D,GAHgB,MAAM,KAAK,IAAI,WAAWC,CAAU,CAAC,EAClD,IAAKC,GAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAC1C,KAAK,EAAE,IACMT,EAAO,MAAO,CAAE,MAAO,GAAM,SAAUK,CAAK,CAC9D,SACMC,IAAaN,EAAO,MAAO,CAAE,MAAO,GAAM,SAAUK,CAAK,CAEjE,CACF,OAASK,EAAG,CACV,eAAQ,MAAM,6BAA8BA,CAAC,EACtC,CAAE,MAAO,EAAM,CACxB,CAEA,MAAO,CAAE,MAAO,EAAM,CACxB,CASA,eAAsBC,GAAWrB,EAAgBU,EAAeC,EAAiC,CAE/F,OADe,MAAMF,GAAmBT,EAAQU,EAAOC,CAAI,GAC7C,KAChB,CASO,SAASW,GAAWtB,EAAgBuB,EAAkBC,EAAwB,CACnF,IAAMC,EAAQ,mBAAmB,GAAGD,CAAM,IAAID,CAAQ,EAAE,EAClDG,EAAS,IAAI,gBAAgB,CACjC,OAAA1B,EACA,OAAAwB,EACA,UAAW,OACX,OAAQ,IACR,OAAQ,IACV,CAAC,EACD,MAAO,kBAAkBC,CAAK,IAAIC,EAAO,SAAS,CAAC,EACrD,CAQO,SAASC,IAAgC,CAC9C,OAAa,CACX,IAAMC,EAAc,IAAI,YAAY,CAAC,EACrC,OAAO,gBAAgBA,CAAW,EAClC,IAAM/B,EAAM+B,EAAY,CAAC,EAAI,IACvBC,EAAMhC,EAAM,GAClB,GAAIgC,EAAM,GAAI,CACZ,IAAMC,EAASjC,EAAI,SAAS,EAAE,SAAS,EAAG,GAAG,EACvCkC,EAAaF,EAAI,SAAS,EAChC,MAAO,GAAGC,CAAM,GAAGC,CAAU,EAC/B,CACF,CACF,CAQO,SAASC,IAA8B,CAC5C,IAAMC,EAAmB,CAAC,EAC1B,QAASC,EAAI,EAAGA,EAAI,EAAGA,IACrBD,EAAO,KAAKN,GAAsB,CAAC,EAErC,OAAOM,EAAO,KAAK,GAAG,CACxB,CAQO,SAASE,IAAiC,CAC/C,MAAO,CAACH,GAAoB,CAAC,CAC/B,CASA,eAAsBI,GAAgBC,EAA8B,CAClE,IAAM1C,EAAa0C,EAAI,QAAQ,SAAU,EAAE,EAAE,YAAY,EAAE,KAAK,EAC1DpB,EAAO,IAAI,YAAY,EAAE,OAAOtB,CAAU,EAC1C2C,EAAO,MAAM,OAAO,OAAO,OAAO,UAAWrB,CAAI,EACvD,OAAO,MAAM,KAAK,IAAI,WAAWqB,CAAI,CAAC,EACnC,IAAInB,GAAKA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EACxC,KAAK,EAAE,CACZ,CAkBA,eAAsBoB,GAAwBF,EAAaG,EAAqC,CAC9F,IAAMC,EAAY,MAAML,GAAgBC,CAAG,EACrCK,EAAkBL,EAAI,QAAQ,SAAU,EAAE,EAAE,YAAY,EAAE,KAAK,EAErE,OAAOG,EAAY,UAAUG,GAAQ,CACnC,GAAI,CAACA,EAAM,MAAO,GAClB,GAAI,OAAOA,GAAS,SAClB,OAAIA,IAASF,EAAkB,GACRE,EAAK,QAAQ,SAAU,EAAE,EAAE,YAAY,EAAE,KAAK,IAC3CD,EACrB,GAAI,OAAOC,GAAS,SAAU,CACnC,GAAIA,EAAK,MAAQA,EAAK,OAASF,EAAW,MAAO,GACjD,GAAIE,EAAK,IAEP,OADuB,OAAOA,EAAK,GAAG,EAAE,QAAQ,SAAU,EAAE,EAAE,YAAY,EAAE,KAAK,IACvDD,CAE9B,CACA,MAAO,EACT,CAAC,CACH,CF9QAE,KGbA,eAAsBC,GAAgBC,EAAeC,EAAgBC,EAA8B,CACjG,GAAI,CAACF,GAAS,CAACC,EAAQ,MAAO,GAC9B,GAAI,CACF,IAAME,EAAS,IAAI,gBACnB,OAAAA,EAAO,OAAO,SAAUF,CAAM,EAC9BE,EAAO,OAAO,WAAYH,CAAK,EAC/BG,EAAO,OAAO,WAAYD,CAAE,EAUrB,CAAC,EADQ,MARD,MAAM,MAAM,4DAA6D,CACtF,OAAQ,OACR,KAAMC,EACN,QAAS,CACP,eAAgB,mCAClB,EACA,OAAQ,YAAY,QAAQ,GAAI,CAClC,CAAC,GAC4B,KAAK,GACjB,OACnB,MAAY,CACV,MAAO,EACT,CACF,CHAA,eAAsBC,GAA0BC,EAAkBC,EAA6B,CAC7F,IAAMC,EAAY,IAAIC,EAAUF,EAAI,GAAIA,CAAG,EACrCG,EAAc,IAAIC,GAAiBJ,EAAI,EAAE,EACzCK,EAAQ,OAAO,OAAW,IAAe,OAAe,QAAU,KAClEC,EAAWP,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDQ,EAAYR,EAAQ,QAAQ,IAAI,YAAY,EAElD,GAAIA,EAAQ,QAAQ,IAAI,mBAAmB,IAAM,QAAUA,EAAQ,QAAQ,IAAI,0BAA0B,IAAM,OAC7G,OAAO,IAAI,SAAS,kBAAmB,CAAE,OAAQ,GAAI,CAAC,EAGxD,GAAI,MAAMS,EAAW,cAAcH,EAAO,UAAUC,CAAQ,GAAI,GAAI,EAAE,EACpE,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAG1D,GAAM,CAAE,SAAAG,EAAU,SAAAC,EAAU,eAAAC,CAAe,EAAI,MAAMZ,EAAQ,KAAK,EAC5Da,EAAgB,IAAIC,GAAoBb,EAAI,EAAE,EAC9C,CAACc,EAAWC,EAASC,EAAqBC,CAAS,EAAI,MAAM,QAAQ,IAAI,CAC7EL,EAAc,IAAI,sBAAsB,EACxCA,EAAc,IAAI,0BAA0B,EAC5CA,EAAc,IAAI,sBAAsB,EACxCX,EAAU,QAAQ,CACpB,CAAC,EAGD,GAAI,CAACgB,GAAaD,IAAwB,QACxC,OAAO,IAAI,SAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAG9D,GAAID,IAAY,QAAUD,GACpB,CAAC,MAAMI,GAAgBP,EAAgBG,EAAWR,CAAQ,EAC5D,OAAO,IAAI,SAAS,sBAAuB,CAAE,OAAQ,GAAI,CAAC,EAI9D,GAAI,CAACa,GAAe,KAAKV,CAAQ,EAAG,OAAO,IAAI,SAAS,mBAAoB,CAAE,OAAQ,GAAI,CAAC,EAC3F,GAAI,CAACC,GAAY,CAACU,GAAe,KAAKV,CAAQ,EAC5C,OAAO,IAAI,SAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAG9D,GAAI,MAAMT,EAAU,cAAcQ,CAAQ,EACxC,OAAO,IAAI,SAAS,kBAAmB,CAAE,OAAQ,GAAI,CAAC,EAIxD,GAAM,CAAE,SAAAY,EAAU,UAAAC,CAAU,EAAIC,GAAsBxB,CAAO,EAC7D,GAAIsB,IAAa,MAAQC,IAAc,KACrC,OAAO,IAAI,SAAS,sBAAuB,CAAE,OAAQ,GAAI,CAAC,EAI5D,IAAME,EAAYC,GAAmBzB,EAAI,UAAU,EACnD,GAAIwB,IAAc,UAChB,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,EAE3D,GAAIA,IAAc,SAChB,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAI1D,IAAIE,EACJ,GAAI,CACFA,EAAS,MAAMC,GAAgB3B,EAAI,UAAW,CAChD,MAAQ,CACN,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,CAC3D,CAEA,IAAM4B,EAAiB,MAAMC,GAAanB,EAAU,CAAC,EAC/CoB,EAASC,EAAW,EAAE,EAEtBC,EADMjC,EAAgB,IACP,UAAYA,EAAQ,QAAQ,IAAI,aAAa,GAAK,KACjEkC,EAAOhB,EAAY,QAAU,OAE/BiB,EAAc,GAClB,GAAI,CACF,MAAMjC,EAAU,OAAO,CAAE,GAAI6B,EAAQ,SAAArB,EAAU,aAAcmB,EAAgB,KAAAK,EAAM,SAAAD,EAAU,gBAAiB,CAAE,CAAC,EACjHE,EAAc,GAGd,IAAMC,EAAe,IAAIC,EAAapC,EAAI,EAAE,EACtCqC,EAAmBN,EAAW,CAAC,EAC/BO,EAAuBL,IAAS,QAAU,EAAI,EACpD,MAAME,EAAa,OAAO,CACxB,GAAIE,EACJ,SAAUP,EACV,KAAM,UACN,SAAU,CACR,SAAU,CAAC,+CAA+C,EAC1D,IAAK,CAAE,QAAS,GAAM,cAAe,EAAK,EAC1C,mBAAoBQ,EACpB,iBAAkB,GAClB,eAAgB,QAChB,gBAAiB,CAAE,QAAS,GAAO,gBAAiB,oBAAqB,CAC3E,CACF,CAAC,EAGD,IAAMC,EAAcC,GAAoB,EAClCC,EAAkB,MAAMC,GAAgBH,CAAW,EACzD,MAAMtC,EAAU,mBAAmB6B,EAAQ,CAAC,CAAE,KAAMW,CAAgB,CAAC,CAAQ,EAE7E,GAAM,CAAE,QAAAE,EAAS,aAAAC,EAAa,EAAI,MAAMC,GAAc7C,EAAK8B,EAAQxB,EAAUC,EAAWc,EAAUC,EAAW,EAAK,EAC5GwB,GAAc,MAAMC,GAAoBJ,EAAQ,GAAIb,CAAM,EAChE,MAAM3B,EAAY,OAAO2B,EAAQ,SAAUxB,EAAUC,EAAW,OAAWuC,EAAW,EACtF,IAAME,EAAgBC,GAAyBL,GAAc5C,EAAK,EAAK,EACjEkD,GAAYnB,EAAW,EAAE,EACzBoB,GAAaC,GAAiBF,GAAWlD,EAAK,EAAK,EAEnDqD,EAAa,OAAOrD,EAAI,+BAA+B,GAAK,GAC5DsD,EAAc,MAAMC,GAAQ,CAChC,OAAQzB,EACR,KAAMG,EACN,UAAWU,EAAQ,GACnB,IAAK,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAAIU,EAAa,EACpD,EAAG3B,CAAM,EAEH8B,EAAU,IAAI,QACpB,OAAAA,EAAQ,OAAO,aAAcR,CAAa,EAC1CQ,EAAQ,OAAO,aAAcL,EAAU,EACvCK,EAAQ,OAAO,eAAgB,kBAAkB,EAC1C,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,GAAM,YAAAF,EAAa,YAAAf,CAAY,CAAC,EAAG,CAAE,QAAAiB,CAAQ,CAAC,CAC9F,OAASC,EAAQ,CACf,GAAIvB,EAEF,GAAI,CACF,MAAMlC,EAAI,GAAG,QAAQ,yCAAyC,EAAE,KAAK8B,CAAM,EAAE,IAAI,EACjF,MAAM9B,EAAI,GAAG,QAAQ,gCAAgC,EAAE,KAAK8B,CAAM,EAAE,IAAI,CAC1E,OAAS4B,EAAY,CACnB,QAAQ,MAAM,mDAAoDA,CAAU,CAC9E,CAEF,OAAO,IAAI,SAASD,EAAE,QAAS,CAAE,OAAQ,GAAI,CAAC,CAChD,CACF,CI3JAE,KAaAC,KACAC,KCbAC,KAEO,IAAMC,GAAN,KAAmB,CACxB,YAAoBC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAKpB,MAAM,WAAWC,EAAoC,CACnD,GAAM,CAAE,QAAAC,CAAQ,EAAI,MAAM,KAAK,GAAG,QAChC,mEACF,EAAE,KAAKD,CAAM,EAAE,IAAa,EAC5B,OAAOC,CACT,CAKA,MAAM,QAAQC,EAAYF,EAAyC,CACjE,OAAO,MAAM,KAAK,GAAG,QACnB,qDACF,EAAE,KAAKE,EAAIF,CAAM,EAAE,MAAe,CACpC,CAKA,MAAM,kBAAkBG,EAA+C,CACrE,OAAO,MAAM,KAAK,GAAG,QACnB,gDACF,EAAE,KAAKA,CAAY,EAAE,MAAe,CACtC,CAMA,MAAM,sBAAsBC,EAAoBJ,EAAyC,CACvF,IAAMK,EAAS,MAAM,KAAK,GAAG,QAC3B,4EACF,EAAE,KAAKD,EAAYA,EAAYJ,CAAM,EAAE,MAAe,EACtD,GAAIK,EAAQ,OAAOA,EAGnB,IAAMC,EAAe,MAAM,KAAK,WAAWN,CAAM,EAC3CO,EAAUH,EAAW,QAAQ,KAAM,EAAE,EAAE,QAAQ,MAAO,GAAG,EAAE,QAAQ,MAAO,GAAG,EACnF,OAAOE,EAAa,KAAME,GAAM,CAC9B,IAAMC,EAASD,EAAE,cAAc,QAAQ,KAAM,EAAE,EAAE,QAAQ,MAAO,GAAG,EAAE,QAAQ,MAAO,GAAG,EACvF,OAAOA,EAAE,KAAOJ,GAAcK,IAAWF,CAC3C,CAAC,GAAK,IACR,CAKA,MAAM,OAAOG,EASQ,CACnB,IAAMR,EAAKS,EAAW,EAAE,EAClBC,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAClCC,EAAiBH,EAAK,WAAa,KAAK,UAAUA,EAAK,UAAU,EAAI,KAE3E,aAAM,KAAK,GAAG,QACZ;AAAA,gDAEF,EAAE,KACAR,EACAQ,EAAK,OACLA,EAAK,KACLA,EAAK,aACLA,EAAK,UACLA,EAAK,UACLA,EAAK,UACLG,EACAH,EAAK,QAAU,KACfE,EACA,IACF,EAAE,IAAI,EAEC,CACL,GAAAV,EACA,QAASQ,EAAK,OACd,KAAMA,EAAK,KACX,cAAeA,EAAK,aACpB,WAAYA,EAAK,UACjB,UAAWA,EAAK,UAChB,WAAYA,EAAK,UACjB,WAAYG,EACZ,OAAQH,EAAK,QAAU,KACvB,WAAYE,EACZ,aAAc,IAChB,CACF,CAKA,MAAM,WAAWV,EAAYF,EAAgBc,EAAgC,CAI3E,OAHY,MAAM,KAAK,GAAG,QACxB,2DACF,EAAE,KAAKA,EAAMZ,EAAIF,CAAM,EAAE,IAAI,GAClB,OACb,CAKA,MAAM,YAAYE,EAAYa,EAAqC,CACjE,IAAMH,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAIxC,OAHY,MAAM,KAAK,GAAG,QACxB,mEACF,EAAE,KAAKG,EAAWH,EAAKV,CAAE,EAAE,IAAI,GACpB,OACb,CAKA,MAAM,OAAOA,EAAYF,EAAkC,CAIzD,OAHY,MAAM,KAAK,GAAG,QACxB,mDACF,EAAE,KAAKE,EAAIF,CAAM,EAAE,IAAI,GACZ,OACb,CAKA,MAAM,YAAYA,EAAiC,CAIjD,OAHY,MAAM,KAAK,GAAG,QACxB,0DACF,EAAE,KAAKA,CAAM,EAAE,MAAyB,IAC5B,OAAS,CACvB,CACF,ED1HAgB,KEfO,SAASC,GAAgBC,EAA0C,CACxE,IAAMC,EAAQD,aAAkB,WAAaA,EAAS,IAAI,WAAWA,CAAM,EACvEE,EAAS,GACb,QAASC,EAAI,EAAGA,EAAIF,EAAM,OAAQE,IAChCD,GAAU,OAAO,aAAaD,EAAME,CAAC,CAAC,EAExC,OAAO,KAAKD,CAAM,EACf,QAAQ,MAAO,GAAG,EAClB,QAAQ,MAAO,GAAG,EAClB,QAAQ,MAAO,EAAE,CACtB,CAQO,SAASE,GAAgBC,EAAyB,CACvD,IAAIC,EAASD,EAAI,QAAQ,KAAM,GAAG,EAAE,QAAQ,KAAM,GAAG,EACrD,KAAOC,EAAO,OAAS,IAAM,GAC3BA,GAAU,IAEZ,IAAMJ,EAAS,KAAKI,CAAM,EACpBL,EAAQ,IAAI,WAAWC,EAAO,MAAM,EAC1C,QAASC,EAAI,EAAGA,EAAID,EAAO,OAAQC,IACjCF,EAAME,CAAC,EAAID,EAAO,WAAWC,CAAC,EAEhC,OAAOF,CACT,CAOO,SAASM,IAAoC,CAClD,IAAMN,EAAQ,IAAI,WAAW,EAAE,EAC/B,cAAO,gBAAgBA,CAAK,EACrBF,GAAgBE,CAAK,CAC9B,CASO,SAASO,GAAWC,EAA6B,CACtD,GAAIA,EAAI,CAAC,IAAM,GAAM,CACnB,GAAIA,EAAI,SAAW,GAAI,OAAOA,EAC9B,MAAM,IAAI,MAAM,kDAAkD,CACpE,CAEA,IAAIC,EAAS,EAMb,GALID,EAAI,CAAC,EAAI,MACXC,EAAS,GAAKD,EAAI,CAAC,EAAI,MAIrBA,EAAIC,GAAQ,IAAM,EAAM,MAAM,IAAI,MAAM,qCAAqC,EACjF,IAAMC,EAAOF,EAAIC,GAAQ,EACrB,EAAID,EAAI,SAASC,EAAQA,EAASC,CAAI,EAI1C,GAHAD,GAAUC,EAGNF,EAAIC,GAAQ,IAAM,EAAM,MAAM,IAAI,MAAM,qCAAqC,EACjF,IAAME,EAAOH,EAAIC,GAAQ,EACrBG,EAAIJ,EAAI,SAASC,EAAQA,EAASE,CAAI,EAG1C,KAAO,EAAE,OAAS,IAAM,EAAE,CAAC,IAAM,GAAM,EAAI,EAAE,SAAS,CAAC,EACvD,KAAOC,EAAE,OAAS,IAAMA,EAAE,CAAC,IAAM,GAAMA,EAAIA,EAAE,SAAS,CAAC,EAEvD,IAAMC,EAAQ,IAAI,WAAW,EAAE,EAC/B,OAAAA,EAAM,IAAI,EAAG,GAAK,EAAE,MAAM,EAC1BA,EAAM,IAAID,EAAG,GAAKA,EAAE,MAAM,EACnBC,CACT,CCzDO,SAASC,GAAWC,EAAoC,CAC7D,IAAIC,EAAS,EAEb,SAASC,EAASC,EAA+B,CAC/C,GAAIA,EAAO,GAAI,OAAOA,EACtB,GAAIA,IAAS,GAAI,OAAOH,EAAKC,GAAQ,EACrC,GAAIE,IAAS,GAAI,CACf,IAAMC,EAAOJ,EAAKC,CAAM,GAAK,EAAKD,EAAKC,EAAS,CAAC,EACjD,OAAAA,GAAU,EACHG,CACT,CACA,GAAID,IAAS,GAAI,CACf,IAAMC,GACFJ,EAAKC,CAAM,GAAK,KAAQ,IACzBD,EAAKC,EAAS,CAAC,GAAK,KACpBD,EAAKC,EAAS,CAAC,GAAK,GACrBD,EAAKC,EAAS,CAAC,EACjB,OAAAA,GAAU,EACHG,CACT,CACA,GAAID,IAAS,GAAI,CACf,IAAME,EAAK,QACPL,EAAKC,CAAM,GAAK,KAAQ,IACvBD,EAAKC,EAAS,CAAC,GAAK,KACpBD,EAAKC,EAAS,CAAC,GAAK,GACrBD,EAAKC,EAAS,CAAC,CACnB,EACMK,EAAK,QACPN,EAAKC,EAAS,CAAC,GAAK,KAAQ,IAC3BD,EAAKC,EAAS,CAAC,GAAK,KACpBD,EAAKC,EAAS,CAAC,GAAK,GACrBD,EAAKC,EAAS,CAAC,CACnB,EACA,OAAAA,GAAU,EACFI,GAAM,IAAOC,CACvB,CACA,MAAM,IAAI,MAAM,+BAA+BH,CAAI,EAAE,CACvD,CAEA,SAASI,GAAiB,CACxB,GAAIN,GAAUD,EAAK,OAAQ,MAAM,IAAI,MAAM,6BAA6B,EACxE,IAAMQ,EAAcR,EAAKC,GAAQ,EAC3BQ,EAAYD,GAAe,EAC3BL,EAAOK,EAAc,GAE3B,OAAQC,EAAW,CACjB,IAAK,GAAG,CAEN,IAAML,EAAMF,EAASC,CAAI,EACzB,OAAO,OAAOC,GAAQ,SAAW,OAAOA,CAAG,EAAIA,CACjD,CACA,IAAK,GAAG,CAEN,IAAMA,EAAMF,EAASC,CAAI,EACzB,OAAO,OAAOC,GAAQ,SAAW,OAAO,CAAC,GAAKA,CAAG,EAAI,GAAK,OAAOA,CAAG,CACtE,CACA,IAAK,GAAG,CAEN,IAAMM,EAAM,OAAOR,EAASC,CAAI,CAAC,EAC3BQ,EAAQX,EAAK,SAASC,EAAQA,EAASS,CAAG,EAChD,OAAAT,GAAUS,EACHC,CACT,CACA,IAAK,GAAG,CAEN,IAAMD,EAAM,OAAOR,EAASC,CAAI,CAAC,EAC3BQ,EAAQX,EAAK,SAASC,EAAQA,EAASS,CAAG,EAChD,OAAAT,GAAUS,EACH,IAAI,YAAY,EAAE,OAAOC,CAAK,CACvC,CACA,IAAK,GAAG,CAEN,IAAMD,EAAM,OAAOR,EAASC,CAAI,CAAC,EAC3BS,EAAa,CAAC,EACpB,QAASC,EAAI,EAAGA,EAAIH,EAAKG,IACvBD,EAAI,KAAKL,EAAU,CAAC,EAEtB,OAAOK,CACT,CACA,IAAK,GAAG,CAEN,IAAMF,EAAM,OAAOR,EAASC,CAAI,CAAC,EAC3BW,EAAoC,CAAC,EAC3C,QAASD,EAAI,EAAGA,EAAIH,EAAKG,IAAK,CAC5B,IAAME,EAAMR,EAAU,EAChBH,EAAMG,EAAU,EACtBO,EAAIC,CAAG,EAAIX,CACb,CACA,OAAOU,CACT,CACA,IAAK,GAEH,OAAIX,IAAS,GAAW,GACpBA,IAAS,GAAW,GACpBA,IAAS,GAAW,KACxB,OAEF,QACE,MAAM,IAAI,MAAM,gCAAgCM,CAAS,EAAE,CAC/D,CACF,CAGA,MAAO,CAAE,MADKF,EAAU,EACR,UAAWN,CAAO,CACpC,CCzHA,eAAsBe,GACpBC,EAC4B,CAC5B,GAAIA,EAAS,OAAS,GACpB,MAAM,IAAI,MAAM,kCAAkC,EAGpD,IAAMC,EAAWD,EAAS,SAAS,EAAG,EAAE,EAClCE,EAAQF,EAAS,EAAE,EACnBG,GACFH,EAAS,EAAE,GAAK,KAAQ,IACzBA,EAAS,EAAE,GAAK,KAChBA,EAAS,EAAE,GAAK,GACjBA,EAAS,EAAE,EAIb,GAAI,CADwB,GAAQE,EAAQ,IAE1C,MAAM,IAAI,MAAM,uDAAuD,EAGzE,IAAIE,EAAS,GACPC,EAAcL,EAAS,SAASI,EAAQA,EAAS,EAAE,EACzDA,GAAU,GACV,IAAME,EAAS,MAAM,KAAKD,CAAW,EAClC,IAAKE,GAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAC1C,KAAK,EAAE,EAEJC,EAAaR,EAASI,CAAM,GAAK,EAAKJ,EAASI,EAAS,CAAC,EAC/DA,GAAU,EAEV,IAAMK,EAAcT,EAAS,SAASI,EAAQA,EAASI,CAAS,EAChEJ,GAAUI,EACV,IAAME,EAAeC,GAAgBF,CAAW,EAG1CG,EAAeZ,EAAS,SAASI,CAAM,EACvC,CAAE,MAAOS,CAAQ,EAAIC,GAAWF,CAAY,EAG5CG,EAAMF,EAAQ,CAAC,GAAK,GACtBG,EAAgB,GAEpB,GAAID,IAAQ,GAAI,CAEd,IAAME,EAAIJ,EAAQ,EAAE,EACd,EAAIA,EAAQ,EAAE,EACpB,GAAI,CAACI,GAAK,CAAC,GAAKA,EAAE,SAAW,IAAM,EAAE,SAAW,GAC9C,MAAM,IAAI,MAAM,wCAAwC,EAG1D,IAAMC,EAAoB,IAAI,WAAW,EAAE,EAC3CA,EAAkB,CAAC,EAAI,EACvBA,EAAkB,IAAID,EAAG,CAAC,EAC1BC,EAAkB,IAAI,EAAG,EAAE,EAE3B,IAAMC,EAAY,MAAM,OAAO,OAAO,UACpC,MACAD,EACA,CAAE,KAAM,QAAS,WAAY,OAAQ,EACrC,GACA,CAAC,QAAQ,CACX,EAEME,EAAc,MAAM,OAAO,OAAO,UAAU,OAAQD,CAAS,EACnEH,EAAgBL,GAAgBS,CAAU,CAC5C,SAAWL,IAAQ,KAAM,CAEvB,IAAMM,EAAIR,EAAQ,EAAE,EACdS,EAAIT,EAAQ,EAAE,EACpB,GAAI,CAACQ,GAAK,CAACC,EACT,MAAM,IAAI,MAAM,wCAAwC,EAG1D,IAAMC,EAAM,CACV,IAAK,MACL,EAAGZ,GAAgBU,CAAC,EACpB,EAAGV,GAAgBW,CAAC,EACpB,IAAK,QACL,IAAK,EACP,EAEMH,EAAY,MAAM,OAAO,OAAO,UACpC,MACAI,EACA,CAAE,KAAM,oBAAqB,KAAM,SAAU,EAC7C,GACA,CAAC,QAAQ,CACX,EAEMH,EAAc,MAAM,OAAO,OAAO,UAAU,OAAQD,CAAS,EACnEH,EAAgBL,GAAgBS,CAAU,CAC5C,KACE,OAAM,IAAI,MAAM,+BAA+BL,CAAG,EAAE,EAGtD,MAAO,CACL,SAAAd,EACA,MAAAC,EACA,UAAAC,EACA,OAAAG,EACA,aAAAI,EACA,cAAAM,EACA,UAAWD,CACb,CACF,CAQA,eAAsBS,GACpBC,EAC4B,CAC5B,GAAM,CACJ,eAAAC,EACA,kBAAAC,EACA,kBAAAC,EACA,eAAAC,EACA,aAAAC,CACF,EAAIL,EAGEM,EAAkBC,GAAgBN,CAAc,EAChDO,EAAa,KAAK,MAAM,IAAI,YAAY,EAAE,OAAOF,CAAe,CAAC,EAEvE,GAAIE,EAAW,OAAS,kBACtB,MAAM,IAAI,MAAM,4BAA4BA,EAAW,IAAI,EAAE,EAG/D,GAAIA,EAAW,YAAcL,EAC3B,MAAM,IAAI,MAAM,6BAA6B,EAG/C,IAAMM,EAAkB,IAAI,IAAID,EAAW,MAAM,EAC3CE,EAAoBN,EAAe,SAAS,KAAK,EACnD,IAAI,IAAIA,CAAc,EACtB,IAAI,IAAI,WAAWA,CAAc,EAAE,EAEvC,GAAIK,EAAgB,OAAO,YAAY,IAAMC,EAAkB,OAAO,YAAY,EAChF,MAAM,IAAI,MACR,sCAAsCA,EAAkB,MAAM,cAAcD,EAAgB,MAAM,EACpG,EAIF,GAAI,EADgBA,EAAgB,WAAa,aAAeA,EAAgB,WAAa,cACzEA,EAAgB,WAAa,SAC/C,MAAM,IAAI,MAAM,6CAA6CA,EAAgB,QAAQ,EAAE,EAIzF,IAAME,EAAmBJ,GAAgBL,CAAiB,EACpD,CAAE,MAAOU,CAAY,EAAIvB,GAAWsB,CAAgB,EAE1D,GAAI,CAACC,EAAY,SACf,MAAM,IAAI,MAAM,uCAAuC,EAGzD,IAAMC,EAAS,MAAMvC,GAAyBsC,EAAY,QAAQ,EAG5DE,EAAmB,IAAI,WAC3B,MAAM,OAAO,OAAO,OAAO,UAAW,IAAI,YAAY,EAAE,OAAOT,CAAY,CAAC,CAC9E,EACA,QAASU,EAAI,EAAGA,EAAI,GAAIA,IACtB,GAAIF,EAAO,SAASE,CAAC,IAAMD,EAAiBC,CAAC,EAC3C,MAAM,IAAI,MAAM,4BAA4B,EAKhD,GAAI,EAAEF,EAAO,MAAQ,GACnB,MAAM,IAAI,MAAM,iCAAiC,EAGnD,OAAOA,CACT,CC9KA,eAAsBG,GACpBC,EACgC,CAChC,GAAM,CACJ,eAAAC,EACA,kBAAAC,EACA,UAAAC,EACA,cAAAC,EACA,UAAAC,EACA,kBAAAC,EACA,eAAAC,EACA,aAAAC,EACA,kBAAAC,EAAoB,CACtB,EAAIT,EAGEU,EAAkBC,GAAgBV,CAAc,EAChDW,EAAa,KAAK,MAAM,IAAI,YAAY,EAAE,OAAOF,CAAe,CAAC,EAEvE,GAAIE,EAAW,OAAS,eACtB,MAAM,IAAI,MAAM,4BAA4BA,EAAW,IAAI,EAAE,EAG/D,GAAIA,EAAW,YAAcN,EAC3B,MAAM,IAAI,MAAM,6BAA6B,EAG/C,IAAMO,EAAkB,IAAI,IAAID,EAAW,MAAM,EAC3CE,EAAoBP,EAAe,SAAS,KAAK,EACnD,IAAI,IAAIA,CAAc,EACtB,IAAI,IAAI,WAAWA,CAAc,EAAE,EAEvC,GAAIM,EAAgB,OAAO,YAAY,IAAMC,EAAkB,OAAO,YAAY,EAChF,MAAM,IAAI,MACR,sCAAsCA,EAAkB,MAAM,cAAcD,EAAgB,MAAM,EACpG,EAIF,GAAI,EADgBA,EAAgB,WAAa,aAAeA,EAAgB,WAAa,cACzEA,EAAgB,WAAa,SAC/C,MAAM,IAAI,MAAM,6CAA6CA,EAAgB,QAAQ,EAAE,EAIzF,IAAME,EAAgBJ,GAAgBT,CAAiB,EACvD,GAAIa,EAAc,OAAS,GACzB,MAAM,IAAI,MAAM,kCAAkC,EAGpD,IAAMC,EAAWD,EAAc,SAAS,EAAG,EAAE,EACvCE,EAAQF,EAAc,EAAE,EACxBG,GACFH,EAAc,EAAE,GAAK,KAAQ,IAC9BA,EAAc,EAAE,GAAK,KACrBA,EAAc,EAAE,GAAK,GACtBA,EAAc,EAAE,EAEZI,EAAmB,IAAI,WAC3B,MAAM,OAAO,OAAO,OAAO,UAAW,IAAI,YAAY,EAAE,OAAOX,CAAY,CAAC,CAC9E,EACA,QAASY,EAAI,EAAGA,EAAI,GAAIA,IACtB,GAAIJ,EAASI,CAAC,IAAMD,EAAiBC,CAAC,EACpC,MAAM,IAAI,MAAM,4BAA4B,EAKhD,GAAI,EAAEH,EAAQ,GACZ,MAAM,IAAI,MAAM,sDAAsD,EAIxE,GAAIC,EAAY,GAAKT,EAAoB,GAAKS,GAAaT,EACzD,MAAM,IAAI,MACR,6FACF,EAKF,IAAMY,EAAiB,IAAI,WACzB,MAAM,OAAO,OAAO,OAAO,UAAWX,CAAe,CACvD,EACMY,EAAa,IAAI,WAAWP,EAAc,OAASM,EAAe,MAAM,EAC9EC,EAAW,IAAIP,EAAe,CAAC,EAC/BO,EAAW,IAAID,EAAgBN,EAAc,MAAM,EAEnD,IAAMQ,EAAWZ,GAAgBR,CAAS,EACpCqB,EAAYb,GAAgBP,CAAa,EAE/C,GAAIC,IAAc,GAAI,CAEpB,IAAMoB,EAAY,MAAM,OAAO,OAAO,UACpC,OACAD,EACA,CAAE,KAAM,QAAS,WAAY,OAAQ,EACrC,GACA,CAAC,QAAQ,CACX,EAEME,EAASC,GAAWJ,CAAQ,EAQlC,GAAI,CAPU,MAAM,OAAO,OAAO,OAChC,CAAE,KAAM,QAAS,KAAM,SAAU,EACjCE,EACAC,EACAJ,CACF,EAGE,MAAM,IAAI,MAAM,wCAAwC,CAE5D,SAAWjB,IAAc,KAAM,CAE7B,IAAMoB,EAAY,MAAM,OAAO,OAAO,UACpC,OACAD,EACA,CAAE,KAAM,oBAAqB,KAAM,SAAU,EAC7C,GACA,CAAC,QAAQ,CACX,EASA,GAAI,CAPU,MAAM,OAAO,OAAO,OAChC,CAAE,KAAM,mBAAoB,EAC5BC,EACAF,EACAD,CACF,EAGE,MAAM,IAAI,MAAM,wCAAwC,CAE5D,KACE,OAAM,IAAI,MAAM,qCAAqCjB,CAAS,EAAE,EAGlE,MAAO,CAAE,UAAAa,CAAU,CACrB,CL3HA,eAAsBU,GAAmBC,EAAkBC,EAA6B,CACtF,IAAMC,EAAM,IAAI,IAAIF,EAAQ,GAAG,EACzBG,EAAY,IAAIC,EAAUH,EAAI,GAAIA,CAAG,EACrCI,EAAc,IAAIC,GAAiBL,EAAI,EAAE,EACzCM,EAAS,OAAe,QACxBC,EAAWR,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDS,EAAYT,EAAQ,QAAQ,IAAI,YAAY,EAGlD,GAAIE,EAAI,WAAa,sBAAwBF,EAAQ,SAAW,OAAQ,CACtE,GAAI,MAAMU,EAAW,cAAcH,EAAO,cAAcC,CAAQ,GAAI,GAAI,GAAG,EACzE,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAE1D,GAAM,CAAE,SAAAG,EAAU,eAAAC,CAAe,EAAI,MAAMZ,EAAQ,KAAK,EAElDa,EAAgB,IAAIC,GAAoBb,EAAI,EAAE,EAC9C,CAACc,EAAWC,CAAO,EAAI,MAAM,QAAQ,IAAI,CAC7CH,EAAc,IAAI,sBAAsB,EACxCA,EAAc,IAAI,yBAAyB,CAC7C,CAAC,EACD,GAAIG,IAAY,QAAUD,GACpB,CAAC,MAAME,GAAgBL,EAAgBG,EAAWP,CAAQ,EAC5D,OAAO,IAAI,SAAS,sBAAuB,CAAE,OAAQ,GAAI,CAAC,EAI9D,IAAMU,EAAO,MAAMf,EAAU,cAAcQ,CAAQ,EACnD,GAAI,CAACO,EAEH,OADgB,MAAMf,EAAU,QAAQ,EAAE,MAAM,IAAM,EAAK,EAElD,IAAI,SAAS,sBAAuB,CAAE,OAAQ,GAAI,CAAC,GAE5D,MAAMO,EAAW,cAAcH,EAAO,cAAcC,CAAQ,GAAI,IAAK,GAAG,EACjE,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,GAIvD,IAAMW,EAAe,MAAMC,GAAqBnB,EAAKiB,EAAK,EAAE,EACtDG,EAAgBC,GAAoBH,EAAclB,CAAG,EAErDsB,EAAoB,CAACL,EAAK,mBAC1BM,EAAgB,CAAC,CAACN,EAAK,aACvBO,EAAmBP,EAAK,kBAAoB,EAI5CQ,EAAW,MADI,IAAIC,GAAa1B,EAAI,EAAE,EACR,WAAWiB,EAAK,EAAE,EAChDU,EAAcF,EAAS,OAAS,EAClCG,EAAuB,KACvBC,EAAmC,KAEnCF,IACFE,EAAoBC,GAA0B,EAC9CF,EAAkB,CAChB,UAAWC,EACX,KAAM5B,EAAI,SACV,iBAAkBwB,EAAS,IAAIM,IAAM,CACnC,GAAIA,EAAE,cACN,KAAM,aACN,WAAYA,EAAE,WAAa,KAAK,MAAMA,EAAE,UAAU,EAAI,MACxD,EAAE,EACF,QAAS,IACT,iBAAkB,WACpB,GAIF,IAAMC,EAAQC,EAAW,EAAE,EACvBC,EAA4B,KAC5BV,IAAqB,GAAKP,EAAK,kBACjCiB,EAAaC,GAAelB,EAAK,eAAe,GAGlD,IAAMmB,EAAa,OAAOpC,EAAI,mBAAmB,GAAK,IACtD,aAAMS,EAAW,IAAIH,EAAO,iBAAiBY,CAAY,GAAI,CAC3D,MAAAc,EACA,eAAgB,EAChB,iBAAkBH,EAClB,KAAM5B,EAAI,QACZ,EAAGmC,CAAU,EAEN,IAAI,SAAS,KAAK,UAAU,CACjC,kBAAAd,EACA,cAAAC,EACA,YAAAI,EACA,gBAAAC,EACA,iBAAAJ,EACA,MAAAQ,EACA,WAAAE,CACF,CAAC,EAAG,CACF,QAAS,CACP,aAAcd,EACd,eAAgB,kBAClB,CACF,CAAC,CACH,CAGA,GAAInB,EAAI,WAAa,mBAAqBF,EAAQ,SAAW,OAAQ,CACnE,GAAIA,EAAQ,QAAQ,IAAI,mBAAmB,IAAM,QAAUA,EAAQ,QAAQ,IAAI,0BAA0B,IAAM,OAC7G,OAAO,IAAI,SAAS,kBAAmB,CAAE,OAAQ,GAAI,CAAC,EAExD,GAAI,MAAMU,EAAW,cAAcH,EAAO,cAAcC,CAAQ,GAAI,GAAI,GAAG,EACzE,OAAO,IAAI,SAAS,0BAA2B,CAAE,OAAQ,GAAI,CAAC,EAGhE,IAAMW,EAAemB,GAAkBtC,EAAQ,QAAQ,IAAI,QAAQ,CAAC,EACpE,GAAI,CAACmB,EAAc,OAAO,IAAI,SAAS,sCAAuC,CAAE,OAAQ,GAAI,CAAC,EAE7F,IAAMoB,EAAS,MAAMC,GAAuBvC,EAAKkB,CAAY,EAC7D,GAAI,CAACoB,EAAQ,OAAO,IAAI,SAAS,qCAAsC,CAAE,OAAQ,GAAI,CAAC,EAEtF,IAAME,EAAe,MAAM/B,EAAW,IAKnCH,EAAO,iBAAiBY,CAAY,EAAE,EACzC,GAAI,CAACsB,EACH,aAAMC,GAAyBzC,EAAKkB,CAAY,EACzC,IAAI,SAAS,qCAAsC,CAAE,OAAQ,GAAI,CAAC,EAE3E,GAAM,CAAE,MAAAc,CAAM,EAAIQ,EAEZvB,EAAO,MAAMf,EAAU,QAAQoC,CAAM,EAC3C,GAAI,CAACrB,EAAM,OAAO,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAEhE,GAAM,CAAE,SAAAyB,EAAU,cAAAC,EAAe,SAAAC,EAAU,YAAAC,EAAa,iBAAAC,EAAkB,aAAAC,CAAa,EAAI,MAAMhD,EAAQ,KAAK,EAE1GiD,EAAiB,GAGfC,EAAe,IAAIvB,GAAa1B,EAAI,EAAE,EACtCkD,EAAe,MAAMD,EAAa,WAAWX,CAAM,EACnDa,EAAcD,EAAa,OAAS,EACpCE,EAAU,CAAC,CAACnC,EAAK,aACjBoC,EAAkB,CAAC,CAACpC,EAAK,mBACzBqC,EAAcF,GAAWD,EAKzBI,EAAwBJ,GAAe,CAAC,CAACL,EAI/C,GAAI,EAHwB,CAAC,CAAC7B,EAAK,oBAAsBsC,GAG/B,CACxB,GAAI,CAACb,EACH,OAAO,IAAI,SAAS,uBAAwB,CAAE,OAAQ,GAAI,CAAC,EAE7D,IAAKzB,EAAK,kBAAoB,KAAO,EAAG,CAEtC,IAAMuC,EAAmB,MAAMC,GAAWxC,EAAK,gBAAiBe,CAAK,EACrE,GAAIU,IAAac,EAAkB,CACjC,MAAM/C,EAAW,cAAcH,EAAO,cAAcC,CAAQ,GAAI,IAAK,GAAG,EACxE,MAAMH,EAAY,OAAOkC,EAAQ,aAAc/B,EAAUC,EAAW,CAAE,OAAQ,gBAAiB,CAAC,EAChG,IAAMkD,EAAY,MAAMC,GAA2BrD,EAAOY,EAAclB,CAAG,EAC3E,OAAI0D,GAAa,EACR,IAAI,SAAS,mBAAoB,CAAE,OAAQ,GAAI,CAAC,EAEhD,IAAI,SAAS,qBAAqBA,CAAS,WAAWA,EAAY,EAAI,IAAM,EAAE,cAAe,CAAE,OAAQ,GAAI,CAAC,CAEvH,CACF,KAAO,CAGL,GAAI,CADkB,MAAME,GAAelB,EAAUzB,EAAK,gBAAiB,CAAC,EACxD,CAClB,MAAMR,EAAW,cAAcH,EAAO,cAAcC,CAAQ,GAAI,IAAK,GAAG,EACxE,MAAMH,EAAY,OAAOkC,EAAQ,aAAc/B,EAAUC,EAAW,CAAE,OAAQ,gBAAiB,CAAC,EAChG,IAAMkD,EAAY,MAAMC,GAA2BrD,EAAOY,EAAclB,CAAG,EAC3E,OAAI0D,GAAa,EACR,IAAI,SAAS,mBAAoB,CAAE,OAAQ,GAAI,CAAC,EAEhD,IAAI,SAAS,qBAAqBA,CAAS,WAAWA,EAAY,EAAI,IAAM,EAAE,cAAe,CAAE,OAAQ,GAAI,CAAC,CAEvH,CACAV,EAAiB,EACnB,CACF,CAEA,IAAIa,EAAgB,GAChBC,EAAoB,GACpBC,EAAmB,GACnBC,GAAoB,EACpBC,GAEJ,GAAIX,EACF,GAAIR,GAAoBN,EAAa,iBAAkB,CAErD,IAAM0B,EAAepB,EAAiB,GAChCqB,EAAUjB,EAAa,KAAKnB,GAAKA,EAAE,gBAAkBmC,CAAY,EACvE,GAAI,CAACC,EAAS,CACZ,MAAM/D,EAAY,OAAOkC,EAAQ,sBAAuB/B,EAAUC,EAAW,CAAE,OAAQ,sBAAuB,CAAC,EAC/G,IAAMkD,EAAY,MAAMC,GAA2BrD,EAAOY,EAAclB,CAAG,EAC3E,OAAI0D,GAAa,EACR,IAAI,SAAS,kBAAmB,CAAE,OAAQ,GAAI,CAAC,EAE/C,IAAI,SAAS,oBAAoBA,CAAS,WAAWA,EAAY,EAAI,IAAM,EAAE,cAAe,CAAE,OAAQ,GAAI,CAAC,CAEtH,CAEA,GAAI,CACF,GAAM,CAAE,UAAAU,CAAU,EAAI,MAAMC,GAA6B,CACvD,eAAgBvB,EAAiB,SAAS,eAC1C,kBAAmBA,EAAiB,SAAS,kBAC7C,UAAWA,EAAiB,SAAS,UACrC,cAAeqB,EAAQ,WACvB,UAAWA,EAAQ,UACnB,kBAAmB3B,EAAa,iBAChC,eAAgB,GAAGvC,EAAI,QAAQ,KAAKA,EAAI,IAAI,GAC5C,aAAcuC,EAAa,MAAQvC,EAAI,SACvC,kBAAmBkE,EAAQ,UAC7B,CAAC,EAED,MAAMlB,EAAa,YAAYkB,EAAQ,GAAIC,CAAS,EACpDL,EAAmB,EACrB,OAASO,EAAU,CACjB,QAAQ,KAAK,uCAAwCA,EAAI,SAAWA,CAAG,EACvE,MAAMlE,EAAY,OAAOkC,EAAQ,sBAAuB/B,EAAUC,EAAW,CAAE,OAAQ8D,EAAI,OAAQ,CAAC,EACpG,IAAMZ,EAAY,MAAMC,GAA2BrD,EAAOY,EAAclB,CAAG,EAC3E,OAAI0D,GAAa,EACR,IAAI,SAAS,4BAA6B,CAAE,OAAQ,GAAI,CAAC,EAEzD,IAAI,SAAS,8BAA8BA,CAAS,WAAWA,EAAY,EAAI,IAAM,EAAE,cAAe,CAAE,OAAQ,GAAI,CAAC,CAEhI,CACF,UAAYN,GAAWD,GAAeE,IAAoBR,EAAa,CACrE,IAAI0B,EAAsB,CAAC,EAC3B,GAAI,CACF,IAAMC,EAAMvD,EAAK,mBACjBsD,EAAe,OAAOC,GAAQ,SAAW,KAAK,MAAMA,GAAO,IAAI,EAAKA,GAAO,CAAC,EACvE,MAAM,QAAQD,CAAY,IAAGA,EAAe,CAACA,CAAY,EAChE,MAAQ,CAAE,CAEV,GADmB,MAAME,GAAwB5B,EAAa0B,CAAY,IACvD,GAAI,CACrB,MAAMnE,EAAY,OAAOkC,EAAQ,mBAAoB/B,EAAUC,EAAW,CAAE,OAAQ,cAAe,CAAC,EACpG,IAAMkD,EAAY,MAAMC,GAA2BrD,EAAOY,EAAclB,CAAG,EAC3E,OAAI0D,GAAa,EACR,IAAI,SAAS,uBAAwB,CAAE,OAAQ,GAAI,CAAC,EAEpD,IAAI,SAAS,yBAAyBA,CAAS,WAAWA,EAAY,EAAI,IAAM,EAAE,cAAe,CAAE,OAAQ,GAAI,CAAC,CAE3H,CAEA,IAAMgB,EAAUC,GAAoB,EAC9BC,EAAc,MAAMC,GAAgBH,CAAO,EACjD,MAAMxE,EAAU,mBAAmBoC,EAAQ,CAAC,CAAE,KAAMsC,CAAY,CAAC,CAAQ,EACzEX,GAAqBS,EACrBZ,EAAoB,GACpBE,GAAoB,CACtB,SAAWZ,GAAWT,EAAe,CACnC,IAAMmC,EAAa,MAAMC,GAAmB9D,EAAK,aAAe,GAAI0B,EAAeC,CAAQ,EAC3F,GAAI,CAACkC,EAAW,MAAO,CACrB,MAAM1E,EAAY,OAAOkC,EAAQ,mBAAoB/B,EAAUC,CAAS,EACxE,IAAMkD,EAAY,MAAMC,GAA2BrD,EAAOY,EAAclB,CAAG,EAC3E,OAAI0D,GAAa,EACR,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAEjD,IAAI,SAAS,sBAAsBA,CAAS,WAAWA,EAAY,EAAI,IAAM,EAAE,cAAe,CAAE,OAAQ,GAAI,CAAC,CAExH,CAGA,GAAIoB,EAAW,WAAa,OAAW,CACrC,IAAME,EAAY,aAAa1C,CAAM,IAAIwC,EAAW,QAAQ,GAC5D,GAAI,MAAMrE,EAAW,IAAIH,EAAO0E,CAAS,EACvC,aAAM5E,EAAY,OAAOkC,EAAQ,mBAAoB/B,EAAUC,EAAW,CAAE,OAAQ,gBAAiB,CAAC,EAC/F,IAAI,SAAS,4EAA6E,CAAE,OAAQ,GAAI,CAAC,EAElH,MAAMC,EAAW,IAAIH,EAAO0E,EAAW,EAAG,EAAE,CAC9C,CAEAnB,EAAgB,EAClB,KAAO,CACL,IAAMH,EAAY,MAAMC,GAA2BrD,EAAOY,EAAclB,CAAG,EAC3E,OAAI0D,GAAa,EACR,IAAI,SAAS,2BAA4B,CAAE,OAAQ,GAAI,CAAC,EAExD,IAAI,SAAS,mEAAmEA,CAAS,WAAWA,EAAY,EAAI,IAAM,EAAE,cAAe,CAAE,OAAQ,GAAI,CAAC,CAErK,CAIF,MAAMjB,GAAyBzC,EAAKkB,CAAY,EAChD,MAAMT,EAAW,OAAOH,EAAO,iBAAiBY,CAAY,EAAE,EAC9D,MAAMT,EAAW,OAAOH,EAAO,wBAAwBC,CAAQ,EAAE,EAEjE,GAAM,CAAE,SAAA0E,EAAU,UAAAC,EAAU,EAAIC,GAAsBpF,CAAO,EAC7D,GAAIkF,IAAa,MAAQC,KAAc,KACrC,OAAO,IAAI,SAAS,sBAAuB,CAAE,OAAQ,GAAI,CAAC,EAE5D,GAAM,CAAE,QAAAE,GAAS,aAAAC,CAAa,EAAI,MAAMC,GAActF,EAAKsC,EAAQ/B,EAAUC,EAAWyE,EAAUC,GAAW,CAAC,CAACnC,CAAY,EACrHwC,EAAc,MAAMC,GAAoBJ,GAAQ,GAAI9C,CAAM,EAE5DyB,EACF,MAAM3D,EAAY,OAAOkC,EAAQ,yBAA0B/B,EAAUC,EAAW,OAAW+E,CAAW,EAC7F1B,EACT,MAAMzD,EAAY,OAAOkC,EAAQ,sBAAuB/B,EAAUC,EAAW,OAAW+E,CAAW,EAC1FzB,GACT,MAAM1D,EAAY,OAAOkC,EAAQ,oBAAqB/B,EAAUC,EAAW,CAAE,UAAWwD,EAAkB,EAAGuB,CAAW,EAE1H,MAAMnF,EAAY,OAAOkC,EAAQ,gBAAiB/B,EAAUC,EAAW,OAAW+E,CAAW,EAE7F,IAAME,EAAYxD,EAAW,EAAE,EACzByD,EAAaC,GAAiBF,EAAWzF,EAAK,CAAC,CAAC+C,CAAY,EAE5D6C,EAAS,MAAMC,GAAqB7F,CAAG,EACvC8F,EAAS,MAAMC,GAAgBH,CAAM,EACrCI,EAAa,OAAOhG,EAAI,+BAA+B,GAAK,GAC5DiG,EAAc,MAAMC,GAAQ,CAChC,OAAQ5D,EACR,KAAMrB,EAAK,KACX,UAAWmE,GAAQ,GACnB,IAAK,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAAIY,EAAa,EACpD,EAAGF,CAAM,EAEHK,EAAU,IAAI,QAAQ,CAAE,eAAgB,kBAAmB,CAAC,EAClE,OAAAA,EAAQ,OAAO,aAAcC,GAAyBf,EAAcrF,EAAK,CAAC,CAAC+C,CAAY,CAAC,EACxFoD,EAAQ,OAAO,aAAcT,CAAU,EACvCS,EAAQ,OAAO,aAAcE,GAAmB,CAAC,EAE1C,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,GAAM,YAAAJ,EAAa,eAAAjD,EAAgB,mBAAAiB,EAAmB,CAAC,EAAG,CAAE,QAAAkC,CAAQ,CAAC,CACrH,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CMjWAG,KAKAC,KAEAC,KAKA,eAAsBC,GAAqBC,EAAkBC,EAA6B,CACxF,IAAMC,EAAS,OAAe,QACxBC,EAAWH,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDI,EAAYJ,EAAQ,QAAQ,IAAI,YAAY,EAC5CK,EAAc,IAAIC,GAAiBL,EAAI,EAAE,EAE/C,GAAI,MAAMM,EAAW,cAAcL,EAAO,gBAAgBC,CAAQ,GAAI,GAAI,EAAE,EAC1E,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAG1D,IAAMK,EAAeC,GAAuBT,EAAQ,QAAQ,IAAI,QAAQ,CAAC,EACzE,GAAI,CAACQ,EAAc,OAAO,IAAI,SAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAE/E,GAAM,CAAE,sBAAAE,CAAsB,EAAI,KAAM,uCAClC,CAAE,SAAAC,EAAU,UAAAC,CAAU,EAAIF,EAAsBV,CAAO,EACvD,CAAE,QAAAa,EAAS,KAAAC,EAAM,gBAAAC,EAAiB,OAAAC,CAAO,EAAI,MAAMC,GAAchB,EAAKO,EAAcG,EAAUC,CAAS,EAE7G,GAAI,CAACC,GAAW,CAACC,GAAQ,CAACC,EAAiB,CACzC,GAAID,EAAM,CACR,IAAMI,EAASV,EAAeW,GAAwBX,CAAY,EAAI,KAChEY,EAAcF,EAAS,MAAMG,GAAoBH,EAAO,IAAKJ,EAAK,EAAE,EAAI,KAC9E,MAAMT,EAAY,OAAOS,EAAK,GAAI,SAAUX,EAAUC,EAAW,CAAE,OAAQY,GAAU,SAAU,EAAGI,CAAW,CAC/G,CACA,aAAMb,EAAW,cAAcL,EAAO,gBAAgBC,CAAQ,GAAI,IAAK,EAAE,EAClE,IAAI,SAAS,KAAK,UAAU,CAAE,MAAO,wBAAyB,OAAQa,GAAU,SAAU,CAAC,EAAG,CACnG,OAAQ,IACR,QAAS,CACP,aAAcM,GAA8B,EAC5C,eAAgB,kBAClB,CACF,CAAC,CACH,CAEA,IAAMC,EAAS,MAAMC,GAAqBvB,CAAG,EACvCwB,EAAS,MAAMC,GAAgBH,CAAM,EACrCI,EAAa,OAAO1B,EAAI,+BAA+B,GAAK,GAC5D2B,EAAc,MAAMC,GAAQ,CAChC,OAAQf,EAAK,GACb,KAAMA,EAAK,KACX,UAAWD,EAAQ,GACnB,IAAK,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAAIc,EAAa,EACpD,EAAGF,CAAM,EAEHK,EAAU,IAAI,QAAQ,CAAE,eAAgB,kBAAmB,CAAC,EAC5DC,EAAiBlB,EAAQ,GAAG,WAAW,IAAI,EACjD,OAAAiB,EAAQ,OAAO,aAAcE,GAAyBjB,EAAiBd,EAAK8B,CAAc,CAAC,EAEpF,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,GAAM,YAAAH,CAAY,CAAC,EAAG,CAAE,QAAAE,CAAQ,CAAC,CACjF,CC5DAG,KAIAC,KAEAC,KAKA,eAAsBC,GAAoBC,EAAkBC,EAA6B,CACvF,IAAMC,EAAWF,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDG,EAAYH,EAAQ,QAAQ,IAAI,YAAY,EAC5CI,EAAc,IAAIC,GAAiBJ,EAAI,EAAE,EAEzCK,EAAeC,GAAuBP,EAAQ,QAAQ,IAAI,QAAQ,CAAC,EACzE,GAAIM,EAAc,CAChB,IAAME,EAASC,GAAwBH,CAAY,EACnD,GAAIE,EAAQ,CAEV,IAAME,EAAS,MADM,IAAIC,GAAaV,EAAI,EAAE,EACV,iBAAiBO,EAAO,GAAG,EAG7D,GAFA,MAAMI,GAAkBX,EAAKO,EAAO,GAAG,EACvCK,GAAwBL,EAAO,GAAG,EAC9BE,EAAQ,CACV,IAAMI,EAAc,MAAMC,GAAoBP,EAAO,IAAKE,CAAM,EAChE,MAAMN,EAAY,OAAOM,EAAQ,SAAUR,EAAUC,EAAW,CAAE,OAAQ,aAAc,EAAGW,CAAW,CACxG,CACF,CACF,CACA,IAAME,EAAkB,IAAI,QAAQ,CAAE,eAAgB,kBAAmB,CAAC,EAC1E,OAAAA,EAAgB,OAAO,aAAcC,GAA8B,CAAC,EACpED,EAAgB,OAAO,aAAc,sDAAsD,EACpF,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAASA,CACX,CAAC,CACH,CCpCAE,KACAC,KACAC,KAGAC,KACAC,KACAC,KAKA,eAAeC,GAAgBC,EAAuBC,EAAyC,CAE7F,GAAI,CACF,GAAI,MAAMC,GAAeF,EAAeC,CAAa,EACnD,MAAO,EAEX,MAAY,CAAC,CAGb,GAAI,CACF,IAAME,EAAe,KAAKF,CAAa,EACjCG,EAAW,IAAI,WAAWD,EAAa,MAAM,EACnD,QAASE,EAAI,EAAGA,EAAIF,EAAa,OAAQE,IACvCD,EAASC,CAAC,EAAIF,EAAa,WAAWE,CAAC,EAEzC,GAAID,EAAS,SAAW,GAAI,CAC1B,IAAME,EAAOF,EAAS,MAAM,EAAG,EAAE,EAC3BG,EAAaH,EAAS,MAAM,EAAE,EAE9BI,EAAU,MAAM,KAAKF,CAAI,EAAE,IAAIG,GAAKA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,EAC5EC,EAAc,IAAI,YAAY,EAAE,OAAOV,EAAgBQ,CAAO,EAE9DG,EAAqB,MAAM,OAAO,OAAO,OAAO,UAAWD,CAAW,EACtEE,EAAe,IAAI,WAAWD,CAAkB,EAEtD,GAAIC,EAAa,SAAWL,EAAW,OAAQ,CAC7C,IAAIM,EAAS,EACb,QAASR,EAAI,EAAGA,EAAIO,EAAa,OAAQP,IACvCQ,GAAUD,EAAaP,CAAC,EAAIE,EAAWF,CAAC,EAE1C,GAAIQ,IAAW,EAAG,MAAO,EAC3B,CACF,CACF,MAAY,CAAC,CAEb,MAAO,EACT,CAKA,eAAsBC,GAAyBC,EAAkBC,EAA6B,CAC5F,IAAMC,EAAM,IAAI,IAAIF,EAAQ,GAAG,EACzBG,EAAY,IAAIC,EAAUH,EAAI,GAAIA,CAAG,EACrCI,EAAc,IAAIC,GAAiBL,EAAI,EAAE,EACzCM,EAAS,OAAe,QACxBC,EAAWR,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDS,EAAYT,EAAQ,QAAQ,IAAI,YAAY,EAGlD,GAAIE,EAAI,WAAa,4BAA8BF,EAAQ,SAAW,MAAO,CAC3E,IAAMU,EAAaV,EAAQ,QAAQ,IAAI,eAAe,GAAK,GACvDW,EAAc,GAIlB,GAHID,EAAW,WAAW,SAAS,IACjCC,EAAcD,EAAW,MAAM,CAAC,GAE9B,CAACC,EAAa,OAAO,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,EAErE,GAAI,CACF,IAAMC,EAAS,MAAMC,GAAqBZ,CAAG,EACvCa,EAAS,MAAMC,GAAgBH,CAAM,EACrCI,EAAU,MAAMC,GACpBN,EACAG,CACF,EACA,GAAI,CAACE,EAAS,OAAO,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,EAEjE,IAAME,EAAS,MAAMf,EAAU,QAAQa,EAAQ,MAAM,EAQ/CG,EAAW,CAAC,EAAED,GAAQ,UAAY,CAAC,iBAAiB,KAAKA,EAAO,QAAQ,GAExEE,EAAQC,EAAW,EAAE,EACrBC,EAAW,gBAAgBN,EAAQ,SAAS,GAClD,aAAMO,EAAW,IAAIhB,EAAOe,EAAU,CAAE,MAAAF,EAAO,SAAAD,CAAS,EAAG,GAAG,EAEvD,IAAI,SAAS,KAAK,UAAU,CAAE,MAAAC,EAAO,OAAQD,CAAS,CAAC,EAAG,CAC/D,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,MAAY,CACV,OAAO,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,CACrD,CACF,CAGA,GAAIjB,EAAI,WAAa,4BAA8BF,EAAQ,SAAW,OAAQ,CAC5E,IAAMU,EAAaV,EAAQ,QAAQ,IAAI,eAAe,GAAK,GACvDW,EAAc,GAIlB,GAHID,EAAW,WAAW,SAAS,IACjCC,EAAcD,EAAW,MAAM,CAAC,GAE9B,CAACC,EAAa,OAAO,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,EAErE,GAAI,CACF,IAAMC,EAAS,MAAMC,GAAqBZ,CAAG,EACvCa,EAAS,MAAMC,GAAgBH,CAAM,EACrCI,EAAU,MAAMC,GACpBN,EACAG,CACF,EACA,GAAI,CAACE,EAAS,OAAO,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,EAEjE,GAAM,CAAE,QAAAQ,EAAS,MAAAJ,CAAM,EAAI,MAAMpB,EAAQ,KAAK,EAC9C,GAAI,CAACwB,EAAS,OAAO,IAAI,SAAS,kBAAmB,CAAE,OAAQ,GAAI,CAAC,EACpE,GAAI,CAACJ,EAAO,OAAO,IAAI,SAAS,gBAAiB,CAAE,OAAQ,GAAI,CAAC,EAGhE,IAAMK,EAAe,IAAIC,GAAazB,EAAI,EAAE,EACtC0B,EAAU,MAAMF,EAAa,WAAWT,EAAQ,SAAS,EAC/D,GAAI,CAACW,EAAS,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAGtE,IAAMT,EAAS,MAAMf,EAAU,QAAQa,EAAQ,MAAM,EACrD,GAAI,CAACE,GAAU,CAACA,EAAO,SAAU,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,EAG1F,IAAMU,EAAgB,gBAAgBZ,EAAQ,SAAS,GACjDa,EAAmB,MAAMN,EAAW,IAA2ChB,EAAOqB,CAAa,EACzG,GAAI,CAACC,GAAkB,MACrB,OAAO,IAAI,SAAS,uCAAwC,CAAE,OAAQ,GAAI,CAAC,EAE7E,IAAMC,EAAcD,EAAiB,MAC/BV,EAAW,CAAC,CAACU,EAAiB,SAMpC,GAHA,MAAMN,EAAW,OAAOhB,EAAOqB,CAAa,EAGxCR,IAAUU,EACZ,OAAO,IAAI,SAAS,gBAAiB,CAAE,OAAQ,GAAI,CAAC,EAGtD,IAAMR,EAAW,eAAeK,EAAQ,EAAE,GAEpCI,GADsB,MAAMR,EAAW,IAAuBhB,EAAOe,CAAQ,IACvC,OAAS,EAE/CU,EAAc,MAAMC,GAAoBN,EAAQ,GAAIX,EAAQ,MAAM,EAEpEkB,EAAa,GACjB,GAAIf,EAEFe,EAAa,MAAMlD,GAAgBwC,EAASN,EAAO,QAAQ,EACvDgB,GAEF,MAAM/B,EAAU,cAAca,EAAQ,OAAQQ,CAAO,MAElD,CAEL,IAAMW,EAAyB,MAAMC,GAAclB,EAAO,SAAUY,CAAW,EAG/E,GAAIN,EAAQ,SAAWW,EAAuB,OAAQ,CACpD,IAAIrC,EAAS,EACb,QAASR,EAAI,EAAGA,EAAIkC,EAAQ,OAAQlC,IAClCQ,GAAU0B,EAAQ,WAAWlC,CAAC,EAAI6C,EAAuB,WAAW7C,CAAC,EAEvE4C,EAAapC,IAAW,CAC1B,CACF,CAEA,GAAI,CAACoC,EAAY,CACf,IAAMG,EAAkBN,EAAiB,EACzC,OAAIM,GAAmB,GAErB,MAAMZ,EAAa,cAAcE,EAAQ,EAAE,EAC3C,MAAMJ,EAAW,OAAOhB,EAAOe,CAAQ,EACvC,MAAMjB,EAAY,OAAOW,EAAQ,OAAQ,kBAAmBR,EAAUC,EAAW,CAAE,OAAQ,mBAAoB,EAAGuB,CAAW,EACtH,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,GAAO,MAAO,mBAAoB,CAAC,EAAG,CAClF,OAAQ,IACR,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,IAEH,MAAMT,EAAW,IAAIhB,EAAOe,EAAU,CAAE,MAAOe,CAAgB,EAAG,GAAG,EACrE,MAAMhC,EAAY,OAAOW,EAAQ,OAAQ,kBAAmBR,EAAUC,EAAW,CAAE,OAAQ,gBAAiB,QAAS4B,CAAgB,EAAGL,CAAW,EAC5I,IAAI,SAAS,KAAK,UAAU,CACjC,QAAS,GACT,MAAO,gBACP,kBAAmB,EAAIK,CACzB,CAAC,EAAG,CACF,OAAQ,IACR,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,EACH,CAGA,IAAMC,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACxC,aAAMb,EAAa,cAAcE,EAAQ,GAAIW,CAAG,EAChDC,GAAwBZ,EAAQ,EAAE,EAClC,MAAMJ,EAAW,OAAOhB,EAAOe,CAAQ,EACvC,MAAMjB,EAAY,OAAOW,EAAQ,OAAQ,qBAAsBR,EAAUC,EAAW,OAAWuB,CAAW,EAEnG,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,OAASQ,EAAQ,CACf,eAAQ,MAAM,wBAAyBA,CAAC,EACjC,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,CACrD,CACF,CAGA,GAAItC,EAAI,WAAa,0BAA4BF,EAAQ,SAAW,OAAQ,CAC1E,IAAMU,EAAaV,EAAQ,QAAQ,IAAI,eAAe,GAAK,GACvDW,EAAc,GAIlB,GAHID,EAAW,WAAW,SAAS,IACjCC,EAAcD,EAAW,MAAM,CAAC,GAE9B,CAACC,EAAa,OAAO,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,EAErE,GAAI,CACF,IAAMC,EAAS,MAAMC,GAAqBZ,CAAG,EACvCa,EAAS,MAAMC,GAAgBH,CAAM,EACrCI,EAAU,MAAMC,GACpBN,EACAG,CACF,EACA,GAAI,CAACE,EAAS,OAAO,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,EAEjE,IAAMS,EAAe,IAAIC,GAAazB,EAAI,EAAE,EACtC0B,EAAU,MAAMF,EAAa,WAAWT,EAAQ,SAAS,EAC/D,OAAKW,GAEL,MAAMF,EAAa,aAAaE,EAAQ,EAAE,EAC1CY,GAAwBZ,EAAQ,EAAE,EAC3B,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,GANoB,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,CAOxE,MAAiB,CACf,OAAO,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,CACrD,CACF,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CCvPAc,KAEAC,KACAC,KA0BA,eAAsBC,GAA4BC,EAAkBC,EAA6B,CAC/F,IAAMC,EAAM,IAAI,IAAIF,EAAQ,GAAG,EACzBG,EAAOD,EAAI,SACXE,EAAY,IAAIC,EAAUJ,EAAI,GAAIA,CAAG,EACrCK,EAAc,IAAIC,GAAiBN,EAAI,EAAE,EACzCO,EAAS,OAAe,QACxBC,EAAWT,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDU,EAAYV,EAAQ,QAAQ,IAAI,YAAY,EAGlD,GAAIG,IAAS,kCAAoCH,EAAQ,SAAW,OAAQ,CAC1E,GAAI,MAAMW,EAAW,cAAcH,EAAO,gBAAgBC,CAAQ,GAAI,GAAI,GAAG,EAC3E,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAG1D,GAAM,CAAE,SAAAG,EAAU,eAAAC,CAAe,EAAI,MAAMb,EAAQ,KAAK,EACxD,GAAI,CAACY,EACH,OAAO,IAAI,SAAS,uBAAwB,CAAE,OAAQ,GAAI,CAAC,EAI7D,IAAME,EAAgB,IAAIC,GAAoBd,EAAI,EAAE,EAC9C,CAACe,EAAWC,CAAO,EAAI,MAAM,QAAQ,IAAI,CAC7CH,EAAc,IAAI,sBAAsB,EACxCA,EAAc,IAAI,yBAAyB,CAC7C,CAAC,EACD,GAAIG,IAAY,QAAUD,GACpB,CAAC,MAAME,GAAgBL,EAAgBG,EAAWP,CAAQ,EAC5D,OAAO,IAAI,SAAS,sBAAuB,CAAE,OAAQ,GAAI,CAAC,EAI9D,IAAMU,EAAO,MAAMf,EAAU,cAAcQ,CAAQ,EACnD,GAAI,CAACO,EACH,aAAMR,EAAW,cAAcH,EAAO,gBAAgBC,CAAQ,GAAI,IAAK,GAAG,EACnE,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAKvD,IAAMW,EAAW,MADI,IAAIC,GAAapB,EAAI,EAAE,EACR,WAAWkB,EAAK,EAAE,EAChDG,EAAcF,EAAS,OAAS,EAChCG,EAAW,CAAC,CAACJ,EAAK,eAAiB,CAAC,CAACA,EAAK,aAAe,CAAC,CAACA,EAAK,uBAElEK,EAAoB,GAClBC,EAAkBN,EAAK,mBAC7B,GAAIM,EACF,GAAI,CACF,IAAMC,EAAS,OAAOD,GAAoB,SAAW,KAAK,MAAMA,CAAe,EAAIA,EACnFD,EAAoB,MAAM,QAAQE,CAAM,GAAKA,EAAO,OAAS,CAC/D,MAAQ,CACNF,EAAoB,OAAOC,GAAoB,UAAYA,EAAgB,OAAS,CACtF,MACSN,EAAK,+BACdK,EAAoB,IAItB,GAAI,CAACF,GAAe,CAACC,GAAY,CAACC,EAChC,OAAO,IAAI,SAAS,KAAK,UAAU,CACjC,UAAW,GACX,OAAQ,mBACV,CAAC,EAAG,CACF,OAAQ,IACR,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,EAIH,IAAIG,EAAmC,KACnCC,EAAuB,KACvBN,IACFK,EAAoBE,GAA0B,EAC9CD,EAAkB,CAChB,UAAWD,EACX,KAAMzB,EAAI,SACV,iBAAkBkB,EAAS,IAAIU,IAAM,CACnC,GAAIA,EAAE,cACN,KAAM,aACN,WAAYA,EAAE,WAAa,KAAK,MAAMA,EAAE,UAAU,EAAI,MACxD,EAAE,EACF,QAAS,IACT,iBAAkB,WACpB,GAGF,IAAMC,EAAgBC,EAAW,EAAE,EAC7BC,EAA+B,CACnC,OAAQd,EAAK,GACb,SAAUA,EAAK,SACf,iBAAkBQ,EAClB,KAAMzB,EAAI,SACV,eAAgB,CAClB,EAGA,aAAMS,EAAW,IAAIH,EAAO,mBAAmBuB,CAAa,GAAIE,EAAe,GAAG,EAE3E,IAAI,SAAS,KAAK,UAAU,CACjC,UAAW,GACX,cAAAF,EACA,YAAAT,EACA,gBAAAM,EACA,SAAAL,EACA,kBAAAC,CACF,CAAC,EAAG,CACF,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAGA,GAAIrB,IAAS,oCAAsCH,EAAQ,SAAW,OAAQ,CAC5E,GAAM,CAAE,cAAA+B,EAAe,iBAAAG,EAAkB,cAAAC,EAAe,SAAAC,EAAU,YAAAC,CAAY,EAAI,MAAMrC,EAAQ,KAAK,EACrG,GAAI,CAAC+B,EACH,OAAO,IAAI,SAAS,6BAA8B,CAAE,OAAQ,GAAI,CAAC,EAGnE,IAAMO,EAAW,mBAAmBP,CAAa,GAC3CQ,EAAQ,MAAM5B,EAAW,IAAmBH,EAAO8B,CAAQ,EACjE,GAAI,CAACC,EACH,OAAO,IAAI,SAAS,sCAAuC,CAAE,OAAQ,GAAI,CAAC,EAG5E,GAAIA,EAAM,gBAAkB,EAC1B,aAAM5B,EAAW,OAAOH,EAAO8B,CAAQ,EAChC,IAAI,SAAS,+CAAgD,CAAE,OAAQ,GAAI,CAAC,EAGrF,IAAMnB,EAAO,MAAMf,EAAU,QAAQmC,EAAM,MAAM,EACjD,GAAI,CAACpB,EACH,aAAMR,EAAW,OAAOH,EAAO8B,CAAQ,EAChC,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAIvD,GAAIJ,GAAoBK,EAAM,iBAAkB,CAC9C,IAAMC,EAAe,IAAInB,GAAapB,EAAI,EAAE,EAEtCwC,GADW,MAAMD,EAAa,WAAWrB,EAAK,EAAE,GAC7B,KAAKW,GAAKA,EAAE,gBAAkBI,EAAiB,EAAE,EAE1E,GAAI,CAACO,EACH,OAAAF,EAAM,iBACN,MAAM5B,EAAW,IAAIH,EAAO8B,EAAUC,EAAO,GAAG,EACzC,IAAI,SAAS,yBAA0B,CAAE,OAAQ,GAAI,CAAC,EAG/D,GAAI,CACF,GAAM,CAAE,UAAAG,CAAU,EAAI,MAAMC,GAA6B,CACvD,eAAgBT,EAAiB,SAAS,eAC1C,kBAAmBA,EAAiB,SAAS,kBAC7C,UAAWA,EAAiB,SAAS,UACrC,cAAeO,EAAQ,WACvB,UAAWA,EAAQ,UACnB,kBAAmBF,EAAM,iBACzB,eAAgB,GAAGrC,EAAI,QAAQ,KAAKA,EAAI,IAAI,GAC5C,aAAcqC,EAAM,KACpB,kBAAmBE,EAAQ,UAC7B,CAAC,EAED,MAAMD,EAAa,YAAYC,EAAQ,GAAIC,CAAS,EACpD,MAAMpC,EAAY,OAAOa,EAAK,GAAI,yBAA0BV,EAAUC,EAAW,CAAE,KAAM,iBAAkB,CAAC,CAC9G,MAAmB,CACjB,OAAA6B,EAAM,iBACN,MAAM5B,EAAW,IAAIH,EAAO8B,EAAUC,EAAO,GAAG,EACzC,IAAI,SAAS,8BAA+B,CAAE,OAAQ,GAAI,CAAC,CACpE,CACF,SAESF,EAAa,CACpB,IAAIO,EAAoC,CAAC,EACzC,GAAI,CACF,IAAMC,EAAM1B,EAAK,mBACjByB,EAAe,OAAOC,GAAQ,SAAW,KAAK,MAAMA,GAAO,IAAI,EAAKA,GAAO,CAAC,EACvE,MAAM,QAAQD,CAAY,IAAGA,EAAe,CAACA,CAAY,EAChE,MAAQ,CAAC,CAET,IAAME,EAAa,MAAMC,GAAwBV,EAAaO,CAAY,EAC1E,GAAIE,IAAe,GACjB,OAAAP,EAAM,iBACN,MAAM5B,EAAW,IAAIH,EAAO8B,EAAUC,EAAO,GAAG,EACzC,IAAI,SAAS,uBAAwB,CAAE,OAAQ,GAAI,CAAC,EAG7D,MAAMnC,EAAU,mBAAmBe,EAAK,GAAI2B,EAAYF,CAAY,EACpE,MAAMtC,EAAY,OAAOa,EAAK,GAAI,oBAAqBV,EAAUC,EAAW,CAAE,KAAM,kBAAmB,UAAWkC,EAAa,OAAS,CAAE,CAAC,CAC7I,SAEST,GAAiBhB,EAAK,YAAa,CAC1C,IAAM6B,EAAa,MAAMC,GAAmB9B,EAAK,YAAagB,EAAeC,CAAQ,EACrF,GAAI,CAACY,EAAW,MACd,OAAAT,EAAM,iBACN,MAAM5B,EAAW,IAAIH,EAAO8B,EAAUC,EAAO,GAAG,EACzC,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAI1D,GAAIS,EAAW,WAAa,OAAW,CACrC,IAAME,EAAU,aAAa/B,EAAK,EAAE,IAAI6B,EAAW,QAAQ,GAE3D,GADiB,MAAMrC,EAAW,IAAIH,EAAO0C,CAAO,EAElD,OAAAX,EAAM,iBACN,MAAM5B,EAAW,IAAIH,EAAO8B,EAAUC,EAAO,GAAG,EACzC,IAAI,SAAS,oDAAqD,CAAE,OAAQ,GAAI,CAAC,EAE1F,MAAM5B,EAAW,IAAIH,EAAO0C,EAAS,GAAM,GAAG,CAChD,CAEA,MAAM5C,EAAY,OAAOa,EAAK,GAAI,sBAAuBV,EAAUC,EAAW,CAAE,KAAM,iBAAkB,CAAC,CAC3G,KAEE,QAAO,IAAI,SAAS,mCAAoC,CAAE,OAAQ,GAAI,CAAC,EAIzE,MAAMC,EAAW,OAAOH,EAAO8B,CAAQ,EAEvC,IAAMa,EAAanB,EAAW,EAAE,EAC1BoB,EAAyB,CAC7B,OAAQjC,EAAK,GACb,SAAUA,EAAK,QACjB,EACA,aAAMR,EAAW,IAAIH,EAAO,mBAAmB2C,CAAU,GAAIC,EAAY,GAAG,EAErE,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,GAAM,WAAAD,CAAW,CAAC,EAAG,CACjE,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAGA,GAAIhD,IAAS,mCAAqCH,EAAQ,SAAW,OAAQ,CAC3E,GAAM,CAAE,WAAAmD,EAAY,YAAAE,CAAY,EAAI,MAAMrD,EAAQ,KAAK,EACvD,GAAI,CAACmD,GAAc,CAACE,EAClB,OAAO,IAAI,SAAS,4CAA6C,CAAE,OAAQ,GAAI,CAAC,EAGlF,GAAI,CAACC,GAAe,KAAKD,CAAW,EAClC,OAAO,IAAI,SAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAG9D,IAAME,EAAW,mBAAmBJ,CAAU,GACxCC,EAAa,MAAMzC,EAAW,IAAgBH,EAAO+C,CAAQ,EACnE,GAAI,CAACH,EACH,OAAO,IAAI,SAAS,iCAAkC,CAAE,OAAQ,GAAI,CAAC,EAIvE,MAAMzC,EAAW,OAAOH,EAAO+C,CAAQ,EAEvC,IAAMpC,EAAO,MAAMf,EAAU,QAAQgD,EAAW,MAAM,EACtD,GAAI,CAACjC,EACH,OAAO,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAIvD,IAAMqC,EAAiB,MAAMC,GAAaJ,EAAa,CAAC,EACxD,aAAMjD,EAAU,eAAee,EAAK,GAAIqC,EAAgB,CAAC,EAIzD,MADqB,IAAIE,GAAazD,EAAI,EAAE,EACzB,kBAAkBkB,EAAK,EAAE,EAE5C,MAAMb,EAAY,OAAOa,EAAK,GAAI,0BAA2BV,EAAUC,EAAW,CAAE,OAAQ,uBAAwB,CAAC,EAE9G,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CClSA,eAAsBiD,GAAkBC,EAAkBC,EAA6B,CACrF,GAAI,CAEF,IAAMC,EADM,IAAI,IAAIF,EAAQ,GAAG,EACd,SAGjB,OAAIE,IAAS,oBAAsBA,IAAS,2BACnC,MAAMC,GAAwBH,EAASC,CAAG,EAI/CC,IAAS,mBACJ,MAAME,GAA0BJ,EAASC,CAAG,EAIjDC,IAAS,sBAAwBA,IAAS,kBACrC,MAAMG,GAAmBL,EAASC,CAAG,EAI1CC,IAAS,oBACJ,MAAMI,GAAqBN,EAASC,CAAG,EAI5CC,IAAS,mBACJ,MAAMK,GAAoBP,EAASC,CAAG,EAI3CC,EAAK,WAAW,2BAA2B,EACtC,MAAMM,GAA4BR,EAASC,CAAG,EAInDC,IAAS,4BAA8BA,IAAS,yBAC3C,MAAMO,GAAyBT,EAASC,CAAG,EAG7C,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,OAASS,EAAQ,CACf,eAAQ,MAAM,8BAA+BA,EAAE,SAAWA,CAAC,EACpD,IAAI,SAASA,EAAE,SAAW,wBAAyB,CACxD,OAAQ,IACR,QAAS,CAAE,eAAgB,2BAA4B,CACzD,CAAC,CACH,CACF,CC1DO,IAAMC,GAAN,KAAW,CAIhB,OAAO,QAAQC,EAAqB,CAClC,OAAOA,EAAK,OAAS,OACvB,CAKA,OAAO,iBAAiBA,EAAYC,EAA2B,CAE7D,OAAOA,EAAQ,WAAaD,EAAK,EACnC,CACF,ECdAE,KASA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAe,IAAIC,EAAaJ,EAAI,EAAE,EAG5C,GAAID,EAAQ,SAAW,MAAO,CAC5B,IAAMM,EAAU,MAAMF,EAAa,YAAYF,EAAK,EAAE,EACtD,OAAO,IAAI,SAAS,KAAK,UAAUI,CAAO,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAClG,CAGA,GAAIN,EAAQ,SAAW,OAAQ,CAC7B,IAAMO,EAAO,MAAMP,EAAQ,KAAK,EAEhC,GAAI,CAACO,EAAK,MAAQ,CAACC,GAAmB,KAAKD,EAAK,IAAI,EAClD,OAAO,IAAI,SAAS,8BAA+B,CAAE,OAAQ,GAAI,CAAC,EAGpE,IAAME,EAAmB,MAAML,EAAa,YAAYF,EAAK,EAAE,EACzDQ,EAAc,OAAOT,EAAI,qBAAqB,GAAK,GACzD,GAAIQ,EAAiB,QAAUC,EAAa,OAAO,IAAI,SAAS,+BAA+BA,CAAW,IAAK,CAAE,OAAQ,GAAI,CAAC,EAG9H,GADiBD,EAAiB,KAAKE,GAAKA,EAAE,OAASJ,EAAK,IAAI,EAClD,OAAO,IAAI,SAAS,kCAAmC,CAAE,OAAQ,GAAI,CAAC,EAEpF,IAAMK,EAAQC,EAAW,CAAC,EACpBC,EAAqBb,EAAI,yBAA2B,QAAaA,EAAI,yBAA2B,GAClG,OAAOA,EAAI,sBAAsB,EACjC,GACEc,EAAwBd,EAAI,6BAA+B,QAAaA,EAAI,6BAA+B,GAC7G,KAAK,IAAI,OAAOA,EAAI,0BAA0B,EAAGa,CAAkB,EACnE,KAAK,IAAI,EAAGA,CAAkB,EAC5BE,EAAyBf,EAAI,qCAAuC,QAAaA,EAAI,qCAAuC,GAC9H,KAAK,IAAI,OAAOA,EAAI,kCAAkC,EAAGa,CAAkB,EAC3E,KAAK,IAAI,EAAGA,CAAkB,EAC5BG,EAA6BhB,EAAI,yCAA2C,QAAaA,EAAI,yCAA2C,GAC1I,KAAK,IAAI,OAAOA,EAAI,sCAAsC,EAAGe,CAAsB,EACnF,KAAK,IAAI,EAAGA,CAAsB,EAEhCE,EAAuBhB,GAAM,OAAS,QAAUa,EAAwBE,EACxEE,EAAmC,CACvC,SAAU,CAAC,+CAA+C,EAC1D,IAAK,CAAE,QAAS,GAAM,cAAe,EAAK,EAC1C,mBAAoBD,EACpB,iBAAkB,GAClB,eAAgB,QAChB,gBAAiB,CAAE,QAAS,GAAO,gBAAiB,oBAAqB,CAC3E,EACA,MAAMd,EAAa,OAAO,CAAE,GAAIQ,EAAO,SAAUV,EAAK,GAAI,KAAMK,EAAK,MAAQ,kBAAmB,SAAUY,CAAgB,CAAC,EAG3H,GAAI,CACF,IAAMC,EAAiB,MAAMnB,EAAI,GAAG,QAClC,0HACF,EACG,KAAKC,EAAK,EAAE,EACZ,MAAkD,EAErD,GAAIkB,GAAgB,WAAY,CAC9B,IAAMC,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACxC,MAAMpB,EAAI,GAAG,QACX,+FACF,EACG,KAAKW,EAAOQ,EAAe,WAAYC,EAAKD,EAAe,WAAa,CAAC,EACzE,IAAI,EAEP,IAAME,EAAiB,MAAMrB,EAAI,GAAG,QAClC,wIACF,EACG,KAAKC,EAAK,EAAE,EACZ,MAA0D,EAEzDoB,GACF,MAAMrB,EAAI,GAAG,QACX,gHACF,EACG,KAAKW,EAAOU,EAAe,aAAcA,EAAe,GAAIA,EAAe,KAAMD,CAAG,EACpF,IAAI,CAEX,CACF,OAASE,EAAG,CACV,QAAQ,MAAM,6DAA8DA,CAAC,CAC/E,CAEA,OAAO,IAAI,SAAS,KAAK,UAAU,CAAE,GAAIX,CAAM,CAAC,EAAG,CAAE,OAAQ,GAAI,CAAC,CACpE,CAEA,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,CAC3D,CAKA,eAAsBY,GACpBxB,EACAC,EACAC,EACAuB,EACAtB,EACAuB,EACmB,CACnB,IAAMtB,EAAe,IAAIC,EAAaJ,EAAI,EAAE,EACtC0B,EAAW,IAAIC,GAAS3B,EAAI,EAAE,EAC9B4B,EAAYJ,EAAQ,GAM1B,GAAItB,EAAU,SAAW,EAAG,CAC1B,GAAIH,EAAQ,SAAW,SACrB,aAAMI,EAAa,OAAOyB,CAAS,EAC5B,IAAI,SAAS,KAAM,CAAE,OAAQ,GAAI,CAAC,EAG3C,GAAI7B,EAAQ,SAAW,QAAS,CAC9B,GAAM,CAAE,KAAA8B,CAAK,EAAI,MAAM9B,EAAQ,KAAK,EACpC,MAAI,CAAC8B,GAAQ,CAACtB,GAAmB,KAAKsB,CAAI,EAAU,IAAI,SAAS,8BAA+B,CAAE,OAAQ,GAAI,CAAC,GAC/G,MAAM1B,EAAa,WAAWyB,EAAWC,CAAI,EAC7CJ,EAAI,UAAUK,GAAS,WAAWF,EAAW,GAAM5B,CAAG,CAAC,EAChD,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,EAC5G,CAEA,OAAID,EAAQ,SAAW,MACd,IAAI,SAAS,KAAK,UAAUyB,CAAO,EAAG,CAC3C,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,EAGI,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,CAC3D,CAGA,GAAItB,EAAU,CAAC,IAAM,cAAgBH,EAAQ,SAAW,OAAQ,CAC9D,IAAMgC,EAASnB,EAAW,EAAE,EAC5B,aAAMT,EAAa,UAAUyB,EAAWG,CAAM,EAC9CN,EAAI,UAAUK,GAAS,WAAWF,EAAW,GAAO5B,CAAG,CAAC,EACjD,IAAI,SAAS,KAAK,UAAU,CAAE,YAAa+B,CAAO,CAAC,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAClH,CAGA,GAAI7B,EAAU,CAAC,IAAM,YAAcH,EAAQ,SAAW,QAAS,CAC7D,IAAMiC,EAAc,MAAMjC,EAAQ,KAAK,EAGjCc,EAAqBb,EAAI,yBAA2B,QAAaA,EAAI,yBAA2B,GAClG,OAAOA,EAAI,sBAAsB,EACjC,GACEiC,EAAoBjC,EAAI,oCAAsC,QAAaA,EAAI,oCAAsC,GACvH,KAAK,IAAI,OAAOA,EAAI,iCAAiC,EAAGa,CAAkB,EAC1EA,EACEqB,EAAgBlC,EAAI,qCAAuC,QAAaA,EAAI,qCAAuC,GACrH,KAAK,IAAI,OAAOA,EAAI,kCAAkC,EAAGa,CAAkB,EAC3E,KAAK,IAAI,EAAGA,CAAkB,EAC5BsB,EAAmBlC,GAAM,OAAS,QAAUgC,EAAoBC,EAMtE,GAJIF,EAAY,oBAAsB,OACpCA,EAAY,mBAAqB,KAAK,IAAI,OAAOA,EAAY,kBAAkB,EAAGG,CAAgB,GAGhGH,EAAY,UAAY,MAAM,QAAQA,EAAY,QAAQ,EAC5D,QAAWI,KAAOJ,EAAY,SAAU,CAGtC,IAAMK,EAAUD,EAAI,WAAW,UAAU,EACnCE,EAAUF,EAAI,WAAW,SAAS,EAClCG,EAAUH,EAAI,WAAW,QAAQ,EACjCI,EAAUJ,EAAI,WAAW,QAAQ,EACjCK,EAAUL,EAAI,WAAW,QAAQ,EACjCM,EAAUN,EAAI,WAAW,QAAQ,EACjCO,EAAUP,EAAI,WAAW,SAAS,EAElCQ,EAAa,CAACR,EAAI,SAAS,IAAI,GAAK,CAACA,EAAI,WAAW,GAAG,EAE7D,GAAI,CAACC,GAAW,CAACC,GAAU,CAACC,GAAS,CAACC,GAAS,CAACC,GAAS,CAACC,GAAS,CAACC,GAAU,CAACC,EAC7E,OAAO,IAAI,SAAS,mHAAoH,CAAE,OAAQ,GAAI,CAAC,EAGzJ,IAAMC,EAAaD,EAAa,SAASR,CAAG,GAAKA,EACjD,GAAI,CAACU,GAAUD,CAAU,EACvB,OAAO,IAAI,SAAS,yEAA0E,CAAE,OAAQ,GAAI,CAAC,CAEjH,CAIF,IAAME,EAAa,MAAM5C,EAAa,QAAQyB,CAAS,EACnDoB,EAAU,GACd,GAAI,CACF,IAAMC,EAAc,KAAK,MAAMF,GAAY,UAAY,IAAI,EACvDE,GAAa,oBAAsB,OACrCD,EAAU,OAAOC,EAAY,kBAAkB,EAEnD,MAAQ,CAAC,CAET,MAAM9C,EAAa,eAAeyB,EAAWI,CAAW,EAGxD,IAAMkB,EAAUlB,EAAY,mBAC5B,GAAIkB,GAAW,MAAQ,OAAOA,CAAO,EAAIF,EACvC,GAAI,OAAOE,CAAO,IAAM,EAEtBzB,EAAI,WAAW,SAAY,CACzB,GAAI,CACF,MAAMzB,EAAI,GAAG,QAAQ,uCAAuC,EAAE,KAAK4B,CAAS,EAAE,IAAI,EAClF,MAAMF,EAAS,QAAQ,sBAAsBE,CAAS,CACxD,OAASN,EAAQ,CACf,QAAQ,MAAM,uDAAwDA,GAAG,SAAWA,CAAC,CACvF,CACF,GAAG,CAAC,MACC,CACL,IAAM6B,EAAY,KAAK,MAAM,KAAK,IAAI,EAAI,IAAQ,OAAOD,CAAO,EAAI,GAAK,IAAK,EAC9EzB,EAAI,UAAUC,EAAS,QAAQE,EAAWuB,CAAS,CAAC,CACtD,CAIF,OAAA1B,EAAI,UAAUK,GAAS,WAAWF,EAAW,GAAO5B,CAAG,CAAC,EACjD,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAC5G,CAGA,GAAIE,EAAU,CAAC,IAAM,QAAUH,EAAQ,SAAW,OAAQ,CACxD,GAAM,CAAE,OAAAqD,EAAQ,KAAAC,CAAK,EAAI,MAAMtD,EAAQ,KAAK,EACtCuD,EAAWC,GAAcH,EAAQC,CAAI,EACrCG,EAAS,MAAM1B,GAAS,QAAQ/B,EAAS,CAAE,KAAMqD,EAAQ,KAAAC,EAAM,IAAKC,CAAS,EAAG,CAAE,UAAA1B,EAAW,UAAW,KAAK,IAAI,EAAG,IAAA5B,EAAK,IAAAyB,CAAI,CAAC,EACpI,OAAO,IAAI,SAAS,KAAK,UAAU,CACjC,OAAQ+B,EAAO,OACf,OAAQA,EAAO,OACf,QAASA,EAAO,OAAO,OAAS,EAAIC,GAAeD,EAAO,MAAM,EAAI,CAAC,EACrE,YAAaA,EAAO,YACpB,QAASA,EAAO,QAChB,QAASA,EAAO,QAChB,UAAWzD,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtD,YAAcA,EAAgB,IAAI,SAAWA,EAAQ,QAAQ,IAAI,cAAc,GAAK,UACpF,QAAS,EACX,CAAC,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CACzD,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CC9PA2D,KAEO,IAAMC,GAAN,KAAuB,CAC5B,YAAoBC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAEpB,MAAM,gBAAgBC,EAA2C,CAC/D,GAAM,CAAE,QAAAC,CAAQ,EAAI,MAAM,KAAK,GAAG,QAAQ,0EAA0E,EACjH,KAAKD,CAAS,EAAE,IAAiB,EACpC,OAAOC,CACT,CAEA,MAAM,eAAeD,EAAmBE,EAAoC,CAC1E,IAAMC,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAClCC,EAAKC,EAAW,EAAE,EAClBC,EAAc,EAEpB,QAASC,EAAU,EAAGA,EAAUD,EAAaC,IAAW,CACtD,IAAMC,EAAQC,GAAqB,CAAC,EACpC,GAAI,CACF,aAAM,KAAK,GAAG,QACZ,2GACF,EAAE,KAAKL,EAAIJ,EAAWE,EAAMM,EAAOL,EAAKA,CAAG,EAAE,IAAI,EAC1C,CAAE,GAAAC,EAAI,WAAYJ,EAAW,KAAAE,EAAM,MAAAM,EAAO,WAAYL,EAAK,WAAYA,CAAI,CACpF,OAASO,EAAU,CACjB,GAAIH,EAAUD,EAAc,IAAM,OAAOI,CAAG,EAAE,SAAS,0BAA0B,GAAK,OAAOA,CAAG,EAAE,SAAS,mBAAmB,GAC5H,SAEF,MAAMA,CACR,CACF,CACA,MAAM,IAAI,MAAM,gDAAgD,CAClE,CAEA,MAAM,sBAAsBN,EAAYJ,EAAmBE,EAAgC,CACzF,IAAMC,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAGxC,OAFe,MAAM,KAAK,GAAG,QAAQ,mFAAmF,EACrH,KAAKD,EAAMC,EAAKC,EAAIJ,CAAS,EAAE,IAAI,GACxB,OAChB,CAEA,MAAM,uBAAuBI,EAAYJ,EAAoC,CAC3E,IAAMG,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAClCG,EAAc,EAEpB,QAASC,EAAU,EAAGA,EAAUD,EAAaC,IAAW,CACtD,IAAMI,EAAWF,GAAqB,CAAC,EACvC,GAAI,CAGF,GAAI,EAFW,MAAM,KAAK,GAAG,QAAQ,oFAAoF,EACtH,KAAKE,EAAUR,EAAKC,EAAIJ,CAAS,EAAE,IAAI,GAC9B,QACV,MAAM,IAAI,MAAM,qCAAqC,EAEvD,OAAOW,CACT,OAASD,EAAU,CACjB,GAAIH,EAAUD,EAAc,IAAM,OAAOI,CAAG,EAAE,SAAS,0BAA0B,GAAK,OAAOA,CAAG,EAAE,SAAS,mBAAmB,GAC5H,SAEF,MAAMA,CACR,CACF,CACA,MAAM,IAAI,MAAM,gDAAgD,CAClE,CAEA,MAAM,kBAAkBN,EAAYJ,EAAqC,CAGvE,OAFe,MAAM,KAAK,GAAG,QAAQ,2DAA2D,EAC7F,KAAKI,EAAIJ,CAAS,EAAE,IAAI,GACb,OAChB,CACF,EC/DA,eAAsBY,GACpBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAU,IAAIC,GAAiBN,EAAI,EAAE,EAE3C,GAAID,EAAQ,SAAW,MAAO,CAC5B,IAAMQ,EAAU,MAAMF,EAAQ,gBAAgBH,CAAS,EACvD,OAAO,IAAI,SAAS,KAAK,UAAUK,CAAO,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAClG,CAEA,GAAIR,EAAQ,SAAW,OAAQ,CAC7B,GAAII,EAAU,SAAW,GAAKA,EAAU,CAAC,IAAM,eAAgB,CAC7D,IAAMK,EAAOL,EAAU,CAAC,EAClBM,EAAW,MAAMJ,EAAQ,uBAAuBG,EAAMN,CAAS,EACrE,OAAO,IAAI,SAAS,KAAK,UAAU,CAAE,MAAOO,CAAS,CAAC,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAC9G,CAEA,IAAMC,EAAO,MAAMX,EAAQ,KAAK,EAChC,GAAI,CAACW,EAAK,KAAM,OAAO,IAAI,SAAS,mBAAoB,CAAE,OAAQ,GAAI,CAAC,EACvE,GAAI,CAACC,GAAc,KAAKD,EAAK,IAAI,EAAG,OAAO,IAAI,SAAS,mCAAoC,CAAE,OAAQ,GAAI,CAAC,EAE3G,IAAME,EAAa,MAAMP,EAAQ,gBAAgBH,CAAS,EAC1D,GAAIU,EAAW,KAAKC,GAAMA,EAAG,KAAK,YAAY,IAAMH,EAAK,KAAK,YAAY,CAAC,EACzE,OAAO,IAAI,SAAS,mCAAoC,CAAE,OAAQ,GAAI,CAAC,EAEzE,IAAMI,EAAS,OAAOd,EAAI,6BAA6B,GAAK,IAC5D,GAAIY,EAAW,QAAUE,EAAQ,OAAO,IAAI,SAAS,oCAAoCA,CAAM,IAAK,CAAE,OAAQ,GAAI,CAAC,EAEnH,IAAMC,EAAS,MAAMV,EAAQ,eAAeH,EAAWQ,EAAK,IAAI,EAChE,OAAO,IAAI,SAAS,KAAK,UAAUK,CAAM,EAAG,CAAE,OAAQ,IAAK,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAC9G,CAEA,GAAIhB,EAAQ,SAAW,SAAWI,EAAU,SAAW,EAAG,CACxD,IAAMK,EAAOL,EAAU,CAAC,EAClBO,EAAO,MAAMX,EAAQ,KAAK,EAChC,OAAKW,EAAK,KACLC,GAAc,KAAKD,EAAK,IAAI,GAEd,MAAML,EAAQ,gBAAgBH,CAAS,GAC3C,KAAKW,GAAMA,EAAG,KAAOL,GAAQK,EAAG,KAAK,YAAY,IAAMH,EAAK,KAAK,YAAY,CAAC,EACpF,IAAI,SAAS,mCAAoC,CAAE,OAAQ,GAAI,CAAC,GAGzE,MAAML,EAAQ,sBAAsBG,EAAMN,EAAWQ,EAAK,IAAI,EACvD,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,GAR/D,IAAI,SAAS,mCAAoC,CAAE,OAAQ,GAAI,CAAC,EADpF,IAAI,SAAS,mBAAoB,CAAE,OAAQ,GAAI,CAAC,CAUzE,CAEA,GAAIX,EAAQ,SAAW,UAAYI,EAAU,SAAW,EAAG,CACzD,IAAMK,EAAOL,EAAU,CAAC,EACxB,aAAME,EAAQ,kBAAkBG,EAAMN,CAAS,EACxC,IAAI,SAAS,KAAM,CAAE,OAAQ,GAAI,CAAC,CAC3C,CAEA,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,CAC3D,CC3DA,eAAsBc,GACpBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAY,IAAIC,GAAUN,EAAI,EAAE,EAEtC,GAAID,EAAQ,SAAW,MAAO,CAC5B,IAAMQ,EAAU,MAAMF,EAAU,SAASH,CAAS,EAClD,OAAO,IAAI,SAAS,KAAK,UAAUK,CAAO,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAClG,CAEA,GAAIR,EAAQ,SAAW,OAAQ,CAC7B,IAAMS,EAAO,MAAMT,EAAQ,KAAK,EAIhC,GADe,MAAM,QAAQS,CAAI,GAAMA,GAAQ,MAAM,QAAQA,EAAK,KAAK,EAC3D,CACV,IAAMC,EAAiB,MAAM,QAAQD,CAAI,EAAIA,EAAOA,EAAK,MACnDE,EAAO,IAAI,IACXC,EAA8B,CAAC,EAErC,QAAWC,KAAKH,EAAS,CACvB,GAAI,CAACG,GAAK,OAAOA,EAAE,SAAY,SAAU,SACzC,IAAMC,EAAUD,EAAE,QAAQ,KAAK,EAAE,YAAY,EAC7C,GAAI,CAACC,GAAWH,EAAK,IAAIG,CAAO,EAAG,SACnCH,EAAK,IAAIG,CAAO,EAEhB,IAAMC,EAAO,OAAOF,EAAE,MAAQ,OAAO,EAAE,YAAY,EAC/CE,IAAS,SAAWA,IAAS,SAAWA,IAAS,YAErDH,EAAW,KAAK,CACd,KAAMG,EACN,QAAAD,EACA,IAAKD,EAAE,KAAO,KACd,OAAQA,EAAE,QAAU,KACpB,MAAOA,EAAE,OAAS,KAClB,QAASA,EAAE,SAAW,KACtB,WAAY,OAAOA,EAAE,YAAe,SAAWA,EAAE,WAAa,MAChE,CAAC,CACH,CAEA,GAAID,EAAW,SAAW,EACxB,OAAO,IAAI,SAAS,KAAK,UAAU,CAAE,MAAO,EAAG,QAAS,yBAA0B,CAAC,EAAG,CACpF,OAAQ,IACR,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,EAGH,IAAMI,EAAgB,MAAMV,EAAU,SAASH,CAAS,EAClDc,EAAmB,IAAI,IAAID,EAAc,IAAIH,GAAKA,EAAE,QAAQ,KAAK,EAAE,YAAY,CAAC,CAAC,EACjFK,EAAgBN,EAAW,OAAOC,GAAK,CAACI,EAAiB,IAAIJ,EAAE,OAAQ,CAAC,EAE1EM,EAAgB,EACpB,OAAID,EAAc,OAAS,IACzBC,EAAgB,MAAMb,EAAU,aAAaH,EAAWe,CAAa,EACrEb,EAAI,UAAUe,GAAS,WAAWjB,EAAW,GAAOF,CAAG,CAAC,GAGnD,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,GAAM,MAAOkB,CAAc,CAAC,EAAG,CAC3E,OAAQ,IACR,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAEA,IAAME,EAAOZ,EACPK,EAAUO,EAAK,QAAUA,EAAK,QAAQ,KAAK,EAAI,GACrD,OAAKP,EAGY,MAAMR,EAAU,iBAAiBH,EAAWW,CAAO,EAE3D,IAAI,SAAS,sCAAuC,CAAE,OAAQ,GAAI,CAAC,GAE5E,MAAMR,EAAU,QAAQH,EAAWkB,CAAI,EACvChB,EAAI,UAAUe,GAAS,WAAWjB,EAAW,GAAOF,CAAG,CAAC,EACjD,IAAI,SAAS,KAAM,CAAE,OAAQ,GAAI,CAAC,GARhC,IAAI,SAAS,iCAAkC,CAAE,OAAQ,GAAI,CAAC,CASzE,CAEA,GAAID,EAAQ,SAAW,MAAO,CAC5B,IAAMqB,EAAO,MAAMrB,EAAQ,KAAK,EAC1Bc,EAAUO,EAAK,QAAUA,EAAK,QAAQ,KAAK,EAAI,GACrD,OAAKP,EAGY,MAAMR,EAAU,0BAA0BH,EAAWW,EAASO,EAAK,EAAE,EAE7E,IAAI,SAAS,sCAAuC,CAAE,OAAQ,GAAI,CAAC,GAE5E,MAAMf,EAAU,WAAWe,EAAK,GAAIlB,EAAWkB,CAAI,EACnDhB,EAAI,UAAUe,GAAS,WAAWjB,EAAW,GAAOF,CAAG,CAAC,EACjD,IAAI,SAAS,KAAM,CAAE,OAAQ,GAAI,CAAC,GARhC,IAAI,SAAS,iCAAkC,CAAE,OAAQ,GAAI,CAAC,CASzE,CAEA,GAAID,EAAQ,SAAW,SAAU,CAC/B,GAAM,CAAE,GAAAsB,CAAG,EAAI,MAAMtB,EAAQ,KAAK,EAClC,aAAMM,EAAU,WAAWgB,EAAInB,CAAS,EACxCE,EAAI,UAAUe,GAAS,WAAWjB,EAAW,GAAOF,CAAG,CAAC,EACjD,IAAI,SAAS,KAAM,CAAE,OAAQ,GAAI,CAAC,CAC3C,CAEA,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,CAC3D,CC7GO,IAAMsB,GAAN,KAAgB,CACrB,YAAoBC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAEpB,MAAM,SAASC,EAAoC,CACjD,GAAM,CAAE,QAAAC,CAAQ,EAAI,MAAM,KAAK,GAAG,QAAQ,0CAA0C,EAAE,KAAKD,CAAS,EAAE,IAAU,EAChH,OAAOC,CACT,CAEA,MAAM,QAAQD,EAAmBE,EAA+B,CAE9D,OADe,MAAM,KAAK,GAAG,QAAQ,mDAAmD,EAAE,KAAKF,EAAWE,CAAG,EAAE,IAAI,GACrG,OAChB,CAUA,MAAM,aAAaF,EAAmBG,EAAkF,CACtH,GAAI,CAACA,GAASA,EAAM,SAAW,EAAG,MAAO,GACzC,IAAMC,EAAaD,EAAM,IAAIE,GAAQ,CACnC,IAAMH,EAAM,OAAOG,GAAS,SAAWA,EAAK,KAAK,EAAIA,EAAK,IAAI,KAAK,EAC7DC,EAAW,OAAOD,GAAS,UAAYA,EAAK,UAAY,OACzDA,EAAK,QAAU,EAAI,EACpB,EACJ,OAAO,KAAK,GAAG,QAAQ,+DAA+D,EAAE,KAAKL,EAAWE,EAAKI,CAAO,CACtH,CAAC,EAEGC,EAAW,EACf,QAASC,EAAI,EAAGA,EAAIJ,EAAW,OAAQI,GAAK,IAAK,CAC/C,IAAMC,EAAQL,EAAW,MAAMI,EAAGA,EAAI,GAAG,EACnCP,EAAU,MAAM,KAAK,GAAG,MAAMQ,CAAK,EACzC,QAAWC,KAAOT,EACZS,EAAI,UAASH,GAAYG,EAAI,KAAK,SAAW,EAErD,CACA,OAAOH,CACT,CAQA,MAAM,WAAWI,EAAYX,EAAmBM,EAAoC,CAKlF,OAJe,MAAM,KAAK,GACvB,QAAQ,8DAA8D,EACtE,KAAKA,EAAU,EAAI,EAAGK,EAAIX,CAAS,EACnC,IAAI,GACO,OAChB,CAGA,MAAM,YAAYW,EAAYX,EAAyC,CACrE,OAAO,MAAM,KAAK,GACf,QAAQ,qDAAqD,EAC7D,KAAKW,EAAIX,CAAS,EAClB,MAAmB,CACxB,CAEA,MAAM,WAAWW,EAAYX,EAAqC,CAEhE,OADe,MAAM,KAAK,GAAG,QAAQ,mDAAmD,EAAE,KAAKW,EAAIX,CAAS,EAAE,IAAI,GACpG,OAChB,CAMA,MAAM,qBAAqBW,EAAYC,EAAoBN,EAAyBO,EAA2B,KAAwB,CACrI,OAA6BP,GAAY,MACxB,MAAM,KAAK,GAAG,QAC3B,+EACF,EACG,KAAKM,EAAKN,EAASO,EAAWF,CAAE,EAChC,IAAI,GACO,SAEC,MAAM,KAAK,GAAG,QAC3B,kEACF,EACG,KAAKC,EAAKC,EAAWF,CAAE,EACvB,IAAI,GACO,OAElB,CAEA,MAAM,oBAAoBX,EAAqC,CAM7D,OALe,MAAM,KAAK,GAAG,QAC3B,0DACF,EACG,KAAKA,CAAS,EACd,IAAI,GACO,OAChB,CACF,ECnGO,IAAMc,GAAN,KAAqB,CAC1B,YAAoBC,EAAgB,CAAhB,QAAAA,CAAiB,CAAjB,GAEpB,MAAM,gBAAgBC,EAAgBC,EAA0BC,EAA+B,CAC7F,IAAMC,EAAa,IAAI,WAAWF,CAAW,EACvCG,EAAa,CAAC,EAGpBA,EAAW,KACT,KAAK,GAAG,QAAQ,2CAA2C,EAAE,KAAKJ,CAAM,CAC1E,EAGA,IAAIK,EAAa,EACjB,QAASC,EAAS,EAAGA,EAASH,EAAW,OAAQG,GAAU,QAAkB,CAC3E,IAAMC,EAAYJ,EAAW,MAAMG,EAAQA,EAAS,OAAgB,EAAE,OACtEF,EAAW,KACT,KAAK,GAAG,QACN,oGACF,EAAE,KAAKJ,EAAQK,EAAYE,EAAWL,CAAG,CAC3C,EACAG,GACF,CAGA,OADgB,MAAM,KAAK,GAAG,MAAMD,CAAU,GAC/B,MAAMI,GAAKA,EAAE,OAAO,CACrC,CAUA,MAAM,aAAaR,EAAkC,CAInD,MAAO,CAAC,CAHI,MAAM,KAAK,GAAG,QACxB,gEACF,EAAE,KAAKA,CAAM,EAAE,MAA2B,CAE5C,CAEA,MAAM,aAAaA,EAA6C,CAC9D,GAAM,CAAE,QAAAS,CAAQ,EAAI,MAAM,KAAK,GAAG,QAChC,uFACF,EACG,KAAKT,CAAM,EACX,IAAyC,EAE5C,GAAI,CAACS,GAAWA,EAAQ,SAAW,EAAG,OAAO,KAE7C,IAAMC,EAAcD,EAAQ,OAAO,CAACE,EAAKC,IAAQ,CAC/C,IAAMC,EAAMD,EAAI,mBAAmB,YAAeA,EAAI,mBAA2B,OACjF,OAAOD,EAAME,CACf,EAAG,CAAC,EACEC,EAAW,IAAI,WAAWJ,CAAW,EAEvCJ,EAAS,EACb,QAAWM,KAAOH,EAAS,CACzB,IAAMM,EAAQ,IAAI,WAAWH,EAAI,kBAAkB,EACnDE,EAAS,IAAIC,EAAOT,CAAM,EAC1BA,GAAUS,EAAM,MAClB,CAEA,OAAOD,EAAS,MAClB,CAEA,MAAM,gBAAgBd,EAAkC,CAMtD,OALe,MAAM,KAAK,GAAG,QAC3B,2CACF,EACG,KAAKA,CAAM,EACX,IAAI,GACO,OAChB,CAEA,MAAM,8BAA8BgB,EAA2C,CAC7E,GAAM,CAAE,QAAAP,CAAQ,EAAI,MAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMzC,EACE,KAAKO,CAAS,EACd,IAA0D,EAE7D,GAAI,CAACP,GAAWA,EAAQ,SAAW,EAAG,MAAO,CAAC,EAG9C,IAAMQ,EAAS,IAAI,IACnB,QAAWL,KAAOH,EACXQ,EAAO,IAAIL,EAAI,OAAO,GACzBK,EAAO,IAAIL,EAAI,QAAS,CAAC,CAAC,EAE5BK,EAAO,IAAIL,EAAI,OAAO,EAAG,KAAKA,EAAI,kBAAkB,EAItD,IAAMM,EAAwB,CAAC,EAC/B,OAAW,CAAC,CAAEC,CAAM,IAAKF,EAAO,QAAQ,EAAG,CACzC,IAAMP,EAAcS,EAAO,OAAO,CAACR,EAAKI,IAAU,CAChD,IAAMF,EAAME,EAAM,YAAeA,EAAc,OAC/C,OAAOJ,EAAME,CACf,EAAG,CAAC,EACEC,EAAW,IAAI,WAAWJ,CAAW,EACvCJ,EAAS,EACb,QAAWS,KAASI,EAAQ,CAC1B,IAAMC,EAAW,IAAI,WAAWL,CAAK,EACrCD,EAAS,IAAIM,EAAUd,CAAM,EAC7BA,GAAUc,EAAS,MACrB,CACAF,EAAO,KAAKJ,EAAS,MAAqB,CAC5C,CAEA,OAAOI,CACT,CACF,EC3GA,eAAsBG,GACpBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACe,CAEf,IAAMC,GADQ,MAAMT,EAAU,SAASD,CAAS,GACtB,OAAQW,GAAM,CAAC,CAACA,EAAE,OAAO,EAEnD,GAAID,EAAY,SAAW,EAEzB,MAAMP,EAAkB,mBAAmBH,CAAS,MAC/C,CAEL,IAAMY,EAASC,GAAY,OAAON,EAAYC,CAAiB,EAC3DM,EAAe,GAEnB,QAAWC,KAAQL,EACjB,GAAI,CAEF,IAAMM,EAAM,MAAMd,EAAe,aAAaa,EAAK,EAAE,EACrD,GAAIC,EAAK,CACP,IAAMC,EAASJ,GAAY,eAAe,IAAI,WAAWG,CAAG,CAAC,EAC7DJ,EAAO,MAAMK,CAAM,EACnBH,EAAe,EACjB,CACF,OAASI,EAAK,CACZ,QAAQ,MAAM,oDAAoDH,EAAK,EAAE,IAAKG,CAAG,CACnF,CAGF,GAAIJ,EAAc,CAChB,IAAMK,EAASP,EAAO,aAAa,EACnC,MAAMT,EAAkB,mBAAmBH,EAAWmB,EAAO,OAAuBb,CAAG,CACzF,MACE,MAAMH,EAAkB,mBAAmBH,CAAS,CAExD,CAGA,MAAMI,EAAa,oBAAoBJ,EAAWM,CAAG,EAEjDD,GAAO,OAAOA,EAAI,WAAc,YAClCA,EAAI,UAAUe,GAAc,MAAMpB,EAAW,GAAMS,CAAG,CAAC,CAE3D,CCjEA,SAASY,GAASC,EAAsB,CACtC,GAAIA,EAAI,SAAS,GAAG,EAAG,MAAO,GAE9B,IAAIC,EAAW,EACTC,EAAMF,EAAI,OAChB,GAAIE,EAAM,GAAKA,EAAM,GAAI,MAAO,GAChC,QAASC,EAAI,EAAGA,EAAID,EAAKC,IAAK,CAC5B,IAAMC,EAAOJ,EAAI,WAAWG,CAAC,EAC7B,GAAIC,IAAS,GACXH,YACSG,EAAO,IAAMA,EAAO,GAC7B,MAAO,EAEX,CACA,OAAOH,IAAa,CACtB,CAMO,SAASI,GAAUC,EAA6B,CAErD,IAAIC,EAAQ,EACNL,EAAMI,EAAK,OACjB,KAAOC,EAAQL,GAAK,CAClB,IAAME,EAAOE,EAAK,WAAWC,CAAK,EAClC,GAAIH,IAAS,IAAMA,IAAS,GAAKA,IAAS,IAAMA,IAAS,GACvDG,QAEA,MAEJ,CAEA,IAAIC,EAAMN,EACV,KAAOM,EAAMD,GAAO,CAClB,IAAMH,EAAOE,EAAK,WAAWE,EAAM,CAAC,EACpC,GAAIJ,IAAS,IAAMA,IAAS,GAAKA,IAAS,IAAMA,IAAS,GACvDI,QAEA,MAEJ,CAEA,GAAID,GAASC,EACX,OAAO,KAIT,IAAMC,EAAYH,EAAK,WAAWC,CAAK,EAIvC,GAHIE,IAAc,IAAMA,IAAc,IAGlCA,IAAc,IAAMF,EAAQ,EAAIC,GAAOF,EAAK,WAAWC,EAAQ,CAAC,IAAM,GACxE,OAAO,KAGT,IAAMG,EAAYJ,EAAK,UAAUC,EAAOC,CAAG,EAG3C,GAAIE,EAAU,WAAW,IAAI,EAAG,CAC9B,IAAIC,EAASD,EAAU,UAAU,CAAC,EAC5BE,EAAWD,EAAO,QAAQ,GAAG,EAC/BC,IAAa,KACfD,EAASA,EAAO,UAAU,EAAGC,CAAQ,GAEvC,IAAMC,EAAYF,EAAO,QAAQ,GAAG,EACpC,OAAIE,IAAc,KAChBF,EAASA,EAAO,UAAU,EAAGE,CAAS,GAExCF,EAASA,EAAO,KAAK,EAAE,YAAY,EAC5BA,GAAU,IACnB,CAGA,IAAIG,EAAa,GACXC,EAAUL,EAAU,OAC1B,QAASP,EAAI,EAAGA,EAAIY,EAASZ,IAAK,CAChC,IAAMC,EAAOM,EAAU,WAAWP,CAAC,EACnC,GAAIC,IAAS,IAAMA,IAAS,EAAG,CAC7BU,EAAaX,EACb,KACF,CACF,CAEA,GAAIW,IAAe,GACjB,OAAOJ,EAAU,YAAY,EACxB,CACL,IAAMM,EAAQN,EAAU,UAAU,EAAGI,CAAU,EAE3CG,EAAcH,EAClB,KAAOG,EAAcF,GAAS,CAC5B,IAAMX,EAAOM,EAAU,WAAWO,CAAW,EAC7C,GAAIb,IAAS,IAAMA,IAAS,EAC1Ba,QAEA,MAEJ,CAEA,IAAIC,EAAQ,GACZ,GAAID,EAAcF,EAAS,CACzB,IAAII,EAAYF,EAChB,KAAOE,EAAYJ,GAAS,CAC1B,IAAMX,EAAOM,EAAU,WAAWS,CAAS,EAC3C,GAAIf,IAAS,IAAMA,IAAS,EAC1B,MAEFe,GACF,CACAD,EAAQR,EAAU,UAAUO,EAAaE,CAAS,CACpD,CAEA,GAAID,IAAU,GACZ,GAAInB,GAASiB,CAAK,EAAG,CACnB,IAAML,EAASO,EAAM,YAAY,EACjC,OAAIP,IAAW,aAAeA,IAAW,WAAaA,IAAW,YACxDA,EAEF,IACT,KACE,QAAOK,EAAM,YAAY,MAG3B,QAAOA,EAAM,YAAY,CAE7B,CACF,CCvGA,eAAsBI,GACpBC,EACAC,EACAC,EACAC,EAC0B,CAC1B,IAAMC,EAAW,KAAK,IAAI,EAAIF,EAC1BG,EAAaL,EACbM,EAAgB,EACdC,EAAgB,EAClBC,EAEJ,GAAI,CACF,OAAa,CACX,GAAI,CAACC,GAAUJ,CAAU,EACvB,MAAO,CACL,MAAO,EACP,MAAO,uFACT,EAGF,IAAMK,EAAmBN,EAAW,KAAK,IAAI,EAC7C,GAAIM,GAAoB,EACtB,MAAO,CAAE,MAAO,EAAG,MAAO,mBAAoB,EAShD,GANAF,EAAW,MAAM,MAAMH,EAAY,CACjC,SAAU,SACV,OAAQ,YAAY,QAAQK,CAAgB,CAC9C,CAAC,EAGGF,EAAS,QAAU,KAAOA,EAAS,OAAS,IAAK,CACnD,IAAMG,EAAWH,EAAS,QAAQ,IAAI,UAAU,EAChD,GAAI,CAACG,EACH,MAAO,CACL,MAAO,EACP,MAAO,mBAAmBH,EAAS,MAAM,0BAC3C,EAIF,GADAF,IACIA,EAAgBC,EAClB,MAAO,CACL,MAAO,EACP,MAAO,2BAA2BA,CAAa,GACjD,EAGF,GAAI,CACF,IAAMK,EAAU,IAAI,IAAID,EAAUN,CAAU,EAAE,SAAS,EACjDQ,EAAY,IAAI,IAAID,CAAO,EAAE,SACnC,GAAIC,IAAc,SAAWA,IAAc,SACzC,MAAO,CACL,MAAO,EACP,MAAO,8BAA8BA,CAAS,EAChD,EAEFR,EAAaO,EACb,QACF,MAAQ,CACN,MAAO,CACL,MAAO,EACP,MAAO,8BAA8BD,CAAQ,EAC/C,CACF,CACF,CAEA,KACF,CAEA,GAAI,CAACH,EAAS,GACZ,MAAO,CACL,MAAO,EACP,MAAO,uBAAuBA,EAAS,MAAM,IAAIA,EAAS,UAAU,EACtE,EAIF,IAAMM,EAAgBN,EAAS,QAAQ,IAAI,gBAAgB,EAC3D,GAAIM,GAAiB,SAASA,EAAe,EAAE,EAAIb,EACjD,MAAO,CACL,MAAO,EACP,MAAO,oBAAoB,SAASa,EAAe,EAAE,EAAI,KAAO,MAAM,QAAQ,CAAC,CAAC,mBAAmBb,EAAW,KAAO,MAAM,QAAQ,CAAC,CAAC,KACvI,EAGF,IAAMc,EAASP,EAAS,MAAM,UAAU,EACxC,GAAI,CAACO,EACH,MAAO,CAAE,MAAO,EAAG,MAAO,2CAA4C,EAGxE,IAAMC,EAAU,IAAI,YAAY,QAAS,CAAE,MAAO,GAAO,UAAW,EAAK,CAAC,EACtEC,EAAS,GACTC,EAAQ,EACRC,EAAa,EACbC,EAAY,GAEhB,OAAa,CACX,GAAM,CAAE,KAAAC,EAAM,MAAAC,CAAM,EAAI,MAAMP,EAAO,KAAK,EAC1C,GAAIM,EAAM,MACV,GAAIC,EAAO,CAET,GADAH,GAAcG,EAAM,OAChBH,EAAalB,EAAU,CACzB,MAAMc,EAAO,OAAO,EACpBK,EAAY,GACZ,KACF,CAGA,IAAMG,EAAYP,EAAQ,OAAOM,EAAO,CAAE,OAAQ,EAAK,CAAC,EACxDL,GAAUM,EAGV,IAAMC,EAAQP,EAAO,MAAM;AAAA,CAAI,EAE/BA,EAASO,EAAM,IAAI,GAAK,GAExB,QAAWC,KAAQD,EAAO,CACxB,IAAME,EAASC,GAAUF,CAAI,EACzBC,IACF,MAAMvB,EAASuB,CAAM,EACrBR,IAEJ,CACF,CACF,CAGA,GAAID,EAAQ,CACV,IAAMS,EAASC,GAAUV,CAAM,EAC3BS,IACF,MAAMvB,EAASuB,CAAM,EACrBR,IAEJ,CAEA,OAAIE,GACF,QAAQ,KAAK,gCAAgCnB,EAAW,KAAO,MAAM,QAAQ,CAAC,CAAC,QAAQD,CAAG,EAAE,EAGvF,CAAE,MAAAkB,EAAO,MAAO,IAAK,CAC9B,OAASU,EAAQ,CACf,MAAO,CAAE,MAAO,EAAG,MAAOA,EAAE,SAAW,OAAOA,CAAC,CAAE,CACnD,CACF,CCnKA,IAAMC,GAAiB,EAAI,KAAO,KAUlC,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACwB,CACxB,IAAMC,EAAY,OAAOJ,EAAI,eAAe,GAAK,IAC3CK,EAAiB,OAAOL,EAAI,gBAAgB,GAAK,IACjDM,EAAa,OAAON,EAAI,gBAAgB,GAAK,IAC7CO,EAAoB,OAAOP,EAAI,yBAAyB,GAAK,KAG7DQ,EAAYC,GAAY,OAAOH,EAAYC,CAAiB,EAC9DG,EAAQ,EAGN,CAAE,MAAOC,CAAW,EAAI,MAAMC,GAClCb,EAAK,IACLH,GACAQ,EACA,MAAOS,GAAW,CACZH,EAAQL,IACVG,EAAU,IAAIK,CAAM,EACpBH,IAGIA,EAAQ,MAAU,GACpB,MAAM,IAAI,QAASI,GAAY,WAAWA,EAAS,CAAC,CAAC,EAG3D,CACF,EAEIC,EAA2BJ,EAE/B,OAAKA,EAQH,QAAQ,KACN,kBAAkBb,CAAS,iCAAiCC,EAAK,EAAE,4CACvBY,CAAU,EACxD,GATA,MAAMT,EAAe,gBAAgBH,EAAK,GAAIS,EAAU,aAAa,EAAE,OAAuBL,CAAG,EACjG,QAAQ,IACN,kBAAkBL,CAAS,gCAAgCC,EAAK,EAAE,SAASW,CAAK,WAClF,GAUF,MAAMT,EAAU,qBAAqBF,EAAK,GAAII,EAAK,KAAMY,CAAS,EAC3DA,CACT,CC3DA,eAAsBC,GACpBC,EACAC,EACAC,EACe,CACf,IAAMC,EAAe,IAAIC,EAAaH,EAAI,EAAE,EACtCI,EAAY,IAAIC,GAAUL,EAAI,EAAE,EAChCM,EAAoB,IAAIC,GAAkBP,EAAI,EAAE,EAChDQ,EAAiB,IAAIC,GAAeT,EAAI,EAAE,EAC1CU,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAExC,GAAI,CACF,IAAMC,EAAU,MAAMT,EAAa,QAAQH,CAAS,EACpD,GAAI,CAACY,EAAS,CACZ,QAAQ,MAAM,kBAAkBZ,CAAS,aAAa,EACtD,MACF,CAEA,IAAMa,EAAuBD,EAAQ,iBAAmB,EAElDE,GADQ,MAAMT,EAAU,SAASL,CAAS,GACtB,OAAQe,GAAM,CAAC,CAACA,EAAE,OAAO,EAEnD,GAAID,EAAY,SAAW,EAAG,CAC5B,MAAMP,EAAkB,mBAAmBP,CAAS,EACpD,MAAMG,EAAa,oBAAoBH,EAAWW,CAAG,EACrD,MACF,CAGmB,CAACG,EAAY,KAAMC,IAAOA,EAAE,gBAAkB,GAAKF,CAAY,GAGhF,QAAQ,IAAI,kBAAkBb,CAAS,8BAA8B,EAIvE,IAAMgB,EAAeF,EAClB,OAAQC,IAAOA,EAAE,gBAAkB,IAAMF,CAAY,EACrD,KAAK,CAACI,EAAGC,KAAOD,EAAE,gBAAkB,IAAMC,EAAE,gBAAkB,EAAE,EAEnE,GAAIF,EAAa,SAAW,EAAG,CAE7B,IAAMG,EAAa,OAAOlB,EAAI,gBAAgB,GAAK,IAC7CmB,EAAoB,OAAOnB,EAAI,yBAAyB,GAAK,KAInE,MAAME,EAAa,oBAAoBH,EAAWW,CAAG,EAErD,MAAMU,GACJrB,EACAK,EACAI,EACAF,EACAJ,EACAD,EACAS,EACAQ,EACAC,EACAnB,CACF,EACA,MACF,CAGA,IAAMqB,EAAON,EAAa,CAAC,EAC3B,MAAMO,GAAevB,EAAWsB,EAAMrB,EAAKI,EAAWI,EAAgBE,CAAG,EAIzE,IAAMa,GADe,MAAMnB,EAAU,SAASL,CAAS,GACf,OAAQe,GAAM,CAAC,CAACA,EAAE,OAAO,EAGjE,GAFgBS,EAAmB,MAAOT,IAAOA,EAAE,gBAAkB,GAAKF,CAAY,EAEzE,CACX,IAAMM,EAAa,OAAOlB,EAAI,gBAAgB,GAAK,IAC7CmB,EAAoB,OAAOnB,EAAI,yBAAyB,GAAK,KAGnE,MAAME,EAAa,oBAAoBH,EAAWW,CAAG,EAErD,MAAMU,GACJrB,EACAK,EACAI,EACAF,EACAJ,EACAD,EACAS,EACAQ,EACAC,EACAnB,CACF,EACA,QAAQ,IACN,kBAAkBD,CAAS,SAASwB,EAAmB,MAAM,2DAE/D,CACF,KAAO,CACL,IAAMC,EAAYD,EAAmB,OAClCT,IAAOA,EAAE,gBAAkB,IAAMF,CACpC,EAAE,OACF,QAAQ,IACN,kBAAkBb,CAAS,KAAKyB,CAAS,0CAC3C,CACF,CACF,OAASC,EAAG,CACV,QAAQ,MAAM,uCAAuC1B,CAAS,IAAK0B,CAAC,EAEpE,MAAMvB,EAAa,oBAAoBH,EAAWW,CAAG,CACvD,CACF,CAKA,eAAsBgB,GACpB3B,EACAC,EACAC,EACe,CACf,IAAMC,EAAe,IAAIC,EAAaH,EAAI,EAAE,EACtCI,EAAY,IAAIC,GAAUL,EAAI,EAAE,EAChCM,EAAoB,IAAIC,GAAkBP,EAAI,EAAE,EAChDQ,EAAiB,IAAIC,GAAeT,EAAI,EAAE,EAC1CU,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAExC,GAAI,CAEF,GAAI,CADY,MAAMR,EAAa,QAAQH,CAAS,EACtC,CACZ,QAAQ,MAAM,kBAAkBA,CAAS,aAAa,EACtD,MACF,CAGA,IAAMc,GADQ,MAAMT,EAAU,SAASL,CAAS,GACtB,OAAQe,GAAM,CAAC,CAACA,EAAE,OAAO,EAEnD,GAAID,EAAY,SAAW,EAAG,CAC5B,MAAMP,EAAkB,mBAAmBP,CAAS,EACpD,MAAMG,EAAa,oBAAoBH,EAAWW,CAAG,EACrD,MACF,CAGA,QAAWW,KAAQR,EACjB,MAAMS,GAAevB,EAAWsB,EAAMrB,EAAKI,EAAWI,EAAgBE,CAAG,EAI3E,MAAMR,EAAa,oBAAoBH,EAAWW,CAAG,EAErD,IAAMQ,EAAa,OAAOlB,EAAI,gBAAgB,GAAK,IAC7CmB,EAAoB,OAAOnB,EAAI,yBAAyB,GAAK,KAGnE,MAAMoB,GACJrB,EACAK,EACAI,EACAF,EACAJ,EACAD,EACAS,EACAQ,EACAC,EACAnB,CACF,EACA,QAAQ,IAAI,kBAAkBD,CAAS,+BAA+B,CACxE,OAAS0B,EAAG,CACV,QAAQ,MAAM,sDAAsD1B,CAAS,IAAK0B,CAAC,EAEnF,MAAMvB,EAAa,oBAAoBH,EAAWW,CAAG,CACvD,CACF,CAMA,eAAsBiB,GACpB5B,EACAC,EACAC,EACe,CACf,IAAMG,EAAY,IAAIC,GAAUL,EAAI,EAAE,EAChCQ,EAAiB,IAAIC,GAAeT,EAAI,EAAE,EAC1CM,EAAoB,IAAIC,GAAkBP,EAAI,EAAE,EAChDE,EAAe,IAAIC,EAAaH,EAAI,EAAE,EAGtC4B,GADU,MAAM1B,EAAa,QAAQH,CAAS,IAChB,iBAAmB,EAMjDc,GAAe,MAAMT,EAAU,SAASL,CAAS,GAAG,OAAQe,GAAM,CAAC,CAACA,EAAE,OAAO,EAe7Ee,GAdW,MAAM,QAAQ,IAC7BhB,EAAY,IAAKC,GACfN,EACG,aAAaM,EAAE,EAAE,EAGjB,MAAM,IAAM,EAAI,CACrB,CACF,GAM8B,UAAWgB,GAAY,CAACA,CAAO,EAC7D,GAAID,IAAiB,GAAI,CACvB,IAAME,EAAUlB,EAAYgB,CAAY,EACxC,GAAI,CACF,MAAMP,GACJvB,EACAgC,EACA/B,EACAI,EACAI,EACA,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,CAC9B,CACF,OAASiB,EAAG,CACV,QAAQ,MAAM,wBAAwBM,EAAQ,EAAE,0BAA2BN,CAAC,CAC9E,CACF,CAEA,MAAML,GACJrB,EACAK,EACAI,EACAF,EACAJ,EACAD,EACA,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAC5B,OAAOD,EAAI,gBAAgB,GAAK,IAChC,OAAOA,EAAI,yBAAyB,GAAK,KACzCA,CACF,EAKA,MAAME,EAAa,oBAAoBH,EAAW6B,CAAkB,CACtE,CClPA,eAAsBI,GACpBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAY,IAAIC,GAAUN,EAAI,EAAE,EAChCO,EAAe,IAAIC,EAAaR,EAAI,EAAE,EAE5C,GAAID,EAAQ,SAAW,MAAO,CAQ5B,GAAII,EAAU,CAAC,IAAM,QAAS,CAC5B,IAAMM,EAAS,OAAON,EAAU,CAAC,CAAC,EAClC,GAAI,CAAC,OAAO,UAAUM,CAAM,GAAKA,GAAU,EACzC,OAAO,IAAI,SAAS,kBAAmB,CAAE,OAAQ,GAAI,CAAC,EAKxD,IAAMC,GAFa,IAAI,IAAIX,EAAQ,GAAG,EAAE,aAAa,IAAI,QAAQ,GAAK,IAAI,KAAK,EAAE,YAAY,EAEpE,QAAQ,OAAQ,EAAE,EAC3C,GAAI,CAACW,GAAUA,EAAO,OAAS,KAAO,CAAC,iBAAiB,KAAKA,CAAM,EACjE,OAAO,IAAI,SAAS,sCAAuC,CAAE,OAAQ,GAAI,CAAC,EAG5E,IAAMC,EAAO,MAAMN,EAAU,YAAYI,EAAQP,CAAS,EAC1D,GAAI,CAACS,EAAM,OAAO,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAEhE,IAAMC,EAAS,MAAM,IAAIC,GAAeb,EAAI,EAAE,EAAE,aAAaS,CAAM,EACnE,GAAI,CAACG,EACH,OAAO,IAAI,SAAS,KAAK,UAAU,CAAE,OAAAF,EAAQ,QAAS,GAAO,OAAQ,EAAM,CAAC,EAAG,CAC7E,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,EAGH,IAAII,EACJ,GAAI,CACFA,EAAQC,GAAY,eAAe,IAAI,WAAWH,CAAM,CAAC,CAC3D,OAASI,EAAK,CACZ,eAAQ,MAAM,wDAAwDP,CAAM,IAAKO,CAAG,EAC7E,IAAI,SAAS,KAAK,UAAU,CAAE,OAAAN,EAAQ,QAAS,GAAO,OAAQ,GAAO,MAAO,kBAAmB,CAAC,EAAG,CACxG,OAAQ,IACR,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAEA,IAAIO,EAAoBP,EACpBQ,EAAyB,KAC7B,KAAOD,GAAW,CAChB,GAAIH,EAAM,KAAKG,CAAS,EAAG,CAAEC,EAAUD,EAAW,KAAO,CACzD,IAAME,EAAMF,EAAU,QAAQ,GAAG,EACjC,GAAIE,IAAQ,GAAI,MAChBF,EAAYA,EAAU,MAAME,EAAM,CAAC,CACrC,CAEA,OAAO,IAAI,SAAS,KAAK,UAAU,CACjC,OAAAT,EACA,QAASQ,IAAY,KAGrB,aAAcA,EAId,kBAAmB,OAAOlB,EAAI,yBAAyB,GAAK,KAG5D,QAAS,CAAC,CAACW,EAAK,QAChB,OAAQ,EACV,CAAC,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CACzD,CAEA,IAAMS,EAAU,MAAMf,EAAU,SAASH,CAAS,EAClD,OAAO,IAAI,SAAS,KAAK,UAAUkB,CAAO,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAClG,CAEA,GAAIrB,EAAQ,SAAW,QAAS,CAC9B,IAAMU,EAAS,OAAON,EAAU,CAAC,CAAC,EAClC,GAAI,CAAC,OAAO,UAAUM,CAAM,GAAKA,GAAU,EACzC,OAAO,IAAI,SAAS,kBAAmB,CAAE,OAAQ,GAAI,CAAC,EAGxD,IAAIY,EACJ,GAAI,CACFA,EAAO,MAAMtB,EAAQ,KAAK,CAC5B,MAAQ,CACN,OAAO,IAAI,SAAS,oCAAqC,CAAE,OAAQ,GAAI,CAAC,CAC1E,CACA,OAAI,OAAOsB,EAAK,SAAY,UACnB,IAAI,SAAS,oCAAqC,CAAE,OAAQ,GAAI,CAAC,EAG7D,MAAMhB,EAAU,YAAYI,EAAQP,CAAS,EAG1C,MAAMG,EAAU,WAAWI,EAAQP,EAAWmB,EAAK,OAAO,GAU1EjB,EAAI,UACFkB,GAAoBpB,EAAWF,EAAKI,CAAG,EACpC,KAAK,IAAMmB,GAAS,WAAWrB,EAAW,GAAMF,CAAG,CAAC,EACpD,MAAOwB,GAAM,QAAQ,MAAM,oCAAoCtB,CAAS,IAAKsB,CAAC,CAAC,CACpF,EAEO,IAAI,SAAS,KAAK,UAAU,CAAE,GAAIf,EAAQ,QAASY,EAAK,OAAQ,CAAC,EAAG,CACzE,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,GAhBQ,IAAI,SAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAJ5C,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,CAqBlE,CAEA,GAAItB,EAAQ,SAAW,OAAQ,CAC7B,GAAII,EAAU,CAAC,IAAM,OAEnB,OAAAC,EAAI,UAAUqB,GAAuBvB,EAAWF,EAAKI,CAAG,CAAC,EAClD,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,cAAe,CAAC,EAAG,CAAE,OAAQ,GAAI,CAAC,EAGlF,IAAMiB,EAAO,MAAMtB,EAAQ,KAAK,EAG1B2B,EAAgB,MAAM,QAAQL,CAAI,EACpCA,EACCA,GAAM,OAASA,GAAM,MAAQA,GAAM,MAAQA,GAAM,KAAOA,GAAM,SAAWA,GAAM,QAAUA,GAAM,YAAcA,GAAM,UAGxH,GADe,MAAM,QAAQK,CAAa,EAC9B,CACV,IAAMC,EAAiBD,EACjBE,EAAO,IAAI,IACXC,EAAiD,CAAC,EAExD,QAAWC,KAAQH,EAAS,CAC1B,IAAII,EAAS,OAAOD,GAAS,SACzBA,EAAK,KAAK,EACTA,GAAM,KAAOA,GAAM,MAAQA,GAAM,KAAOA,GAAM,SAAWA,GAAM,QAAUA,GAAM,QAAUA,GAAM,cAAgB,GACpH,GAAI,OAAOC,GAAW,SAAU,SAEhC,GADAA,EAASA,EAAO,QAAQ,eAAgB,EAAE,EAAE,KAAK,EAC7CA,EAAO,WAAW,IAAI,EACxBA,EAAS,SAASA,CAAM,WACf,CAACA,EAAO,WAAW,SAAS,GAAK,CAACA,EAAO,WAAW,UAAU,EACvE,GAAIA,EAAO,SAAS,GAAG,GAAK,CAACA,EAAO,SAAS,GAAG,GAAKA,EAAO,OAAS,EACnEA,EAAS,WAAWA,CAAM,OAE1B,UAGJ,GAAI,CAACC,GAAUD,CAAM,EAAG,SACxB,IAAME,EAAOF,EAAO,YAAY,EAChC,GAAIH,EAAK,IAAIK,CAAI,EAAG,SACpBL,EAAK,IAAIK,CAAI,EAEb,IAAMC,EAAWJ,GAAQ,OAAOA,GAAS,UAAYA,EAAK,UAAY,OACjEA,EAAK,QAAU,EAAI,EACpB,EACJD,EAAW,KAAK,CAAE,IAAKE,EAAQ,QAAAG,CAAQ,CAAC,CAC1C,CAEA,GAAIL,EAAW,SAAW,EACxB,OAAO,IAAI,SAAS,KAAK,UAAU,CAAE,MAAO,EAAG,QAAS,wBAAyB,CAAC,EAAG,CACnF,OAAQ,IACR,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,EAGH,IAAMM,EAAgB,MAAM9B,EAAU,SAASH,CAAS,EAClDkC,EAAe,IAAI,IAAID,EAAc,IAAIE,GAAKA,EAAE,IAAI,KAAK,EAAE,YAAY,CAAC,CAAC,EACzEC,EAAgBT,EAAW,OAAOC,GAAQ,CAACM,EAAa,IAAIN,EAAK,IAAI,YAAY,CAAC,CAAC,EAErFS,EAAgB,EACpB,OAAID,EAAc,OAAS,IACzBC,EAAgB,MAAMlC,EAAU,aAAaH,EAAWoC,CAAa,EACrElC,EAAI,UAAUoC,GAAuBtC,EAAWF,EAAKI,CAAG,CAAC,EACzDA,EAAI,UAAUmB,GAAS,WAAWrB,EAAW,GAAMF,CAAG,CAAC,GAGlD,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,GAAM,MAAOuC,CAAc,CAAC,EAAG,CAC3E,OAAQ,IACR,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAEA,GAAM,CAAE,IAAKE,CAAQ,EAAIpB,EACzB,MAAI,CAACoB,GAAY,CAACA,EAAQ,WAAW,SAAS,GAAK,CAACA,EAAQ,WAAW,UAAU,EACxE,IAAI,SAAS,0BAA2B,CAAE,OAAQ,GAAI,CAAC,EAE3DT,GAAUS,CAAO,GAItB,MAAMpC,EAAU,QAAQH,EAAWuC,CAAO,EAE1CrC,EAAI,UAAUoC,GAAuBtC,EAAWF,EAAKI,CAAG,CAAC,EACzDA,EAAI,UAAUmB,GAAS,WAAWrB,EAAW,GAAMF,CAAG,CAAC,EAChD,IAAI,SAAS,KAAM,CAAE,OAAQ,GAAI,CAAC,GAPhC,IAAI,SAAS,qEAAsE,CAAE,OAAQ,GAAI,CAAC,CAQ7G,CAEA,GAAID,EAAQ,SAAW,SAAU,CAC/B,GAAM,CAAE,GAAA2C,CAAG,EAAI,MAAM3C,EAAQ,KAAK,EAClC,aAAMM,EAAU,WAAWqC,EAAIxC,CAAS,EAExCE,EAAI,UACFkB,GAAoBpB,EAAWF,EAAKI,CAAG,EACpC,KAAK,IAAMmB,GAAS,WAAWrB,EAAW,GAAMF,CAAG,CAAC,EACpD,MAAOwB,GAAM,QAAQ,MAAM,iDAAiDtB,CAAS,IAAKsB,CAAC,CAAC,CACjG,EACO,IAAI,SAAS,KAAM,CAAE,OAAQ,GAAI,CAAC,CAC3C,CAEA,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,CAC3D,CCvOAmB,KCyDA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAgBL,EAAQ,QAAQ,IAAI,SAAS,EACnD,GAAI,CAACK,GAAiBA,EAAc,YAAY,IAAM,YACpD,OAAO,IAAI,SAAS,8BAA+B,CAAE,OAAQ,GAAI,CAAC,EAIpE,IAAMC,EAAY,WAIlB,GAAI,OAAOA,EAAU,cAAkB,IACrC,OAAO,IAAI,SAAS,yDAA0D,CAAE,OAAQ,GAAI,CAAC,EAG/F,IAAMC,EAAO,IAAID,EAAU,cACrB,CAACE,EAAUC,CAAQ,EAAI,OAAO,OAAOF,CAAI,EAG9CE,EAA+C,OAAO,EAEvD,IAAMC,EAAYP,EAAQ,GACtBQ,EAAW,GACXC,EAAgB,EAChBC,EAAS,EACTC,EAAsD,KACtDC,EAAa,GAGbC,EAA2C,KACzCC,EAAwB,IAAI,QAAeC,GAAY,CAC3DF,EAAsBE,CACxB,CAAC,EAEGd,GAAO,OAAOA,EAAI,WAAc,YAClCA,EAAI,UAAUa,CAAqB,EAOrC,IAAME,EAAkB,SAA2B,CACjD,GAAI,EAAAR,GAAYI,GAAcH,GAAiB,GAC/C,CAAAG,EAAa,GAEb,GAAI,CAYF,GAAI,CAVU,MAAMd,EAAI,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAMlC,EACE,KAAKS,EAAWE,EAAeA,EAAeC,CAAM,EACpD,MAAyC,EAI1C,OAIF,GAAM,CAAE,QAAAO,CAAQ,EAAI,MAAMnB,EAAI,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAWxC,EACE,KAAKS,EAAWE,EAAeA,EAAeC,CAAM,EACpD,IAAmB,EAEtB,GAAIO,GAAWA,EAAQ,OAAS,EAAG,CACjC,IAAMC,EAASD,EAAQ,CAAC,EAIxB,GAHAR,EAAgBS,EAAO,UACvBR,EAAS,OAAOQ,EAAO,EAAE,EAErB,CAACV,EAAU,CACb,IAAMW,EAA8B,CAClC,KAAM,WACN,KAAMF,EACN,gBAAiBR,EACjB,SAAUC,CACZ,EACAJ,EAAS,KAAK,KAAK,UAAUa,CAAO,CAAC,CACvC,CACF,CACF,OAASC,EAAc,CACrB,QAAQ,MAAM,oCAAqCA,CAAG,CACxD,QAAE,CACAR,EAAa,EACf,EACF,EAGAD,EAAe,YAAY,IAAM,CAC1BK,EAAgB,CACvB,EAAG,GAAI,EAGPV,EAAS,iBAAiB,UAAW,MAAOe,GAAwB,CAClE,GAAI,CACF,IAAMC,EAAU,OAAOD,EAAM,MAAS,SAAWA,EAAM,KAAO,IAAI,YAAY,EAAE,OAAOA,EAAM,IAAmB,EAC1GE,EAAM,KAAK,MAAMD,CAAO,EAE9B,OAAQC,EAAI,KAAM,CAChB,IAAK,YAAa,CAChBd,EAAgBc,EAAI,eACpBb,EAASa,EAAI,QAAU,EACvB,MAAMP,EAAgB,EACtB,KACF,CACA,IAAK,YAAa,CACZO,EAAI,iBAAmB,SAAWd,EAAgBc,EAAI,gBACtDA,EAAI,SAAW,SAAWb,EAASa,EAAI,QAC3C,MAAMP,EAAgB,EACtB,KACF,CACA,IAAK,WAAY,CACf,IAAMQ,EAAQ,KAAK,IAAID,EAAI,OAAS,IAAK,GAAG,EACtC,CAAE,QAAAN,CAAQ,EAAI,MAAMnB,EAAI,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAWxC,EACE,KAAKS,EAAWgB,EAAI,MAAOA,EAAI,MAAOC,CAAK,EAC3C,IAAmB,EAEtB,GAAI,CAAChB,EAAU,CACb,IAAMW,EAA8B,CAClC,KAAM,YACN,KAAMF,GAAW,CAAC,EAClB,SAAUA,GAAS,QAAU,IAAMO,CACrC,EACAlB,EAAS,KAAK,KAAK,UAAUa,CAAO,CAAC,CACvC,CACA,KACF,CACA,IAAK,OAAQ,CACX,GAAI,CAACX,EAAU,CACb,IAAMW,EAA8B,CAAE,KAAM,MAAO,EACnDb,EAAS,KAAK,KAAK,UAAUa,CAAO,CAAC,EAChCH,EAAgB,CACvB,CACA,KACF,CACA,QACE,KACJ,CACF,OAASI,EAAc,CACrB,QAAQ,MAAM,sCAAuCA,CAAG,CAC1D,CACF,CAAC,EAED,IAAMK,EAAU,IAAY,CAC1B,GAAI,CAAAjB,EACJ,CAAAA,EAAW,GACPG,IACF,cAAcA,CAAY,EAC1BA,EAAe,MAEjB,GAAI,CACFL,EAAS,MAAM,CACjB,MAAQ,CAER,CACIO,IACFA,EAAoB,EACpBA,EAAsB,MAE1B,EAEA,OAAAP,EAAS,iBAAiB,QAASmB,CAAO,EAC1CnB,EAAS,iBAAiB,QAASmB,CAAO,EAEnC,IAAI,SAAS,KAAM,CACxB,OAAQ,IACR,UAAWpB,CACb,CAAC,CACH,CD1PA,eAAsBqB,GACpBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAW,IAAIC,GAASN,EAAI,EAAE,EAC9BO,EAAYL,EAAQ,GAG1B,GAAIC,EAAU,CAAC,IAAM,OAAQ,CAE3B,GAAIA,EAAU,CAAC,IAAM,MAAQJ,EAAQ,QAAQ,IAAI,SAAS,GAAG,YAAY,IAAM,YAC7E,OAAOS,GAAoBT,EAASC,EAAKC,EAAMC,EAASE,CAAG,EAG7D,GAAIL,EAAQ,SAAW,MACrB,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,EAG3D,IAAMU,EAAY,IAAI,IAAIV,EAAQ,GAAG,EAAE,aACjCW,EAAQD,EAAU,IAAI,OAAO,GAAK,MAClCE,EAASF,EAAU,IAAI,QAAQ,EAC/BG,EAASH,EAAU,IAAI,QAAQ,EAC/BI,EAASJ,EAAU,IAAI,QAAQ,EAC/BK,EAAgBL,EAAU,IAAI,iBAAiB,EAC/CM,EAAcN,EAAU,IAAI,cAAc,EAC1CO,EAAMP,EAAU,IAAI,KAAK,EACzBQ,EAASR,EAAU,IAAI,QAAQ,GAAK,OACpCS,EAAaT,EAAU,IAAI,aAAa,GAAK,OAC7CU,EAAaV,EAAU,IAAI,aAAa,GAAK,OAC7CW,EAASX,EAAU,IAAI,QAAQ,GAAK,OACpCY,EAAaZ,EAAU,IAAI,OAAO,EAClCa,EAAWb,EAAU,IAAI,KAAK,EAEhCc,EACAC,EAAQ,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAClCC,EAAqBzB,EAAI,yBAA2B,QAAaA,EAAI,yBAA2B,GAClG,OAAOA,EAAI,sBAAsB,EACjC,GACE0B,EAA4B,KAAK,MAAMxB,EAAQ,QAAQ,EACvDyB,EAAmBD,EAAS,qBAAuB,OACrD,KAAK,IAAI,OAAOA,EAAS,kBAAkB,EAAGD,CAAkB,EAChEA,EAEJ,GAAIE,IAAqB,EACvB,OAAIxB,EAAU,OAAS,GAAKA,EAAU,CAAC,IAAM,SACpC,IAAI,SAAS,gBAAiB,CAAE,OAAQ,GAAI,CAAC,EAE/C,IAAI,SAAS,KAAK,UAAU,CAAC,CAAC,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,EAG7F,IAAMyB,EAAqB,KAAK,MAAMJ,EAASG,EAAmB,GAAK,IAAK,EAE5E,GAAIN,GAAcC,EAChBC,EAAQ,KAAK,IAAI,SAASF,CAAU,EAAGO,CAAkB,EACzDJ,EAAQ,SAASF,CAAQ,MACpB,CAEL,OADAC,EAAQC,EACAd,EAAO,CACb,IAAK,MAAOa,GAAS,IAAK,MAC1B,IAAK,KAAMA,GAAS,KAAM,MAC1B,IAAK,MAAOA,GAAS,MAAO,MAC5B,IAAK,KAAMA,GAAS,OAAQ,MAC5B,IAAK,MAAOA,GAAS,OAAS,MAC9B,QAASA,GAAS,MAAO,KAC3B,CACAA,EAAQ,KAAK,IAAIA,EAAOK,CAAkB,CAC5C,CAEA,GAAIzB,EAAU,OAAS,EAAG,CACxB,GAAIA,EAAU,CAAC,IAAM,SAAU,CAC7B,IAAM0B,GAAU,MAAMxB,EAAS,QAAQE,EAAW,CAChD,MAAAgB,EACA,MAAAC,EACA,OAAQZ,GAAU,OAClB,OAAQC,GAAU,OAClB,gBAAiBC,GAAiB,OAClC,aAAcC,GAAe,OAC7B,IAAKC,GAAO,OACZ,OAAAC,EACA,YAAaC,EACb,YAAaC,EACb,OAAAC,EACA,OAAQ,EACV,CAAC,EACD,OAAO,IAAI,SAAS,KAAK,UAAUS,EAAO,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAClG,CAEA,IAAMC,EAAQ,SAAS3B,EAAU,CAAC,EAAG,EAAE,EACvC,GAAI,MAAM2B,CAAK,EACb,OAAO,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAGvD,IAAMC,GAAiBtB,EAAU,IAAI,WAAW,EAC1CuB,GAAYD,GAAiB,SAASA,GAAgB,EAAE,EAAI,OAE5DE,EAAY,MAAM5B,EAAS,OAAOE,EAAWuB,EAAOE,EAAS,EACnE,OAAKC,EAGE,IAAI,SAAS,KAAK,UAAUA,CAAS,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,EAFzF,IAAI,SAAS,gBAAiB,CAAE,OAAQ,GAAI,CAAC,CAGxD,CAEA,IAAIC,EAAQ,SAASzB,EAAU,IAAI,OAAO,GAAK,KAAM,EAAE,EACnD,MAAMyB,CAAK,GAAKA,GAAS,EAC3BA,EAAQ,GACCA,EAAQ,MACjBA,EAAQ,KAGV,IAAML,EAAU,MAAMxB,EAAS,QAAQE,EAAW,CAChD,MAAAgB,EACA,MAAAC,EACA,OAAQZ,GAAU,OAClB,OAAQC,GAAU,OAClB,OAAQF,EAAS,SAASA,CAAM,EAAI,OACpC,MAAAuB,EACA,gBAAiBpB,GAAiB,OAClC,aAAcC,GAAe,OAC7B,IAAKC,GAAO,OACZ,OAAAC,EACA,YAAaC,EACb,YAAaC,EACb,OAAAC,CACF,CAAC,EACD,OAAO,IAAI,SAAS,KAAK,UAAUS,CAAO,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAClG,CAGA,GAAI1B,EAAU,CAAC,IAAM,YAAa,CAChC,GAAIJ,EAAQ,SAAW,MACrB,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,EAG3D,IAAMU,EAAY,IAAI,IAAIV,EAAQ,GAAG,EAAE,aACjCW,EAAQD,EAAU,IAAI,OAAO,EAC7BY,EAAaZ,EAAU,IAAI,OAAO,EAClCa,EAAWb,EAAU,IAAI,KAAK,EAC9BK,EAAgBL,EAAU,IAAI,iBAAiB,GAAK,OACtDc,EAAmBC,EAAQ,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAAOW,EAElE,GAAId,GAAcC,EAAYC,EAAQ,SAASF,CAAU,EAAGG,EAAQ,SAASF,CAAQ,EAAGa,EAAW,4BAEjG,QAAQzB,EAAO,CACb,IAAK,MAAOa,EAAQC,EAAQ,IAAKW,EAAW,oBAAqB,MACjE,IAAK,KAAMZ,EAAQC,EAAQ,KAAMW,EAAW,oBAAqB,MACjE,IAAK,MAAOZ,EAAQC,EAAQ,MAAOW,EAAW,wBAAyB,MACvE,IAAK,KAAMZ,EAAQC,EAAQ,OAAQW,EAAW,0BAA2B,MACzE,IAAK,MAAOZ,EAAQC,EAAQ,OAASW,EAAW,0BAA2B,MAC3E,QAASZ,EAAQC,EAAQ,MAAOW,EAAW,wBAAyB,KACtE,CAGF,IAAMC,EAAcjC,EAAU,CAAC,EAC/B,GAAIiC,EAAa,CACf,IAAMC,EAAS,OAAe,QACxBC,EAAc,iBAAiB/B,CAAS,IAAI6B,CAAW,IAAI3B,EAAU,SAAS,CAAC,GAC/E8B,EAAS,MAAMC,EAAW,IAASH,EAAOC,CAAW,EAC3D,GAAIC,EACF,OAAO,IAAI,SAAS,KAAK,UAAUA,CAAM,EAAG,CAC1C,QAAS,CACP,eAAgB,mBAChB,UAAW,MACX,gBAAiB,mBACnB,CACF,CAAC,EAGH,IAAIE,EAAY,KAChB,GAAIL,IAAgB,UAAW,CAC7B,IAAMvB,EAASJ,EAAU,IAAI,QAAQ,GAAK,OAC1CgC,EAAO,MAAMpC,EAAS,WAAWE,EAAWgB,EAAOC,EAAOX,EAAQC,CAAa,CACjF,SAAWsB,IAAgB,QACzBK,EAAO,MAAMpC,EAAS,SAASE,EAAWgB,EAAOC,EAAOW,EAAUrB,CAAa,UACtEsB,IAAgB,cACzBK,EAAO,MAAMpC,EAAS,cAAcE,EAAWgB,EAAOC,EAAOV,CAAa,UACjEsB,IAAgB,cACzBK,EAAO,MAAMpC,EAAS,cAAcE,EAAWgB,EAAOC,EAAOV,CAAa,UACjEsB,IAAgB,UACzBK,EAAO,MAAMpC,EAAS,WAAWE,EAAWgB,EAAOC,EAAOV,CAAa,UAC9DsB,IAAgB,eAAgB,CACzC,IAAMM,EAAajC,EAAU,IAAI,OAAO,EAClCyB,EAAQQ,EAAa,SAASA,EAAY,EAAE,EAAI,OACtDD,EAAO,MAAMpC,EAAS,gBAAgBE,EAAWgB,EAAOC,EAAOV,EAAeoB,IAAU,QAAa,CAAC,MAAMA,CAAK,EAAIA,EAAQ,MAAS,CACxI,SAAWE,IAAgB,OAAQ,CACjC,IAAMO,EAAclC,EAAU,IAAI,cAAc,GAAK,OAC/CiC,EAAajC,EAAU,IAAI,OAAO,EAClCyB,EAAQQ,EAAa,SAASA,EAAY,EAAE,EAAI,OACtDD,EAAO,MAAMpC,EAAS,gBAAgBE,EAAWoC,EAAapB,EAAOC,EAAOV,EAAeoB,IAAU,QAAa,CAAC,MAAMA,CAAK,EAAIA,EAAQ,MAAS,CACrJ,KACE,QAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,EAGlD,aAAMM,EAAW,IAAIH,EAAOC,EAAaG,EAAM,GAAG,EAC3C,IAAI,SAAS,KAAK,UAAUA,CAAI,EAAG,CACxC,QAAS,CACP,eAAgB,mBAChB,UAAW,OACX,gBAAiB,mBACnB,CACF,CAAC,CACH,CAEA,IAAMJ,EAAS,OAAe,QACxBO,EAAW,aAAarC,CAAS,IAAIgB,CAAK,IAAIC,CAAK,IAAIW,CAAQ,IAAIrB,GAAiB,KAAK,GACzFyB,EAAS,MAAMC,EAAW,IAASH,EAAOO,CAAQ,EACxD,GAAIL,EACF,OAAO,IAAI,SAAS,KAAK,UAAUA,CAAM,EAAG,CAC1C,QAAS,CACP,eAAgB,mBAChB,UAAW,MACX,gBAAiB,mBACnB,CACF,CAAC,EAGH,IAAMM,EAAY,MAAMxC,EAAS,aAAaE,EAAWgB,EAAOC,EAAOW,EAAUrB,CAAa,EAC9F,aAAM0B,EAAW,IAAIH,EAAOO,EAAUC,EAAW,EAAE,EAE5C,IAAI,SAAS,KAAK,UAAUA,CAAS,EAAG,CAC7C,QAAS,CACP,eAAgB,mBAChB,UAAW,OACX,gBAAiB,mBACnB,CACF,CAAC,CACH,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CEnOA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAe,IAAIC,GAAaN,EAAI,EAAE,EACtCO,EAAcJ,EAAU,CAAC,EAG/B,GAAII,IAAgB,UAAYR,EAAQ,SAAW,MAAO,CACxD,GAAM,CAACS,EAAQC,EAAaC,EAAaC,CAAQ,EAAI,MAAM,QAAQ,IAAI,CACrEX,EAAI,GAAG,QAAQ,kFAAkF,EAC9F,KAAKE,EAAQ,EAAE,EACf,MAAsE,EACzEF,EAAI,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,OAKd,EACE,KAAKC,EAAK,EAAE,EACZ,IAA4B,EAC/BD,EAAI,GAAG,QAAQ,wEAAwE,EACpF,KAAKE,EAAQ,EAAE,EACf,MAA8B,EACjCG,EAAa,WAAWJ,EAAK,EAAE,CACjC,CAAC,EAED,OAAO,SAAS,KAAK,CACnB,QAAS,EAAQO,EACjB,QAAS,GAAQA,IAAWA,EAAO,YAAc,QAAaA,EAAO,YAAc,IACnF,UAAWA,GAAQ,YAAc,KACjC,iBAAkBC,EAAY,SAAW,CAAC,GAAG,IAAKG,GAAMA,EAAE,UAAU,EACpE,eAAgB,EAAQF,EACxB,iBAAkBC,EAAS,OAC3B,UAAWH,GAAQ,YAAc,IACnC,CAAC,CACH,CAGA,GAAID,IAAgB,eAAiBR,EAAQ,SAAW,MAAO,CAE7D,IAAMc,EADM,IAAI,IAAId,EAAQ,GAAG,EACT,aAAa,IAAI,YAAY,EAE/Ce,EAAuD,KAE3D,GAAID,EAAW,CACb,IAAME,EAAU,MAAMV,EAAa,sBAAsBQ,EAAWZ,EAAK,EAAE,EACvEc,IACFD,EAAU,MAAMd,EAAI,GAAG,QACrB,6EACF,EACG,KAAKe,EAAQ,EAAE,EACf,MAA4C,EAEnD,CAeA,OAbKD,IAEHA,EAAU,MAAMd,EAAI,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAM9B,EACE,KAAKC,EAAK,EAAE,EACZ,MAA4C,GAG5Ca,EAIE,SAAS,KAAK,CACnB,YAAaA,EAAQ,aACrB,GAAIA,EAAQ,EACd,CAAC,EANQ,IAAI,SAAS,yCAA0C,CAAE,OAAQ,GAAI,CAAC,CAOjF,CAGA,GAAIP,IAAgB,QAAUR,EAAQ,SAAW,OAAQ,CACvD,IAAIiB,EACJ,GAAI,CACFA,EAAO,MAAMjB,EAAQ,KAAK,CAC5B,MAAQ,CACN,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,CAC1D,CAEA,GAAM,CAAE,UAAAkB,EAAW,UAAAJ,EAAW,YAAAK,EAAa,GAAAC,EAAI,SAAAC,CAAS,EAAIJ,EAC5D,GAAI,CAACC,GAAa,CAACJ,GAAa,CAACK,GAAe,CAACC,EAC/C,OAAO,IAAI,SAAS,kEAAmE,CACrF,OAAQ,GACV,CAAC,EAIH,IAAIJ,EAAU,MAAMV,EAAa,sBAAsBQ,EAAWZ,EAAK,EAAE,EACzE,GAAI,CAACc,EAAS,CACZ,IAAMM,EAAe,MAAMhB,EAAa,WAAWJ,EAAK,EAAE,EACtDoB,EAAa,OAAS,IACxBN,EAAUM,EAAa,CAAC,EAE5B,CACA,GAAI,CAACN,EACH,OAAO,IAAI,SAAS,oCAAqC,CAAE,OAAQ,GAAI,CAAC,EAG1E,IAAMO,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAClCC,EAAe,OAAON,GAAc,SAAWA,EAAY,KAAK,UAAUA,CAAS,EAEnFO,EAAQ,CACZxB,EAAI,GAAG,QACL,0LACF,EAAE,KAAKE,EAAQ,GAAIqB,EAAcD,CAAG,EACpCtB,EAAI,GAAG,QACL,oPACF,EAAE,KAAKe,EAAQ,GAAIb,EAAQ,GAAIgB,EAAaC,EAAIG,CAAG,CACrD,EAEA,OAAIF,GAAYA,EAAS,aAAeA,EAAS,IAAMA,EAAS,MAC9DI,EAAM,KACJxB,EAAI,GAAG,QACL,qQACF,EAAE,KAAKE,EAAQ,GAAIkB,EAAS,YAAaA,EAAS,GAAIA,EAAS,KAAME,CAAG,CAC1E,EAGF,MAAMtB,EAAI,GAAG,MAAMwB,CAAK,EACxBC,GAA6BvB,EAAQ,EAAE,EAEhC,SAAS,KAAK,CAAE,QAAS,EAAK,CAAC,CACxC,CAGA,GAAIK,IAAgB,QAAUR,EAAQ,SAAW,OAAQ,CACvD,IAAIiB,EACJ,GAAI,CACFA,EAAO,MAAMjB,EAAQ,KAAK,CAC5B,MAAQ,CACN,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,CAC1D,CAEA,GAAM,CAAE,UAAAc,EAAW,YAAAK,EAAa,GAAAC,CAAG,EAAIH,EACvC,GAAI,CAACH,GAAa,CAACK,GAAe,CAACC,EACjC,OAAO,IAAI,SAAS,uDAAwD,CAAE,OAAQ,GAAI,CAAC,EAG7F,IAAIJ,EAAU,MAAMV,EAAa,sBAAsBQ,EAAWZ,EAAK,EAAE,EACzE,GAAI,CAACc,EAAS,CACZ,IAAMM,EAAe,MAAMhB,EAAa,WAAWJ,EAAK,EAAE,EACtDoB,EAAa,OAAS,IACxBN,EAAUM,EAAa,CAAC,EAE5B,CACA,GAAI,CAACN,EACH,OAAO,IAAI,SAAS,oCAAqC,CAAE,OAAQ,GAAI,CAAC,EAG1E,IAAMO,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACxC,aAAMtB,EAAI,GAAG,QACX,oPACF,EACG,KAAKe,EAAQ,GAAIb,EAAQ,GAAIgB,EAAaC,EAAIG,CAAG,EACjD,IAAI,EAEA,SAAS,KAAK,CAAE,QAAS,EAAK,CAAC,CACxC,CAGA,OAAIvB,EAAQ,SAAW,WAAa,CAACQ,GAAeA,IAAgB,SAClE,MAAMP,EAAI,GAAG,MAAM,CACjBA,EAAI,GAAG,QAAQ,gDAAgD,EAAE,KAAKE,EAAQ,EAAE,EAChFF,EAAI,GAAG,QAAQ,4DAA4D,EAAE,KAAKE,EAAQ,EAAE,EAC5FF,EAAI,GAAG,QAAQ,6DAA6D,EAAE,KAAKE,EAAQ,EAAE,CAC/F,CAAC,EAEDuB,GAA6BvB,EAAQ,EAAE,EAChC,SAAS,KAAK,CAAE,QAAS,EAAK,CAAC,GAGjC,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CC5LA,eAAsBwB,GAAsBC,EAAkBC,EAAUC,EAAmBC,EAA0C,CAEnI,IAAMC,EADM,IAAI,IAAIJ,EAAQ,GAAG,EACT,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO,EAClDK,EAAe,IAAIC,EAAaL,EAAI,EAAE,EAG5C,GAAIG,EAAU,SAAW,EACvB,OAAKF,EACEK,GAAoCP,EAASC,EAAKC,EAAME,CAAS,EADtD,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,EAKhE,GAAIA,EAAU,QAAU,EAAG,CACzB,IAAMI,EAAYJ,EAAU,CAAC,EACvBK,EAAU,MAAMJ,EAAa,QAAQG,CAAS,EAEpD,GAAI,CAACC,EAAS,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAEtE,GAAI,CAACP,EAAM,OAAO,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,EAC9D,GAAI,CAACQ,GAAK,iBAAiBR,EAAMO,CAAO,EAAG,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAEnG,IAAME,EAAcP,EAAU,CAAC,EAG/B,GAAI,CAACO,GAAe,CAAC,aAAc,WAAY,MAAM,EAAE,SAASA,CAAW,EACzE,OAAOC,GAA0BZ,EAASC,EAAKC,EAAMO,EAASL,EAAWD,CAAG,EAG9E,OAAQQ,EAAa,CACnB,IAAK,gBACH,OAAOE,GAAiCb,EAASC,EAAKC,EAAMM,EAAWJ,EAAWD,CAAG,EACvF,IAAK,QACH,OAAOW,GAA0Bd,EAASC,EAAKC,EAAMM,EAAWJ,EAAWD,CAAG,EAChF,IAAK,QACH,OAAOY,GAA0Bf,EAASC,EAAKC,EAAMM,EAAWJ,EAAWD,CAAG,EAChF,IAAK,OACL,IAAK,YACH,OAAOa,GAAqChB,EAASC,EAAKC,EAAMO,EAASL,EAAWD,CAAG,EACzF,IAAK,OACH,OAAOc,GAAyBjB,EAASC,EAAKC,EAAMO,EAASL,EAAWD,CAAG,EAC7E,QACE,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CACpD,CACF,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CCvDAe,KASA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAY,IAAIC,EAAUL,EAAI,GAAIA,CAAG,EACrCM,EAAW,IAAIC,GAASP,EAAI,EAAE,EAC9BQ,EAASN,EAAU,CAAC,EAG1B,GAAIM,IAAW,KAAM,CACnB,GAAIT,EAAQ,SAAW,MAAO,CAC5B,IAAMU,EAAS,MAAML,EAAU,QAAQH,EAAK,EAAE,EACxCS,EAAqBV,EAAI,yBAA2B,QAAaA,EAAI,yBAA2B,GAClG,OAAOA,EAAI,sBAAsB,EACjC,GACEW,EAAoBX,EAAI,oCAAsC,QAAaA,EAAI,oCAAsC,GACvH,KAAK,IAAI,OAAOA,EAAI,iCAAiC,EAAGU,CAAkB,EAC1EA,EACEE,EAAgBZ,EAAI,qCAAuC,QAAaA,EAAI,qCAAuC,GACrH,KAAK,IAAI,OAAOA,EAAI,kCAAkC,EAAGU,CAAkB,EAC3E,KAAK,IAAI,EAAGA,CAAkB,EAG5BG,EAAgB,MADD,IAAIC,GAAad,EAAI,EAAE,EACH,YAAYC,EAAK,EAAE,EACtDc,EAAa,CAAC,CAAEN,GAAQ,cAAiBI,EAAgB,EAE3DG,EAAkB,GACtB,GAAIP,GAAQ,mBACV,GAAI,CACF,IAAMQ,EAAS,OAAOR,EAAO,oBAAuB,SAAW,KAAK,MAAMA,EAAO,kBAAkB,EAAIA,EAAO,mBAC1G,MAAM,QAAQQ,CAAM,GAAKA,EAAO,OAAS,IAC3CD,EAAkB,GAEtB,MAAQ,CACNA,EAAkB,EACpB,MACSP,GAAQ,+BACjBO,EAAkB,IAGpB,IAAME,EAAmBjB,EAAK,OAAS,QACnCkB,GAAkBnB,EAAI,UAAU,GAAK,CAACoB,GAAkBpB,EAAI,UAAU,EACtE,GAEJ,OAAO,IAAI,SAAS,KAAK,UAAU,CACjC,GAAIC,EAAK,GACT,SAAUQ,GAAQ,UAAY,GAC9B,KAAMR,EAAK,KACX,aAAc,CAAC,CAAEQ,GAAQ,aACzB,mBAAoB,CAAC,CAAEA,GAAQ,mBAC/B,eAAgBI,EAChB,YAAaE,EACb,kBAAmBC,EACnB,wBAAyB,CAAC,CAAEP,GAAQ,6BACpC,SAAUA,GAAQ,UAAY,KAC9B,OAAQA,GAAQ,QAAU,QAC1B,iBAAkBA,GAAQ,kBAAoB,EAC9C,YAAa,CAAC,CAAEA,GAAQ,SACxB,qBAAsBA,GAAQ,sBAAwB,GACtD,uBAAwBR,EAAK,OAAS,QAAUU,EAAoBC,EACpE,mBAAoBM,CACtB,CAAC,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CACzD,CAEA,GAAInB,EAAQ,SAAW,QAAS,CAC9B,IAAMsB,EAAO,MAAMtB,EAAQ,KAAK,EAChC,GAAI,CACF,GAAIsB,EAAK,uBAAyB,OAAW,CAC3C,IAAMC,EAAU,OAAOD,EAAK,oBAAoB,EAChD,MAAMjB,EAAU,yBAAyBH,EAAK,GAAIqB,CAAO,CAC3D,CAEA,GAAID,EAAK,WAAa,OAAW,CAC/B,IAAME,EAAcF,EAAK,SACzB,GAAI,CAACE,GAAe,CAACC,GAAe,KAAKD,CAAW,EAClD,OAAO,IAAI,SAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAE9D,MAAMnB,EAAU,eAAeH,EAAK,GAAIsB,CAAW,CACrD,CAEA,GAAIF,EAAK,WAAa,OAAW,CAC/B,IAAMI,EAAcJ,EAAK,SACzB,GAAII,IAAgB,MAAQ,OAAOA,GAAgB,UAAYA,IAAgB,GAC7E,GAAI,CACF,KAAK,eAAe,OAAW,CAAE,SAAUA,CAAY,CAAC,CAC1D,MAAY,CACV,OAAO,IAAI,SAAS,0BAA2B,CAAE,OAAQ,GAAI,CAAC,CAChE,CAEF,MAAMrB,EAAU,eAAeH,EAAK,GAAIwB,GAAe,IAAI,CAC7D,CAEA,GAAIJ,EAAK,SAAW,OAAW,CAC7B,IAAMK,EAAYL,EAAK,OACvB,GAAIK,IAAc,MAAQ,OAAOA,GAAc,UAAYA,IAAc,GACvE,GAAI,CACF,KAAK,eAAeA,CAAS,CAC/B,MAAY,CACV,OAAO,IAAI,SAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,CAC9D,CAEF,MAAMtB,EAAU,aAAaH,EAAK,GAAIyB,GAAa,OAAO,CAC5D,CAEA,OAAO,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,CAAC,CACvD,OAASC,EAAQ,CACf,OAAIA,EAAE,SAAS,SAAS,0BAA0B,EAAU,IAAI,SAAS,gCAAiC,CAAE,OAAQ,GAAI,CAAC,EAClH,IAAI,SAAS,4BAA6B,CAAE,OAAQ,GAAI,CAAC,CAClE,CACF,CAEA,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,CAC3D,CAGA,GAAInB,IAAW,QAAUT,EAAQ,SAAW,SAC1C,aAAMO,EAAS,cAAcL,EAAK,EAAE,EAC7B,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,CAAC,EAIvD,GAAIO,IAAW,UAAYT,EAAQ,SAAW,OAAQ,CACpD,GAAM,CAAE,kBAAA6B,CAAkB,EAAI,KAAM,uCAC9BC,EAAe9B,EAAQ,QAAQ,IAAI,QAAQ,GAAK,GAChD+B,EAAeC,GAAuBF,CAAY,EAClDG,EAAYF,GAAeG,GAAwBH,CAAY,GAAG,KAAO,KAC/E,OAAIE,GAAW,MAAMJ,EAAkB5B,EAAKgC,CAAS,EACrD,MAAM5B,EAAU,OAAOH,EAAK,EAAE,EACvB,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,aAAciC,GAA8B,EAAG,eAAgB,kBAAmB,CAC/F,CAAC,CACH,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CClJAC,KCAAC,KAIAC,KA2BA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACuB,CAEvB,GAAIF,EAAQ,iBAAkB,CAC5B,IAAMG,EAAS,OAAe,QACxBC,EAAc,MAAMC,EAAW,IACnCF,EACA,2BAA2BJ,EAAO,EAAE,EACtC,EACA,GAAI,CAACK,EACH,MAAO,CAAE,QAAS,GAAO,MAAO,4CAA6C,OAAQ,yBAA0B,EAGjH,IAAME,EAAe,IAAIC,GAAaN,EAAI,EAAE,EAEtCO,GADe,MAAMF,EAAa,WAAWP,EAAO,EAAE,GAC/B,KAAMU,GAAMA,EAAE,gBAAkBT,EAAQ,iBAAiB,EAAE,EACxF,GAAI,CAACQ,EACH,MAAO,CAAE,QAAS,GAAO,MAAO,oBAAqB,OAAQ,mBAAoB,EAGnF,IAAME,EAAa,IAAI,IAAIR,EAAQ,GAAG,EACtC,GAAI,CACF,GAAM,CAAE,UAAAS,CAAU,EAAI,MAAMC,GAA6B,CACvD,eAAgBZ,EAAQ,iBAAiB,SAAS,eAClD,kBAAmBA,EAAQ,iBAAiB,SAAS,kBACrD,UAAWA,EAAQ,iBAAiB,SAAS,UAC7C,cAAeQ,EAAQ,WACvB,UAAWA,EAAQ,UACnB,kBAAmBJ,EAAY,UAC/B,eAAgB,GAAGM,EAAW,QAAQ,KAAKA,EAAW,IAAI,GAC1D,aAAcN,EAAY,KAC1B,kBAAmBI,EAAQ,UAC7B,CAAC,EAED,aAAMF,EAAa,YAAYE,EAAQ,GAAIG,CAAS,EACpD,MAAMN,EAAW,OAAOF,EAAO,2BAA2BJ,EAAO,EAAE,EAAE,EAC9D,CAAE,QAAS,GAAM,OAAQ,SAAU,CAC5C,MAAQ,CACN,MAAO,CAAE,QAAS,GAAO,MAAO,4BAA6B,OAAQ,iBAAkB,CACzF,CACF,CAGA,GAAIC,EAAQ,cACV,MAAI,CAACD,EAAO,cAAgB,CAACA,EAAO,YAC3B,CAAE,QAAS,GAAO,MAAO,sBAAuB,OAAQ,kBAAmB,EAEtE,MAAMc,GAAWd,EAAO,YAAaC,EAAQ,cAAeA,EAAQ,QAAQ,EAInF,CAAE,QAAS,GAAM,OAAQ,MAAO,EAF9B,CAAE,QAAS,GAAO,MAAO,oBAAqB,OAAQ,cAAe,EAMhF,IAAMc,EAAMd,EAAQ,aAAeA,EAAQ,SAC3C,GAAIc,EAEF,OADc,MAAMC,GAAeD,EAAKf,EAAO,gBAAiBA,EAAO,kBAAoB,CAAC,EAIrF,CAAE,QAAS,GAAM,OAAQ,UAAW,EAFlC,CAAE,QAAS,GAAO,MAAO,gCAAiC,OAAQ,wBAAyB,EAMtG,GAAIC,EAAQ,YAAa,CACvB,IAAIgB,EAAsB,CAAC,EAC3B,GAAI,CACFA,EAAe,OAAOjB,EAAO,oBAAuB,SAChD,KAAK,MAAMA,EAAO,kBAAkB,EACpCA,EAAO,oBAAsB,CAAC,CACpC,MAAQ,CAAC,CAET,OADmB,MAAMkB,GAAwBjB,EAAQ,YAAagB,CAAY,IAC/D,GACV,CAAE,QAAS,GAAO,MAAO,uBAAwB,OAAQ,sBAAuB,EAElF,CAAE,QAAS,GAAM,OAAQ,cAAe,CACjD,CAEA,MAAO,CAAE,QAAS,GAAO,MAAO,+DAAgE,OAAQ,yBAA0B,CACpI,CDjGA,eAAsBE,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAY,IAAIC,EAAUL,EAAI,GAAIA,CAAG,EACrCM,EAAc,IAAIC,GAAiBP,EAAI,EAAE,EACzCQ,EAAWT,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDU,EAAYV,EAAQ,QAAQ,IAAI,YAAY,EAC5CW,EAASR,EAAU,CAAC,EACpBS,EAAcV,EAAK,UAAY,MAAMW,GAAoBX,EAAK,UAAWA,EAAK,EAAE,EAAI,KAG1F,GAAIS,IAAW,YAAcX,EAAQ,SAAW,OAAQ,CACtD,IAAMc,EAAQ,MAAMd,EAAQ,KAAK,EAC3B,CAAE,YAAAe,CAAY,EAAID,EACxB,GAAI,CAACC,GAAe,CAACC,GAAe,KAAKD,CAAW,EAClD,OAAO,IAAI,SAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAE9D,IAAME,EAAS,MAAMZ,EAAU,QAAQH,EAAK,EAAE,EAC9C,GAAI,CAACe,EAAQ,OAAO,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAElE,IAAMC,EAAa,MAAMC,GAAiBF,EAAQH,EAAMb,EAAKD,CAAO,EACpE,GAAI,CAACkB,EAAW,QACd,aAAMX,EAAY,OAAOL,EAAK,GAAI,uBAAwBO,EAAUC,EAAW,CAAE,OAAQQ,EAAW,MAAO,EAAGN,CAAW,EAClH,IAAI,SAASM,EAAW,OAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAGlF,IAAME,EAAiB,MAAMC,GAAaN,EAAa,CAAC,EACxD,aAAMV,EAAU,eAAeH,EAAK,GAAIkB,EAAgB,CAAC,EACzD,MAAMb,EAAY,OAAOL,EAAK,GAAI,0BAA2BO,EAAUC,EAAW,CAAE,OAAQQ,EAAW,MAAO,EAAGN,CAAW,EACrH,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAGA,GAAID,IAAW,oBAAsBX,EAAQ,SAAW,OAAQ,CAC9D,IAAMc,EAAQ,MAAMd,EAAQ,KAAK,EAC3B,CAAE,WAAAsB,CAAW,EAAIR,EACvB,GAAI,CAACQ,EACH,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,EAE3D,IAAML,EAAS,MAAMZ,EAAU,QAAQH,EAAK,EAAE,EAC9C,GAAI,CAACe,EAAQ,OAAO,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAGlE,IAAMC,EAAa,MAAMC,GAAiBF,EAAQH,EAAMb,EAAKD,CAAO,EACpE,GAAI,CAACkB,EAAW,QACd,aAAMX,EAAY,OAAOL,EAAK,GAAI,uBAAwBO,EAAUC,EAAW,CAAE,OAAQQ,EAAW,OAAQ,KAAM,WAAY,EAAGN,CAAW,EACrI,IAAI,SAASM,EAAW,OAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAGlF,IAAKD,EAAO,kBAAoB,KAAO,EAAG,CACxC,IAAMG,EAAiB,MAAMC,GAAaC,EAAY,CAAC,EACvD,MAAMjB,EAAU,eAAeH,EAAK,GAAIkB,EAAgB,CAAC,EACzD,MAAMb,EAAY,OAAOL,EAAK,GAAI,0BAA2BO,EAAUC,EAAW,CAAE,OAAQ,WAAY,EAAGE,CAAW,CACxH,CAEA,OAAO,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CErFAW,KA4BA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAY,IAAIC,EAAUL,EAAI,GAAIA,CAAG,EACrCM,EAAc,IAAIC,GAAiBP,EAAI,EAAE,EACzCQ,EAAWT,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDU,EAAYV,EAAQ,QAAQ,IAAI,YAAY,EAC5CW,EAASR,EAAU,CAAC,EACpBS,EAAcV,EAAK,UAAY,MAAMW,GAAoBX,EAAK,UAAWA,EAAK,EAAE,EAAI,KAG1F,GAAIS,IAAW,OAAQ,CACrB,IAAMG,EAAYX,EAAU,CAAC,EAG7B,GAAIW,IAAc,SAAWd,EAAQ,SAAW,MAAO,CACrD,IAAMe,EAAS,MAAMV,EAAU,QAAQH,EAAK,EAAE,EAC9C,GAAIa,GAAQ,aACV,OAAO,IAAI,SAAS,0BAA2B,CAAE,OAAQ,GAAI,CAAC,EAEhE,IAAMC,EAASC,GAAmB,EAC5BC,EAAO,IAAI,IAAIlB,EAAQ,GAAG,EAAE,SAC5BmB,EAAMC,GAAWJ,EAAQD,GAAQ,UAAY,OAAQG,CAAI,EAC/D,OAAO,IAAI,SAAS,KAAK,UAAU,CAAE,OAAAF,EAAQ,IAAAG,CAAI,CAAC,EAAG,CACnD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAGA,GAAIL,IAAc,WAAad,EAAQ,SAAW,OAAQ,CACxD,GAAM,CAAE,OAAAgB,EAAQ,cAAAK,EAAe,KAAAC,CAAK,EAAK,MAAMtB,EAAQ,KAAK,EAK5D,GAAI,CAACgB,GAAU,CAACK,EAAe,OAAO,IAAI,SAAS,0BAA2B,CAAE,OAAQ,GAAI,CAAC,EAG7F,GAAI,CADY,MAAME,GAAWP,EAAQK,EAAeC,CAAI,EAC9C,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAGtE,IAAME,EAAgBC,GAAqB,EACrCC,EAAc,MAAM,QAAQ,IAChCF,EAAc,IAAI,MAAOG,IAAO,CAC9B,KAAM,MAAMC,GAAgBD,CAAC,CAC/B,EAAE,CACJ,EAEA,aAAMtB,EAAU,WAAWH,EAAK,GAAIc,EAAQU,CAAkB,EAE9D,MAAMrB,EAAU,mBAAmBH,EAAK,GAAI,EAAI,EAChD,MAAMK,EAAY,OAAOL,EAAK,GAAI,aAAcO,EAAUC,EAAW,OAAWE,CAAW,EAEpF,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,GAAM,cAAeY,CAAc,CAAC,EAAG,CACnF,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAGA,GAAIV,IAAc,YAAcd,EAAQ,SAAW,QAAS,CAC1D,IAAMe,EAAS,MAAMV,EAAU,QAAQH,EAAK,EAAE,EAExC2B,EAAW,MADI,IAAIC,GAAa7B,EAAI,EAAE,EACR,WAAWC,EAAK,EAAE,EAEtD,GAAI,EADW,CAAC,CAACa,GAAQ,cAAgBc,EAAS,OAAS,GAC9C,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,EAEtE,GAAM,CAAE,cAAAE,CAAc,EAAK,MAAM/B,EAAQ,KAAK,EAC9C,aAAMK,EAAU,mBAAmBH,EAAK,GAAI,CAAC,CAAC6B,CAAa,EACpD,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAGA,GAAI,CAACjB,GAAad,EAAQ,SAAW,SAAU,CAC7C,GAAM,CAAE,SAAAgC,CAAS,EAAK,MAAMhC,EAAQ,KAAK,EACnCe,EAAS,MAAMV,EAAU,QAAQH,EAAK,EAAE,EAC9C,GAAI,CAACa,EAAQ,OAAO,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAElE,GAAI,CAACA,EAAO,qBACN,CAACiB,GAAY,CAAE,MAAMC,GAAeD,EAAUjB,EAAO,gBAAiBA,EAAO,kBAAoB,CAAC,GACpG,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,EAM7D,IAAMmB,GADW,MADI,IAAIJ,GAAa7B,EAAI,EAAE,EACR,WAAWC,EAAK,EAAE,GAC1B,OAAS,EAErC,aAAMG,EAAU,WAAWH,EAAK,GAAIgC,CAAU,EAC9C,MAAM3B,EAAY,OAAOL,EAAK,GAAI,eAAgBO,EAAUC,EAAW,OAAWE,CAAW,EACtF,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CACF,CAGA,GAAID,IAAW,OAASR,EAAU,CAAC,IAAM,YAAcH,EAAQ,SAAW,QAAS,CACjF,IAAMe,EAAS,MAAMV,EAAU,QAAQH,EAAK,EAAE,EAExC2B,EAAW,MADI,IAAIC,GAAa7B,EAAI,EAAE,EACR,WAAWC,EAAK,EAAE,EAEtD,GAAI,EADW,CAAC,CAACa,GAAQ,cAAgBc,EAAS,OAAS,GAC9C,OAAO,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,EAEtE,GAAM,CAAE,cAAAE,CAAc,EAAK,MAAM/B,EAAQ,KAAK,EAC9C,aAAMK,EAAU,mBAAmBH,EAAK,GAAI,CAAC,CAAC6B,CAAa,EACpD,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CClIA,eAAsBI,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAY,IAAIC,EAAUL,EAAI,GAAIA,CAAG,EACrCM,EAAS,MAAMF,EAAU,QAAQH,EAAK,EAAE,EAC9C,GAAI,CAACK,EAAQ,OAAO,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAElE,IAAMC,EAAQ,MAAMR,EAAQ,KAAK,EAG3BS,EAAa,MAAMC,GAAiBH,EAAQC,EAAMP,EAAKD,CAAO,EACpE,GAAI,CAACS,EAAW,QACd,OAAO,IAAI,SAASA,EAAW,OAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAIlF,GAAIT,EAAQ,SAAW,OAAQ,CAC7B,GAAM,CAAE,QAAAW,CAAQ,EAAIH,EAEpB,MAAI,CAACG,GAAW,CAAC,oBAAoB,KAAKA,CAAO,EACxC,IAAI,SAAS,0BAA2B,CAAE,OAAQ,GAAI,CAAC,GAIhE,MAAMN,EAAU,cAAcH,EAAK,GAAIS,CAAO,EACvC,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,EACH,CAGA,OAAIX,EAAQ,SAAW,UACrB,MAAMK,EAAU,cAAcH,EAAK,GAAI,IAAI,EACpC,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,GAGI,IAAI,SAAS,qBAAsB,CAAE,OAAQ,GAAI,CAAC,CAC3D,CC1DAU,KAUAC,KAmBA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAY,IAAIC,EAAUL,EAAI,GAAIA,CAAG,EACrCM,EAAe,IAAIC,GAAaP,EAAI,EAAE,EACtCQ,EAAc,IAAIC,GAAiBT,EAAI,EAAE,EACzCU,EAAWX,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDY,EAAYZ,EAAQ,QAAQ,IAAI,YAAY,EAC5Ca,EAAYV,EAAU,CAAC,EACvBW,EAAcZ,EAAK,UAAY,MAAMa,GAAoBb,EAAK,UAAWA,EAAK,EAAE,EAAI,KAG1F,GAAI,CAACW,GAAab,EAAQ,SAAW,MAAO,CAC1C,IAAMgB,EAAW,MAAMT,EAAa,WAAWL,EAAK,EAAE,EACtD,OAAO,IAAI,SAAS,KAAK,UAAUc,CAAQ,EAAG,CAC5C,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAGA,GAAIH,IAAc,gBAAkBb,EAAQ,SAAW,OAAQ,CAC7D,IAAMiB,EAAmB,MAAMV,EAAa,WAAWL,EAAK,EAAE,EAC9D,GAAIe,EAAiB,SAAW,EAC9B,OAAO,IAAI,SAAS,yBAA0B,CAAE,OAAQ,GAAI,CAAC,EAG/D,IAAMC,EAAYC,GAA0B,EAEtCC,EADa,IAAI,IAAIpB,EAAQ,GAAG,EACd,SAClBqB,EAAS,OAAe,QAC9B,MAAMC,EAAW,IAAID,EAAO,2BAA2BnB,EAAK,EAAE,GAAI,CAAE,UAAAgB,EAAW,KAAAE,CAAK,EAAG,GAAG,EAE1F,IAAMG,EAAU,CACd,UAAAL,EACA,KAAAE,EACA,QAAS,IACT,iBAAkB,YAClB,iBAAkBH,EAAiB,IAAKO,IAAO,CAC7C,GAAIA,EAAE,cACN,WAAYA,EAAE,GACd,KAAM,aACN,WAAYA,EAAE,WAAa,KAAK,MAAMA,EAAE,UAAU,EAAI,MACxD,EAAE,CACJ,EAEA,OAAO,IAAI,SAAS,KAAK,UAAUD,CAAO,EAAG,CAC3C,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAGA,GAAIV,IAAc,YAAcV,EAAU,CAAC,IAAM,WAAaH,EAAQ,SAAW,OAAQ,CACvF,IAAMiB,EAAmB,MAAMV,EAAa,WAAWL,EAAK,EAAE,EACxDgB,EAAYC,GAA0B,EAEtCC,EADa,IAAI,IAAIpB,EAAQ,GAAG,EACd,SAClBqB,EAAS,OAAe,QAC9B,MAAMC,EAAW,IAAID,EAAO,0BAA0BnB,EAAK,EAAE,GAAI,CAAE,UAAAgB,EAAW,KAAAE,CAAK,EAAG,GAAG,EAGzF,IAAMK,GADS,MAAMpB,EAAU,QAAQH,EAAK,EAAE,IACrB,UAAYA,EAAK,UAAY,GAChDwB,EAAkBD,EAAW,GAAGA,CAAQ,KAAKL,CAAI,IAAMA,EAEvDG,EAAU,CACd,UAAAL,EACA,GAAI,CACF,KAAME,EACN,GAAIA,CACN,EACA,KAAM,CACJ,GAAIO,GAAgB,IAAI,YAAY,EAAE,OAAOzB,EAAK,EAAE,CAAC,EACrD,KAAMuB,GAAYL,EAClB,YAAaM,CACf,EACA,iBAAkB,CAChB,CAAE,KAAM,aAAc,IAAK,EAAG,EAC9B,CAAE,KAAM,aAAc,IAAK,IAAK,CAClC,EACA,uBAAwB,CACtB,iBAAkB,YAClB,YAAa,WACf,EACA,QAAS,IACT,mBAAoBT,EAAiB,IAAKO,IAAO,CAC/C,GAAIA,EAAE,cACN,KAAM,aACN,WAAYA,EAAE,WAAa,KAAK,MAAMA,EAAE,UAAU,EAAI,MACxD,EAAE,CACJ,EAEA,OAAO,IAAI,SAAS,KAAK,UAAUD,CAAO,EAAG,CAC3C,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAGA,GAAIV,IAAc,YAAcV,EAAU,CAAC,IAAM,UAAYH,EAAQ,SAAW,OAAQ,CACtF,IAAMqB,EAAS,OAAe,QACxBO,EAAc,MAAMN,EAAW,IAAyCD,EAAO,0BAA0BnB,EAAK,EAAE,EAAE,EACxH,GAAI,CAAC0B,EACH,OAAO,IAAI,SAAS,iDAAkD,CAAE,OAAQ,GAAI,CAAC,EAEvF,MAAMN,EAAW,OAAOD,EAAO,0BAA0BnB,EAAK,EAAE,EAAE,EAElE,IAAM2B,EAAQ,MAAM7B,EAAQ,KAAK,EAC3B,CAAE,KAAA8B,EAAM,WAAAC,CAAW,EAAIF,EAC7B,GAAI,CAACE,GAAc,CAACA,EAAW,SAC7B,OAAO,IAAI,SAAS,0BAA2B,CAAE,OAAQ,GAAI,CAAC,EAGhE,IAAMC,EAAa,IAAI,IAAIhC,EAAQ,GAAG,EAChCiC,EAAiB,GAAGD,EAAW,QAAQ,KAAKA,EAAW,IAAI,GACjE,GAAI,CACF,IAAME,EAAS,MAAMC,GAA2B,CAC9C,eAAgBJ,EAAW,SAAS,eACpC,kBAAmBA,EAAW,SAAS,kBACvC,kBAAmBH,EAAY,UAC/B,eAAAK,EACA,aAAcL,EAAY,IAC5B,CAAC,EAID,GADiB,MAAMrB,EAAa,kBAAkB2B,EAAO,YAAY,EAEvE,OAAO,IAAI,SAAS,qCAAsC,CAAE,OAAQ,GAAI,CAAC,EAG3E,IAAME,GAAeN,GAAQ,IAAI,KAAK,EACtC,GAAI,CAACO,GAAmB,KAAKD,CAAW,EACtC,OAAO,IAAI,SAAS,8BAA+B,CAAE,OAAQ,GAAI,CAAC,EAEpE,IAAME,EAAa,MAAM,QAAQP,EAAW,SAAS,UAAU,EAAIA,EAAW,SAAS,WAAa,OAE9FQ,EAAU,MAAMhC,EAAa,OAAO,CACxC,OAAQL,EAAK,GACb,KAAMkC,EACN,aAAcF,EAAO,aACrB,UAAWA,EAAO,cAClB,UAAWA,EAAO,UAClB,UAAWA,EAAO,UAClB,WAAAI,EACA,OAAQJ,EAAO,MACjB,CAAC,EAGD,MAAM7B,EAAU,mBAAmBH,EAAK,GAAI,EAAI,EAEhD,IAAMsC,EAAS,MAAMnC,EAAU,QAAQH,EAAK,EAAE,EAC1CuC,EAAyB,CAAC,EAC9B,GAAID,EAAQ,CACV,IAAIE,EAA0B,GAC9B,GAAIF,EAAO,mBACT,GAAI,CACF,IAAMN,EAAS,OAAOM,EAAO,oBAAuB,SAChD,KAAK,MAAMA,EAAO,kBAAkB,EACpCA,EAAO,mBACP,MAAM,QAAQN,CAAM,GAAKA,EAAO,OAAS,IAC3CQ,EAA0B,GAE9B,MAAQ,CACFF,EAAO,mBAAmB,OAAS,IACrCE,EAA0B,GAE9B,MACSF,EAAO,+BAChBE,EAA0B,IAK5B,GAAI,CAACA,EAAyB,CAC5B,IAAMC,EAAgBC,GAAqB,EACrCC,EAAmC,MAAM,QAAQ,IACrDF,EAAc,IAAI,MAAOG,IAAO,CAC9B,KAAM,MAAMC,GAAgBD,CAAC,CAC/B,EAAE,CACJ,EACA,MAAMzC,EAAU,mBAAmBH,EAAK,GAAI2C,CAAW,EACvDJ,EAAeE,CACjB,CACF,CAEA,aAAMlC,EAAY,OAAOP,EAAK,GAAI,qBAAsBS,EAAUC,EAAW,CAAE,KAAMwB,CAAY,EAAGtB,CAAW,EACxG,IAAI,SACT,KAAK,UAAU,CACb,GAAGyB,EACH,cAAeE,EAAa,OAAS,EAAIA,EAAe,MAC1D,CAAC,EACD,CACE,OAAQ,IACR,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CACF,CACF,OAASO,EAAU,CACjB,eAAQ,KAAK,8CAA+CA,EAAI,SAAWA,CAAG,EACvE,IAAI,SAASA,EAAI,SAAW,8BAA+B,CAAE,OAAQ,GAAI,CAAC,CACnF,CACF,CAGA,GAAInC,GAAab,EAAQ,SAAW,QAAS,CAC3C,IAAMiD,EAAYpC,EACZ,CAAE,KAAAiB,CAAK,EAAK,MAAM9B,EAAQ,KAAK,EAC/BkD,GAAepB,GAAQ,IAAI,KAAK,EACtC,GAAI,CAACoB,GAAe,CAACb,GAAmB,KAAKa,CAAW,EACtD,OAAO,IAAI,SAAS,8BAA+B,CAAE,OAAQ,GAAI,CAAC,EAIpE,GAAI,CADY,MAAM3C,EAAa,QAAQ0C,EAAW/C,EAAK,EAAE,EAC/C,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAEtE,IAAMiD,EAAU,MAAM5C,EAAa,WAAW0C,EAAW/C,EAAK,GAAIgD,CAAW,EAC7E,OAAO,IAAI,SAAS,KAAK,UAAU,CAAE,QAAAC,CAAQ,CAAC,EAAG,CAC/C,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAGA,GAAItC,GAAab,EAAQ,SAAW,SAAU,CAC5C,IAAMiD,EAAYpC,EACZuC,EAAU,MAAM7C,EAAa,QAAQ0C,EAAW/C,EAAK,EAAE,EAC7D,GAAI,CAACkD,EAAS,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,EAEtE,MAAM7C,EAAa,OAAO0C,EAAW/C,EAAK,EAAE,EAC5C,MAAMO,EAAY,OAAOP,EAAK,GAAI,kBAAmBS,EAAUC,EAAW,CAAE,KAAMwC,EAAQ,IAAK,EAAGtC,CAAW,EAG7G,IAAMuC,EAAoB,MAAM9C,EAAa,YAAYL,EAAK,EAAE,EAC1DsC,EAAS,MAAMnC,EAAU,QAAQH,EAAK,EAAE,EAC9C,OAAImD,IAAsB,GAAK,CAACb,GAAQ,cACtC,MAAMnC,EAAU,mBAAmBH,EAAK,GAAI,EAAK,EAG5C,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,EAAG,CACrD,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CChRAoD,KAkBA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAY,IAAIC,EAAUL,EAAI,GAAIA,CAAG,EACrCM,EAAc,IAAIC,GAAiBP,EAAI,EAAE,EACzCQ,EAAWT,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDU,EAAYV,EAAQ,QAAQ,IAAI,YAAY,EAC5CW,EAAYR,EAAU,CAAC,EACvBS,EAAcV,EAAK,UAAY,MAAMW,GAAoBX,EAAK,UAAWA,EAAK,EAAE,EAAI,KAG1F,GAAIS,IAAc,UAAYX,EAAQ,SAAW,OAAQ,CACvD,IAAMc,EAAQ,MAAMd,EAAQ,KAAK,EAC3Be,EAAS,MAAMV,EAAU,QAAQH,EAAK,EAAE,EAC9C,GAAI,CAACa,EAAQ,OAAO,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAElE,IAAMC,EAAa,MAAMC,GAAiBF,EAAQD,EAAMb,EAAKD,CAAO,EACpE,GAAI,CAACgB,EAAW,QACd,OAAO,IAAI,SAASA,EAAW,OAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAIlF,GAAIA,EAAW,SAAW,WAAaA,EAAW,SAAW,eAC3D,OAAO,IAAI,SAAS,kGAAmG,CACrH,OAAQ,GACV,CAAC,EAGH,IAAME,EAAeC,GAAoB,EAEnCC,EAAoC,CAAC,CAAE,KAD3B,MAAMC,GAAgBH,CAAY,CACS,CAAQ,EAErE,aAAMb,EAAU,mBAAmBH,EAAK,GAAIkB,CAAQ,EACpD,MAAMb,EAAY,OAAOL,EAAK,GAAI,uBAA+BO,EAAUC,EAAW,CAAE,OAAQM,EAAW,MAAO,EAAGJ,CAAW,EAEzH,IAAI,SACT,KAAK,UAAU,CACb,QAAS,GACT,aAAcM,CAChB,CAAC,EACD,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CACpD,CACF,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CC1CA,eAAsBI,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CAGnB,OAFeD,EAAU,CAAC,EAEV,CACd,IAAK,WACL,IAAK,mBACH,OAAOE,GAAsBL,EAASC,EAAKC,EAAMC,EAAWC,CAAG,EAEjE,IAAK,OACL,IAAK,MACH,OAAOE,GAAwBN,EAASC,EAAKC,EAAMC,EAAWC,CAAG,EAEnE,IAAK,MACH,OAAOG,GAAiBP,EAASC,EAAKC,EAAMC,EAAWC,CAAG,EAE5D,IAAK,WACH,OAAOI,GAAsBR,EAASC,EAAKC,EAAMC,EAAWC,CAAG,EAEjE,IAAK,gBACH,OAAOK,GAA0BT,EAASC,EAAKC,EAAMC,EAAWC,CAAG,EAErE,QACE,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CACpD,CACF,CCvDAM,KAMA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAc,IAAIC,GAAiBL,EAAI,EAAE,EACzCM,EAAWP,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDQ,EAAYR,EAAQ,QAAQ,IAAI,YAAY,EAC5CS,EAASN,EAAU,CAAC,EAG1B,GAAIM,IAAW,YAAcT,EAAQ,SAAW,MAAO,CACrD,IAAMU,EAAS,IAAI,IAAIV,EAAQ,GAAG,EAAE,aAC9BW,EAAQ,KAAK,IAAI,SAASD,EAAO,IAAI,OAAO,GAAK,KAAM,EAAE,EAAG,EAAE,EAC9DE,EAASF,EAAO,IAAI,QAAQ,EAAI,SAASA,EAAO,IAAI,QAAQ,EAAI,EAAE,EAAI,OACtEG,EAAU,MAAMR,EAAY,WAAWH,EAAK,GAAIS,EAAOC,CAAM,EACnE,OAAO,IAAI,SAAS,KAAK,UAAUC,CAAO,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAClG,CAGA,GAAIJ,IAAW,WAAY,CAEzB,GAAI,CAACN,EAAU,CAAC,GAAKH,EAAQ,SAAW,MAAO,CAC7C,GAAM,CAAE,aAAAc,CAAa,EAAI,KAAM,uCAEzBC,EAAW,MADI,IAAID,EAAab,EAAI,EAAE,EACR,kBAAkBC,EAAK,EAAE,EAEvDc,EAAehB,EAAQ,QAAQ,IAAI,QAAQ,GAAK,GAChDiB,EAAeC,GAAuBF,CAAY,EAClDG,EAAmBF,GAAeG,GAAwBH,CAAY,GAAG,KAAO,KAEhFI,EAAc,MAAM,QAAQ,IAAIN,EAAS,IAAI,MAAMO,IAAM,CAC7D,GAAGA,EACH,GAAI,MAAMC,GAAoBD,EAAE,GAAIpB,EAAK,EAAE,EAC3C,WAAYoB,EAAE,KAAOH,CACvB,EAAE,CAAC,EAEH,OAAO,IAAI,SAAS,KAAK,UAAUE,CAAW,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CACtG,CAGA,GAAIlB,EAAU,CAAC,IAAM,UAAYH,EAAQ,SAAW,SAAU,CAC5D,GAAM,CAAE,aAAAc,CAAa,EAAI,KAAM,uCACzBU,EAAe,IAAIV,EAAab,EAAI,EAAE,EAEtCgB,EAAeC,GAAuBlB,EAAQ,QAAQ,IAAI,QAAQ,GAAK,EAAE,EACzEmB,EAAmBF,GAAeG,GAAwBH,CAAY,GAAG,KAAO,KAEtF,GAAI,CAACE,EACH,OAAO,IAAI,SAAS,4BAA6B,CAAE,OAAQ,GAAI,CAAC,EAIlE,IAAMM,GADW,MAAMD,EAAa,kBAAkBtB,EAAK,EAAE,GAC9B,OAAOoB,GAAKA,EAAE,KAAOH,CAAgB,EAE9D,CAAE,kBAAAO,CAAkB,EAAI,KAAM,uCAC9B,CAAE,wBAAAC,CAAwB,EAAI,KAAM,uCAC1C,QAAWL,KAAKG,EACd,MAAMC,EAAkBzB,EAAKqB,EAAE,EAAE,EACjCK,EAAwBL,EAAE,EAAE,EAG9B,IAAMM,EAAqB,MAAML,GAAoBJ,EAAkBjB,EAAK,EAAE,EAC9E,aAAMG,EAAY,OAAOH,EAAK,GAAI,kBAAmBK,EAAUC,EAAW,CAAE,OAAQ,qBAAsB,MAAOiB,EAAc,MAAO,EAAGG,CAAkB,EAEpJ,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,GAAM,cAAeH,EAAc,MAAO,CAAC,EAAG,CAC1F,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CAGA,GAAItB,EAAU,CAAC,GAAKH,EAAQ,SAAW,SAAU,CAC/C,IAAM6B,EAAoB1B,EAAU,CAAC,EAC/B,CAAE,aAAAW,CAAa,EAAI,KAAM,uCAGzBC,EAAW,MAFI,IAAID,EAAab,EAAI,EAAE,EAER,kBAAkBC,EAAK,EAAE,EACzD4B,EAAiC,KACrC,QAAWR,KAAKP,EACd,GAAI,MAAMQ,GAAoBD,EAAE,GAAIpB,EAAK,EAAE,IAAM2B,EAAmB,CAClEC,EAAkBR,EAAE,GACpB,KACF,CAGF,GAAI,CAACQ,EACH,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,EAGlD,GAAM,CAAE,kBAAAJ,CAAkB,EAAI,KAAM,uCACpC,MAAMA,EAAkBzB,EAAK6B,CAAe,EAE5C,IAAMb,EAAeC,GAAuBlB,EAAQ,QAAQ,IAAI,QAAQ,GAAK,EAAE,EACzEmB,EAAmBF,GAAeG,GAAwBH,CAAY,GAAG,KAAO,KAChFW,EAAqBT,EAAmB,MAAMI,GAAoBJ,EAAkBjB,EAAK,EAAE,EAAI,KAKrG,OAHA,MAAMG,EAAY,OAAOH,EAAK,GAAI,kBAAmBK,EAAUC,EAAW,CAAE,OAAQ,cAAe,eAAgBqB,CAAkB,EAAGA,CAAiB,EAGrJC,IAAoBX,EACf,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,GAAM,WAAY,EAAK,CAAC,EAAG,CACvE,QAAS,CAAE,aAAcY,GAA8B,EAAG,eAAgB,kBAAmB,CAC/F,CAAC,EAGI,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,GAAM,WAAY,EAAM,CAAC,EAAG,CACxE,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CACF,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CCnGA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAe,IAAIC,GAAaL,EAAI,EAAE,EACtCM,EAAe,IAAIC,EAAaP,EAAI,EAAE,EACtCQ,EAAcN,EAAU,CAAC,EAG/B,GAAIM,IAAgB,UAAYT,EAAQ,SAAW,MAAO,CACxD,GAAM,CAACU,EAAUC,CAAQ,EAAI,MAAM,QAAQ,IAAI,CAC7CJ,EAAa,YAAYL,EAAK,EAAE,EAChCG,EAAa,WAAWH,EAAK,EAAE,CACjC,CAAC,EAEGU,EAAgF,KAChFC,EAAwC,CAAC,EACzCC,EAA6C,KAEjD,GAAIJ,EAAS,OAAS,EAAG,CACvB,GAAM,CAACK,EAAQC,EAAYC,CAAM,EAAI,MAAM,QAAQ,IAAI,CACrDhB,EAAI,GAAG,QACL,sIACF,EACG,KAAKC,EAAK,EAAE,EACZ,MAAsE,EACzED,EAAI,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,SAKd,EACE,KAAKC,EAAK,EAAE,EACZ,IAA4B,EAC/BD,EAAI,GAAG,QACL,4HACF,EACG,KAAKC,EAAK,EAAE,EACZ,MAA8B,CACnC,CAAC,EAEDU,EAASG,EACTF,EAAcG,EAAW,SAAW,CAAC,EACrCF,EAAcG,CAChB,CAEA,OAAO,SAAS,KAAK,CACnB,QAAS,EAAQL,EACjB,QAAS,GAAQA,IAAWA,EAAO,YAAc,QAAaA,EAAO,YAAc,IACnF,UAAWA,GAAQ,YAAc,KACjC,gBAAiBC,EAAY,IAAKK,GAAMA,EAAE,UAAU,EACpD,eAAgB,EAAQJ,EACxB,iBAAkBH,EAAS,OAC3B,UAAWC,GAAQ,YAAc,IACnC,CAAC,CACH,CAGA,GAAIH,IAAgB,eAAiBT,EAAQ,SAAW,MAAO,CAE7D,IAAMmB,EADM,IAAI,IAAInB,EAAQ,GAAG,EACT,aAAa,IAAI,YAAY,EAE/CoB,EAAuD,KAE3D,GAAID,EAAW,CACb,IAAME,EAAU,MAAMhB,EAAa,sBAAsBc,EAAWjB,EAAK,EAAE,EACvEmB,IACFD,EAAU,MAAMnB,EAAI,GAAG,QACrB,6EACF,EACG,KAAKoB,EAAQ,EAAE,EACf,MAA4C,EAEnD,CAeA,OAbKD,IAEHA,EAAU,MAAMnB,EAAI,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAM9B,EACE,KAAKC,EAAK,EAAE,EACZ,MAA4C,GAG5CkB,EAIE,SAAS,KAAK,CACnB,YAAaA,EAAQ,aACrB,GAAIA,EAAQ,EACd,CAAC,EANQ,IAAI,SAAS,yCAA0C,CAAE,OAAQ,GAAI,CAAC,CAOjF,CAGA,GAAIX,IAAgB,wBAA0BT,EAAQ,SAAW,MAAO,CACtE,IAAMoB,EAAU,MAAMnB,EAAI,GAAG,QAC3B,wIACF,EACG,KAAKC,EAAK,EAAE,EACZ,MAA0D,EAE7D,OAAKkB,EAIE,SAAS,KAAK,CACnB,YAAaA,EAAQ,aACrB,GAAIA,EAAQ,GACZ,KAAMA,EAAQ,IAChB,CAAC,EAPQ,IAAI,SAAS,iCAAkC,CAAE,OAAQ,GAAI,CAAC,CAQzE,CAGA,GAAIX,IAAgB,QAAUT,EAAQ,SAAW,OAAQ,CACvD,IAAIsB,EACJ,GAAI,CACFA,EAAO,MAAMtB,EAAQ,KAAK,CAC5B,MAAQ,CACN,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,CAC1D,CAEA,GAAM,CAAE,UAAAuB,EAAW,UAAAJ,EAAW,YAAAK,EAAa,GAAAC,EAAI,SAAAC,CAAS,EAAIJ,EAC5D,GAAI,CAACC,GAAc,CAACJ,GAAa,CAACO,EAChC,OAAO,IAAI,SAAS,qEAAsE,CACxF,OAAQ,GACV,CAAC,EAGH,IAAIL,EAAe,KACnB,GAAIF,IACFE,EAAU,MAAMhB,EAAa,sBAAsBc,EAAWjB,EAAK,EAAE,EACjE,CAACmB,GAAS,CACZ,IAAMM,EAAe,MAAMtB,EAAa,WAAWH,EAAK,EAAE,EACtDyB,EAAa,OAAS,IACxBN,EAAUM,EAAa,CAAC,EAE5B,CAGF,IAAMjB,EAAW,MAAMH,EAAa,YAAYL,EAAK,EAAE,EACjD0B,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAClCC,EAAe,OAAON,GAAc,SAAWA,EAAY,KAAK,UAAUA,CAAS,EAEnFO,EAAe,CAAC,EAChBC,EAAmBrB,EAAS,OAAS,EAAIA,EAAS,CAAC,EAAE,GAAK,KAEhE,GAAIqB,EAAkB,CAEhBV,GAAWG,GAAeC,GAC5BK,EAAM,KACJ7B,EAAI,GAAG,QACL,oPACF,EAAE,KAAKoB,EAAQ,GAAIU,EAAkBP,EAAaC,EAAIG,CAAG,CAC3D,EAIF,QAAWI,KAAKtB,EACdoB,EAAM,KACJ7B,EAAI,GAAG,QACL,uNACF,EAAE,KAAK+B,EAAE,GAAIH,EAAcD,CAAG,CAChC,EAEIF,GAAYA,EAAS,aAAeA,EAAS,IAAMA,EAAS,MAC9DI,EAAM,KACJ7B,EAAI,GAAG,QACL,qQACF,EAAE,KAAK+B,EAAE,GAAIN,EAAS,YAAaA,EAAS,GAAIA,EAAS,KAAME,CAAG,CACpE,CAGN,CAEIE,EAAM,OAAS,GACjB,MAAM7B,EAAI,GAAG,MAAM6B,CAAK,EAG1B,QAAWE,KAAKtB,EACduB,GAA6BD,EAAE,EAAE,EAGnC,OAAO,SAAS,KAAK,CAAE,QAAS,EAAK,CAAC,CACxC,CAGA,GAAIvB,IAAgB,QAAUT,EAAQ,SAAW,OAAQ,CACvD,IAAIsB,EACJ,GAAI,CACFA,EAAO,MAAMtB,EAAQ,KAAK,CAC5B,MAAQ,CACN,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,CAC1D,CAEA,GAAM,CAAE,UAAAmB,EAAW,YAAAK,EAAa,GAAAC,CAAG,EAAIH,EACvC,GAAI,CAACH,GAAa,CAACK,GAAe,CAACC,EACjC,OAAO,IAAI,SAAS,uDAAwD,CAAE,OAAQ,GAAI,CAAC,EAG7F,IAAIJ,EAAU,MAAMhB,EAAa,sBAAsBc,EAAWjB,EAAK,EAAE,EACzE,GAAI,CAACmB,EAAS,CACZ,IAAMM,EAAe,MAAMtB,EAAa,WAAWH,EAAK,EAAE,EACtDyB,EAAa,OAAS,IACxBN,EAAUM,EAAa,CAAC,EAE5B,CACA,GAAI,CAACN,EACH,OAAO,IAAI,SAAS,oCAAqC,CAAE,OAAQ,GAAI,CAAC,EAG1E,IAAMX,EAAW,MAAMH,EAAa,YAAYL,EAAK,EAAE,EACjD6B,EAAmBrB,EAAS,OAAS,EAAIA,EAAS,CAAC,EAAE,GAAK,KAChE,GAAI,CAACqB,EACH,OAAO,IAAI,SAAS,6BAA8B,CAAE,OAAQ,GAAI,CAAC,EAGnE,IAAMH,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACxC,aAAM3B,EAAI,GAAG,QACX,oPACF,EACG,KAAKoB,EAAQ,GAAIU,EAAkBP,EAAaC,EAAIG,CAAG,EACvD,IAAI,EAEA,SAAS,KAAK,CAAE,QAAS,EAAK,CAAC,CACxC,CAGA,GAAInB,IAAgB,iBAAmBT,EAAQ,SAAW,OAAQ,CAChE,IAAIsB,EACJ,GAAI,CACFA,EAAO,MAAMtB,EAAQ,KAAK,CAC5B,MAAQ,CACN,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,CAC1D,CAEA,GAAM,CAAE,YAAAwB,EAAa,GAAAC,EAAI,KAAAS,CAAK,EAAIZ,EAClC,GAAI,CAACE,GAAe,CAACC,GAAM,CAACS,EAC1B,OAAO,IAAI,SAAS,kDAAmD,CAAE,OAAQ,GAAI,CAAC,EAGxF,IAAMxB,EAAW,MAAMH,EAAa,YAAYL,EAAK,EAAE,EACjD0B,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAClCE,EAAe,CAAC,EACtB,QAAWE,KAAKtB,EACdoB,EAAM,KACJ7B,EAAI,GAAG,QACL,qQACF,EAAE,KAAK+B,EAAE,GAAIR,EAAaC,EAAIS,EAAMN,CAAG,CACzC,EAEF,OAAIE,EAAM,OAAS,GACjB,MAAM7B,EAAI,GAAG,MAAM6B,CAAK,EAEnB,SAAS,KAAK,CAAE,QAAS,EAAK,CAAC,CACxC,CAGA,GAAIrB,IAAgB,UAAYT,EAAQ,SAAW,OAAQ,CACzD,IAAMU,EAAW,MAAMH,EAAa,YAAYL,EAAK,EAAE,EACvD,MAAMD,EAAI,GAAG,QACX,yGACF,EAAE,KAAKC,EAAK,EAAE,EAAE,IAAI,EAEpB,QAAW8B,KAAKtB,EACduB,GAA6BD,EAAE,EAAE,EAGnC,OAAO,SAAS,KAAK,CAAE,QAAS,GAAM,QAAS,EAAK,CAAC,CACvD,CAGA,GAAIhC,EAAQ,SAAW,WAAa,CAACS,GAAeA,IAAgB,QAAS,CAE3E,IAAM0B,EADM,IAAI,IAAInC,EAAQ,GAAG,EACb,aAAa,IAAI,OAAO,IAAM,OAC1CU,EAAW,MAAMH,EAAa,YAAYL,EAAK,EAAE,EAEnDiC,EAEF,MAAMlC,EAAI,GAAG,MAAM,CACjBA,EAAI,GAAG,QAAQ,4FAA4F,EAAE,KAAKC,EAAK,EAAE,EACzHD,EAAI,GAAG,QAAQ,uGAAuG,EAAE,KAAKC,EAAK,EAAE,EACpID,EAAI,GAAG,QAAQ,yGAAyG,EAAE,KAAKC,EAAK,EAAE,CACxI,CAAC,EAGD,MAAMD,EAAI,GAAG,QACX,yGACF,EAAE,KAAKC,EAAK,EAAE,EAAE,IAAI,EAGtB,QAAW8B,KAAKtB,EACduB,GAA6BD,EAAE,EAAE,EAGnC,OAAO,SAAS,KAAK,CAAE,QAAS,GAAM,OAAQG,EAAO,QAAS,EAAM,CAAC,CACvE,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CC/TA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAASF,EAAU,CAAC,EAG1B,OAAIE,IAAW,MAAQA,IAAW,UAAYA,IAAW,OAChDC,GAAgBN,EAASC,EAAKC,EAAMC,EAAWC,CAAG,EAIvDC,IAAW,YAAcA,IAAW,QAAUA,IAAW,YAAcA,IAAW,OAASA,IAAW,oBAAsBA,IAAW,OAASA,IAAW,gBACtJE,GAAsBP,EAASC,EAAKC,EAAMC,EAAWC,CAAG,EAI7DC,IAAW,YAAcA,IAAW,WAC/BG,GAAsBR,EAASC,EAAKC,EAAMC,EAAWC,CAAG,EAI7DC,IAAW,OACNI,GAAyBT,EAASC,EAAKC,EAAMC,EAAWC,CAAG,EAG7D,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CCtCAM,KACAC,KASA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAY,IAAIC,EAAUL,EAAI,GAAIA,CAAG,EACrCM,EAAe,IAAIC,EAAaP,EAAI,EAAE,EAE5C,GAAIE,EAAU,CAAC,IAAM,QAAS,CAC5B,GAAIH,EAAQ,SAAW,MAAO,CAC5B,IAAMS,EAAQ,MAAMJ,EAAU,QAAQ,EACtC,OAAO,IAAI,SAAS,KAAK,UAAUI,CAAK,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAChG,CAEA,GAAIT,EAAQ,SAAW,OAAQ,CAC7B,GAAM,CAAE,SAAAU,EAAU,SAAAC,EAAU,KAAAC,CAAK,EAAI,MAAMZ,EAAQ,KAAK,EACxD,GAAI,CAACU,GAAY,CAACG,GAAe,KAAKH,CAAQ,EAC5C,OAAO,IAAI,SAAS,0BAA2B,CAAE,OAAQ,GAAI,CAAC,EAEhE,GAAI,CAACC,GAAY,CAACG,GAAe,KAAKH,CAAQ,EAC5C,OAAO,IAAI,SAAS,wBAAyB,CAAE,OAAQ,GAAI,CAAC,EAE9D,IAAMI,EAAiB,MAAMC,GAAaL,EAAU,CAAC,EAC/CM,EAASC,EAAW,EAAE,EAC5B,GAAI,CACF,aAAMb,EAAU,OAAO,CAAE,GAAIY,EAAQ,SAAAP,EAAU,aAAcK,EAAgB,KAAMH,GAAQ,OAAQ,gBAAiB,CAAE,CAAC,EAChH,IAAI,SAAS,KAAK,UAAU,CAAE,GAAIK,CAAO,CAAC,EAAG,CAAE,OAAQ,GAAI,CAAC,CACrE,OAASE,EAAQ,CACf,OAAO,IAAI,SAASA,EAAE,QAAS,CAAE,OAAQ,GAAI,CAAC,CAChD,CACF,CAEA,GAAInB,EAAQ,SAAW,UAAYG,EAAU,CAAC,EAAG,CAC/C,IAAMiB,EAAWjB,EAAU,CAAC,EAC5B,OAAIiB,IAAalB,EAAK,GAAW,IAAI,SAAS,yBAA0B,CAAE,OAAQ,GAAI,CAAC,GACvF,MAAMK,EAAa,cAAca,CAAQ,EACzC,MAAMf,EAAU,OAAOe,CAAQ,EACxB,IAAI,SAAS,KAAM,CAAE,OAAQ,GAAI,CAAC,EAC3C,CACF,CAGA,GAAIjB,EAAU,CAAC,IAAM,WAAY,CAC/B,IAAMkB,EAAiB,IAAIC,GAAoBrB,EAAI,EAAE,EACrD,GAAID,EAAQ,SAAW,MAAO,CAC5B,IAAMuB,EAAW,MAAMF,EAAe,OAAO,EAE7C,cAAOE,EAAS,WACT,IAAI,SAAS,KAAK,UAAUA,CAAQ,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CACnG,CACA,GAAIvB,EAAQ,SAAW,QAAS,CAC9B,IAAMwB,EAAO,MAAMxB,EAAQ,KAAK,EAEhC,cAAOwB,EAAK,WACZ,MAAMH,EAAe,QAAQG,CAAI,EAC1B,IAAI,SAAS,KAAK,UAAU,CAAE,QAAS,EAAK,CAAC,CAAC,CACvD,CACF,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CCpEA,eAAsBC,GACpBC,EACAC,EACAC,EACAC,EACmB,CAEnB,IAAMC,EADM,IAAI,IAAIJ,EAAQ,GAAG,EACT,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO,EAGxD,OAAII,EAAU,CAAC,IAAM,UACZC,GAA6BL,EAASC,EAAKC,EAAME,EAAWD,CAAG,EAIpEC,EAAU,CAAC,IAAM,QACdE,GAAK,QAAQJ,CAAI,EACfK,GAAmBP,EAASC,EAAKC,EAAME,EAAWD,CAAG,EAD5B,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,EAIpE,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CCzBAK,KAMA,eAAsBC,GAAoBC,EAAkBC,EAA6B,CACvF,IAAMC,EAAM,IAAI,IAAIF,EAAQ,GAAG,EACzBG,EAAQ,OAAO,OAAW,IAAe,OAAe,QAAU,KAExE,GAAID,EAAI,WAAa,kBAAmB,CACtC,IAAME,EAAWJ,EAAQ,QAAQ,IAAI,kBAAkB,GAAK,YACtDK,EAAqB,MAAMC,EAAW,IAAYH,EAAO,cAAcC,CAAQ,EAAE,EACjFG,EAAMP,EAAgB,GAEtBQ,EAAUD,GAAI,SAAWP,EAAQ,QAAQ,IAAI,cAAc,GAAK,UAChES,EAASF,GAAI,QAAUP,EAAQ,QAAQ,IAAI,WAAW,GAAK,UAC3DU,EAAOH,GAAI,MAAQP,EAAQ,QAAQ,IAAI,WAAW,GAAK,UACvDW,EAAWJ,GAAI,UAAYP,EAAQ,QAAQ,IAAI,aAAa,GAAK,UACjEY,EAAML,GAAI,IAAM,OAAOA,EAAG,GAAG,EAAKP,EAAQ,QAAQ,IAAI,QAAQ,EAAI,OAAOA,EAAQ,QAAQ,IAAI,QAAQ,CAAC,EAAI,EAC1Ga,EAAiBN,GAAI,gBAAkBP,EAAQ,QAAQ,IAAI,WAAW,GAAK,UAEjF,OAAO,IAAI,SAAS,KAAK,UAAU,CACjC,GAAII,EACJ,QAAAI,EACA,OAAAC,EACA,KAAAC,EACA,SAAAC,EACA,IAAAC,EACA,eAAAC,EACA,mBAAoBR,GAAsB,KAC1C,iBAAkBJ,EAAI,mBAAqB,WAC7C,CAAC,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CACzD,CAEA,GAAIC,EAAI,WAAa,eAAgB,CACnC,IAAMY,EAA+B,CAAC,EACtC,OAAW,CAACC,EAAKC,CAAK,IAAK,OAAO,QAAQf,CAAG,EAC3C,GAAIc,EAAI,WAAW,YAAY,GAAK,OAAOC,GAAU,SACnD,GAAI,CACF,IAAMC,EAAYF,EAAI,QAAQ,aAAc,EAAE,EAC1CG,EAAWF,EAAM,KAAK,EAC1BE,EAAWA,EACR,QAAQ,aAAc,EAAE,EACxB,QAAQ,SAAU,EAAE,EACpB,QAAQ,SAAU,EAAE,EACpB,KAAK,EACRJ,EAAQG,CAAS,EAAI,KAAK,MAAMC,CAAQ,CAC1C,MAAY,CAEZ,CAGJ,OAAO,IAAI,SAAS,KAAK,UAAUJ,CAAO,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAClG,CAEA,GAAIZ,EAAI,WAAa,kBAAmB,CACtC,IAAMiB,EAAYlB,EAAI,mBAAqB,YACvCmB,EAAoC,KACpCC,EAAsC,KAEpCC,EAAgB,MAAOC,GAA+C,CAC1E,IAAMC,EAAa,CACjB,uCACA,2BACF,EACA,QAAWC,KAAUD,EACnB,GAAI,CACF,IAAME,EAAM,MAAM,MAAM,GAAGD,CAAM,SAASN,CAAS,SAASI,CAAI,GAAI,CAClE,QAAS,CAAE,OAAU,sBAAuB,EAC5C,OAAQ,YAAY,QAAQ,GAAI,CAClC,CAAC,EACD,GAAIG,EAAI,GAAI,CACV,IAAMC,EAAO,MAAMD,EAAI,KAAK,EAC5B,GAAIC,GAAM,QAAQ,OAAS,EAAG,CAC5B,IAAMC,EAASD,EAAK,OAAO,KAAME,GAAWA,EAAE,QAAUN,IAAS,IAAM,EAAI,GAAG,EAC9E,GAAIK,GAAQ,KACV,OAAOA,EAAO,IAElB,CACF,CACF,OAASE,EAAG,CACV,QAAQ,MAAM,yCAAyCP,CAAI,QAAQE,CAAM,IAAKK,CAAC,CACjF,CAEF,OAAO,IACT,EAEA,GAAI,CACF,GAAM,CAACC,EAAIC,CAAI,EAAI,MAAM,QAAQ,IAAI,CACnCV,EAAc,GAAG,EACjBA,EAAc,MAAM,CACtB,CAAC,EACDF,EAAqBW,EACrBV,EAAuBW,CACzB,OAASF,EAAG,CACV,QAAQ,MAAM,sDAAuDA,CAAC,CACxE,CAEA,OAAO,IAAI,SAAS,KAAK,UAAU,CACjC,GAAIV,EACJ,KAAMC,CACR,CAAC,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CACzD,CAEA,GAAInB,EAAI,WAAa,yBAA0B,CAC7C,IAAM+B,EAAYhC,EAAI,iBAAmB,KAAK,MAAMA,EAAI,gBAAgB,EAAI,CAAC,EAC7E,OAAO,IAAI,SAAS,KAAK,UAAUgC,CAAS,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CACpG,CAEA,GAAI/B,EAAI,WAAa,uBAAwB,CAC3C,IAAMgC,EAAUjC,EAAI,wBAA0B,KAAK,MAAMA,EAAI,uBAAuB,EAAI,CAAC,EACzF,OAAO,IAAI,SAAS,KAAK,UAAUiC,CAAO,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAClG,CAEA,GAAIhC,EAAI,WAAa,oCAAqC,CACxD,IAAMiC,EAAUC,GAA4BnC,CAAG,EAC/C,OAAO,IAAI,SAAS,KAAK,UAAUkC,CAAO,EAAG,CAAE,QAAS,CAAE,eAAgB,kBAAmB,CAAE,CAAC,CAClG,CAEA,GAAIjC,EAAI,SAAS,WAAW,YAAY,EAAG,CACzC,IAAMmC,EAASnC,EAAI,SAAS,QAAQ,aAAc,EAAE,EACpD,GAAI,CAACmC,EACH,OAAO,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAIvD,IAAMC,EAAY,oCAAoCD,CAAM,GAC5D,GAAI,CACF,IAAME,EAAU,MAAM,MAAMD,CAAS,EAE/BE,EAAa,IAAI,QAAQD,EAAQ,OAAO,EAC9C,OAAAC,EAAW,IAAI,gBAAiB,yBAAyB,EACzDA,EAAW,OAAO,6BAA6B,EAExC,IAAI,SAASD,EAAQ,KAAM,CAChC,OAAQA,EAAQ,OAChB,WAAYA,EAAQ,WACpB,QAASC,CACX,CAAC,CACH,MAAY,CACV,OAAO,IAAI,SAAS,sBAAuB,CAAE,OAAQ,GAAI,CAAC,CAC5D,CACF,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,CC5IAC,KAUA,eAAsBC,GACpBC,EACAC,EACAC,EACe,CACf,GAAI,CACF,IAAMC,EAAS,OAAe,QACxBC,EAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EAKlCC,EAAiB,8BACjBC,EAAc,MAAMC,EAAW,IAAYJ,EAAOE,CAAc,EAEtE,GAAI,CAACC,GAAeF,EAAME,GAAe,KAAM,CAC7C,GAAI,CACF,IAAME,EAAY,IAAIC,EAAUR,EAAI,GAAIA,CAAG,EACrC,CAAE,gBAAAS,EAAiB,aAAAC,CAAa,EAAI,MAAMH,EAAU,sBAAsBJ,CAAG,GAC/EM,EAAkB,GAAKC,EAAe,IACxC,QAAQ,IAAI,sCAAsCD,CAAe,wBAAwBC,CAAY,WAAW,CAEpH,OAASC,EAAG,CACV,QAAQ,MAAM,6CAA8CA,CAAC,CAC/D,CAEA,GAAI,CACF,IAAMC,EAAW,IAAIC,GAASb,EAAI,EAAE,EAC9Bc,EAAmBd,EAAI,yBAA2B,QAAaA,EAAI,yBAA2B,GAChG,OAAOA,EAAI,sBAAsB,EACjC,GACEe,EAAoBf,EAAI,0BAA4B,QAAaA,EAAI,0BAA4B,GACnG,OAAOA,EAAI,uBAAuB,EAClC,IACEgB,EAAqBhB,EAAI,2BAA6B,QAAaA,EAAI,2BAA6B,GACtG,OAAOA,EAAI,wBAAwB,EACnC,IACJ,MAAMY,EAAS,cAAcE,EAAkBC,EAAmBC,CAAkB,CACtF,OAASL,EAAG,CACV,QAAQ,MAAM,oCAAqCA,CAAC,CACtD,CAEA,GAAI,CACF,GAAM,CAAE,aAAAM,CAAa,EAAI,KAAM,uCACzBC,EAAe,IAAID,EAAajB,EAAI,EAAE,EACtC,CAAE,gBAAAmB,EAAiB,eAAAC,CAAe,EAAI,MAAMF,EAAa,sBAAsBf,CAAG,GACpFgB,EAAkB,GAAKC,EAAiB,IAC1C,QAAQ,IAAI,iBAAiBD,CAAe,2BAA2BC,CAAc,oBAAoB,CAE7G,OAAST,EAAG,CACV,QAAQ,MAAM,iCAAkCA,CAAC,CACnD,CAEA,MAAML,EAAW,IAAIJ,EAAOE,EAAgBD,EAAK,IAAI,EACrD,QAAQ,IAAI,oCAAqC,IAAI,KAAK,EAAE,YAAY,CAAC,CAC3E,CAIA,GAAI,CACF,MAAMkB,GAAuBrB,EAAI,EAAE,CACrC,OAASW,EAAG,CACV,QAAQ,MAAM,0CAA2CA,CAAC,CAC5D,CAOA,GAAI,CACF,IAAMW,EAAkB,4BAElBC,EAAgB,MAAMjB,EAAW,IAAYJ,EAAOoB,CAAe,EAGzE,GAAI,CAFoB,GAAQC,GAAkBpB,EAAMoB,EAAgB,KAElD,CACpB,IAAMC,EAAkB,OAAOxB,EAAI,yBAAyB,GAAK,MAC3DyB,EAAatB,EAAMqB,EACnBE,EAAe,IAAIC,EAAa3B,EAAI,EAAE,EACtC4B,EAAY,OAAO5B,EAAI,eAAe,GAAK,EAC3C6B,EAAc,MAAMH,EAAa,eAAeD,EAAYG,CAAS,EAE3E,GAAIC,EAAY,OAAS,EAAG,CAE1B,MAAMvB,EAAW,OAAOJ,EAAOoB,CAAe,EAE9C,QAAWQ,KAAUD,EACnB,GAAI,CAGF,MAAME,GAAuBD,EAAO,GAAI9B,EAAKC,CAAG,CAClD,OAAS+B,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAChE,QAAQ,MAAM,kCAAkCF,EAAO,EAAE,IAAKG,CAAQ,CACxE,CAEF,QAAQ,IAAI,0BAA0BJ,EAAY,MAAM,cAAc,CACxE,MAEE,MAAMvB,EAAW,IAAIJ,EAAOoB,EAAiBnB,EAAK,GAAsB,CAE5E,CACF,OAASQ,EAAY,CACnB,IAAMsB,EAAWtB,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,EAC1D,QAAQ,MAAM,4BAA6BsB,CAAQ,CACrD,CACF,OAAStB,EAAY,CACnB,IAAMsB,EAAWtB,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,EAC1D,QAAQ,MAAM,2BAA4BsB,CAAQ,CACpD,CACF,CCzHA,eAAsBC,GAAqBC,EAAkBC,EAAUC,EAA0C,CAC/G,IAAMC,EAAW,IAAI,QAAQ,IAAI,IAAI,uCAAuC,EAAGH,CAAO,EAChFI,EAAS,OAAe,QAE1BC,EAAW,MAAMD,EAAM,MAAMD,CAAQ,EACzC,GAAI,CAACE,EACH,GAAI,CAEF,IAAMC,EAAM,MAAM,MADN,0EACiB,CAC3B,QAAS,CACP,aAAc,8CAChB,CACF,CAAC,EACD,GAAI,CAACA,EAAI,GACP,MAAM,IAAI,MAAM,iDAAiDA,EAAI,MAAM,EAAE,EAG/E,IAAMC,EAAO,MAAMD,EAAI,KAAK,EAI5B,GAAIC,GAAQA,EAAK,QAAS,CACxB,IAAMC,EAAqB,CAAC,EACxBD,EAAK,QAAQ,YACfC,EAAc,UAAYD,EAAK,QAAQ,WAErCA,EAAK,QAAQ,OACfC,EAAc,KAAOD,EAAK,QAAQ,MAEpCA,EAAK,QAAUC,CACjB,CAIID,GAAQA,EAAK,SAAWA,EAAK,QAAQ,WAAa,MAAM,QAAQA,EAAK,QAAQ,UAAU,UAAU,GACnGA,EAAK,QAAQ,UAAU,WAAW,QAASE,GAAc,CACnDA,EAAK,KAAO,QAAaA,EAAK,KAAO,OACvCA,EAAK,GAAK,OAAOA,EAAK,EAAE,EAAE,SAAS,EAAG,GAAG,EAE7C,CAAC,EAGHJ,EAAW,IAAI,SAAS,KAAK,UAAUE,CAAI,EAAG,CAC5C,QAAS,CACP,eAAgB,kCAChB,gBAAiB,0BACjB,8BAA+B,GACjC,CACF,CAAC,EAGDL,EAAI,UAAUE,EAAM,IAAID,EAAUE,EAAS,MAAM,CAAC,CAAC,CACrD,OAASK,EAAG,CACV,eAAQ,MAAM,+DAAgEA,CAAC,EACxE,IAAI,SAAS,KAAK,UAAU,CAAE,MAAO,0BAA2B,CAAC,EAAG,CACzE,OAAQ,IACR,QAAS,CACP,eAAgB,kBAClB,CACF,CAAC,CACH,CAGF,OAAOL,CACT,ClDtDAM,KAEA,IAAOC,GAAQ,CACb,MAAM,MAAMC,EAAkBC,EAAUC,EAA0C,CAChF,IAAMC,EAAa,IAAI,WAAW,EAAE,EACpC,OAAO,gBAAgBA,CAAU,EACjC,IAAMC,EAAQ,MAAM,KAAKD,CAAU,EAAE,IAAIE,GAAKA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,EAElFC,EAA2B,KAiM3BC,EAAW,MA/LO,SAA+B,CACnD,IAAMC,EAAM,IAAI,IAAIR,EAAQ,GAAG,EAG/B,GAAIQ,EAAI,WAAa,mBACnB,OAAOC,GAAqBT,EAASC,EAAKC,CAAG,EAI/C,GAAIM,EAAI,SAAS,WAAW,OAAO,EAAG,CACpC,IAAIE,EAAc,GAClB,GAAI,CACFA,EAAc,CAACT,EAAI,EACrB,MAAY,CACVS,EAAc,EAChB,CACA,IAAMC,EAAqB,CAACC,GAAkBX,EAAI,UAAU,EAE5D,GAAIS,GAAeC,EACjB,OAAO,IAAI,SAAS,KAAK,UAAU,CACjC,MAAO,sBACP,YAAAD,EACA,mBAAAC,EACA,QAAS,8GACX,CAAC,EAAG,CACF,OAAQ,IACR,QAAS,CACP,eAAgB,mBAChB,8BAA+B,GACjC,CACF,CAAC,EAIHL,EAAc,MAAMO,GAAeb,EAASC,EAAKC,CAAG,EAEpD,IAAMY,EAAgB,CACpB,kBACA,mBACA,qBACA,2BACA,mBACA,oBACA,mBACA,2BACA,kBACA,cACF,EAAE,SAASN,EAAI,QAAQ,GACvBA,EAAI,SAAS,WAAW,YAAY,GACpCA,EAAI,SAAS,WAAW,eAAe,GACvCA,EAAI,SAAS,WAAW,2BAA2B,EAGnD,MAAI,CAACF,GAAe,CAACQ,EACZ,IAAI,SAAS,eAAgB,CAAE,OAAQ,GAAI,CAAC,EAIjDR,GAAeA,EAAY,UAMzB,CALyB,CAC3B,2BACA,mBACA,mBACF,EAAE,SAASE,EAAI,QAAQ,EAEd,IAAI,SAAS,iBAAkB,CAAE,OAAQ,GAAI,CAAC,EAKrDF,GAAe,CAAC,OAAQ,MAAO,QAAS,QAAQ,EAAE,SAASN,EAAQ,MAAM,GAAK,CAACc,GAC7E,CAACC,GAAaf,CAAO,EAChB,IAAI,SAAS,yBAA0B,CAAE,OAAQ,GAAI,CAAC,EAK7DQ,EAAI,SAAS,WAAW,YAAY,EAC/BQ,GAAkBhB,EAASC,CAAG,EAGrCO,EAAI,WAAa,mBACjBA,EAAI,WAAa,gBACjBA,EAAI,WAAa,mBACjBA,EAAI,SAAS,WAAW,eAAe,GACvCA,EAAI,SAAS,WAAW,YAAY,EAE7BS,GAAoBjB,EAASC,CAAG,EAErCO,EAAI,SAAS,WAAW,eAAe,EAClCU,GAAsBlB,EAASC,EAAKK,EAAaJ,CAAG,EAEzDM,EAAI,SAAS,WAAW,cAAc,GAAKA,EAAI,SAAS,WAAW,YAAY,EAC1EW,GAAqBnB,EAASC,EAAKK,EAAcJ,CAAG,EAEtD,IAAI,SAAS,gBAAiB,CAAE,OAAQ,GAAI,CAAC,CACtD,CAGA,GAAIM,EAAI,WAAa,YAAa,CAChC,IAAMY,EAAMZ,EAAI,aAAa,IAAI,KAAK,EACtC,GAAI,CAACY,GAAO,CAACC,GAAiB,KAAKD,CAAG,EACpC,OAAO,IAAI,SAAS,mCAAoC,CAAE,OAAQ,GAAI,CAAC,EAEzE,IAAME,EAASC,GAAyBf,EAAI,OAAQY,CAAG,EACvD,OAAO,IAAI,SAASE,EAAQ,CAC1B,QAAS,CACP,eAAgB,4BAChB,gBAAiB,UACnB,CACF,CAAC,CACH,CAGA,IAAME,EAAShB,EAAI,SAAS,MAAM,CAAC,EAC7BiB,EAAaJ,GAAiB,KAAKG,CAAM,EACzCE,EAAe1B,EAAQ,SAAW,QACnBQ,EAAI,aAAa,IAAI,KAAK,GAC1BR,EAAQ,QAAQ,IAAI,QAAQ,GAAG,SAAS,aAAa,EAE1E,GAAIyB,IAAeC,GAAgB1B,EAAQ,SAAW,WACpD,OAAIA,EAAQ,SAAW,UACd,IAAI,SAAS,KAAM,CACxB,OAAQ,IACR,QAAS,CACP,8BAA+B,IAC/B,+BAAgC,qBAChC,+BAAgC,uBAChC,yBAA0B,OAC5B,CACF,CAAC,EAEI2B,GAAiB3B,EAASC,EAAKC,EAAKsB,CAAM,EAInD,GAAI,CACF,IAAIjB,EAAW,MAAON,EAAY,OAAO,MAAMD,CAAO,EAStD,GARIO,EAAS,SAAW,KAAO,CAACC,EAAI,SAAS,WAAW,UAAU,IAAMR,EAAQ,SAAW,OAASA,EAAQ,SAAW,UACrHO,EAAW,MAAON,EAAY,OAAO,MAAM,IAAI,QAAQO,EAAI,OAAS,IAAK,CACvE,OAAQR,EAAQ,OAChB,QAASA,EAAQ,OACnB,CAAC,CAAC,GAIAQ,EAAI,SAAS,SAAS,OAAO,EAAG,CAClC,IAAMoB,EAAc,IAAI,QAAQrB,EAAS,OAAO,EAChD,OAAAqB,EAAY,IAAI,eAAgB,kBAAkB,EAClDA,EAAY,IAAI,+BAAgC,aAAa,EACtD,IAAI,SAASrB,EAAS,KAAM,CACjC,OAAQA,EAAS,OACjB,WAAYA,EAAS,WACrB,QAASqB,CACX,CAAC,CACH,CAGA,IADoBrB,EAAS,QAAQ,IAAI,cAAc,GAAK,IAC5C,SAAS,WAAW,EAAG,CACrC,IAAIG,EAAc,GAClB,GAAI,CACFA,EAAc,CAACT,EAAI,EACrB,MAAY,CACVS,EAAc,EAChB,CACA,IAAMC,EAAqB,CAACC,GAAkBX,EAAI,UAAU,EAExD4B,EAAY,KAChB,GAAI,CACDA,EAAY,KAAK,UAAU,CACzB,MAAAzB,EACA,YAAAM,EACA,mBAAAC,CACF,CAAC,CACJ,MAAY,CAAE,CAEd,OAAO,IAAI,aAAa,EACrB,GAAG,OAAQ,CACV,QAAQmB,EAAS,CACfA,EAAQ,QAAQ,kBAAkB1B,CAAK,gCAAgCyB,CAAS,aAAc,CAAE,KAAM,EAAK,CAAC,CAC9G,CACF,CAAC,EACA,UAAUtB,CAAQ,CACvB,CAEA,OAAOA,CACT,MAAY,CACV,OAAO,IAAI,SAAS,oBAAqB,CAAE,OAAQ,GAAI,CAAC,CAC1D,CACF,GAEmC,EAG7BwB,EAAYzB,EAClB,GAAIyB,GAAa,CAACC,GAAehC,EAAQ,QAAQ,IAAI,QAAQ,CAAC,EAAG,CAC/D,IAAMiC,EAAiBF,EAAU,WAAW,WAAW,IAAI,EAC3D,GAAI,CACF,IAAMG,EAAYC,EAAW,EAAE,EAC/B5B,EAAS,QAAQ,OAAO,aAAc6B,GAAiBF,EAAWjC,EAAKgC,CAAc,CAAC,CACxF,MAAY,CAEV,IAAMI,EAAa,IAAI,QAAQ9B,EAAS,OAAO,EACzC2B,EAAYC,EAAW,EAAE,EAC/BE,EAAW,OAAO,aAAcD,GAAiBF,EAAWjC,EAAKgC,CAAc,CAAC,EAChF1B,EAAW,IAAI,SAASA,EAAS,KAAM,CACrC,OAAQA,EAAS,OACjB,WAAYA,EAAS,WACrB,QAAS8B,CACX,CAAC,CACH,CACF,CAGA,GAAI9B,EAAS,QAAQ,IAAI,cAAc,GAAG,SAAS,yBAAyB,EAAG,CAC7E,IAAM+B,EAAa,IAAI,QAAQ/B,EAAS,OAAO,EAC/C,OAAA+B,EAAW,IAAI,8BAA+B,GAAG,EACjDA,EAAW,IAAI,+BAAgC,oBAAoB,EACnEA,EAAW,IAAI,+BAAgC,sBAAsB,EAC9D,IAAI,SAAS/B,EAAS,KAAM,CACjC,OAAQA,EAAS,OACjB,WAAYA,EAAS,WACrB,QAAS+B,CACX,CAAC,CACH,CAEA,OAAOC,GAAqBhC,EAAUH,CAAK,CAC7C,EAEA,MAAM,UAAUoC,EAAuBvC,EAAUC,EAAsC,CACrF,MAAMuC,GAAgBD,EAAOvC,EAAKC,CAAG,CACvC,CACF,ED9OA,IAAMwC,GAAqC,CACzC,QAAS,2BACT,MAAO,wCACP,OAAQ,wCACR,OAAQ,0BACR,QAAS,kCACT,QAAS,aACT,OAAQ,YACR,OAAQ,aACR,OAAQ,gBACR,OAAQ,eACR,eAAgB,4BAChB,QAAS,kBACX,EAEaC,GAAN,KAAiB,CAItB,YAAoBC,EAA4B,CAA5B,aAAAA,EAClB,IAAMC,EAAYD,EAAQ,WAAaE,GAAK,KAAKC,GAAe,EAAG,QAAQ,EAG3E,KAAK,QAAQ,IAAI,OAAS,CACxB,MAAO,MAAOC,GAAoC,CAChD,GAAI,CACF,IAAMC,EAAM,IAAI,IAAID,EAAI,GAAG,EACvBE,EAAW,mBAAmBD,EAAI,QAAQ,GAC1CC,IAAa,KAAOA,IAAa,MACnCA,EAAW,eAGb,IAAMC,EAAWL,GAAK,UAAUI,CAAQ,EAAE,QAAQ,iBAAkB,EAAE,EAChEE,EAAWN,GAAK,KAAKD,EAAWM,CAAQ,EAE9C,GAAIE,GAAG,WAAWD,CAAQ,GAAKC,GAAG,SAASD,CAAQ,EAAE,OAAO,EAAG,CAC7D,IAAME,EAAMR,GAAK,QAAQM,CAAQ,EAAE,YAAY,EACzCG,EAAcb,GAAWY,CAAG,GAAK,2BACjCE,EAAcH,GAAG,aAAaD,CAAQ,EAE5C,OAAO,IAAI,SAASI,EAAa,CAC/B,OAAQ,IACR,QAAS,CACP,eAAgBD,EAChB,gBAAiBD,IAAQ,QAAU,WAAa,sCAChD,6BAA8B,cAC9B,+BAAgC,iBAChC,+BAAgC,aAClC,CACF,CAAC,CACH,CAEA,OAAO,IAAI,SAAS,YAAa,CAAE,OAAQ,GAAI,CAAC,CAClD,MAAc,CACZ,OAAO,IAAI,SAAS,cAAe,CAAE,OAAQ,GAAI,CAAC,CACpD,CACF,CACF,CACF,CAvCoB,QAHZ,OAA6B,KAC7B,UAAqB,GA2C7B,OAAuB,CACrB,OAAO,IAAI,QAAQ,CAACG,EAASC,IAAW,CACtC,GAAM,CAAE,KAAAC,EAAM,KAAAC,CAAK,EAAI,KAAK,QAE5B,KAAK,OAASC,GAAK,aAAa,MAAOb,EAA2Bc,IAA6B,CAC7F,MAAM,KAAK,kBAAkBd,EAAKc,CAAG,CACvC,CAAC,EAGD,KAAK,OAAO,iBAAmB,KAC/B,KAAK,OAAO,eAAiB,KAC7B,KAAK,OAAO,GAAG,aAAeC,GAAW,CACvCA,EAAO,WAAW,EAAI,CACxB,CAAC,EAED,IAAMC,EAAuBC,GAAa,CACxC,KAAK,OAAS,KAEd,QAAQ,MAAMC,GAAgB,CAC5B,YAAa,6BACb,SAAU,OACV,KAAAP,EACA,IAAAM,EACA,gBAAiB,kCAAkCN,EAAO,CAAC,GAC7D,CAAC,CAAC,EAEFD,EAAOO,CAAG,CACZ,EAEA,KAAK,OAAO,KAAK,QAASD,CAAmB,EAE7C,KAAK,OAAO,OAAOL,EAAMC,EAAM,IAAM,CACnC,KAAK,QAAQ,eAAe,QAASI,CAAmB,EACxD,KAAK,QAAQ,GAAG,QAAUC,GAAe,CACvC,QAAQ,MAAM,+BAAgCA,CAAG,CACnD,CAAC,EACD,KAAK,UAAY,GACjB,QAAQ,IAAI,yDAAyDL,CAAI,IAAID,CAAI,EAAE,EACnFF,EAAQ,CACV,CAAC,CACH,CAAC,CACH,CAEA,MAAc,kBAAkBT,EAA2Bc,EAAyC,CAClG,GAAI,CACF,IAAMK,EAASnB,EAAI,QAAQ,mBAAmB,GAAgB,OACxDY,EAAOZ,EAAI,QAAQ,MAAQ,GAAG,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,GACpEoB,EAAU,IAAI,IAAIpB,EAAI,KAAO,IAAK,GAAGmB,CAAK,MAAMP,CAAI,EAAE,EAEtDS,EAAU,IAAI,QACpB,OAAW,CAACC,EAAGC,CAAC,IAAK,OAAO,QAAQvB,EAAI,OAAO,EACxCuB,IACD,MAAM,QAAQA,CAAC,EACjBA,EAAE,QAASC,GAAQH,EAAQ,OAAOC,EAAGE,CAAG,CAAC,EAEzCH,EAAQ,IAAIC,EAAG,OAAOC,CAAC,CAAC,GAK5B,GAAI,CAACF,EAAQ,IAAI,kBAAkB,EAAG,CACpC,IAAMI,EAAWzB,EAAI,OAAO,eAAe,QAAQ,WAAY,EAAE,GAAK,YACtEqB,EAAQ,IAAI,mBAAoBI,CAAQ,CAC1C,CAGKJ,EAAQ,IAAI,eAAe,GAC9BA,EAAQ,IAAI,gBAAiB,KAAK,EAE/BA,EAAQ,IAAI,gBAAgB,GAC/BA,EAAQ,IAAI,iBAAkB,KAAK,EAGrC,IAAIK,EAAY,KAChB,GAAI1B,EAAI,SAAW,OAASA,EAAI,SAAW,OAAQ,CACjD,IAAM2B,EAAmB,CAAC,EAC1B,cAAiBC,KAAS5B,EACxB2B,EAAO,KAAK,OAAOC,GAAU,SAAWC,GAAO,KAAKD,CAAK,EAAIA,CAAK,EAEpEF,EAAOG,GAAO,OAAOF,CAAM,CAC7B,CAEA,IAAMG,EAAU,IAAI,QAAQV,EAAQ,SAAS,EAAG,CAC9C,OAAQpB,EAAI,OACZ,QAAAqB,EACA,KAAAK,EAEA,OAAQ,MACV,CAAC,EAEKK,EAAwB,CAC5B,UAAUC,EAAuB,CAC/BA,EAAQ,MAAOf,GAAQ,QAAQ,MAAM,gCAAiCA,CAAG,CAAC,CAC5E,EACA,wBAAyB,CAAC,CAC5B,EAEMgB,EAAW,MAAMC,GAAO,MAAMJ,EAAS,KAAK,QAAQ,IAAKC,CAAG,EASlE,GAPAjB,EAAI,WAAamB,EAAS,OAC1BnB,EAAI,cAAgBmB,EAAS,WAE7BA,EAAS,QAAQ,QAAQ,CAACT,EAAKW,IAAQ,CACrCrB,EAAI,UAAUqB,EAAKX,CAAG,CACxB,CAAC,EAEGS,EAAS,KAAM,CACjB,IAAMG,EAASH,EAAS,KAAK,UAAU,EACvC,OAAa,CACX,GAAM,CAAE,KAAAI,EAAM,MAAAC,CAAM,EAAI,MAAMF,EAAO,KAAK,EAC1C,GAAIC,EAAM,MACVvB,EAAI,MAAMwB,CAAK,CACjB,CACF,CACAxB,EAAI,IAAI,CACV,OAASG,EAAU,CACjB,QAAQ,MAAM,2CAA4CA,CAAG,EACxDH,EAAI,cACPA,EAAI,WAAa,IACjBA,EAAI,UAAU,eAAgB,kBAAkB,EAChDA,EAAI,IAAI,KAAK,UAAU,CAAE,MAAO,wBAAyB,QAASG,EAAI,OAAQ,CAAC,CAAC,EAEpF,CACF,CAEA,MAAsB,CACpB,OAAO,IAAI,QAASR,GAAY,CAC1B,KAAK,QAAU,KAAK,UACtB,KAAK,OAAO,MAAM,IAAM,CACtB,KAAK,UAAY,GACjB,QAAQ,IAAI,gCAAgC,EAC5CA,EAAQ,CACV,CAAC,EAEDA,EAAQ,CAEZ,CAAC,CACH,CACF,E7E3MA8B,KiIXA,OAAOC,OAAQ,UACf,OAAOC,OAAS,WAChB,OAAOC,OAAW,aAClB,OAAS,gBAAAC,OAAoB,cAG7BC,KAeO,SAASC,GAAaC,EAAcC,EAAcC,EAAoB,IAAiC,CAC5G,OAAO,IAAI,QAASC,GAAY,CAC9B,IAAMC,EAAS,IAAIC,GAAI,OACnBC,EAAY,GAEVC,EAAUC,GAA6B,CAC3C,GAAI,CAAAF,EACJ,CAAAA,EAAY,GACZ,GAAI,CAAEF,EAAO,QAAQ,CAAG,MAAQ,CAAC,CACjCD,EAAQK,CAAK,EACf,EAEAJ,EAAO,WAAWF,CAAS,EAC3BE,EAAO,KAAK,UAAW,IAAMG,EAAO,QAAQ,CAAC,EAC7CH,EAAO,KAAK,UAAW,IAAMG,EAAO,MAAM,CAAC,EAC3CH,EAAO,KAAK,QAAS,IAAMG,EAAO,MAAM,CAAC,EAGzC,IAAME,EAAaR,IAAS,WAAaA,IAAS,KAAO,YAAcA,EACvE,GAAI,CACFG,EAAO,QAAQJ,EAAMS,CAAU,CACjC,MAAQ,CACNF,EAAO,MAAM,CACf,CACF,CAAC,CACH,CASO,SAASG,GAAaV,EAAcC,EAA0C,CACnF,OAAO,IAAI,QAASE,GAAY,CAC9B,IAAMC,EAASO,GAAM,aAAa,MAAM,EACpCL,EAAY,GAEVC,EAAUC,GAA6B,CAC3C,GAAI,CAAAF,EACJ,CAAAA,EAAY,GACZ,GAAI,CAAEF,EAAO,MAAM,CAAG,MAAQ,CAAC,CAC/BD,EAAQK,CAAK,EACf,EAEAJ,EAAO,KAAK,QAAUQ,GAAiB,CACrC,IAAMC,EAAQD,GAA2B,KAEvCL,EADEM,IAAS,aACJ,SAEA,MAFQ,CAInB,CAAC,EAED,GAAI,CACFT,EAAO,KAAKJ,EAAMC,EAAM,IAAM,CAC5BM,EAAO,MAAM,CACf,CAAC,CACH,MAAQ,CACNA,EAAO,QAAQ,CACjB,CACF,CAAC,CACH,CAoBO,SAASO,GAAgBC,EAA8B,CAC5D,GAAI,CAACC,GAAG,WAAWD,CAAM,EACvB,MAAO,CACL,OAAQ,GACR,WAAY,mEACd,EAGF,GAAI,CACF,IAAME,EAAQD,GAAG,SAASD,CAAM,EAC1BG,EAAgBD,EAAM,KAAO,KAAO,KACtC,IAAIA,EAAM,MAAQ,KAAO,OAAO,QAAQ,CAAC,CAAC,MAC1C,IAAIA,EAAM,KAAO,MAAM,QAAQ,CAAC,CAAC,MAEjCE,EAAkB,EAClBC,EAAa,EACbC,EAAgB,EAEpB,GAAI,CACF,IAAMC,EAAQ,IAAIC,GAAaR,EAAQ,CAAE,SAAU,EAAK,CAAC,EAEnDS,EAAWF,EAAM,QAAQ,uFAAuF,EAAE,IAAI,EACxHE,GAAYA,EAAS,IAAM,IAE7BL,EADeG,EAAM,QAAQ,6CAA6C,EAAE,IAAI,GACtD,OAAS,GAGrC,IAAMG,EAAaH,EAAM,QAAQ,+EAA+E,EAAE,IAAI,EAClHG,GAAcA,EAAW,IAAM,IAEjCL,EADgBE,EAAM,QAAQ,qCAAqC,EAAE,IAAI,GACnD,OAAS,GAGjC,IAAMI,EAAYJ,EAAM,QAAQ,kFAAkF,EAAE,IAAI,EACpHI,GAAaA,EAAU,IAAM,IAE/BL,EADgBC,EAAM,QAAQ,wCAAwC,EAAE,IAAI,GACnD,OAAS,GAGpCA,EAAM,MAAM,CACd,MAAQ,CAER,CAEA,IAAMK,EAAU,CACdT,EACAC,EAAkB,EAAI,GAAGA,CAAe,cAAgB,KACxDC,EAAa,EAAI,GAAGA,CAAU,WAAa,KAC3CC,EAAgB,EAAI,GAAGA,CAAa,cAAgB,IACtD,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,EAE3B,MAAO,CACL,OAAQ,GACR,cAAAH,EACA,gBAAAC,EACA,WAAAC,EACA,cAAAC,EACA,WAAY,UAAUM,CAAO,GAC/B,CACF,OAASf,EAAc,CAErB,MAAO,CACL,OAAQ,GACR,WAAY,yBAHGA,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAGjB,GAC/C,CACF,CACF,CAQA,eAAsBgB,GAAqBC,EAA0BC,EAAsC,CACzG,GAAM,CAACC,EAAWC,EAAUC,CAAQ,EAAI,MAAM,QAAQ,IAAI,CACxDlC,GAAa8B,EAAO,SAAUA,EAAO,IAAI,EACxCA,EAAO,WAAyD,QAAQ,QAAQ,UAAmB,EAA/EnB,GAAamB,EAAO,QAASA,EAAO,IAAI,EAC5DA,EAAO,WAAyD,QAAQ,QAAQ,UAAmB,EAA/E9B,GAAa8B,EAAO,QAASA,EAAO,IAAI,CAC/D,CAAC,EAEKK,EAAWpB,GAAgBe,EAAO,MAAM,EAExCM,EAAgB,GAAQN,EAAO,YAAcA,EAAO,aACpDO,EAAgBD,GAAiBnB,GAAG,WAAWa,EAAO,UAAU,GAAKb,GAAG,WAAWa,EAAO,WAAW,EAEvGQ,EAAY,2DACZC,GAAkBR,CAAY,IAChCO,EAAYE,GAAkBT,CAAY,EACtC,wCACA,oDAGN,IAAMU,EAAiBT,IAAc,SACjC,UAAUF,EAAO,IAAI,IAAIA,EAAO,QAAQ,wBACxC,UAAUA,EAAO,IAAI,IAAIA,EAAO,QAAQ,8BAEtCY,EAAgBZ,EAAO,WACzB,2BACAG,IAAa,SACX,SAASH,EAAO,IAAI,IAAIA,EAAO,OAAO,qBACtC,SAASA,EAAO,IAAI,IAAIA,EAAO,OAAO,8BAExCa,EAAgB,GAChBb,EAAO,WACTa,EAAgB,2BACNP,EAEAC,EAEDH,IAAa,SACtBS,EAAgB,SAASb,EAAO,IAAI,IAAIA,EAAO,OAAO,wBAEtDa,EAAgB,SAASb,EAAO,IAAI,IAAIA,EAAO,OAAO,8BAJtDa,EAAgB,SAASb,EAAO,IAAI,IAAIA,EAAO,OAAO,6CAFtDa,EAAgB,SAASb,EAAO,IAAI,IAAIA,EAAO,OAAO,gDASxD,IAAMc,EAAqBd,EAAO,kBAC9B,aAAaA,EAAO,iBAAiB,IACrC,kCAEEe,EAASC,GAAa,CAC1B,MAAO,8CACP,WAAY,IACZ,OAAQ,SACV,CAAC,EAEKC,EAAkBC,GAAsB,CAC5C,MAAO;AAAA,qBACP,MAAO,CACL,CAAE,MAAO,sBAAuB,MAAOP,CAAe,EACtD,CAAE,MAAO,kBAAmB,MAAOC,CAAc,EACjD,CAAE,MAAO,qBAAsB,MAAOC,CAAc,CACtD,CACF,CAAC,EAEKM,EAAkBD,GAAsB,CAC5C,MAAO;AAAA,qBACP,MAAO,CACL,CAAE,MAAO,cAAe,MAAOlB,EAAO,MAAO,EAC7C,CAAE,MAAO,kBAAmB,MAAOK,EAAS,UAAW,CACzD,CACF,CAAC,EAEKe,EAAaF,GAAsB,CACvC,MAAO;AAAA,yBACP,MAAO,CACL,CAAE,MAAO,kBAAmB,MAAO,IAAI,QAAQ,SAAS,IAAI,KAAK,QAAQ,QAAQ,IAAI,QAAQ,IAAI,GAAI,EACrG,CAAE,MAAO,aAAc,MAAOV,CAAU,EACxC,CAAE,MAAO,sBAAuB,MAAOM,CAAmB,CAC5D,CACF,CAAC,EAEGO,EAAa,GACjB,GAAInB,IAAc,SAAWC,IAAa,QAAUH,EAAO,YAAa,CACtE,IAAMsB,EAAoB,QAAQ,WAAa,QAC3C,yDACA,kCAEJD,EAAaE,GAAU,CACrB,kCACA,0DAA0DD,CAAiB,EAC7E,CAAC,CACH,CAEA,IAAME,EAAS,CAACT,EAAQE,EAAiBE,EAAiBC,EAAYC,CAAU,EAC7E,OAAO,OAAO,EACd,KAAK;AAAA,CAAI,EAEZ,QAAQ,IAAIG,CAAM,CACpB,CCjRA,OAAOC,OAAQ,UACf,OAAOC,OAAU,YACjB,OAAS,YAAAC,OAAgB,qBAQlB,SAASC,IAA0B,CACxC,GAAI,CACF,OAAAD,GAAS,cAAe,CAAE,MAAO,QAAS,CAAC,EACpC,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAOO,SAASE,IAAuB,CACrC,OAAO,OAAO,QAAQ,QAAW,YAAc,QAAQ,OAAO,IAAM,CACtE,CAOO,SAASC,IAA4C,CAC1D,IAAMC,EAAW,QAAQ,SACnBC,EAAa,QAAQ,KAAK,CAAC,EAAIN,GAAK,QAAQ,QAAQ,KAAK,CAAC,CAAC,EAAI,GAC/DO,EAAO,QAAQ,IAAI,WAAa,QAAQ,IAAI,MAAQ,OACpDC,EAAU,QAAQ,IAAI,UACxB,SAAS,QAAQ,IAAI,SAAS,GAC7B,QAAQ,IAAI,MAAQ,QAAQ,IAAI,EAEjCC,EAAU,GACd,OAAIH,GAAcP,GAAG,WAAWO,CAAU,EACxCG,EAAU,IAAIJ,CAAQ,MAAMC,CAAU,IAEtCG,EAAU,aAGL,CACL,QAAAA,EACA,SAAAJ,EACA,WAAAC,EACA,QAAS,QAAQ,WAAa,QAAU,QAAQ,IAAI,EAAIE,EACxD,KAAAD,CACF,CACF,CCtDA,OAAOG,OAAQ,UACf,OAAS,YAAAC,OAAgB,qBASlB,IAAMC,GAAe,aACfC,GAAoB,GAAGD,EAAY,WACnCE,GAAuB,uBAAuBD,EAAiB,GAQrE,SAASE,GAAoBC,EAAqC,CACvE,GAAM,CAAE,QAAAC,EAAS,QAAAC,EAAS,KAAAC,CAAK,EAAIH,EAEnC,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAMFG,CAAI;AAAA,mBACQD,CAAO;AAAA,YACdD,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAiBnB,CAOA,eAAsBG,GAAmBC,EAA+C,CACtF,IAAMC,EAASC,GAAY,EAE3B,OAAQF,EAAQ,CACd,IAAK,UAAW,CACTC,IACH,QAAQ,MAAME,EAAiB,CAC7B,MAAO,QACP,MAAO,oBACP,QAAS,oEACT,UAAW,CACT,4DACF,CACF,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,GAGhB,IAAMR,EAAUS,GAAsB,EAChCC,EAAcX,GAAoBC,CAAO,EAE/C,GAAI,CACFW,GAAG,cAAcb,GAAsBY,EAAa,OAAO,EAE3DE,GAAS,0BAA2B,CAAE,MAAO,SAAU,CAAC,EACxDA,GAAS,oBAAoBhB,EAAY,GAAI,CAAE,MAAO,SAAU,CAAC,EACjEgB,GAAS,qBAAqBhB,EAAY,GAAI,CAAE,MAAO,SAAU,CAAC,EAElE,IAAMiB,EAASC,GAAa,CAC1B,MAAO,+CACP,WAAY,IACZ,OAAQ,UACR,MAAO,CACL,CAAE,MAAO,eAAgB,MAAOhB,EAAqB,EACrD,CAAE,MAAO,kBAAmB,MAAO,GAAGE,EAAQ,IAAI,yBAA0B,EAC5E,CAAE,MAAO,mBAAoB,MAAO,oCAAqC,EACzE,CAAE,MAAO,eAAgB,MAAO,yCAA0C,CAC5E,CACF,CAAC,EAEKe,EAAeC,GAAkB,CACrC,MAAO,sCACP,MAAO,CACL,CAAE,QAAS,4BAA6B,KAAM,mBAAoB,EAClE,CAAE,QAAS,0BAA2B,KAAM,iCAAkC,EAC9E,CAAE,QAAS,kCAAmC,KAAM,iBAAkB,EACtE,CAAE,QAAS,+BAAgC,KAAM,cAAe,EAChE,CAAE,QAAS,oCAAqC,KAAM,gBAAiB,CACzE,CACF,CAAC,EAED,QAAQ,IAAI;AAAA,EAAOH,EAAS;AAAA;AAAA,EAASE,CAAY,CACnD,OAASE,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAChE,QAAQ,MAAMT,EAAiB,CAC7B,MAAO,QACP,MAAO,8BACP,QAASU,EACT,UAAW,CACT,iEACA,kCAAkCpB,EAAoB,GACxD,CACF,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,CAChB,CACA,KACF,CAEA,IAAK,YAAa,CACXQ,IACH,QAAQ,MAAME,EAAiB,CAC7B,MAAO,QACP,MAAO,oBACP,QAAS,sEACT,UAAW,CACT,8DACF,CACF,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,GAGhB,GAAI,CACF,GAAI,CAAEI,GAAS,kBAAkBhB,EAAY,GAAI,CAAE,MAAO,QAAS,CAAC,CAAG,MAAQ,CAAC,CAChF,GAAI,CAAEgB,GAAS,qBAAqBhB,EAAY,GAAI,CAAE,MAAO,QAAS,CAAC,CAAG,MAAQ,CAAC,CAE/Ee,GAAG,WAAWb,EAAoB,GACpCa,GAAG,WAAWb,EAAoB,EAGpCc,GAAS,0BAA2B,CAAE,MAAO,SAAU,CAAC,EACxD,QAAQ,IAAIJ,EAAiB,CAC3B,MAAO,UACP,MAAO,kBACP,QAAS,2CAA2CV,EAAoB,GAC1E,CAAC,CAAC,CACJ,OAASmB,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAChE,QAAQ,MAAMT,EAAiB,CAC7B,MAAO,QACP,MAAO,2BACP,QAASU,CACX,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,CAChB,CACA,KACF,CAEA,IAAK,QACL,IAAK,OACL,IAAK,UAAW,CACTZ,IACH,QAAQ,MAAME,EAAiB,CAC7B,MAAO,QACP,MAAO,oBACP,QAAS,6BAA6BH,CAAM,kCAC5C,UAAW,CACT,0CAA0CA,CAAM,EAClD,CACF,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,GAGhB,GAAI,CACFO,GAAS,aAAaP,CAAM,IAAIT,EAAY,GAAI,CAAE,MAAO,SAAU,CAAC,EACpE,QAAQ,IAAIY,EAAiB,CAC3B,MAAO,UACP,MAAO,0BACP,QAAS,aAAaH,CAAM,IAAIT,EAAY,EAC9C,CAAC,CAAC,CACJ,OAASqB,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAChE,QAAQ,MAAMT,EAAiB,CAC7B,MAAO,QACP,MAAO,WAAWH,CAAM,UACxB,QAASa,CACX,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,CAChB,CACA,KACF,CAEA,IAAK,SAAU,CACb,GAAI,CACFN,GAAS,oBAAoBhB,EAAY,GAAI,CAAE,MAAO,SAAU,CAAC,CACnE,MAAQ,CAER,CACA,KACF,CAEA,IAAK,OAAQ,CACX,GAAI,CACFgB,GAAS,iBAAiBhB,EAAY,YAAa,CAAE,MAAO,SAAU,CAAC,CACzE,OAASqB,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAChE,QAAQ,MAAMT,EAAiB,CAC7B,MAAO,QACP,MAAO,qBACP,QAAS,sBAAsBU,CAAQ,EACzC,CAAC,CAAC,CACJ,CACA,KACF,CAEA,QACE,QAAQ,MAAMV,EAAiB,CAC7B,MAAO,QACP,MAAO,wBACP,QAAS,2BAA2BH,CAAM,KAC1C,UAAW,CACT,4EACA,0CACF,CACF,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,CAElB,CACF,CCvOA,OAAOc,OAAQ,UACf,OAAOC,OAAU,YACjB,OAAS,YAAAC,OAAgB,qBAUlB,IAAMC,GAAoB,aACpBC,GAAmBC,GAAK,KAAK,QAAQ,IAAI,aAAe,kBAAmB,YAAY,EACvFC,GAAmBD,GAAK,KAAKD,GAAkB,aAAa,EAC5DG,GAAmBF,GAAK,KAAKD,GAAkB,iBAAiB,EAQtE,SAASI,GAAmBC,EAAqC,CACtE,MAAO;AAAA;AAAA,SAAmCA,EAAQ,OAAO;AAAA,GAASA,EAAQ,QAAQ,MAAMA,EAAQ,UAAU,SAASH,EAAgB;AAAA,CACrI,CAOA,eAAsBI,GAAkBC,EAA+C,CACrF,IAAMC,EAAUC,GAAe,EAE/B,OAAQF,EAAQ,CACd,IAAK,UAAW,CACTC,IACH,QAAQ,MAAME,EAAiB,CAC7B,MAAO,QACP,MAAO,oBACP,QAAS,+FACT,UAAW,CACT,sFACA,yCACF,CACF,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,GAGhB,IAAML,EAAUM,GAAsB,EAEtC,GAAI,CACGC,GAAG,WAAWZ,EAAgB,GACjCY,GAAG,UAAUZ,GAAkB,CAAE,UAAW,EAAK,CAAC,EAGpD,IAAMa,EAAaT,GAAmBC,CAAO,EAC7CO,GAAG,cAAcT,GAAkBU,EAAY,OAAO,EAGtD,IAAMC,EAAY,yBAAyBf,EAAiB,aAAaI,EAAgB,+CACzFY,GAASD,EAAW,CAAE,MAAO,SAAU,CAAC,EAGxC,GAAI,CACFC,GAAS,sBAAsBhB,EAAiB,IAAK,CAAE,MAAO,SAAU,CAAC,CAC3E,MAAQ,CAER,CAEA,IAAMiB,EAASC,GAAa,CAC1B,MAAO,iDACP,WAAY,IACZ,OAAQ,UACR,MAAO,CACL,CAAE,MAAO,YAAa,MAAOlB,EAAkB,EAC/C,CAAE,MAAO,UAAW,MAAO,2CAA4C,EACvE,CAAE,MAAO,aAAc,MAAO,iCAAkC,EAChE,CAAE,MAAO,kBAAmB,MAAOI,EAAiB,EACpD,CAAE,MAAO,aAAc,MAAOD,EAAiB,CACjD,CACF,CAAC,EAEKgB,EAAeC,GAAkB,CACrC,MAAO,8CACP,MAAO,CACL,CAAE,QAAS,4BAA6B,KAAM,kBAAmB,EACjE,CAAE,QAAS,0BAA2B,KAAM,0BAA2B,EACvE,CAAE,QAAS,6BAA8B,KAAM,yBAA0B,EACzE,CAAE,QAAS,0BAA2B,KAAM,sBAAuB,EACnE,CAAE,QAAS,+BAAgC,KAAM,wBAAyB,CAC5E,CACF,CAAC,EAED,QAAQ,IAAI;AAAA,EAAOH,EAAS;AAAA;AAAA,EAASE,CAAY,CACnD,OAASE,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAChE,QAAQ,MAAMV,EAAiB,CAC7B,MAAO,QACP,MAAO,sCACP,QAASW,EACT,UAAW,CACT,wDACA,2DACF,CACF,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,CAChB,CACA,KACF,CAEA,IAAK,YAAa,CACXb,IACH,QAAQ,MAAME,EAAiB,CAC7B,MAAO,QACP,MAAO,oBACP,QAAS,2EACT,UAAW,CACT,gEACF,CACF,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,GAGhB,GAAI,CACF,GAAI,CAAEK,GAAS,sBAAsBhB,EAAiB,IAAK,CAAE,MAAO,QAAS,CAAC,CAAG,MAAQ,CAAC,CAC1FgB,GAAS,yBAAyBhB,EAAiB,OAAQ,CAAE,MAAO,SAAU,CAAC,EAE3Ea,GAAG,WAAWT,EAAgB,GAChCS,GAAG,WAAWT,EAAgB,EAGhC,QAAQ,IAAIO,EAAiB,CAC3B,MAAO,UACP,MAAO,kBACP,QAAS,4BAA4BX,EAAiB,yBACxD,CAAC,CAAC,CACJ,OAASqB,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAChE,QAAQ,MAAMV,EAAiB,CAC7B,MAAO,QACP,MAAO,iCACP,QAASW,CACX,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,CAChB,CACA,KACF,CAEA,IAAK,QAAS,CACZ,GAAI,CACFN,GAAS,sBAAsBhB,EAAiB,IAAK,CAAE,MAAO,SAAU,CAAC,EACzE,QAAQ,IAAIW,EAAiB,CAC3B,MAAO,UACP,MAAO,kBACP,QAAS,oCAAoCX,EAAiB,EAChE,CAAC,CAAC,CACJ,OAASqB,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAChE,QAAQ,MAAMV,EAAiB,CAC7B,MAAO,QACP,MAAO,oBACP,QAASW,EACT,UAAW,CACT,2DACA,2EACF,CACF,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,CAChB,CACA,KACF,CAEA,IAAK,OAAQ,CACX,GAAI,CACFN,GAAS,sBAAsBhB,EAAiB,IAAK,CAAE,MAAO,SAAU,CAAC,EACzE,QAAQ,IAAIW,EAAiB,CAC3B,MAAO,UACP,MAAO,kBACP,QAAS,oCAAoCX,EAAiB,EAChE,CAAC,CAAC,CACJ,OAASqB,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAChE,QAAQ,MAAMV,EAAiB,CAC7B,MAAO,QACP,MAAO,mBACP,QAASW,CACX,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,CAChB,CACA,KACF,CAEA,IAAK,UAAW,CACd,GAAI,CACF,GAAI,CAAEN,GAAS,sBAAsBhB,EAAiB,IAAK,CAAE,MAAO,QAAS,CAAC,CAAG,MAAQ,CAAC,CAC1FgB,GAAS,sBAAsBhB,EAAiB,IAAK,CAAE,MAAO,SAAU,CAAC,EACzE,QAAQ,IAAIW,EAAiB,CAC3B,MAAO,UACP,MAAO,oBACP,QAAS,sCAAsCX,EAAiB,EAClE,CAAC,CAAC,CACJ,OAASqB,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAChE,QAAQ,MAAMV,EAAiB,CAC7B,MAAO,QACP,MAAO,sBACP,QAASW,CACX,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,CAChB,CACA,KACF,CAEA,IAAK,SAAU,CACb,GAAI,CACFN,GAAS,wBAAwBhB,EAAiB,gBAAiB,CAAE,MAAO,SAAU,CAAC,CACzF,MAAQ,CACN,QAAQ,IAAIW,EAAiB,CAC3B,MAAO,OACP,MAAO,sBACP,QAAS,SAASX,EAAiB,2DACnC,UAAW,CACT,uFACF,CACF,CAAC,CAAC,CACJ,CACA,KACF,CAEA,IAAK,OAAQ,CACX,GAAI,CAACa,GAAG,WAAWV,EAAgB,EAAG,CACpC,QAAQ,IAAIQ,EAAiB,CAC3B,MAAO,OACP,MAAO,sBACP,QAAS,wBAAwBR,EAAgB,QACjD,QAAS,CACP,qFACF,CACF,CAAC,CAAC,EACF,MACF,CAEA,GAAI,CAGF,IAAMoB,EAFUV,GAAG,aAAaV,GAAkB,OAAO,EACnC,KAAK,EAAE,MAAM,OAAO,EAClB,MAAM,GAAG,EAAE,KAAK;AAAA,CAAI,EAE5C,QAAQ,IAAIqB,GAAa,CACvB,MAAO,gCAAgCrB,EAAgB,GACvD,QAASoB,EACT,WAAY,IACZ,IAAK,oCAAoCpB,EAAgB,kBAC3D,CAAC,CAAC,CACJ,OAASkB,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAChE,QAAQ,MAAMV,EAAiB,CAC7B,MAAO,QACP,MAAO,qBACP,QAAS,2BAA2BW,CAAQ,EAC9C,CAAC,CAAC,CACJ,CACA,KACF,CAEA,QACE,QAAQ,MAAMX,EAAiB,CAC7B,MAAO,QACP,MAAO,wBACP,QAAS,2BAA2BH,CAAM,KAC1C,UAAW,CACT,4EACA,qCACF,CACF,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,CAElB,CACF,CCxQA,eAAsBiB,GAAoBC,EAA+C,CACvF,GAAI,QAAQ,WAAa,QACvB,MAAMC,GAAkBD,CAAM,UACrB,QAAQ,WAAa,QAC9B,MAAME,GAAmBF,CAAM,MAC1B,CAEL,IAAMG,EAAUC,GAAsB,EAChCC,EAAcC,GAAoBH,CAAO,EAE/C,QAAQ,IAAII,EAAiB,CAC3B,MAAO,OACP,MAAO,0BACP,QAAS,0GAA0G,QAAQ,QAAQ,IACnI,QAAS,CACP,yEACF,CACF,CAAC,CAAC,EACF,QAAQ,IAAIC,GAAa,CACvB,MAAO,gCACP,QAASH,EAAY,KAAK,EAC1B,WAAY,GACd,CAAC,CAAC,CACJ,CACF,CrInBA,SAASI,IAAyB,CAChC,GAAM,CAACC,EAAOC,CAAK,EAAI,QAAQ,SAAS,KAAK,MAAM,GAAG,EAAE,IAAI,MAAM,GAC9DD,EAAQ,IAAOA,IAAU,IAAMC,EAAQ,KACzC,QAAQ,MAAMC,EAAiB,CAC7B,MAAO,QACP,MAAO,iCACP,QAAS,yCAAyC,QAAQ,SAAS,IAAI,KACvE,UAAW,CACT,0FACF,CACF,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,EAElB,CAEA,SAASC,IAAkB,CACzB,IAAMC,EAAUC,GAAkB,EAE5BC,EAAOC,GAAe,CAC1B,KAAM,eAAeH,CAAO,qBAC5B,YAAa,mCACb,MAAO,CACL,uBACA,8BACA,8BACA,0BACF,EACA,SAAU,CACR,CAAE,MAAO,SAAU,KAAM,oDAAqD,EAC9E,CAAE,MAAO,mBAAoB,KAAM,oFAAqF,CAC1H,EACA,QAAS,CACP,CAAE,MAAO,eAAgB,KAAM,6CAA8C,EAC7E,CAAE,MAAO,sBAAuB,KAAM,+CAAgD,EACtF,CAAE,MAAO,sBAAuB,KAAM,oCAAqC,EAC3E,CAAE,MAAO,sBAAuB,KAAM,wCAAyC,EAC/E,CAAE,MAAO,uBAAwB,KAAM,4CAA6C,EACpF,CAAE,MAAO,cAAe,KAAM,+DAAgE,EAC9F,CAAE,MAAO,0BAA2B,KAAM,gEAAiE,EAC3G,CAAE,MAAO,gBAAiB,KAAM,gCAAiC,EACjE,CAAE,MAAO,gBAAiB,KAAM,oBAAqB,EACrD,CAAE,MAAO,gBAAiB,KAAM,wBAAyB,EACzD,CAAE,MAAO,SAAU,KAAM,2BAA4B,CACvD,EACA,QAAS,CACP,CAAE,MAAO,8BAA+B,KAAM,0BAA2B,EACzE,CAAE,MAAO,iCAAkC,KAAM,sBAAuB,EACxE,CAAE,MAAO,iCAAkC,KAAM,UAAW,EAC5D,CAAE,MAAO,+BAAgC,KAAM,2BAA4B,EAC3E,CAAE,MAAO,aAAc,KAAM,+CAAgD,EAC7E,CAAE,MAAO,0BAA2B,KAAM,iCAAkC,EAC5E,CAAE,MAAO,2BAA4B,KAAM,iCAAkC,CAC/E,EACA,IAAK,CACH,oGACA,QAAQ,WAAa,QACjB,kHACA,gGACN,CACF,CAAC,EAED,QAAQ,IAAIE,CAAI,CAClB,CAEA,eAAeE,IAAuC,CACpD,GAAI,CACF,GAAM,CAAE,OAAAC,EAAQ,YAAAC,CAAY,EAAIC,GAAU,CACxC,QAAS,CACP,KAAM,CAAE,KAAM,SAAU,MAAO,GAAI,EACnC,WAAY,CAAE,KAAM,QAAS,EAC7B,WAAY,CAAE,KAAM,QAAS,EAC7B,KAAM,CAAE,KAAM,SAAU,MAAO,GAAI,EACnC,GAAI,CAAE,KAAM,QAAS,EACrB,kBAAmB,CAAE,KAAM,QAAS,EACpC,cAAe,CAAE,KAAM,SAAU,EACjC,cAAe,CAAE,KAAM,SAAU,EACjC,OAAQ,CAAE,KAAM,UAAW,MAAO,GAAI,EACtC,KAAM,CAAE,KAAM,SAAU,EACxB,QAAS,CAAE,KAAM,UAAW,MAAO,GAAI,CACzC,EACA,iBAAkB,EACpB,CAAC,EAYD,IAVIF,EAAO,MAAQC,EAAY,CAAC,GAAG,YAAY,IAAM,UACnDP,GAAU,EACV,QAAQ,KAAK,CAAC,GAGZM,EAAO,UACT,QAAQ,IAAI,eAAeJ,GAAkB,CAAC,EAAE,EAChD,QAAQ,KAAK,CAAC,GAGZI,EAAO,QAAUC,EAAY,CAAC,GAAG,YAAY,IAAM,SAAU,CAC/D,GAAM,CAAE,OAAAE,EAAQ,IAAAC,CAAI,EAAIC,GAAoBL,CAA2B,EACvE,MAAMM,GAAqBH,EAAQC,EAAI,UAAU,EACjD,QAAQ,KAAK,CAAC,CAChB,CAEA,GAAIH,EAAY,CAAC,GAAG,YAAY,IAAM,UAAW,CAC/C,IAAMM,EAASN,EAAY,CAAC,GAAG,YAAY,GAAK,SAChD,MAAMO,GAAoBD,CAAM,EAChC,QAAQ,KAAK,CAAC,CAChB,CAEA,IAAME,EAAqB,QAAQ,WAAa,QAC5C,yFACA,+EAEJ,OAAIR,EAAY,OAAS,IACvB,QAAQ,MAAMR,EAAiB,CAC7B,MAAO,QACP,MAAO,kCACP,QAAS,qCAAqCQ,EAAY,KAAK,GAAG,CAAC,KACnE,UAAW,CACT,kDACAQ,EACA,iEACF,CACF,CAAC,CAAC,EACFf,GAAU,EACV,QAAQ,KAAK,CAAC,GAGTM,CACT,OAASU,EAAc,CACrB,IAAMC,EAAWD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAC1DD,EAAqB,QAAQ,WAAa,QAC5C,yFACA,+EAEJ,QAAQ,MAAMhB,EAAiB,CAC7B,MAAO,QACP,MAAO,kCACP,QAASkB,EACT,UAAW,CACT,kDACAF,EACA,iEACF,CACF,CAAC,CAAC,EACFf,GAAU,EACV,QAAQ,KAAK,CAAC,CAChB,CACF,CAEA,eAAekB,IAA2B,CACxCtB,GAAiB,EACjB,IAAMuB,EAAU,MAAMd,GAAS,EAE/B,QAAQ,IAAIe,GAAa,CACvB,MAAO,4BAA4BlB,GAAkB,CAAC,gBACtD,WAAY,GACd,CAAC,CAAC,EAGFmB,GAAgB,EAGhB,GAAM,CAAE,OAAAZ,EAAQ,IAAAC,CAAI,EAAIC,GAAoBQ,CAAO,EAEnD,QAAQ,IAAIG,GAAsB,CAChC,MAAO,4CACP,MAAO,CACL,CAAE,MAAO,sBAAuB,MAAO,UAAUb,EAAO,IAAI,IAAIA,EAAO,QAAQ,EAAG,EAClF,CAAE,MAAO,kBAAmB,MAAOA,EAAO,WAAa,WAAa,SAASA,EAAO,IAAI,IAAIA,EAAO,OAAO,EAAG,EAC7G,CAAE,MAAO,qBAAsB,MAAOA,EAAO,WAAa,WAAa,SAASA,EAAO,IAAI,IAAIA,EAAO,OAAO,EAAG,CAClH,CACF,CAAC,CAAC,EAGEc,GAAkBb,EAAI,UAAU,GAAK,CAACc,GAAkBd,EAAI,UAAU,GACxE,QAAQ,KAAKX,EAAiB,CAC5B,MAAO,UACP,MAAO,mBACP,QAAS,4CACT,UAAW,CACT,+IACF,CACF,CAAC,CAAC,EAIJ,IAAM0B,EAAKC,GAAiBjB,EAAO,MAAM,EACzCC,EAAI,GAAKe,EAGT,IAAME,EAAalB,EAAO,WAKrB,KALkC,IAAImB,GAAa,CACtD,KAAMnB,EAAO,QACb,KAAMA,EAAO,KACb,kBAAmBA,EAAO,kBAC1B,IAAAC,CACF,CAAC,EAEKmB,EAAapB,EAAO,WAOrB,KAPkC,IAAIqB,GAAa,CACtD,KAAMrB,EAAO,QACb,KAAMA,EAAO,KACb,WAAYA,EAAO,WACnB,YAAaA,EAAO,YACpB,kBAAmBA,EAAO,kBAC1B,IAAAC,CACF,CAAC,EAEKqB,EAAa,IAAIC,GAAW,CAChC,KAAMvB,EAAO,SACb,KAAMA,EAAO,KACb,IAAAC,CACF,CAAC,EAGGiB,GAAW,MAAMA,EAAU,MAAM,EACjCE,GAAW,MAAMA,EAAU,MAAM,EACrC,MAAME,EAAW,MAAM,EAGvB,IAAME,EAAY,YAAY,SAAY,CACxC,GAAI,CACF,IAAMC,EAAiB,CACrB,KAAM,YACN,cAAe,KAAK,IAAI,EACxB,KAAM,YACN,SAAU,CAAC,CACb,EACMC,EAAwB,CAC5B,UAAUC,EAAuB,CAC/BA,EAAQ,MAAOpB,GAAQ,QAAQ,MAAM,qBAAsBA,CAAG,CAAC,CACjE,EACA,wBAAyB,CAAC,CAC5B,EAEA,MAAMqB,GAAO,UAAUH,EAAuBxB,EAAKyB,CAAG,CACxD,OAASnB,EAAK,CACZ,QAAQ,MAAM,0BAA2BA,CAAG,CAC9C,CACF,EAAG,GAAK,EAEFsB,EAAS,CAAC,EAAET,GAAapB,EAAO,YAAcA,EAAO,aAC3D,QAAQ,IAAIW,GAAa,CACvB,MAAO,oDACP,WAAY,IACZ,OAAQ,UACR,MAAO,CACL,CACE,MAAO,kBACP,MAAOO,EAAY,SAASlB,EAAO,IAAI,IAAIA,EAAO,OAAO,GAAK,0BAChE,EACA,CACE,MAAO,gBACP,MAAO6B,EAAS,SAAS7B,EAAO,IAAI,IAAIA,EAAO,OAAO,GAAK,2BAC7D,EACA,CACE,MAAO,eACP,MAAO,UAAUA,EAAO,IAAI,IAAIA,EAAO,QAAQ,EACjD,EACA,CACE,MAAO,kBACP,MAAOA,EAAO,MAChB,CACF,CACF,CAAC,CAAC,EAGF,IAAM8B,EAAW,MAAOC,GAAmB,CACzC,QAAQ,IAAI;AAAA,wBAA2BA,CAAM,+BAA+B,EAC5E,cAAcP,CAAS,EAEvB,MAAM,QAAQ,IAAI,CAChBN,GAAW,KAAK,EAChBE,GAAW,KAAK,EAChBE,EAAW,KAAK,CAClB,CAAC,EAED,GAAI,CACF,MAAMU,GAAc/B,CAAG,CACzB,MAAQ,CAER,CAEA,GAAI,CACFe,EAAG,MAAM,MAAM,EACf,QAAQ,IAAI,0CAA0C,CACxD,MAAY,CAEZ,CAEA,QAAQ,IAAI,6CAA6C,EACzD,QAAQ,KAAK,CAAC,CAChB,EAEA,QAAQ,GAAG,SAAU,IAAMc,EAAS,QAAQ,CAAC,EAC7C,QAAQ,GAAG,UAAW,IAAMA,EAAS,SAAS,CAAC,CACjD,CAEArB,GAAU,EAAE,MAAOF,GAAQ,CACzB,QAAQ,MAAMjB,EAAiB,CAC7B,MAAO,QACP,MAAO,mCACP,QAASiB,GAAK,SAAW,OAAOA,CAAG,CACrC,CAAC,CAAC,EACF,QAAQ,KAAK,CAAC,CAChB,CAAC",
6
+ "names": ["cacheUtils", "init_cache", "__esmMin", "key", "cache", "url", "response", "data", "ttlSeconds", "tags", "headers", "limit", "windowSec", "cacheKey", "current", "now", "getRequestCoordinates", "request", "cf", "lat", "lon", "headerLat", "headerLon", "calculateDistanceInKm", "lat1", "lon1", "lat2", "lon2", "dLat", "dLon", "init_geo", "__esmMin", "pbkdf2", "data", "salt", "iterations", "keyLength", "dataBuffer", "baseKey", "hashBuffer", "hashWithPBKDF2", "actualSalt", "derived", "b", "hashPassword", "password", "version", "combinedBytes", "verifyPassword", "storedHash", "combined", "c", "originalHash", "testHashArray", "result", "i", "generateZBase32Token", "length", "bytes", "token", "ZBASE32_ALPHABET", "generateId", "byteLength", "extractSaltHex", "hmacSha256", "key", "encoder", "keyBuffer", "cryptoKey", "signature", "generateSessionHash", "sessionId", "userId", "hashChallenge", "pinHash", "nonce", "saltBytes", "init_crypto", "__esmMin", "createRefreshTokenCookie", "refreshToken", "env", "keepLoggedIn", "maxAge", "REFRESH_TOKEN_COOKIE_NAME", "createBlankRefreshTokenCookie", "readRefreshTokenCookie", "cookieHeader", "match", "createCsrfCookie", "token", "maxAgeStr", "readCsrfCookie", "createPreauthCookie", "preauthTtl", "PREAUTH_COOKIE_NAME", "clearPreauthCookie", "readPreauthCookie", "init_cookies", "__esmMin", "session_exports", "__export", "SessionModel", "init_session", "__esmMin", "db", "sessionId", "session", "id", "userId", "createdAt", "expiresAt", "ipAddress", "userAgent", "latitude", "longitude", "lastActiveAt", "now", "results", "res1", "res2", "row", "base64urlEncode", "buf", "stringToEncode", "bytes", "i", "base64urlDecode", "str", "base64", "binaryString", "stringToUint8Array", "isMissingJwtSecret", "secret", "isPresetJwtSecret", "PLACEHOLDER_JWT_SECRETS", "getJwtSecretStatus", "isUsableJwtSecret", "isStrongJwtSecret", "MIN_JWT_SECRET_LENGTH", "importJwtSecret", "saltedData", "JWT_KEY_SALT", "secretBytes", "signJWT", "payload", "key", "encodedHeader", "encodedPayload", "dataToSign", "signatureBuffer", "encodedSignature", "verifyJWT", "token", "parts", "dataToVerify", "signatureBytes", "payloadBytes", "payloadStr", "init_jwt", "__esmMin", "createRefreshTokenString", "sessionId", "version", "payload", "base64urlEncode", "parseRefreshTokenString", "token", "jsonStr", "base64urlDecode", "createSession", "env", "userId", "ipAddress", "userAgent", "latitude", "longitude", "keepLoggedIn", "generateId", "SESSION_ID_LENGTH", "now", "expiresAt", "expirationDays", "expirationMinutes", "session", "SessionModel", "refreshToken", "rotateSession", "refreshTokenString", "currentLat", "currentLon", "parsed", "sessionModel", "result", "user", "invalidateSession", "distance", "calculateDistanceInKm", "maxDistance", "lastActive", "timeoutSeconds", "timeRemaining", "totalDurationInSeconds", "extensionThreshold", "newCounter", "newRefreshToken", "init_session", "__esmMin", "init_crypto", "init_jwt", "init_geo", "createPreauthSession", "env", "userId", "token", "generateId", "SESSION_ID_LENGTH", "preauthTtl", "expiresAt", "SessionModel", "validatePreauthSession", "sessionModel", "row", "invalidatePreauthSession", "recordFailedPreauthAttempt", "cache", "cacheKey", "state", "cacheUtils", "failedAttempts", "init_preauth", "__esmMin", "init_session", "init_crypto", "init_cache", "auth_exports", "__export", "MIN_JWT_SECRET_LENGTH", "PREAUTH_COOKIE_NAME", "REFRESH_TOKEN_COOKIE_NAME", "SESSION_ID_LENGTH", "ZBASE32_ALPHABET", "calculateDistanceInKm", "clearPreauthCookie", "createBlankRefreshTokenCookie", "createCsrfCookie", "createPreauthCookie", "createPreauthSession", "createRefreshTokenCookie", "createRefreshTokenString", "createSession", "extractSaltHex", "generateId", "generateSessionHash", "generateZBase32Token", "getJwtSecretStatus", "getOrCreateJwtSecret", "getRequestCoordinates", "hmacSha256", "invalidatePreauthSession", "invalidateSession", "isMissingJwtSecret", "isPresetJwtSecret", "isStrongJwtSecret", "isUsableJwtSecret", "parseRefreshTokenString", "readCsrfCookie", "readPreauthCookie", "readRefreshTokenCookie", "recordFailedPreauthAttempt", "rotateSession", "validatePreauthSession", "env", "status", "init_auth", "__esmMin", "init_geo", "init_crypto", "init_cookies", "init_session", "init_preauth", "init_jwt", "middleware_exports", "__export", "applySecurityHeaders", "getCurrentUser", "invalidateAuthUserCache", "validateCsrf", "response", "nonce", "newHeaders", "sessionId", "authUserMemoryCache", "sessionActivityMemory", "request", "env", "ctx", "authHeader", "accessToken", "url", "secret", "getOrCreateJwtSecret", "jwtKey", "importJwtSecret", "payload", "verifyJWT", "cached", "activity", "sessionModel", "SessionModel", "session", "now", "lastActive", "activityRecord", "effectiveLastActive", "timeoutSeconds", "e", "lastWriteAttempt", "intervalSec", "newWriteAttempt", "writePromise", "oldestKey", "validatedUser", "cacheTtlSec", "cookieHeader", "csrfCookie", "readCsrfCookie", "csrfHeader", "init_middleware", "__esmMin", "init_auth", "init_jwt", "init_session", "parseArgs", "MemoryCache", "request", "url", "entry", "response", "maxAgeMatch", "ttl", "bodyBuf", "initNodeGlobals", "defaultCache", "selector", "handler", "handlers", "originalTextPromise", "stream", "controller", "html", "h", "content", "newHeaders", "fs", "path", "crypto", "fileURLToPath", "getPackageRoot", "currentDir", "parent", "grandParent", "getPackageVersion", "pkgPath", "pkg", "loadDotEnv", "filePath", "lines", "line", "trimmed", "eqIdx", "key", "val", "err", "loadEnvFiles", "rootDir", "getServerfullConfig", "cliArgs", "packageRoot", "tlsKeyPath", "tlsCertPath", "udpPort", "dotPort", "httpPort", "host", "dbPath", "defaultProfileKey", "disableUdp", "disableDot", "config", "env", "fs", "path", "DatabaseSync", "NodeD1PreparedStatement", "db", "sql", "values", "v", "colName", "row", "err", "rows", "result", "changes", "lastInsertRowid", "options", "rawRows", "r", "NodeD1Database", "rawDb", "query", "statements", "results", "stmt", "res", "_constraintOrBookmark", "runMigrations", "migrationsDir", "fs", "files", "f", "stmtGet", "stmtInsert", "appliedCount", "file", "filePath", "path", "initServerfullDb", "dbPath", "rootDir", "getPackageRoot", "dir", "DatabaseSync", "dgram", "DNS_TYPES", "DNS_TYPE_TO_CODE", "code", "name", "decodeName", "buffer", "offset", "name", "curr", "jumped", "consumed", "iterations", "terminated", "len", "pointer", "i", "getQTypeName", "type", "DNS_TYPES", "parseDNSQueryFromRaw", "raw", "read", "qtypeOffset", "qtypeCode", "parseDNSQuery", "request", "dnsParam", "base64", "binary", "parseDNSAnswer", "ansCount", "results", "qCount", "nameRead", "typeCode", "ttl", "rdLength", "data", "parts", "j", "bestStart", "bestLen", "currentStart", "currentLen", "replacement", "txtOffset", "txtParts", "txtEnd", "priority", "target", "targetRead", "end", "params", "key", "valLen", "pCurr", "pEnd", "alpns", "aLen", "aStr", "k", "ips", "binStr", "DEFAULT_PRESET_ECH_FRONTING_DOMAINS", "DEFAULT_ECH_FRONTING_DOMAIN", "CLOUDFLARE_DEFAULT_ECH_BASE64", "getPresetEchFrontingDomains", "env", "parsed", "item", "DEFAULT_PRESET_ECH_FRONTING_DOMAINS", "rebuildEchWithFronting", "rawBase64", "newFrontingDomain", "DEFAULT_ECH_FRONTING_DOMAIN", "binary", "bytes", "i", "configId", "kemId", "pkLen", "pkStart", "pkEnd", "publicKey", "offset", "csLen", "cipherSuites", "maxNameLen", "oldNameLen", "extLen", "extensions", "targetName", "nameBytes", "contents", "config", "list", "resultBinary", "e", "buildCloudflareEchConfig", "publicName", "sourceEchBase64", "CLOUDFLARE_DEFAULT_ECH_BASE64", "parseIPv6Bytes", "ip", "bytes", "left", "right", "leftParts", "rightParts", "part", "v", "parts", "i", "encodeHttpsRDataFromParams", "options", "priority", "target", "alpn", "ipv4hints", "ipv6hints", "echBase64", "labels", "label", "alpnVal", "proto", "ipBytes", "p", "binaryStr", "echBytes", "e", "ip6Bytes", "raw16", "encodeHttpsRDataFromString", "value", "tokens", "token", "eqIdx", "k", "ipv4ToNumeric", "ip", "parts", "hasError", "val", "ipInt", "octet", "num", "ipv6ToBigInt", "cleanIp", "ipv4Part", "lastColon", "doubleColonParts", "fullIp", "leftParts", "rightParts", "missingLength", "middle", "ipv4Val", "hexString", "part", "parsed", "isIPv4", "isIPv6", "parseCidr", "cidr", "slashIdx", "ipPart", "prefixStr", "prefixLen", "ipVal", "mask", "createCidrMatcher", "cidrs", "v4Ranges", "v6Ranges", "cidr", "parsed", "parseCidr", "ip", "ipVal", "ipv6ToBigInt", "r", "ipv4ToNumeric", "activeCfProxyCidrs", "activeCfMatcher", "updateActiveCfProxyCidrs", "cidrs", "createCidrMatcher", "getActiveCfProxyCidrs", "isCloudflareIp", "ip", "MEMORY_CACHE", "CACHE_TTL_MS", "SystemSettingsModel", "db", "key", "results", "settings", "expiresAt", "row", "cached", "res", "val", "e", "value", "now", "result", "retryResult", "stmts", "cachedActiveCfEchConfig", "loadPromise", "ensureCloudflareIpRangesLoaded", "db", "getActiveCfProxyCidrs", "loadPromise", "settingsModel", "SystemSettingsModel", "cachedCidrs", "cachedEch", "cachedActiveCfEchConfig", "parsed", "updateActiveCfProxyCidrs", "syncCloudflareIpRanges", "e", "syncCloudflareIpRanges", "db", "force", "settingsModel", "SystemSettingsModel", "now", "lastUpdatedStr", "lastUpdated", "getActiveCfProxyCidrs", "v4Res", "v6Res", "echRes", "v4Text", "v6Text", "validCidrs", "line", "cidr", "parseCidr", "fetchedEch", "echJson", "ans", "match", "e", "updates", "cachedActiveCfEchConfig", "updateActiveCfProxyCidrs", "encodeRData", "type", "value", "data", "v", "bytes", "left", "right", "leftParts", "p", "rightParts", "i", "part", "parts", "labels", "label", "chunk", "j", "encodeHttpsRDataFromString", "buildDNSQuery", "name", "type", "header", "id", "labels", "question", "label", "i", "typeCode", "DNS_TYPE_TO_CODE", "raw", "createServFail", "queryRaw", "res", "buildResponseMulti", "records", "rcode", "qEnd", "qCount", "read", "decodeName", "questionSection", "answerRRs", "totalLength", "record", "value", "ttl", "nameBytes", "data", "encodeRData", "rdlength", "rr", "offset", "tCode", "currentOffset", "e", "fallback", "buildResponse", "answerRR", "parseCidr", "cidr", "slashIdx", "addr", "prefixLen", "bytes", "ipv6ToBytes", "addrBytes", "maskBytes", "parts", "p", "byteLen", "masked", "i", "lastByteBits", "left", "right", "missing", "groups", "val", "buildEcsOptRr", "parsed", "family", "ecsDataLen", "ecsOption", "o", "rdLen", "optRr", "injectEcsIntoQuery", "raw", "arCount", "offset", "qdCount", "len", "rrToSkip", "skipRR", "additionalStart", "existingOptStart", "existingOptEnd", "pos", "rrStart", "newRaw", "before", "after", "newArCount", "rdLength", "init_cache", "SizeCappedMap", "maxEntries", "key", "value", "oldestKey", "bloomMemoryMap", "configCache", "dnsCache", "profileKeyMemoryMap", "purgeTags", "env", "tags", "res", "errorText", "e", "pipelineCache", "profileId", "clearBloomOrEnv", "envOrClearBloom", "clearBloom", "targetEnv", "cache", "tasks", "cacheUtils", "init_auth", "ProfileModel", "db", "id", "profileKey", "apMatch", "e", "filterSql", "params", "results", "ownerId", "name", "profile", "now", "apId", "generateId", "MAX_RETRIES", "attempt", "apToken", "generateZBase32Token", "statements", "r", "err", "settings", "newKey", "threshold", "limit", "profileId", "RuleModel", "db", "profileId", "results", "rule", "normalizedPattern", "createdAt", "rules", "now", "statements", "inserted", "chunk", "res", "id", "pattern", "BloomFilter", "_BloomFilter", "size", "hashes", "bitArray", "words", "expectedItems", "errorRate", "n", "m", "k", "element", "h1", "len", "i", "h2", "arr", "pos", "res", "view", "buffer", "offset", "length", "expectedWords", "actualWords", "bitData", "copy", "other", "a", "b", "data", "seed", "hash", "str", "bytes", "binary", "chunk", "dump", "ProfileBloomModel", "db", "profileId", "bloomFilter", "now", "uint8Array", "statements", "chunkIndex", "offset", "chunkData", "r", "results", "totalLength", "acc", "row", "len", "combined", "chunk", "maxDomains", "falsePositiveRate", "binary", "BloomFilter", "stagingChunks", "init_cache", "pipelineConfig", "context", "track", "profileId", "env", "ctx", "inMem", "bloomMemoryMap", "cachedConfig", "configCache", "bloomMemTtl", "validBloom", "cache", "profileCacheKey", "bloomInternalUrl", "apiCached", "cacheUtils", "bloom", "bloomRes", "buffer", "BloomFilter", "profileModel", "ProfileModel", "ruleModel", "RuleModel", "bloomModel", "ProfileBloomModel", "profile", "rules", "e", "settings", "uint8", "config", "parseDnsStamp", "stampStr", "clean", "rawB64", "end", "b64", "binaryStr", "standardB64", "padLen", "padded", "bytes", "i", "offset", "protocolId", "props", "readLpString", "len", "strBytes", "readHashes", "hashes", "hLen", "actualLen", "hashBytes", "b", "protocol", "serverAddress", "hostname", "path", "providerName", "resolvedUrl", "host", "port", "PASSWORD_REGEX", "USERNAME_REGEX", "AP_NAME_REGEX", "PASSKEY_NAME_REGEX", "PROFILE_NAME_REGEX", "ACCESS_KEY_REGEX", "TOTP_TOKEN_REGEX", "FORBIDDEN_HOSTNAMES", "FORBIDDEN_HOSTNAME_SUFFIXES", "extractEmbeddedDnsIp", "hostname", "dotMatch", "dashMatch", "PRIVATE_AND_RESERVED_CIDRS", "isPrivateOrReservedIp", "createCidrMatcher", "isPublicInternetIP", "ip", "cleanIp", "lastColon", "ipv4Part", "isIPv4", "isIPv6", "ipv6Val", "ipv6ToBigInt", "ipv4Num", "ipv4Str", "isSafeUrl", "urlString", "stamp", "parseDnsStamp", "parseableUrl", "url", "rawHostname", "suffix", "embeddedIp", "connectUniversal", "options", "tls", "net", "resolve", "reject", "client", "isClosed", "readable", "controller", "chunk", "err", "writable", "res", "rej", "UpstreamHttpError", "status", "statusText", "responseBodySnippet", "cfRay", "readFramedDnsResponse", "reader", "timeoutMs", "chunks", "totalBytes", "expectedBodyLength", "timer", "timeoutPromise", "_", "reject", "value", "done", "b0", "b1", "combined", "offset", "chunk", "parseHostAndPort", "address", "defaultPort", "host", "port", "closeIdx", "ip", "rest", "parts", "resolveUpstreamEndpoint", "rawUpstreamUrl", "effectiveUrl", "stamp", "parseDnsStamp", "rawHost", "fetchFromUpstream", "endpoint", "queryRaw", "diagMethod", "diagTarget", "startFetch", "answer", "dotHost", "dotPort", "socket", "connectUniversal", "writer", "framedQuery", "tcpHost", "tcpPort", "response", "snippet", "cfRay", "UpstreamHttpError", "answerBuffer", "latency", "applyEcs", "request", "query", "settings", "ecs", "queryRaw", "clientIp", "injectEcsIntoQuery", "processBestEffortEch", "query", "initialParsedAnswers", "initialAnswer", "context", "settings", "request", "probeResolver", "initialReason", "isHttpsOrSvcb", "echEnabled", "answer", "parsedAnswers", "effectiveReason", "ensureCloudflareIpRangesLoaded", "existingIpv4s", "existingIpv6s", "existingEch", "a", "v4Match", "v6Match", "echMatch", "isCf", "isCloudflareIp", "aQueryRaw", "buildDNSQuery", "aRes", "aAnswers", "parseDNSAnswer", "ans", "e", "frontingDomain", "DEFAULT_ECH_FRONTING_DOMAIN", "echConfigBase64", "buildCloudflareEchConfig", "targetType", "params", "rdataValue", "buildResponse", "logSeqCounter", "generateLogId", "seq", "LogCoreModel", "db", "log", "logId", "profileId", "options", "baseSelect", "whereClauses", "params", "queryStr", "limit", "results", "timestamp", "ownerId", "olderThanTimestamp", "maxRows", "totalDeleted", "batchSize", "currentBatch", "count", "LogAggregationModel", "db", "_profileId", "_sinceSec", "_untilSec", "_minDomainCount", "KemKeyModel", "db", "id", "err", "safetyWindowSec", "batchLimit", "cutoffTimestamp", "result", "profileId", "LogRetentionModel", "db", "settingsModel", "SystemSettingsModel", "maxRetentionDays", "batchLimit", "dailyBudget", "profiles", "todayDate", "deletedToday", "savedDate", "savedCount", "err", "remainingBudget", "canDeleteLogs", "effectiveBatchLimit", "perProfileLimit", "logDeleteStatements", "profile", "days", "settings", "effectiveDays", "threshold", "CHUNK_SIZE", "newlyDeletedLogs", "i", "chunk", "results", "res", "updatedTotal", "deletedOrphanKeys", "KemKeyModel", "kemErr", "e", "LogTrafficAnalytics", "db", "profileId", "since", "until", "search", "accessPointId", "queryStr", "params", "results", "interval", "safeInterval", "LogDomainAnalytics", "db", "profileId", "since", "until", "accessPointId", "queryStr", "params", "results", "LogClientAnalytics", "db", "profileId", "since", "until", "accessPointId", "queryStr", "params", "results", "LogDestinationAnalytics", "db", "profileId", "since", "until", "accessPointId", "limit", "queryStr", "params", "results", "countryCode", "r", "LogAnalyticsModel", "db", "_aggregation", "LogTrafficAnalytics", "LogDomainAnalytics", "LogClientAnalytics", "LogDestinationAnalytics", "profileId", "since", "until", "search", "accessPointId", "interval", "limit", "countryCode", "summary", "trend", "topAllowed", "topBlocked", "clients", "destinations", "LogModel", "db", "LogCoreModel", "LogAggregationModel", "LogRetentionModel", "LogAnalyticsModel", "KemKeyModel", "log", "profileId", "options", "logId", "timestamp", "ownerId", "olderThanTimestamp", "maxRows", "maxRetentionDays", "batchLimit", "dailyBudget", "sinceSec", "untilSec", "minDomainCount", "since", "until", "search", "accessPointId", "interval", "limit", "countryCode", "init_cache", "isBytes", "a", "atitle", "title", "anumber", "abool", "value", "abytes", "length", "bytes", "ofLen", "got", "message", "ahash", "h", "anumber", "aobject", "value", "label", "aopts", "proto", "aexists", "instance", "checkFinished", "aoutput", "out", "abytes", "min", "u32", "arr", "clean", "arrays", "i", "createView", "rotr", "word", "shift", "isLE", "byteSwap", "word", "byteSwap32", "arr", "i", "byteSwap", "swap32IfBE", "isLE", "u", "hasHexBuiltin", "hexes", "_", "bytesToHex", "bytes", "abytes", "hex", "asciiToBase16", "ch", "hexToBytes", "error", "hl", "al", "array", "ai", "hi", "n1", "n2", "char", "concatBytes", "arrays", "sum", "i", "a", "abytes", "res", "pad", "checkOpts", "defaults", "opts", "title", "aopts", "createHasher", "hashCons", "info", "hashC", "msg", "tmp", "randomBytes", "bytesLength", "anumber", "cr", "oidNist", "suffix", "aarray", "item", "title", "inner", "i", "abytes", "value", "length", "anumber", "astring", "prefix", "aobject", "afunction", "bytesToHex", "concatBytes", "arrays", "hexToBytes", "hex", "isBytes", "randomBytes", "bytesLength", "_0n", "_1n", "atitle", "abool", "abignumber", "isPosBig", "asafenumber", "numberToHexUnpadded", "num", "hexToNumber", "bytesToNumberBE", "bytes", "bytesToNumberLE", "copyBytes", "numberToBytesBE", "len", "expectedLen", "numberToBytesLE", "copyBytes", "bytes", "abytes", "asciiToBytes", "ascii", "c", "i", "charCode", "isPosBig", "_0n", "inRange", "min", "max", "aInRange", "title", "n", "bitLen", "bitMask", "asafenumber", "_1n", "createHmacDrbg", "hashLen", "qByteLen", "hmacFn", "anumber", "u8n", "len", "NULL", "byte0", "byte1", "_maxDrbgIters", "v", "k", "i", "reset", "h", "msgs", "concatBytes", "reseed", "seed", "gen", "out", "sl", "pred", "res", "validateObject", "object", "fields", "optFields", "title", "aobject", "checkField", "fieldName", "expectedType", "isOpt", "label", "val", "current", "iter", "f", "_0n", "_1n", "_2n", "_3n", "_4n", "_5n", "_7n", "_8n", "_9n", "_15n", "_16n", "POW_WINDOWED_MIN", "mod", "a", "b", "result", "pow", "num", "power", "modulo", "d", "p", "digits", "table", "i", "w", "digit", "invert", "number", "modulo", "_0n", "_1n", "a", "mod", "b", "x", "u", "q", "r", "m", "invertCt", "prime", "an", "inverse", "pow", "_2n", "assertIsSquare", "Fp", "root", "n", "F", "aoddModulus", "order", "fnName", "sqrt3mod4", "p1div4", "_4n", "sqrt5mod8", "p5div8", "_5n", "_8n", "n2", "v", "nv", "i", "sqrt9mod16", "P", "Fp_", "Field", "tn", "tonelliShanks", "c1", "c2", "c3", "c4", "_7n", "_16n", "tv1", "tv2", "tv3", "tv4", "e1", "e2", "e3", "_3n", "Q", "S", "Z", "_Fp", "FpLegendre", "cc", "Q1div2", "M", "c", "t", "R", "t_tmp", "exponent", "FpSqrt", "_9n", "FIELD_FIELDS", "validateField", "field", "aobject", "asafenumber", "name", "afunction", "_1n", "FpInvertBatch", "Fp", "nums", "passZero", "validateField", "aarray", "abool", "F", "inverted", "multipliedAcc", "acc", "num", "i", "invertedAcc", "FpLegendre", "Fp", "n", "validateField", "F", "aoddModulus", "p1mod2", "_1n", "_2n", "powered", "yes", "zero", "no", "nLength", "nBitLength", "anumber", "_0n", "bits", "bitLen", "_nBitLength", "nByteLength", "FIELD_SQRT", "_Field", "_1n", "ORDER", "opts", "_nbitLength", "num", "mod", "lhs", "rhs", "power", "pow", "invert", "sqrt", "FpSqrt", "numberToBytesLE", "numberToBytesBE", "bytes", "skipValidation", "abytes", "allowedLengths", "BYTES", "isLE", "modFromBytes", "padded", "scalar", "bytesToNumberLE", "bytesToNumberBE", "lst", "FpInvertBatch", "a", "b", "condition", "abool", "Field", "getFieldBytesLength", "fieldOrder", "_1n", "bitLength", "bitLen", "getMinHashLength", "length", "mapHashToField", "key", "isLE", "abytes", "len", "fieldLen", "minLen", "num", "bytesToNumberLE", "bytesToNumberBE", "reduced", "mod", "numberToBytesLE", "numberToBytesBE", "_0n", "_1n", "_4n", "BLIND_BYTES", "BLIND_BITS", "FW_WINDOW", "TABLE_BYTES_MAX", "validatePointCons", "Point", "pc", "afunction", "aobject", "validateField", "normalizeZ", "c", "points", "validateMSMPoints", "invertedZs", "FpInvertBatch", "p", "i", "validateW", "W", "bits", "min", "validateTableBytes", "numPoints", "fpBytes", "bytes", "probeRandomBytes", "randomBytes", "length", "probe", "isBytes", "aarray", "validateMSMScalars", "scalars", "field", "maxScalar", "s", "isPosBig", "pointWindowSizes", "getWindowSize", "P", "oddMultiples", "size", "dbl", "t", "j", "wnafDigits", "half", "mask", "d", "w", "signedWindowDigits", "windows", "shiftBy", "v", "wnafWalk", "zero", "tables", "digits", "max", "acc", "bit", "item", "ScalarMultiplier", "point", "comp", "base", "precomputes", "n", "f", "digit", "start", "idx", "sel", "neg", "transform", "entries", "entry", "scalar", "inRange", "blind", "bytesToNumberBE", "bitMask", "table", "window", "cofactor", "mulAddUnsafe", "allowOversized", "abool", "createField", "order", "field", "isLE", "validateField", "Field", "createCurveFields", "type", "CURVE", "curveOpts", "FpFnLE", "validateObject", "p", "val", "isPosBig", "_0n", "Fp", "Fn", "params", "createKeygen", "randomSecretKey", "getPublicKey", "seed", "secretKey", "_HMAC", "hash", "key", "ahash", "abytes", "blockLen", "pad", "i", "clean", "buf", "aexists", "out", "aoutput", "to", "oHash", "iHash", "finished", "destroyed", "outputLen", "canXOF", "hmac", "hmac_", "message", "_0n", "DERErr", "m", "_DER", "tag", "data", "E", "asafenumber", "astring", "dataLen", "len", "numberToHexUnpadded", "lenLen", "abytes", "pos", "first", "isLong", "length", "lengthBytes", "b", "v", "num", "abignumber", "hex", "bytesToNumberBE", "bytes", "maxScalarBytes", "int", "tlv", "seqBytes", "seqLeftBytes", "rBytes", "rLeftBytes", "sBytes", "sLeftBytes", "sig", "validateObject", "rs", "ss", "seq", "DER", "divNearest", "num", "den", "_2n", "_splitEndoScalar", "k", "basis", "n", "aInRange", "_0n", "a1", "b1", "a2", "b2", "c1", "c2", "k1", "k2", "k1neg", "k2neg", "MAX_NUM", "bitMask", "bitLen", "_1n", "validateSigFormat", "format", "validateSigOpts", "opts", "def", "validateObject", "optsn", "optName", "abool", "_3n", "_4n", "weierstrass", "params", "extraOpts", "validated", "createCurveFields", "Fp", "Fn", "CURVE", "cofactor", "CURVE_ORDER", "endoOpts", "allowInfinityPoint", "clearCofactor", "isTorsionFree", "fromBytes", "toBytes", "randomBytes", "endo", "lengths", "getWLengths", "assertCompressionIsSupported", "pointToBytes", "_c", "point", "isCompressed", "x", "y", "bx", "hasEvenY", "concatBytes", "pprefix", "pointFromBytes", "bytes", "abytes", "comp", "uncomp", "length", "head", "tail", "y2", "weierstrassEquation", "sqrtError", "err", "evenY", "L", "isValidXY", "encodePoint", "decodePoint", "b3", "mulA", "_", "x2", "x3", "left", "right", "_4a3", "_27b2", "acoord", "title", "banZero", "aprjpoint", "other", "Point", "splitEndoScalarN", "pushWnafPair", "points", "scalars", "p", "psi", "validityCache", "X", "Y", "Z", "P", "hex", "hexToBytes", "windowSize", "isLazy", "wnaf", "X1", "Y1", "Z1", "X2", "Y2", "Z2", "U1", "U2", "X3", "Y3", "Z3", "t0", "t1", "t2", "t3", "t4", "t5", "scalar", "f", "normalize", "sc", "mulAddUnsafe", "a", "b", "invertedZ", "iz", "is0", "zz", "bytesToHex", "normalizeZ", "ScalarMultiplier", "ecdh", "ecdhOpts", "validatePointCons", "randomBytes_", "getMinHashLength", "isValidSecretKey", "secretKey", "isValidPublicKey", "publicKey", "publicKeyUncompressed", "l", "randomSecretKey", "seed", "mapHashToField", "getPublicKey", "isProbPub", "item", "allowedLengths", "isBytes", "isPub", "isSec", "getSharedSecret", "secretKeyA", "publicKeyB", "s", "utils", "keygen", "createKeygen", "ecdsa", "hash", "ecdsaOpts", "hash_", "ahash", "hmac", "key", "msg", "fnBits", "blindLength", "csprng", "probeRandomBytes", "defaultSigOpts", "hasLargeRecoveryLifts", "isBiggerThanHalfOrder", "number", "HALF", "validateRS", "assertFieldSignIsSupported", "getRecoveryBit", "r", "assertRecoverableCurve", "validateSigLength", "size", "sizer", "Signature", "recovery", "recid", "DER", "messageHash", "radj", "R", "ir", "h", "bits2int_modN", "u1", "u2", "Q", "rb", "sb", "bits2int", "bytesToNumberBE", "delta", "ORDER_MASK", "int2octets", "validateMsgAndHash", "message", "prehash", "prepSig", "lowS", "extraEntropy", "h1int", "d", "seedArgs", "e", "m", "k2sig", "kBytes", "q", "ibk", "bm", "bd", "ik", "invertCt", "normS", "sign", "createHmacDrbg", "verify", "signature", "end", "sig", "is", "recoverPublicKey", "U32_MASK64", "_32n", "fromBig", "le", "split", "lst", "len", "Ah", "Al", "i", "h", "l", "fromNumH", "fromNumL", "setU64FromNum", "view", "byteOffset", "n", "isLE", "h", "l", "Chi", "a", "b", "c", "Maj", "HashMD", "blockLen", "outputLen", "padOffset", "isLE", "createView", "data", "aexists", "abytes", "view", "buffer", "len", "processed", "pos", "take", "dataView", "out", "aoutput", "setU64FromNum", "oview", "outLen", "state", "i", "res", "to", "length", "finished", "destroyed", "SHA256_IV", "SHA256_K", "SHA256_W", "SHA2_32B", "HashMD", "outputLen", "IV", "A", "B", "C", "D", "E", "F", "G", "H", "to", "view", "offset", "i", "W15", "W2", "s0", "rotr", "s1", "sigma1", "T1", "Chi", "T2", "Maj", "clean", "_SHA256", "SHA256_IV", "sha256", "createHasher", "_SHA256", "oidNist", "checkU32", "title", "isPowerOfTwo", "x", "reverseBits", "bits", "checkU32", "reversed", "i", "log2", "bitReversalInplace", "values", "n", "isPowerOfTwo", "j", "tmp", "FFTCore", "F", "coreOpts", "validateObject", "N", "roots", "dit", "invertButterflies", "skipStages", "brp", "checkU32", "bits", "log2", "isPowerOfTwo", "maxSkipStages", "isDit", "values", "bitReversalInplace", "i", "g", "s", "m", "m2", "stride", "k", "j", "grp", "rootPos", "i0", "i1", "omega", "b", "a", "t", "p256_CURVE", "p256_Point", "weierstrass", "p256_CURVE", "p256", "ecdsa", "sha256", "_0n", "_1n", "_2n", "_7n", "_256n", "_0x71n", "SHA3_PI", "SHA3_ROTL", "_SHA3_IOTA", "round", "R", "x", "y", "t", "j", "IOTAS", "split", "SHA3_IOTA_H", "SHA3_IOTA_L", "rotlSH", "h", "l", "s", "rotlSL", "rotlBH", "rotlBL", "rotlH", "rotlL", "B", "keccakP", "rounds", "anumber", "idx1", "idx0", "B0", "B1", "Th", "Tl", "curH", "curL", "shift", "PI", "b0", "b1", "b2", "b3", "clean", "Keccak", "_Keccak", "blockLen", "suffix", "outputLen", "enableXOF", "abool", "u32", "swap32IfBE", "data", "aexists", "abytes", "state", "state32", "len", "canUseU32", "blockLen32", "data32", "pos", "i", "o", "take", "out", "bufferOut", "bytes", "aoutput", "to", "genKeccak", "info", "createHasher", "sha3_256", "genKeccak", "oidNist", "sha3_512", "genKeccak", "oidNist", "genShake", "suffix", "blockLen", "outputLen", "info", "createHasher", "opts", "checkOpts", "Keccak", "shake128", "oidNist", "shake256", "abytesDoc", "abytes", "randomBytes", "aarray", "item", "title", "inner", "i", "aobject", "value", "equalBytes", "a", "b", "abytes", "diff", "copyBytes", "bytes", "splitCoder", "label", "lengths", "getLength", "c", "bytesLen", "sum", "a", "bufs", "res", "pos", "b", "abytes", "buf", "l", "vecCoder", "vecLen", "coder", "u", "uArr", "aarray", "i", "r", "cleanBytes", "list", "t", "getMask", "bits", "anumber", "genCrystals", "opts", "newPoly", "N", "Q", "F", "ROOT_OF_UNITY", "brvBits", "isKyber", "mod", "a", "modulo", "result", "smod", "r", "getZettas", "out", "i", "b", "reverseBits", "p", "nttZetas", "inv", "_a", "field", "nttOpts", "dif", "FFTCore", "dit", "NTT", "d", "c", "bufLen", "getMask", "mask", "bytesLen", "poly_", "poly", "buf", "pos", "bytes", "createXofShake", "shake", "seed", "blockLen", "_seed", "seedLen", "h", "calls", "xofs", "x", "y", "cleanBytes", "XOF128", "shake128", "N", "Q", "F", "ROOT_OF_UNITY", "crystals", "genCrystals", "PARAMS", "compress", "d", "i", "a", "byteCoder", "polyCoder", "polyAdd", "a_", "b_", "b", "r", "polySub", "BaseCaseMultiply", "a0", "a1", "b0", "b1", "zeta", "c0", "c1", "MultiplyNTTs", "f_", "g_", "f", "g", "z", "SampleNTT", "xof_", "xof", "j", "d1", "d2", "sampleCBDBytes", "buf", "eta", "b32", "u32", "swap32IfBE", "len", "p", "bb", "t0", "sampleCBD", "PRF_", "seed", "nonce", "genKPKE", "opts_", "opts", "K", "PRF", "XOF", "HASH512", "ETA1", "ETA2", "du", "dv", "poly1", "polyV", "polyU", "publicCoder", "splitCoder", "vecCoder", "secretCoder", "cipherCoder", "seedCoder", "encryptCore", "tHat", "getA", "msg", "rHat", "tmp2", "u", "e1", "tmp", "aij", "cleanBytes", "e2", "v", "abytesDoc", "seedDst", "seedHash", "rho", "sigma", "sHat", "x", "e", "aji", "res", "publicKey", "A", "cipherText", "privateKey", "sk", "createKyber", "rawOpts", "KPKE", "HASH256", "KDF", "KPKESecretCoder", "lengths", "msgLen", "seedLen", "validateModulus", "fn", "eke", "ek", "copyBytes", "ok", "equalBytes", "kemLengths", "ownSeed", "s", "randomBytes", "publicKeyHash", "keys", "secretKey", "_sk", "_publicKeyHash", "_z", "ownMsg", "m", "kr", "k768", "start", "test", "Khat", "cipherText2", "isValid", "Kbar", "cached", "ekEmbedded", "storedHash", "shakePRF", "dkLen", "key", "shake256", "sha3_256", "sha3_512", "XOF128", "mk", "params", "ml_kem768", "mk", "PARAMS", "validateKEM", "kem", "title", "k", "aobject", "afunction", "positiveLength", "value", "title", "length", "anumber", "splitLengths", "lst", "name", "coder", "splitCoder", "i", "combineKeys", "realSeedLen", "expandSeed_", "ck_", "expandSeed", "ck", "seedCoder", "splitLengths", "pkCoder", "rootSeedLen", "positiveLength", "expandDecapsulationKey", "seed", "abytes", "expandedRaw", "expandedSeed", "copyBytes", "expanded", "keySecret", "secretKey", "publicKey", "ok", "part", "i", "keys", "cleanBytes", "keygen", "root", "randomBytes", "res", "pk", "combineKEMS", "realMsgLen", "combiner", "kems", "rawCombiner", "rawKems", "validateKEM", "ctCoder", "msgCoder", "sharedSecretLen", "lengths", "combine", "publicKeys", "cipherTexts", "sharedSecrets", "combined", "error", "isBytes", "overlaps", "value", "randomness", "pks", "rand", "sharedSecret", "cipherText", "enc", "encodedCipherText", "ct", "cts", "nistCurveKem", "curve", "scalarLen", "elemLen", "nseed", "Fn", "rejectionSampling", "seed", "sk", "start", "end", "secretKey", "publicKey", "decapsulate", "cipherText", "randomBytes", "abytes", "rand", "ek", "sharedSecret", "cleanBytes", "concreteHybridKem", "label", "mlkem", "curveKem", "totalSeedLen", "combineKEMS", "shake256", "pk", "ct", "ss", "sha3_256", "concatBytes", "asciiToBytes", "ml_kem768_p256", "ml_kem768", "p256", "toBase64", "bytes", "u8", "binary", "i", "fromBase64", "base64", "encryptSensitiveLogData", "recipientPublicKeyJwk", "sensitiveData", "subtle", "ephemKeyPair", "importedRecipientPub", "aesKey", "iv", "plaintextBytes", "ciphertextBuffer", "payload", "toBase64", "encapsulatePqcDek", "publicKeyBytes", "res", "ml_kem768_p256", "toBase64", "deriveDekFromSharedSecret", "sharedSecret", "subtle", "hkdfKey", "encryptSensitiveLogDataWithDek", "dek", "sensitiveData", "iv", "plaintextBytes", "ciphertextBuffer", "payload", "toBase64", "DEK_LIFETIME_MS", "activeDekMap", "inFlightDekPromises", "getCurrentDek", "profileId", "ensureActiveDek", "db", "pqcPublicKeyBase64", "now", "existing", "inFlight", "promise", "rotateDek", "nowSec", "expiresAtSec", "pkBytes", "fromBase64", "kemCtBase64", "sharedSecret", "encapsulatePqcDek", "dek", "deriveDekFromSharedSecret", "kemKeyId", "generateLogId", "err", "activeDek", "invalidateActiveDek", "profileKeyCache", "getProfileLogPublicKey", "db", "profileId", "now", "cached", "row", "columnErr", "key", "ttlMs", "err", "invalidateProfileLogKeyCache", "invalidateActiveDek", "logBatchQueue", "MAX_BATCH_SIZE", "FLUSH_INTERVAL_MS", "CIRCUIT_BREAKER_COOLDOWN_SEC", "memoryCircuitBreakerUntil", "isFlushScheduled", "lastFlushTime", "isWriteQuotaTripped", "cache", "trippedUntil", "cacheUtils", "tripWriteCircuitBreaker", "trippedUntilSec", "flushLogBatch", "env", "logsToFlush", "logModel", "LogModel", "statements", "profileIds", "l", "keyMap", "dekMap", "pid", "pubKey", "activeDek", "getCurrentDek", "ensureActiveDek", "log", "sensitiveData", "encrypted", "encryptSensitiveLogDataWithDek", "encryptSensitiveLogData", "encErr", "errorMsg", "scheduleDeferredFlush", "resolve", "enqueueLog", "settings", "ctx", "action", "generateLogId", "handleBlockOrRedirect", "request", "query", "context", "settings", "action", "reason", "customAnswer", "responseType", "recursiveResolver", "clientIp", "answer", "displayAnswer", "targetQueryRaw", "buildDNSQuery", "targetQuery", "upstreamRes", "parsedTargetAnswers", "parseDNSAnswer", "records", "a", "buildResponseMulti", "buildResponse", "e", "mode", "customIp", "nullIp", "latency", "cfCountry", "enqueueLog", "init_cache", "memoryCache", "MEMORY_CACHE_TTL", "fetchGeoIP", "ip", "isPublicInternetIP", "cacheKey", "now", "memCached", "cache", "cached", "cacheUtils", "err", "storeInCache", "geo", "firstKey", "response", "data", "response2", "data2", "e2", "recordSuccessAndCache", "request", "query", "context", "settings", "action", "effectiveReason", "parsedAnswers", "answer", "minTTL", "rawUpstreamUrl", "ecs", "clientIp", "cfCountry", "firstIp", "a", "destGeoJson", "destCountryCode", "destCountry", "destIsp", "geo", "fetchGeoIP", "latency", "enqueueLog", "dnsCache", "recordFailure", "error", "diagTarget", "diagMethod", "status", "statusText", "errorDetail", "responseBodySnippet", "cfRay", "UpstreamHttpError", "cause", "causeMsg", "failReason", "effectiveDiagTarget", "effectiveDiagMethod", "pipelineResolver", "request", "query", "context", "settings", "action", "reason", "rawUpstreamUrl", "isSafeUrl", "queryRaw", "ecs", "applyEcs", "endpoint", "resolveUpstreamEndpoint", "transportRes", "fetchFromUpstream", "initialParsedAnswers", "parseDNSAnswer", "echRes", "processBestEffortEch", "answer", "parsedAnswers", "effectiveReason", "minTTL", "a", "recordSuccessAndCache", "e", "recordFailure", "customAnswer", "responseType", "handleBlockOrRedirect", "DNSFilter", "domain", "pattern", "d", "p", "rules", "rule", "pipelineFilter", "request", "query", "context", "settings", "rules", "bloom", "track", "domainLower", "whitelist", "r", "DNSFilter", "pipelineResolver", "redirections", "redirectRule", "val", "respType", "blacklist", "blockRule", "currentDomain", "firstDot", "pipeline", "pipelineCache", "request", "query", "context", "timings", "mark", "track", "name", "now", "buildResponse", "cached", "dnsCache", "response", "latency", "config", "pipelineConfig", "matchResult", "pipelineFilter", "block", "pipelineResolver", "final", "getActiveKekVersion", "env", "maxVer", "key", "match", "val", "ver", "getKekSecret", "version", "toBase64", "bytes", "binary", "len", "i", "fromBase64", "base64", "binaryString", "importKek", "secret", "secretData", "hashBuffer", "importDek", "dekBytes", "encryptEnvelope", "plainText", "activeVersion", "kekSecret", "kekKey", "dekKey", "dataIv", "dataBuffer", "encryptedDataBuffer", "dataEncrypted", "dekIv", "encryptedDekBuffer", "dekEncrypted", "decryptEnvelope", "dataEncryptedStr", "dekEncryptedStr", "parseErr", "attemptDecrypt", "kekVersion", "encryptedDekBytes", "decryptedDekBuffer", "encryptedDataBytes", "decryptedDataBuffer", "primaryErr", "rotateEnvelopeDek", "currentKekVersion", "currentVersionNumber", "nextKekVersion", "nextKekSecret", "currentKekSecret", "currentKekKey", "nextKekKey", "newDekIv", "newEncryptedDekBuffer", "updatedDekEncrypted", "UserSecretProcessor", "db", "env", "user", "updatedFields", "shouldUpdateDb", "totpSecret", "decryptEnvelope", "rotatedDek", "rotateEnvelopeDek", "e", "err", "encrypted", "encryptEnvelope", "totpRecoveryKeys", "updateParts", "bindings", "key", "val", "UserCoreModel", "db", "secretProcessor", "id", "user", "username", "e", "results", "now", "timezone", "locale", "err", "UserSecurityModel", "db", "id", "passwordHash", "passwordVersion", "pinHash", "timeout", "UserMfaModel", "db", "env", "id", "secret", "recoveryKeys", "recoveryKeysStr", "encryptedSecret", "encryptEnvelope", "encryptedKeys", "keepMfaState", "skipPassword", "usedIndex", "currentHashes", "updated", "_", "i", "updatedStr", "UserLifecycleModel", "db", "profileId", "now", "thirtyDaysAgo", "ninetyDaysAgo", "deleteLogsStmt", "deleteProfilesStmt", "deleteUsersStmt", "results", "UserModel", "db", "env", "UserSecretProcessor", "UserCoreModel", "UserSecurityModel", "UserMfaModel", "UserLifecycleModel", "id", "username", "user", "timezone", "locale", "passwordHash", "passwordVersion", "pinHash", "timeout", "secret", "recoveryKeys", "keepMfaState", "skipPassword", "usedIndex", "currentHashes", "profileId", "now", "init_cache", "extractRawQuery", "request", "dnsParam", "base64", "binary", "raw", "buffer", "resolveProfileByKey", "profileKey", "env", "ctx", "cache", "now", "inMem", "profileKeyMemoryMap", "cacheKey", "cached", "cacheUtils", "profile", "ProfileModel", "e", "failOpenUpstream", "resolveDefaultProfile", "defaultKey", "profileModel", "profiles", "fullProfile", "handleDoHRequest", "queryRaw", "profileId", "query", "parseDNSQuery", "context", "result", "pipeline", "clientIp", "activeDnsTtl", "nowSec", "lastActiveKey", "lastActiveThrottled", "throttleSec", "UserModel", "rawBytes", "endpoint", "resolveUpstreamEndpoint", "transportRes", "fetchFromUpstream", "fallbackUrl", "reqUrl", "proxyErr", "formatTip", "tip", "indent", "t", "formatHelpMenu", "config", "name", "description", "usage", "commands", "options", "services", "envVars", "gap", "cmdAndOptItems", "cmdAndOptWidth", "item", "renderSection", "title", "items", "customWidth", "width", "lines", "usageText", "u", "tipText", "formatCommandList", "maxCmdWidth", "i", "formatLogBox", "content", "borderChar", "minWidth", "headerText", "borderLength", "border", "leftPad", "rightPad", "formatKeyValueSection", "bullet", "maxLabelWidth", "formatBanner", "subtitle", "contentLengths", "calculatedWidth", "centerText", "text", "pad", "itemLines", "formatDiagnostic", "message", "details", "causes", "solutions", "subIndent", "sections", "d", "causesHeader", "causeLines", "c", "solHeader", "solLines", "s", "formatPortError", "serviceName", "protocol", "port", "err", "alternateOption", "disableOption", "errCode", "UdpDnsServer", "options", "resolve", "reject", "dgram", "startupErrorHandler", "err", "formatPortError", "msg", "rinfo", "rawBytes", "query", "parseDNSQueryFromRaw", "env", "ctx", "promise", "profile", "resolveProfileByKey", "resolveDefaultProfile", "context", "host", "request", "result", "pipeline", "answer", "truncated", "failOpenUpstream", "endpoint", "resolveUpstreamEndpoint", "fetchFromUpstream", "fallbackErr", "fs", "tls", "Buffer", "DotDnsServer", "options", "resolve", "reject", "tlsKeyPath", "tlsCertPath", "port", "host", "formatDiagnostic", "fs", "tlsOptions", "tls", "socket", "startupErrorHandler", "err", "formatPortError", "remoteIp", "buffer", "Buffer", "serverName", "candidateKey", "firstPart", "ACCESS_KEY_REGEX", "MAX_DOT_BUFFER", "chunk", "msgLen", "msgPayload", "rawMsg", "sniKey", "query", "parseDNSQueryFromRaw", "env", "ctx", "promise", "profile", "resolveProfileByKey", "resolveDefaultProfile", "context", "request", "result", "pipeline", "answer", "responseBuf", "failOpenUpstream", "endpoint", "resolveUpstreamEndpoint", "transportRes", "fetchFromUpstream", "fallbackErr", "http", "fs", "path", "Buffer", "init_auth", "generateLinuxSetupScript", "origin", "key", "init_middleware", "handleAuthConfigRequest", "request", "env", "url", "userModel", "UserModel", "settingsModel", "SystemSettingsModel", "siteKey", "signupEnabled", "loginEnabled", "hasUsers", "registrationEnabled", "empty", "username", "USERNAME_REGEX", "exists", "init_auth", "init_jwt", "init_crypto", "ActivityLogModel", "db", "userId", "action", "ip", "userAgent", "extra", "sessionIdHash", "now", "err", "limit", "before", "clampedLimit", "results", "BASE32_ALPHABET", "base32Encode", "bytes", "result", "buffer", "bitsLeft", "byte", "base32Decode", "input", "end", "normalized", "char", "val", "generateTOTPSecret", "computeHOTP", "secret", "counter", "keyBytes", "counterBytes", "view", "cryptoKey", "signature", "hmac", "offset", "verifyTOTPWithStep", "token", "salt", "TOTP_TOKEN_REGEX", "timeStep", "delta", "step", "expected", "data", "hashBuffer", "b", "e", "verifyTOTP", "getTOTPUri", "username", "issuer", "label", "params", "generateRecoveryGroup", "randomBytes", "rem", "first5", "checkDigit", "generateRecoveryKey", "groups", "i", "generateRecoveryKeys", "hashRecoveryKey", "key", "hash", "findMatchingRecoveryKey", "storedItems", "inputHash", "normalizedInput", "item", "init_cache", "verifyTurnstile", "token", "secret", "ip", "params", "handleAuthRegisterRequest", "request", "env", "userModel", "UserModel", "activityLog", "ActivityLogModel", "cache", "clientIp", "userAgent", "cacheUtils", "username", "password", "turnstileToken", "settingsModel", "SystemSettingsModel", "secretKey", "enabled", "registrationEnabled", "isDbEmpty", "verifyTurnstile", "USERNAME_REGEX", "PASSWORD_REGEX", "latitude", "longitude", "getRequestCoordinates", "jwtStatus", "getJwtSecretStatus", "jwtKey", "importJwtSecret", "hashedPassword", "hashPassword", "userId", "generateId", "timezone", "role", "userCreated", "profileModel", "ProfileModel", "defaultProfileId", "defaultRetentionDays", "recoveryKey", "generateRecoveryKey", "recoveryKeyHash", "hashRecoveryKey", "session", "refreshToken", "createSession", "sessionHash", "generateSessionHash", "refreshCookie", "createRefreshTokenCookie", "csrfToken", "csrfCookie", "createCsrfCookie", "expMinutes", "accessToken", "signJWT", "headers", "e", "cleanupErr", "init_auth", "init_jwt", "init_crypto", "init_auth", "PasskeyModel", "db", "userId", "results", "id", "credentialId", "idOrCredId", "direct", "userPasskeys", "cleanId", "p", "pClean", "data", "generateId", "now", "transportsJson", "name", "signCount", "init_cache", "base64UrlEncode", "buffer", "bytes", "binary", "i", "base64UrlDecode", "str", "base64", "generateWebAuthnChallenge", "derToP1363", "der", "offset", "rLen", "sLen", "s", "p1363", "decodeCbor", "data", "offset", "readUint", "info", "val", "hi", "lo", "parseItem", "initialByte", "majorType", "len", "slice", "arr", "i", "map", "key", "parseAttestationAuthData", "authData", "rpIdHash", "flags", "signCount", "offset", "aaguidBytes", "aaguid", "b", "credIdLen", "credIdBytes", "credentialId", "base64UrlEncode", "coseCborData", "coseKey", "decodeCbor", "alg", "publicKeySpki", "x", "uncompressedPoint", "cryptoKey", "spkiBuffer", "n", "e", "jwk", "verifyRegistrationResponse", "options", "clientDataJSON", "attestationObject", "expectedChallenge", "expectedOrigin", "expectedRpId", "clientDataBytes", "base64UrlDecode", "clientData", "clientOriginUrl", "expectedOriginUrl", "attestationBytes", "attestation", "parsed", "expectedRpIdHash", "i", "verifyAuthenticationResponse", "options", "clientDataJSON", "authenticatorData", "signature", "publicKeySpki", "algorithm", "expectedChallenge", "expectedOrigin", "expectedRpId", "previousSignCount", "clientDataBytes", "base64UrlDecode", "clientData", "clientOriginUrl", "expectedOriginUrl", "authDataBytes", "rpIdHash", "flags", "signCount", "expectedRpIdHash", "i", "clientDataHash", "signedData", "sigBytes", "spkiBytes", "cryptoKey", "rawSig", "derToP1363", "handleLoginRequest", "request", "env", "url", "userModel", "UserModel", "activityLog", "ActivityLogModel", "cache", "clientIp", "userAgent", "cacheUtils", "username", "turnstileToken", "settingsModel", "SystemSettingsModel", "secretKey", "enabled", "verifyTurnstile", "user", "preauthToken", "createPreauthSession", "preauthCookie", "createPreauthCookie", "requires_password", "requires_totp", "password_version", "passkeys", "PasskeyModel", "has_passkey", "passkey_options", "passkey_challenge", "generateWebAuthnChallenge", "p", "nonce", "generateId", "serverSalt", "extractSaltHex", "preauthTtl", "readPreauthCookie", "userId", "validatePreauthSession", "preauthState", "invalidatePreauthSession", "password", "totpTokenHash", "totpSalt", "recoveryKey", "passkeyAssertion", "keepLoggedIn", "needsMigration", "passkeyModel", "userPasskeys", "hasPasskeys", "hasTotp", "hasRecoveryKeys", "requiresMfa", "isPasswordlessPasskey", "expectedResponse", "hmacSha256", "remaining", "recordFailedPreauthAttempt", "verifyPassword", "isTotpSuccess", "isRecoverySuccess", "isPasskeySuccess", "recoveryRemaining", "rotatedRecoveryKey", "credentialId", "passkey", "signCount", "verifyAuthenticationResponse", "err", "storedHashes", "raw", "findMatchingRecoveryKey", "rotated", "generateRecoveryKey", "rotatedHash", "hashRecoveryKey", "totpResult", "verifyTOTPWithStep", "replayKey", "latitude", "longitude", "getRequestCoordinates", "session", "refreshToken", "createSession", "sessionHash", "generateSessionHash", "csrfToken", "csrfCookie", "createCsrfCookie", "secret", "getOrCreateJwtSecret", "jwtKey", "importJwtSecret", "expMinutes", "accessToken", "signJWT", "headers", "createRefreshTokenCookie", "clearPreauthCookie", "init_auth", "init_jwt", "init_cache", "handleRefreshRequest", "request", "env", "cache", "clientIp", "userAgent", "activityLog", "ActivityLogModel", "cacheUtils", "refreshToken", "readRefreshTokenCookie", "getRequestCoordinates", "latitude", "longitude", "session", "user", "newRefreshToken", "reason", "rotateSession", "parsed", "parseRefreshTokenString", "sessionHash", "generateSessionHash", "createBlankRefreshTokenCookie", "secret", "getOrCreateJwtSecret", "jwtKey", "importJwtSecret", "expMinutes", "accessToken", "signJWT", "headers", "isKeepLoggedIn", "createRefreshTokenCookie", "init_auth", "init_session", "init_middleware", "handleLogoutRequest", "request", "env", "clientIp", "userAgent", "activityLog", "ActivityLogModel", "refreshToken", "readRefreshTokenCookie", "parsed", "parseRefreshTokenString", "userId", "SessionModel", "invalidateSession", "invalidateAuthUserCache", "sessionHash", "generateSessionHash", "responseHeaders", "createBlankRefreshTokenCookie", "init_auth", "init_jwt", "init_crypto", "init_session", "init_cache", "init_middleware", "verifyLegacyPin", "clientPinHash", "storedPinHash", "verifyPassword", "binaryString", "combined", "i", "salt", "storedHash", "saltHex", "b", "msgWithSalt", "computedHashBuffer", "computedHash", "result", "handleSessionLockRequest", "request", "env", "url", "userModel", "UserModel", "activityLog", "ActivityLogModel", "cache", "clientIp", "userAgent", "authHeader", "accessToken", "secret", "getOrCreateJwtSecret", "jwtKey", "importJwtSecret", "payload", "verifyJWT", "dbUser", "isLegacy", "nonce", "generateId", "cacheKey", "cacheUtils", "pinHash", "sessionModel", "SessionModel", "session", "cacheKeyNonce", "cachedNonceState", "cachedNonce", "failedAttempts", "sessionHash", "generateSessionHash", "isPinValid", "expectedChallengedHash", "hashChallenge", "nextFailedCount", "now", "invalidateAuthUserCache", "e", "init_session", "init_cache", "init_crypto", "handleForgotPasswordRequest", "request", "env", "url", "path", "userModel", "UserModel", "activityLog", "ActivityLogModel", "cache", "clientIp", "userAgent", "cacheUtils", "username", "turnstileToken", "settingsModel", "SystemSettingsModel", "secretKey", "enabled", "verifyTurnstile", "user", "passkeys", "PasskeyModel", "has_passkey", "has_totp", "has_recovery_keys", "rawRecoveryKeys", "parsed", "passkey_challenge", "passkey_options", "generateWebAuthnChallenge", "p", "recoveryToken", "generateId", "recoveryState", "passkeyAssertion", "totpTokenHash", "totpSalt", "recoveryKey", "cacheKey", "state", "passkeyModel", "passkey", "signCount", "verifyAuthenticationResponse", "storedHashes", "raw", "matchIndex", "findMatchingRecoveryKey", "totpResult", "verifyTOTPWithStep", "stepKey", "resetToken", "resetState", "newPassword", "PASSWORD_REGEX", "resetKey", "hashedPassword", "hashPassword", "SessionModel", "handleAuthRequest", "request", "env", "path", "handleAuthConfigRequest", "handleAuthRegisterRequest", "handleLoginRequest", "handleRefreshRequest", "handleLogoutRequest", "handleForgotPasswordRequest", "handleSessionLockRequest", "e", "RBAC", "user", "profile", "init_auth", "handleProfilesCoreCollectionRequest", "request", "env", "user", "pathParts", "profileModel", "ProfileModel", "results", "body", "PROFILE_NAME_REGEX", "existingProfiles", "maxProfiles", "p", "newId", "generateId", "globalMaxRetention", "adminDefaultRetention", "normalUserMaxRetention", "normalUserDefaultRetention", "defaultRetentionDays", "defaultSettings", "existingLogKey", "now", "existingRecKey", "e", "handleProfilesCoreRequest", "profile", "ctx", "logModel", "LogModel", "profileId", "name", "pipeline", "newKey", "newSettings", "adminMaxRetention", "normalUserMax", "userMaxRetention", "url", "isHttps", "isHttp", "isTcp", "isTls", "isDot", "isUdp", "isSdns", "isBareHost", "normalized", "isSafeUrl", "oldProfile", "oldDays", "oldSettings", "newDays", "threshold", "domain", "type", "rawQuery", "buildDNSQuery", "result", "parseDNSAnswer", "init_auth", "AccessPointModel", "db", "profileId", "results", "name", "now", "id", "generateId", "MAX_RETRIES", "attempt", "token", "generateZBase32Token", "err", "newToken", "handleProfileAccessPointsRequest", "request", "env", "user", "profileId", "pathParts", "ctx", "apModel", "AccessPointModel", "results", "apId", "newToken", "body", "AP_NAME_REGEX", "currentAps", "ap", "maxAps", "result", "handleProfileRulesRequest", "request", "env", "user", "profileId", "pathParts", "ctx", "ruleModel", "RuleModel", "results", "body", "rawList", "seen", "validRules", "r", "pattern", "type", "existingRules", "existingPatterns", "rulesToInsert", "insertedCount", "pipeline", "rule", "id", "ListModel", "db", "profileId", "results", "url", "items", "statements", "item", "enabled", "inserted", "i", "chunk", "res", "id", "now", "syncError", "ListBloomModel", "db", "listId", "bloomFilter", "now", "uint8Array", "statements", "chunkIndex", "offset", "chunkData", "r", "results", "totalLength", "acc", "row", "len", "combined", "chunk", "profileId", "groups", "blooms", "chunks", "chunkArr", "combineAndPromote", "profileId", "listModel", "listBloomModel", "profileBloomModel", "profileModel", "ctx", "now", "maxDomains", "falsePositiveRate", "env", "activeLists", "l", "merged", "BloomFilter", "hasMergedAny", "list", "buf", "filter", "err", "binary", "pipelineCache", "isIpLike", "str", "dotCount", "len", "i", "code", "parseLine", "line", "start", "end", "firstChar", "cleanLine", "domain", "caretIdx", "dollarIdx", "firstSpace", "lineLen", "part0", "secondStart", "part1", "secondEnd", "fetchListContent", "url", "maxBytes", "timeoutMs", "onDomain", "deadline", "currentUrl", "redirectCount", "MAX_REDIRECTS", "response", "isSafeUrl", "remainingTimeout", "location", "nextUrl", "nextProto", "contentLength", "reader", "decoder", "buffer", "count", "totalBytes", "truncated", "done", "value", "chunkText", "lines", "line", "domain", "parseLine", "e", "MAX_LIST_BYTES", "syncSingleList", "profileId", "list", "env", "listModel", "listBloomModel", "now", "timeoutMs", "maxListDomains", "maxDomains", "falsePositiveRate", "listBloom", "BloomFilter", "count", "fetchError", "fetchListContent", "domain", "resolve", "syncError", "syncNextListForProfile", "profileId", "env", "ctx", "profileModel", "ProfileModel", "listModel", "ListModel", "profileBloomModel", "ProfileBloomModel", "listBloomModel", "ListBloomModel", "now", "profile", "lastFullSync", "activeLists", "l", "pendingLists", "a", "b", "maxDomains", "falsePositiveRate", "combineAndPromote", "list", "syncSingleList", "activeUpdatedLists", "remaining", "e", "syncAllListsForProfile", "rebuildProfileBloom", "priorListUpdatedAt", "missingIndex", "present", "missing", "handleProfileListsRequest", "request", "env", "user", "profileId", "pathParts", "ctx", "listModel", "ListModel", "profileModel", "ProfileModel", "listId", "domain", "list", "buffer", "ListBloomModel", "bloom", "BloomFilter", "err", "candidate", "matched", "dot", "results", "body", "rebuildProfileBloom", "pipeline", "e", "syncAllListsForProfile", "candidateList", "rawList", "seen", "validItems", "item", "urlStr", "isSafeUrl", "norm", "enabled", "existingLists", "existingUrls", "l", "itemsToInsert", "insertedCount", "syncNextListForProfile", "listUrl", "id", "init_cache", "handleProfileLogsWs", "request", "env", "user", "profile", "ctx", "upgradeHeader", "globalObj", "pair", "clientWs", "serverWs", "profileId", "isClosed", "lastTimestamp", "lastId", "pollInterval", "isChecking", "socketCloseResolver", "socketLifetimePromise", "resolve", "checkForNewLogs", "results", "newest", "payload", "err", "event", "dataStr", "msg", "limit", "cleanup", "handleProfileLogsAndAnalyticsRequest", "request", "env", "user", "profile", "pathParts", "ctx", "logModel", "LogModel", "profileId", "handleProfileLogsWs", "urlParams", "range", "before", "status", "search", "accessPointId", "destCountry", "isp", "domain", "recordType", "geoCountry", "reason", "startParam", "endParam", "since", "until", "globalMaxRetention", "settings", "logRetentionDays", "retentionThreshold", "results", "logId", "timestampParam", "timestamp", "logDetail", "limit", "interval", "subResource", "cache", "subCacheKey", "cached", "cacheUtils", "data", "limitParam", "countryCode", "cacheKey", "analytics", "handleProfileE2eeRequest", "request", "env", "user", "profile", "pathParts", "_ctx", "passkeyModel", "PasskeyModel", "subResource", "logKey", "wrappedKeys", "recoveryKey", "passkeys", "w", "passkeyId", "wrapped", "passkey", "body", "publicKey", "encryptedSk", "iv", "recovery", "userPasskeys", "now", "publicKeyStr", "stmts", "invalidateProfileLogKeyCache", "handleProfilesRequest", "request", "env", "user", "ctx", "pathParts", "profileModel", "ProfileModel", "handleProfilesCoreCollectionRequest", "profileId", "profile", "RBAC", "subResource", "handleProfilesCoreRequest", "handleProfileAccessPointsRequest", "handleProfileRulesRequest", "handleProfileListsRequest", "handleProfileLogsAndAnalyticsRequest", "handleProfileE2eeRequest", "init_auth", "handleMeRequest", "request", "env", "user", "pathParts", "ctx", "userModel", "UserModel", "logModel", "LogModel", "action", "dbUser", "globalMaxRetention", "adminMaxRetention", "normalUserMax", "passkeysCount", "PasskeyModel", "mfaEnabled", "hasRecoveryKeys", "parsed", "jwtSecretWarning", "isUsableJwtSecret", "isStrongJwtSecret", "body", "timeout", "newUsername", "USERNAME_REGEX", "newTimezone", "newLocale", "e", "invalidateSession", "cookieHeader", "refreshToken", "readRefreshTokenCookie", "sessionId", "parseRefreshTokenString", "createBlankRefreshTokenCookie", "init_crypto", "init_crypto", "init_cache", "verifyUserReauth", "dbUser", "payload", "env", "request", "cache", "cachedState", "cacheUtils", "passkeyModel", "PasskeyModel", "passkey", "p", "requestUrl", "signCount", "verifyAuthenticationResponse", "verifyTOTP", "pwd", "verifyPassword", "storedHashes", "findMatchingRecoveryKey", "handlePasswordRequest", "request", "env", "user", "pathParts", "_ctx", "userModel", "UserModel", "activityLog", "ActivityLogModel", "clientIp", "userAgent", "action", "sessionHash", "generateSessionHash", "body", "newPassword", "PASSWORD_REGEX", "dbUser", "authResult", "verifyUserReauth", "hashedPassword", "hashPassword", "clientHash", "init_crypto", "handleTotpAndMfaRequest", "request", "env", "user", "pathParts", "_ctx", "userModel", "UserModel", "activityLog", "ActivityLogModel", "clientIp", "userAgent", "action", "sessionHash", "generateSessionHash", "subAction", "dbUser", "secret", "generateTOTPSecret", "host", "uri", "getTOTPUri", "totpTokenHash", "salt", "verifyTOTP", "plaintextKeys", "generateRecoveryKeys", "storedItems", "k", "hashRecoveryKey", "passkeys", "PasskeyModel", "skip_password", "password", "verifyPassword", "hasPasskey", "handlePinRequest", "request", "env", "user", "_pathParts", "_ctx", "userModel", "UserModel", "dbUser", "body", "authResult", "verifyUserReauth", "pinHash", "init_crypto", "init_cache", "handlePasskeysRequest", "request", "env", "user", "pathParts", "_ctx", "userModel", "UserModel", "passkeyModel", "PasskeyModel", "activityLog", "ActivityLogModel", "clientIp", "userAgent", "subAction", "sessionHash", "generateSessionHash", "passkeys", "existingPasskeys", "challenge", "generateWebAuthnChallenge", "rpId", "cache", "cacheUtils", "options", "p", "username", "userDisplayName", "base64UrlEncode", "cachedState", "body", "name", "credential", "requestUrl", "expectedOrigin", "parsed", "verifyRegistrationResponse", "passkeyName", "PASSKEY_NAME_REGEX", "transports", "created", "dbUser", "recoveryKeys", "hasExistingRecoveryKeys", "plaintextKeys", "generateRecoveryKeys", "storedItems", "k", "hashRecoveryKey", "err", "passkeyId", "trimmedName", "success", "passkey", "remainingPasskeys", "init_crypto", "handleRecoveryKeysRequest", "request", "env", "user", "pathParts", "_ctx", "userModel", "UserModel", "activityLog", "ActivityLogModel", "clientIp", "userAgent", "subAction", "sessionHash", "generateSessionHash", "body", "dbUser", "authResult", "verifyUserReauth", "plaintextKey", "generateRecoveryKey", "newItems", "hashRecoveryKey", "handleSecurityRequest", "request", "env", "user", "pathParts", "ctx", "handlePasswordRequest", "handleTotpAndMfaRequest", "handlePinRequest", "handlePasskeysRequest", "handleRecoveryKeysRequest", "init_auth", "handleSessionsRequest", "request", "env", "user", "pathParts", "ctx", "activityLog", "ActivityLogModel", "clientIp", "userAgent", "action", "params", "limit", "before", "entries", "SessionModel", "sessions", "cookieHeader", "refreshToken", "readRefreshTokenCookie", "currentSessionId", "parseRefreshTokenString", "sessionData", "s", "generateSessionHash", "sessionModel", "otherSessions", "invalidateSession", "invalidateAuthUserCache", "currentSessionHash", "targetSessionHash", "targetSessionId", "createBlankRefreshTokenCookie", "handleAccountE2eeRequest", "request", "env", "user", "pathParts", "_ctx", "passkeyModel", "PasskeyModel", "profileModel", "ProfileModel", "subResource", "profiles", "passkeys", "logKey", "wrappedKeys", "recoveryKey", "keyRes", "wrappedRes", "recRes", "w", "passkeyId", "wrapped", "passkey", "body", "publicKey", "encryptedSk", "iv", "recovery", "userPasskeys", "now", "publicKeyStr", "stmts", "primaryProfileId", "p", "invalidateProfileLogKeyCache", "salt", "purge", "handlePersonalAccountRequest", "request", "env", "user", "pathParts", "ctx", "action", "handleMeRequest", "handleSecurityRequest", "handleSessionsRequest", "handleAccountE2eeRequest", "init_auth", "init_crypto", "handleAdminRequest", "request", "env", "user", "pathParts", "ctx", "userModel", "UserModel", "profileModel", "ProfileModel", "users", "username", "password", "role", "USERNAME_REGEX", "PASSWORD_REGEX", "hashedPassword", "hashPassword", "userId", "generateId", "e", "targetId", "systemSettings", "SystemSettingsModel", "settings", "body", "handleAccountRequest", "request", "env", "user", "ctx", "pathParts", "handlePersonalAccountRequest", "RBAC", "handleAdminRequest", "init_cache", "handleSystemRequest", "request", "env", "url", "cache", "clientIp", "connectedProfileId", "cacheUtils", "cf", "country", "region", "city", "timezone", "asn", "asOrganization", "regions", "key", "value", "regionKey", "cleanVal", "subDomain", "substituteDomainIp", "substituteDomainIpv6", "resolveRecord", "type", "dnsServers", "server", "res", "data", "record", "a", "e", "ip", "ipv6", "upstreams", "filters", "domains", "getPresetEchFrontingDomains", "domain", "targetUrl", "iconRes", "newHeaders", "init_cache", "handleScheduled", "_event", "env", "ctx", "cache", "now", "lastCleanupKey", "lastCleanup", "cacheUtils", "userModel", "UserModel", "clearedProfiles", "deletedUsers", "e", "logModel", "LogModel", "maxRetentionDays", "cleanupBatchLimit", "cleanupDailyBudget", "SessionModel", "sessionModel", "deletedSessions", "deletedPending", "syncCloudflareIpRanges", "lastIdleSyncKey", "lastIdleCheck", "syncIntervalSec", "cutoffTime", "profileModel", "ProfileModel", "batchSize", "syncTargets", "target", "syncNextListForProfile", "err", "errorMsg", "handleMapDataRequest", "request", "env", "ctx", "cacheKey", "cache", "response", "res", "data", "sortedObjects", "geom", "e", "init_jwt", "index_default", "request", "env", "ctx", "nonceBytes", "nonce", "b", "currentUser", "response", "url", "handleMapDataRequest", "isDbMissing", "isJwtSecretMissing", "isUsableJwtSecret", "getCurrentUser", "isPublicRoute", "validateCsrf", "handleAuthRequest", "handleSystemRequest", "handleProfilesRequest", "handleAccountRequest", "key", "ACCESS_KEY_REGEX", "script", "generateLinuxSetupScript", "rawKey", "isKeyValid", "isDoHRequest", "handleDoHRequest", "wasmHeaders", "configStr", "element", "finalUser", "readCsrfCookie", "isKeepLoggedIn", "csrfToken", "generateId", "createCsrfCookie", "newHeaders", "dohHeaders", "applySecurityHeaders", "event", "handleScheduled", "MIME_TYPES", "HttpServer", "options", "staticDir", "path", "getPackageRoot", "req", "url", "pathname", "safePath", "filePath", "fs", "ext", "contentType", "fileContent", "resolve", "reject", "port", "host", "http", "res", "socket", "startupErrorHandler", "err", "formatPortError", "proto", "fullUrl", "headers", "k", "v", "val", "remoteIp", "body", "chunks", "chunk", "Buffer", "request", "ctx", "promise", "response", "index_default", "key", "reader", "done", "value", "init_jwt", "fs", "net", "dgram", "DatabaseSync", "init_jwt", "probeTcpPort", "port", "host", "timeoutMs", "resolve", "socket", "net", "isSettled", "finish", "state", "targetHost", "probeUdpPort", "dgram", "err", "code", "inspectDatabase", "dbPath", "fs", "stats", "sizeFormatted", "migrationsCount", "usersCount", "profilesCount", "rawDb", "DatabaseSync", "migTable", "usersTable", "profTable", "details", "showServerfullStatus", "config", "envJwtSecret", "httpState", "udpState", "dotState", "dbStatus", "tlsConfigured", "tlsFilesExist", "jwtStatus", "isUsableJwtSecret", "isStrongJwtSecret", "httpStatusText", "udpStatusText", "dotStatusText", "defaultProfileText", "banner", "formatBanner", "servicesSection", "formatKeyValueSection", "databaseSection", "envSection", "tipSection", "serviceInstallCmd", "formatTip", "report", "fs", "path", "execSync", "isWindowsAdmin", "isLinuxRoot", "getServiceExecDetails", "nodePath", "scriptPath", "user", "homeDir", "execCmd", "fs", "execSync", "SERVICE_NAME", "SERVICE_FILE_NAME", "SYSTEMD_SERVICE_PATH", "generateSystemdUnit", "details", "execCmd", "workDir", "user", "handleLinuxSystemd", "action", "isRoot", "isLinuxRoot", "formatDiagnostic", "getServiceExecDetails", "unitContent", "fs", "execSync", "banner", "formatBanner", "commandGuide", "formatCommandList", "err", "errorMsg", "fs", "path", "execSync", "WINDOWS_TASK_NAME", "WINDOWS_DATA_DIR", "path", "WINDOWS_LOG_FILE", "WINDOWS_BAT_FILE", "generateWindowsBat", "details", "handleWindowsTask", "action", "isAdmin", "isWindowsAdmin", "formatDiagnostic", "getServiceExecDetails", "fs", "batContent", "createCmd", "execSync", "banner", "formatBanner", "commandGuide", "formatCommandList", "err", "errorMsg", "tailLines", "formatLogBox", "handleServiceAction", "action", "handleWindowsTask", "handleLinuxSystemd", "details", "getServiceExecDetails", "unitContent", "generateSystemdUnit", "formatDiagnostic", "formatLogBox", "checkNodeVersion", "major", "minor", "formatDiagnostic", "printHelp", "version", "getPackageVersion", "menu", "formatHelpMenu", "parseCli", "values", "positionals", "parseArgs", "config", "env", "getServerfullConfig", "showServerfullStatus", "action", "handleServiceAction", "serviceInstallHint", "err", "errorMsg", "bootstrap", "cliArgs", "formatBanner", "initNodeGlobals", "formatKeyValueSection", "isUsableJwtSecret", "isStrongJwtSecret", "db", "initServerfullDb", "udpServer", "UdpDnsServer", "dotServer", "DotDnsServer", "httpServer", "HttpServer", "cronTimer", "scheduledEvent", "ctx", "promise", "index_default", "hasTls", "shutdown", "signal", "flushLogBatch"]
7
+ }