UNPKG

langchain

Version:
1 lines 26.7 kB
{"version":3,"file":"pii.cjs","names":["z","createMiddleware","HumanMessage","AIMessage","ToolMessage"],"sources":["../../../src/agents/middleware/pii.ts"],"sourcesContent":["import { z } from \"zod/v3\";\nimport { sha256 } from \"@langchain/core/utils/hash\";\nimport { AIMessage, HumanMessage, ToolMessage } from \"@langchain/core/messages\";\nimport type { InferInteropZodInput } from \"@langchain/core/utils/types\";\n\nimport { createMiddleware } from \"../middleware.js\";\n\n/**\n * Represents a detected PII match in content\n */\nexport interface PIIMatch {\n /**\n * The matched text\n */\n text: string;\n /**\n * The start index of the match\n */\n start: number;\n /**\n * The end index of the match\n */\n end: number;\n}\n\n/**\n * Error thrown when PII is detected and strategy is 'block'\n */\nexport class PIIDetectionError extends Error {\n constructor(\n public readonly piiType: string,\n public readonly matches: PIIMatch[]\n ) {\n super(`PII detected: ${piiType} found ${matches.length} occurrence(s)`);\n this.name = \"PIIDetectionError\";\n }\n}\n\n/**\n * Strategy for handling detected PII\n */\nexport type PIIStrategy = \"block\" | \"redact\" | \"mask\" | \"hash\";\n\n/**\n * Built-in PII types\n */\nexport type BuiltInPIIType =\n | \"email\"\n | \"credit_card\"\n | \"ip\"\n | \"mac_address\"\n | \"url\";\n\n/**\n * Custom detector function that takes content and returns matches\n */\nexport type PIIDetector = (content: string) => PIIMatch[];\nexport type Detector = PIIDetector | RegExp | string;\n\n/**\n * Configuration for a redaction rule\n */\nexport interface RedactionRuleConfig {\n /**\n * Type of PII to detect (built-in or custom name)\n */\n piiType: BuiltInPIIType | string;\n /**\n * Strategy for handling detected PII\n */\n strategy: PIIStrategy;\n /**\n * Custom detector function or regex pattern string\n */\n detector?: Detector;\n}\n\n/**\n * Resolved redaction rule with a concrete detector function\n */\nexport interface ResolvedRedactionRule {\n piiType: string;\n strategy: PIIStrategy;\n detector: PIIDetector;\n}\n\n/**\n * Email detection regex pattern\n */\nconst EMAIL_PATTERN = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g;\n\n/**\n * Credit card detection regex pattern (basic, will be validated with Luhn)\n */\nconst CREDIT_CARD_PATTERN = /\\b(?:\\d{4}[-\\s]?){3}\\d{4}\\b/g;\n\n/**\n * IP address detection regex pattern\n */\nconst IP_PATTERN =\n /\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b/g;\n\n/**\n * MAC address detection regex pattern\n */\nconst MAC_ADDRESS_PATTERN = /\\b(?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2})\\b/g;\n\n/**\n * URL detection regex pattern\n */\nconst URL_PATTERN = /(?:https?:\\/\\/|www\\.)[^\\s<>\"{}|\\\\^`[\\]]+/gi;\n\n/**\n * Luhn algorithm for credit card validation\n */\nfunction luhnCheck(cardNumber: string): boolean {\n const digits = cardNumber.replace(/\\D/g, \"\");\n let sum = 0;\n let isEven = false;\n\n for (let i = digits.length - 1; i >= 0; i--) {\n let digit = parseInt(digits[i], 10);\n\n if (isEven) {\n digit *= 2;\n if (digit > 9) {\n digit -= 9;\n }\n }\n\n sum += digit;\n isEven = !isEven;\n }\n\n return sum % 10 === 0;\n}\n\n/**\n * Convert regex match to PIIMatch\n */\nfunction regexMatchToPIIMatch(match: RegExpMatchArray): PIIMatch {\n return {\n text: match[0],\n start: match.index ?? 0,\n end: (match.index ?? 0) + match[0].length,\n };\n}\n\n/**\n * Detect email addresses in content\n */\nexport function detectEmail(content: string): PIIMatch[] {\n const matches: PIIMatch[] = [];\n const regex = new RegExp(EMAIL_PATTERN);\n let match: RegExpMatchArray | null;\n\n while ((match = regex.exec(content)) !== null) {\n matches.push(regexMatchToPIIMatch(match));\n }\n\n return matches;\n}\n\n/**\n * Detect credit card numbers in content (validated with Luhn algorithm)\n */\nexport function detectCreditCard(content: string): PIIMatch[] {\n const matches: PIIMatch[] = [];\n const regex = new RegExp(CREDIT_CARD_PATTERN);\n let match: RegExpMatchArray | null;\n\n while ((match = regex.exec(content)) !== null) {\n const cardNumber = match[0].replace(/\\D/g, \"\");\n // Credit cards are typically 13-19 digits\n if (\n cardNumber.length >= 13 &&\n cardNumber.length <= 19 &&\n luhnCheck(cardNumber)\n ) {\n matches.push(regexMatchToPIIMatch(match));\n }\n }\n\n return matches;\n}\n\n/**\n * Detect IP addresses in content (validated)\n */\nexport function detectIP(content: string): PIIMatch[] {\n const matches: PIIMatch[] = [];\n const regex = new RegExp(IP_PATTERN);\n let match: RegExpMatchArray | null;\n\n while ((match = regex.exec(content)) !== null) {\n const ip = match[0];\n // Additional validation: each octet should be 0-255\n const parts = ip.split(\".\");\n if (\n parts.length === 4 &&\n parts.every((part) => {\n const num = parseInt(part, 10);\n return num >= 0 && num <= 255;\n })\n ) {\n matches.push(regexMatchToPIIMatch(match));\n }\n }\n\n return matches;\n}\n\n/**\n * Detect MAC addresses in content\n */\nexport function detectMacAddress(content: string): PIIMatch[] {\n const matches: PIIMatch[] = [];\n const regex = new RegExp(MAC_ADDRESS_PATTERN);\n let match: RegExpMatchArray | null;\n\n while ((match = regex.exec(content)) !== null) {\n matches.push(regexMatchToPIIMatch(match));\n }\n\n return matches;\n}\n\n/**\n * Detect URLs in content\n */\nexport function detectUrl(content: string): PIIMatch[] {\n const matches: PIIMatch[] = [];\n const regex = new RegExp(URL_PATTERN);\n let match: RegExpMatchArray | null;\n\n while ((match = regex.exec(content)) !== null) {\n matches.push(regexMatchToPIIMatch(match));\n }\n\n return matches;\n}\n\n/**\n * Built-in detector map\n */\nconst BUILT_IN_DETECTORS: Record<BuiltInPIIType, PIIDetector> = {\n email: detectEmail,\n credit_card: detectCreditCard,\n ip: detectIP,\n mac_address: detectMacAddress,\n url: detectUrl,\n};\n\n/**\n * Resolve a redaction rule to a concrete detector function\n */\nexport function resolveRedactionRule(\n config: RedactionRuleConfig\n): ResolvedRedactionRule {\n let detector: PIIDetector;\n\n if (config.detector) {\n if (typeof config.detector === \"string\") {\n // Regex pattern string\n const regex = new RegExp(config.detector, \"g\");\n detector = (content: string) => {\n const matches: PIIMatch[] = [];\n let match: RegExpMatchArray | null;\n const regexCopy = new RegExp(regex);\n\n while ((match = regexCopy.exec(content)) !== null) {\n matches.push(regexMatchToPIIMatch(match));\n }\n\n return matches;\n };\n // oxlint-disable-next-line no-instanceof/no-instanceof\n } else if (config.detector instanceof RegExp) {\n detector = (content: string) => {\n // oxlint-disable-next-line no-instanceof/no-instanceof\n if (!(config.detector instanceof RegExp)) {\n throw new Error(\"Detector is required\");\n }\n const matches: PIIMatch[] = [];\n let match: RegExpMatchArray | null;\n while ((match = config.detector.exec(content)) !== null) {\n matches.push(regexMatchToPIIMatch(match));\n }\n\n return matches;\n };\n } else {\n detector = config.detector;\n }\n } else {\n // Use built-in detector\n const builtInType = config.piiType as BuiltInPIIType;\n if (!BUILT_IN_DETECTORS[builtInType]) {\n throw new Error(\n `Unknown PII type: ${config.piiType}. Must be one of: ${Object.keys(\n BUILT_IN_DETECTORS\n ).join(\", \")}, or provide a custom detector.`\n );\n }\n detector = BUILT_IN_DETECTORS[builtInType];\n }\n\n return {\n piiType: config.piiType,\n strategy: config.strategy,\n detector,\n };\n}\n\n/**\n * Apply redact strategy: replace with [REDACTED_TYPE]\n */\nfunction applyRedactStrategy(\n content: string,\n matches: PIIMatch[],\n piiType: string\n): string {\n let result = content;\n // Process matches in reverse order to preserve indices\n for (let i = matches.length - 1; i >= 0; i--) {\n const match = matches[i];\n const replacement = `[REDACTED_${piiType.toUpperCase()}]`;\n result =\n result.slice(0, match.start) + replacement + result.slice(match.end);\n }\n return result;\n}\n\n/**\n * Apply mask strategy: partially mask PII (show last few characters)\n */\nfunction applyMaskStrategy(\n content: string,\n matches: PIIMatch[],\n piiType: string\n): string {\n let result = content;\n // Process matches in reverse order to preserve indices\n for (let i = matches.length - 1; i >= 0; i--) {\n const match = matches[i];\n const text = match.text;\n let masked: string;\n\n if (piiType === \"credit_card\") {\n // Show last 4 digits: ****-****-****-1234\n const digits = text.replace(/\\D/g, \"\");\n const last4 = digits.slice(-4);\n masked = `****-****-****-${last4}`;\n } else if (piiType === \"email\") {\n // Show first char and domain: j***@example.com\n const [local, domain] = text.split(\"@\");\n if (local && domain) {\n masked = `${local[0]}***@${domain}`;\n } else {\n masked = \"***\";\n }\n } else {\n // Default: show last 4 characters\n const visibleChars = Math.min(4, text.length);\n masked = `${\"*\".repeat(\n Math.max(0, text.length - visibleChars)\n )}${text.slice(-visibleChars)}`;\n }\n\n result = result.slice(0, match.start) + masked + result.slice(match.end);\n }\n return result;\n}\n\n/**\n * Apply hash strategy: replace with deterministic hash\n */\nfunction applyHashStrategy(\n content: string,\n matches: PIIMatch[],\n piiType: string\n): string {\n let result = content;\n // Process matches in reverse order to preserve indices\n for (let i = matches.length - 1; i >= 0; i--) {\n const match = matches[i];\n const hash = sha256(match.text).slice(0, 8);\n const replacement = `<${piiType}_hash:${hash}>`;\n result =\n result.slice(0, match.start) + replacement + result.slice(match.end);\n }\n return result;\n}\n\n/**\n * Apply strategy to content based on matches\n */\nexport function applyStrategy(\n content: string,\n matches: PIIMatch[],\n strategy: PIIStrategy,\n piiType: string\n): string {\n if (matches.length === 0) {\n return content;\n }\n\n switch (strategy) {\n case \"block\":\n throw new PIIDetectionError(piiType, matches);\n case \"redact\":\n return applyRedactStrategy(content, matches, piiType);\n case \"mask\":\n return applyMaskStrategy(content, matches, piiType);\n case \"hash\":\n return applyHashStrategy(content, matches, piiType);\n default:\n throw new Error(`Unknown strategy: ${strategy}`);\n }\n}\n\n/**\n * Configuration schema for PII middleware\n */\nconst contextSchema = z.object({\n /**\n * Whether to check user messages before model call\n */\n applyToInput: z.boolean().optional(),\n /**\n * Whether to check AI messages after model call\n */\n applyToOutput: z.boolean().optional(),\n /**\n * Whether to check tool result messages after tool execution\n */\n applyToToolResults: z.boolean().optional(),\n});\n\nexport type PIIMiddlewareConfig = InferInteropZodInput<typeof contextSchema>;\n\n/**\n * Process content for PII detection and apply strategy\n */\nfunction processContent(\n content: string,\n rule: ResolvedRedactionRule\n): { content: string; matches: PIIMatch[] } {\n const matches = rule.detector(content);\n if (matches.length === 0) {\n return { content, matches: [] };\n }\n\n const sanitized = applyStrategy(\n content,\n matches,\n rule.strategy,\n rule.piiType\n );\n return { content: sanitized, matches };\n}\n\n/**\n * Creates a middleware that detects and handles personally identifiable information (PII)\n * in conversations.\n *\n * This middleware detects common PII types and applies configurable strategies to handle them.\n * It can detect emails, credit cards, IP addresses, MAC addresses, and URLs in both user input\n * and agent output.\n *\n * Built-in PII types:\n * - `email`: Email addresses\n * - `credit_card`: Credit card numbers (validated with Luhn algorithm)\n * - `ip`: IP addresses (validated)\n * - `mac_address`: MAC addresses\n * - `url`: URLs (both `http`/`https` and bare URLs)\n *\n * Strategies:\n * - `block`: Raise an exception when PII is detected\n * - `redact`: Replace PII with `[REDACTED_TYPE]` placeholders\n * - `mask`: Partially mask PII (e.g., `****-****-****-1234` for credit card)\n * - `hash`: Replace PII with deterministic hash (e.g., `<email_hash:a1b2c3d4>`)\n *\n * Strategy Selection Guide:\n * | Strategy | Preserves Identity? | Best For |\n * | -------- | ------------------- | --------------------------------------- |\n * | `block` | N/A | Avoid PII completely |\n * | `redact` | No | General compliance, log sanitization |\n * | `mask` | No | Human readability, customer service UIs |\n * | `hash` | Yes (pseudonymous) | Analytics, debugging |\n *\n * @param piiType - Type of PII to detect. Can be a built-in type (`email`, `credit_card`, `ip`, `mac_address`, `url`) or a custom type name.\n * @param options - Configuration options\n * @param options.strategy - How to handle detected PII. Defaults to `\"redact\"`.\n * @param options.detector - Custom detector function or regex pattern string. If not provided, uses built-in detector for the `piiType`.\n * @param options.applyToInput - Whether to check user messages before model call. Defaults to `true`.\n * @param options.applyToOutput - Whether to check AI messages after model call. Defaults to `false`.\n * @param options.applyToToolResults - Whether to check tool result messages after tool execution. Defaults to `false`.\n *\n * @returns Middleware instance for use with `createAgent`\n *\n * @throws {PIIDetectionError} When PII is detected and strategy is `'block'`\n * @throws {Error} If `piiType` is not built-in and no detector is provided\n *\n * @example Basic usage\n * ```typescript\n * import { piiMiddleware } from \"langchain\";\n * import { createAgent } from \"langchain\";\n *\n * // Redact all emails in user input\n * const agent = createAgent({\n * model: \"openai:gpt-4\",\n * middleware: [\n * piiMiddleware(\"email\", { strategy: \"redact\" }),\n * ],\n * });\n * ```\n *\n * @example Different strategies for different PII types\n * ```typescript\n * const agent = createAgent({\n * model: \"openai:gpt-4o\",\n * middleware: [\n * piiMiddleware(\"credit_card\", { strategy: \"mask\" }),\n * piiMiddleware(\"url\", { strategy: \"redact\" }),\n * piiMiddleware(\"ip\", { strategy: \"hash\" }),\n * ],\n * });\n * ```\n *\n * @example Custom PII type with regex\n * ```typescript\n * const agent = createAgent({\n * model: \"openai:gpt-4\",\n * middleware: [\n * piiMiddleware(\"api_key\", {\n * detector: \"sk-[a-zA-Z0-9]{32}\",\n * strategy: \"block\",\n * }),\n * ],\n * });\n * ```\n *\n * @public\n */\nexport function piiMiddleware(\n piiType: BuiltInPIIType | string,\n options: {\n strategy?: PIIStrategy;\n detector?: Detector;\n applyToInput?: boolean;\n applyToOutput?: boolean;\n applyToToolResults?: boolean;\n } = {}\n) {\n const { strategy = \"redact\", detector } = options;\n const resolvedRule = resolveRedactionRule({\n piiType,\n strategy,\n detector,\n });\n\n const middlewareName = `PIIMiddleware[${resolvedRule.piiType}]`;\n\n return createMiddleware({\n name: middlewareName,\n contextSchema,\n beforeModel: async (state, runtime) => {\n const applyToInput =\n runtime.context.applyToInput ?? options.applyToInput ?? true;\n const applyToToolResults =\n runtime.context.applyToToolResults ??\n options.applyToToolResults ??\n false;\n\n if (!applyToInput && !applyToToolResults) {\n return;\n }\n\n const messages = state.messages;\n if (!messages || messages.length === 0) {\n return;\n }\n\n const newMessages = [...messages];\n let anyModified = false;\n\n // Check user input if enabled\n if (applyToInput) {\n // Get last user message\n let lastUserIdx: number | null = null;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (HumanMessage.isInstance(messages[i])) {\n lastUserIdx = i;\n break;\n }\n }\n\n if (lastUserIdx !== null) {\n const lastUserMsg = messages[lastUserIdx];\n if (lastUserMsg && lastUserMsg.content) {\n const content = String(lastUserMsg.content);\n const { content: newContent, matches } = processContent(\n content,\n resolvedRule\n );\n\n if (matches.length > 0) {\n newMessages[lastUserIdx] = new HumanMessage({\n content: newContent,\n id: lastUserMsg.id,\n name: lastUserMsg.name,\n });\n anyModified = true;\n }\n }\n }\n }\n\n // Check tool results if enabled\n if (applyToToolResults) {\n // Find the last AIMessage, then process all ToolMessage objects after it\n let lastAiIdx: number | null = null;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (AIMessage.isInstance(messages[i])) {\n lastAiIdx = i;\n break;\n }\n }\n\n if (lastAiIdx !== null) {\n // Get all tool messages after the last AI message\n for (let i = lastAiIdx + 1; i < messages.length; i++) {\n const msg = messages[i];\n if (ToolMessage.isInstance(msg)) {\n if (!msg.content) {\n continue;\n }\n\n const content = String(msg.content);\n const { content: newContent, matches } = processContent(\n content,\n resolvedRule\n );\n\n if (matches.length > 0) {\n newMessages[i] = new ToolMessage({\n content: newContent,\n id: msg.id,\n name: msg.name,\n tool_call_id: msg.tool_call_id,\n });\n anyModified = true;\n }\n }\n }\n }\n }\n\n if (anyModified) {\n return { messages: newMessages };\n }\n\n return;\n },\n afterModel: async (state, runtime) => {\n const applyToOutput =\n runtime.context.applyToOutput ?? options.applyToOutput ?? false;\n\n if (!applyToOutput) {\n return;\n }\n\n const messages = state.messages;\n if (!messages || messages.length === 0) {\n return;\n }\n\n // Get last AI message\n let lastAiIdx: number | null = null;\n let lastAiMsg: AIMessage | null = null;\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (AIMessage.isInstance(msg)) {\n lastAiMsg = msg;\n lastAiIdx = i;\n break;\n }\n }\n\n if (lastAiIdx === null || !lastAiMsg || !lastAiMsg.content) {\n return;\n }\n\n // Detect PII in message content\n const content = String(lastAiMsg.content);\n const { content: newContent, matches } = processContent(\n content,\n resolvedRule\n );\n\n if (matches.length === 0) {\n return;\n }\n\n // Create updated message\n const updatedMessage = new AIMessage({\n content: newContent,\n id: lastAiMsg.id,\n name: lastAiMsg.name,\n tool_calls: lastAiMsg.tool_calls,\n });\n\n // Return updated messages\n const newMessages = [...messages];\n newMessages[lastAiIdx] = updatedMessage;\n return { messages: newMessages };\n },\n });\n}\n"],"mappings":";;;;;;;;;AA4BA,IAAa,oBAAb,cAAuC,MAAM;CAC3C,YACE,SACA,SACA;AACA,QAAM,iBAAiB,QAAQ,SAAS,QAAQ,OAAO,gBAAgB;AAHvD,OAAA,UAAA;AACA,OAAA,UAAA;AAGhB,OAAK,OAAO;;;;;;AAuDhB,MAAM,gBAAgB;;;;AAKtB,MAAM,sBAAsB;;;;AAK5B,MAAM,aACJ;;;;AAKF,MAAM,sBAAsB;;;;AAK5B,MAAM,cAAc;;;;AAKpB,SAAS,UAAU,YAA6B;CAC9C,MAAM,SAAS,WAAW,QAAQ,OAAO,GAAG;CAC5C,IAAI,MAAM;CACV,IAAI,SAAS;AAEb,MAAK,IAAI,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;EAC3C,IAAI,QAAQ,SAAS,OAAO,IAAI,GAAG;AAEnC,MAAI,QAAQ;AACV,YAAS;AACT,OAAI,QAAQ,EACV,UAAS;;AAIb,SAAO;AACP,WAAS,CAAC;;AAGZ,QAAO,MAAM,OAAO;;;;;AAMtB,SAAS,qBAAqB,OAAmC;AAC/D,QAAO;EACL,MAAM,MAAM;EACZ,OAAO,MAAM,SAAS;EACtB,MAAM,MAAM,SAAS,KAAK,MAAM,GAAG;EACpC;;;;;AAMH,SAAgB,YAAY,SAA6B;CACvD,MAAM,UAAsB,EAAE;CAC9B,MAAM,QAAQ,IAAI,OAAO,cAAc;CACvC,IAAI;AAEJ,SAAQ,QAAQ,MAAM,KAAK,QAAQ,MAAM,KACvC,SAAQ,KAAK,qBAAqB,MAAM,CAAC;AAG3C,QAAO;;;;;AAMT,SAAgB,iBAAiB,SAA6B;CAC5D,MAAM,UAAsB,EAAE;CAC9B,MAAM,QAAQ,IAAI,OAAO,oBAAoB;CAC7C,IAAI;AAEJ,SAAQ,QAAQ,MAAM,KAAK,QAAQ,MAAM,MAAM;EAC7C,MAAM,aAAa,MAAM,GAAG,QAAQ,OAAO,GAAG;AAE9C,MACE,WAAW,UAAU,MACrB,WAAW,UAAU,MACrB,UAAU,WAAW,CAErB,SAAQ,KAAK,qBAAqB,MAAM,CAAC;;AAI7C,QAAO;;;;;AAMT,SAAgB,SAAS,SAA6B;CACpD,MAAM,UAAsB,EAAE;CAC9B,MAAM,QAAQ,IAAI,OAAO,WAAW;CACpC,IAAI;AAEJ,SAAQ,QAAQ,MAAM,KAAK,QAAQ,MAAM,MAAM;EAG7C,MAAM,QAFK,MAAM,GAEA,MAAM,IAAI;AAC3B,MACE,MAAM,WAAW,KACjB,MAAM,OAAO,SAAS;GACpB,MAAM,MAAM,SAAS,MAAM,GAAG;AAC9B,UAAO,OAAO,KAAK,OAAO;IAC1B,CAEF,SAAQ,KAAK,qBAAqB,MAAM,CAAC;;AAI7C,QAAO;;;;;AAMT,SAAgB,iBAAiB,SAA6B;CAC5D,MAAM,UAAsB,EAAE;CAC9B,MAAM,QAAQ,IAAI,OAAO,oBAAoB;CAC7C,IAAI;AAEJ,SAAQ,QAAQ,MAAM,KAAK,QAAQ,MAAM,KACvC,SAAQ,KAAK,qBAAqB,MAAM,CAAC;AAG3C,QAAO;;;;;AAMT,SAAgB,UAAU,SAA6B;CACrD,MAAM,UAAsB,EAAE;CAC9B,MAAM,QAAQ,IAAI,OAAO,YAAY;CACrC,IAAI;AAEJ,SAAQ,QAAQ,MAAM,KAAK,QAAQ,MAAM,KACvC,SAAQ,KAAK,qBAAqB,MAAM,CAAC;AAG3C,QAAO;;;;;AAMT,MAAM,qBAA0D;CAC9D,OAAO;CACP,aAAa;CACb,IAAI;CACJ,aAAa;CACb,KAAK;CACN;;;;AAKD,SAAgB,qBACd,QACuB;CACvB,IAAI;AAEJ,KAAI,OAAO,SACT,KAAI,OAAO,OAAO,aAAa,UAAU;EAEvC,MAAM,QAAQ,IAAI,OAAO,OAAO,UAAU,IAAI;AAC9C,cAAY,YAAoB;GAC9B,MAAM,UAAsB,EAAE;GAC9B,IAAI;GACJ,MAAM,YAAY,IAAI,OAAO,MAAM;AAEnC,WAAQ,QAAQ,UAAU,KAAK,QAAQ,MAAM,KAC3C,SAAQ,KAAK,qBAAqB,MAAM,CAAC;AAG3C,UAAO;;YAGA,OAAO,oBAAoB,OACpC,aAAY,YAAoB;AAE9B,MAAI,EAAE,OAAO,oBAAoB,QAC/B,OAAM,IAAI,MAAM,uBAAuB;EAEzC,MAAM,UAAsB,EAAE;EAC9B,IAAI;AACJ,UAAQ,QAAQ,OAAO,SAAS,KAAK,QAAQ,MAAM,KACjD,SAAQ,KAAK,qBAAqB,MAAM,CAAC;AAG3C,SAAO;;KAGT,YAAW,OAAO;MAEf;EAEL,MAAM,cAAc,OAAO;AAC3B,MAAI,CAAC,mBAAmB,aACtB,OAAM,IAAI,MACR,qBAAqB,OAAO,QAAQ,oBAAoB,OAAO,KAC7D,mBACD,CAAC,KAAK,KAAK,CAAC,iCACd;AAEH,aAAW,mBAAmB;;AAGhC,QAAO;EACL,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB;EACD;;;;;AAMH,SAAS,oBACP,SACA,SACA,SACQ;CACR,IAAI,SAAS;AAEb,MAAK,IAAI,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;EAC5C,MAAM,QAAQ,QAAQ;EACtB,MAAM,cAAc,aAAa,QAAQ,aAAa,CAAC;AACvD,WACE,OAAO,MAAM,GAAG,MAAM,MAAM,GAAG,cAAc,OAAO,MAAM,MAAM,IAAI;;AAExE,QAAO;;;;;AAMT,SAAS,kBACP,SACA,SACA,SACQ;CACR,IAAI,SAAS;AAEb,MAAK,IAAI,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;EAC5C,MAAM,QAAQ,QAAQ;EACtB,MAAM,OAAO,MAAM;EACnB,IAAI;AAEJ,MAAI,YAAY,cAId,UAAS,kBAFM,KAAK,QAAQ,OAAO,GACf,CAAC,MAAM,GACK;WACvB,YAAY,SAAS;GAE9B,MAAM,CAAC,OAAO,UAAU,KAAK,MAAM,IAAI;AACvC,OAAI,SAAS,OACX,UAAS,GAAG,MAAM,GAAG,MAAM;OAE3B,UAAS;SAEN;GAEL,MAAM,eAAe,KAAK,IAAI,GAAG,KAAK,OAAO;AAC7C,YAAS,GAAG,IAAI,OACd,KAAK,IAAI,GAAG,KAAK,SAAS,aAAa,CACxC,GAAG,KAAK,MAAM,CAAC,aAAa;;AAG/B,WAAS,OAAO,MAAM,GAAG,MAAM,MAAM,GAAG,SAAS,OAAO,MAAM,MAAM,IAAI;;AAE1E,QAAO;;;;;AAMT,SAAS,kBACP,SACA,SACA,SACQ;CACR,IAAI,SAAS;AAEb,MAAK,IAAI,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;EAC5C,MAAM,QAAQ,QAAQ;EAEtB,MAAM,cAAc,IAAI,QAAQ,SAAA,GAAA,2BAAA,QADZ,MAAM,KAAK,CAAC,MAAM,GAAG,EACG,CAAC;AAC7C,WACE,OAAO,MAAM,GAAG,MAAM,MAAM,GAAG,cAAc,OAAO,MAAM,MAAM,IAAI;;AAExE,QAAO;;;;;AAMT,SAAgB,cACd,SACA,SACA,UACA,SACQ;AACR,KAAI,QAAQ,WAAW,EACrB,QAAO;AAGT,SAAQ,UAAR;EACE,KAAK,QACH,OAAM,IAAI,kBAAkB,SAAS,QAAQ;EAC/C,KAAK,SACH,QAAO,oBAAoB,SAAS,SAAS,QAAQ;EACvD,KAAK,OACH,QAAO,kBAAkB,SAAS,SAAS,QAAQ;EACrD,KAAK,OACH,QAAO,kBAAkB,SAAS,SAAS,QAAQ;EACrD,QACE,OAAM,IAAI,MAAM,qBAAqB,WAAW;;;;;;AAOtD,MAAM,gBAAgBA,OAAAA,EAAE,OAAO;;;;CAI7B,cAAcA,OAAAA,EAAE,SAAS,CAAC,UAAU;;;;CAIpC,eAAeA,OAAAA,EAAE,SAAS,CAAC,UAAU;;;;CAIrC,oBAAoBA,OAAAA,EAAE,SAAS,CAAC,UAAU;CAC3C,CAAC;;;;AAOF,SAAS,eACP,SACA,MAC0C;CAC1C,MAAM,UAAU,KAAK,SAAS,QAAQ;AACtC,KAAI,QAAQ,WAAW,EACrB,QAAO;EAAE;EAAS,SAAS,EAAE;EAAE;AASjC,QAAO;EAAE,SANS,cAChB,SACA,SACA,KAAK,UACL,KAAK,QAEoB;EAAE;EAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsFxC,SAAgB,cACd,SACA,UAMI,EAAE,EACN;CACA,MAAM,EAAE,WAAW,UAAU,aAAa;CAC1C,MAAM,eAAe,qBAAqB;EACxC;EACA;EACA;EACD,CAAC;AAIF,QAAOC,mBAAAA,iBAAiB;EACtB,MAAM,iBAHgC,aAAa,QAAQ;EAI3D;EACA,aAAa,OAAO,OAAO,YAAY;GACrC,MAAM,eACJ,QAAQ,QAAQ,gBAAgB,QAAQ,gBAAgB;GAC1D,MAAM,qBACJ,QAAQ,QAAQ,sBAChB,QAAQ,sBACR;AAEF,OAAI,CAAC,gBAAgB,CAAC,mBACpB;GAGF,MAAM,WAAW,MAAM;AACvB,OAAI,CAAC,YAAY,SAAS,WAAW,EACnC;GAGF,MAAM,cAAc,CAAC,GAAG,SAAS;GACjC,IAAI,cAAc;AAGlB,OAAI,cAAc;IAEhB,IAAI,cAA6B;AACjC,SAAK,IAAI,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,IACxC,KAAIC,yBAAAA,aAAa,WAAW,SAAS,GAAG,EAAE;AACxC,mBAAc;AACd;;AAIJ,QAAI,gBAAgB,MAAM;KACxB,MAAM,cAAc,SAAS;AAC7B,SAAI,eAAe,YAAY,SAAS;MAEtC,MAAM,EAAE,SAAS,YAAY,YAAY,eADzB,OAAO,YAAY,QAE1B,EACP,aACD;AAED,UAAI,QAAQ,SAAS,GAAG;AACtB,mBAAY,eAAe,IAAIA,yBAAAA,aAAa;QAC1C,SAAS;QACT,IAAI,YAAY;QAChB,MAAM,YAAY;QACnB,CAAC;AACF,qBAAc;;;;;AAOtB,OAAI,oBAAoB;IAEtB,IAAI,YAA2B;AAC/B,SAAK,IAAI,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,IACxC,KAAIC,yBAAAA,UAAU,WAAW,SAAS,GAAG,EAAE;AACrC,iBAAY;AACZ;;AAIJ,QAAI,cAAc,KAEhB,MAAK,IAAI,IAAI,YAAY,GAAG,IAAI,SAAS,QAAQ,KAAK;KACpD,MAAM,MAAM,SAAS;AACrB,SAAIC,yBAAAA,YAAY,WAAW,IAAI,EAAE;AAC/B,UAAI,CAAC,IAAI,QACP;MAIF,MAAM,EAAE,SAAS,YAAY,YAAY,eADzB,OAAO,IAAI,QAElB,EACP,aACD;AAED,UAAI,QAAQ,SAAS,GAAG;AACtB,mBAAY,KAAK,IAAIA,yBAAAA,YAAY;QAC/B,SAAS;QACT,IAAI,IAAI;QACR,MAAM,IAAI;QACV,cAAc,IAAI;QACnB,CAAC;AACF,qBAAc;;;;;AAOxB,OAAI,YACF,QAAO,EAAE,UAAU,aAAa;;EAKpC,YAAY,OAAO,OAAO,YAAY;AAIpC,OAAI,EAFF,QAAQ,QAAQ,iBAAiB,QAAQ,iBAAiB,OAG1D;GAGF,MAAM,WAAW,MAAM;AACvB,OAAI,CAAC,YAAY,SAAS,WAAW,EACnC;GAIF,IAAI,YAA2B;GAC/B,IAAI,YAA8B;AAClC,QAAK,IAAI,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;IAC7C,MAAM,MAAM,SAAS;AACrB,QAAID,yBAAAA,UAAU,WAAW,IAAI,EAAE;AAC7B,iBAAY;AACZ,iBAAY;AACZ;;;AAIJ,OAAI,cAAc,QAAQ,CAAC,aAAa,CAAC,UAAU,QACjD;GAKF,MAAM,EAAE,SAAS,YAAY,YAAY,eADzB,OAAO,UAAU,QAExB,EACP,aACD;AAED,OAAI,QAAQ,WAAW,EACrB;GAIF,MAAM,iBAAiB,IAAIA,yBAAAA,UAAU;IACnC,SAAS;IACT,IAAI,UAAU;IACd,MAAM,UAAU;IAChB,YAAY,UAAU;IACvB,CAAC;GAGF,MAAM,cAAc,CAAC,GAAG,SAAS;AACjC,eAAY,aAAa;AACzB,UAAO,EAAE,UAAU,aAAa;;EAEnC,CAAC"}