@mcp-b/webmcp-local-relay 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -28
- package/dist/browser/embed.js +1 -1
- package/dist/browser/widget.html +1 -1
- package/dist/browser/widget.js +1 -1
- package/dist/{cli.js → cli.mjs} +11 -2
- package/dist/cli.mjs.map +1 -0
- package/dist/{index.d.ts → index.d.mts} +45 -3
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/{mcpRelayServer-rbay_DSr.js → mcpRelayServer-BL8kNwIK.mjs} +221 -109
- package/dist/mcpRelayServer-BL8kNwIK.mjs.map +1 -0
- package/package.json +34 -34
- package/skills/webmcp-local-relay/SKILL.md +2 -2
- package/dist/cli.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -3
- package/dist/mcpRelayServer-rbay_DSr.js.map +0 -1
- /package/dist/{cli.d.ts → cli.d.mts} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcpRelayServer-rbay_DSr.js","names":["options: CliOptions","payload: PersistedRelayPort","candidates: PortStrategyResult[]","DEFAULT_TOOL_INPUT_SCHEMA: Tool['inputSchema']","normalizedCandidate: Record<string, unknown>","now: () => number","providers: ToolProvider[]","result: AggregatedTool[]","provider","message: RelayToBrowserMessage","resolveOptions: { toolName: string; sourceId?: string; requestTabId?: string }","parsedJson: unknown","response: RelayServerToClientMessage","bufferedMessages: RelayServerToClientMessage[]","parsed: unknown","message: RelayClientToServerMessage","toolSourceMap: Record<string, string[]>","next: Record<string, unknown>","mcpCloseError: unknown","sources","parsed: URL","command: string","args: string[]","sources: SourceInfo[]"],"sources":["../src/cli-utils.ts","../src/portStrategy.ts","../src/protocol.ts","../src/naming.ts","../src/registry.ts","../src/schemas.ts","../src/bridgeServer.ts","../src/staticToolSchemas.ts","../src/mcpRelayServer.ts"],"sourcesContent":["/**\n * Parsed CLI options for relay startup.\n */\nexport interface CliOptions {\n host: string;\n port: number;\n portExplicitlySet: boolean;\n allowedOrigins: string[];\n label?: string;\n workspace?: string;\n relayId?: string;\n}\n\n/**\n * Parses supported CLI flags for relay startup.\n */\nexport function parseCliOptions(argv: string[]): CliOptions {\n const options: CliOptions = {\n host: '127.0.0.1',\n port: 9333,\n portExplicitlySet: false,\n // Permissive by default for zero-config local development — any browser page can connect.\n // Use --widget-origin to restrict to trusted origins on shared machines or in production.\n allowedOrigins: ['*'],\n };\n\n const readFlagValue = (flag: string, index: number): string => {\n const next = argv[index + 1];\n if (!next || next.startsWith('-')) {\n throw new Error(`Missing value for ${flag}`);\n }\n return next;\n };\n\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i];\n if (!token) {\n continue;\n }\n\n if (token === '--host' || token === '-H') {\n options.host = readFlagValue(token, i);\n i += 1;\n continue;\n }\n\n if (token === '--port' || token === '-p') {\n const raw = readFlagValue(token, i);\n i += 1;\n const value = Number.parseInt(raw, 10);\n if (!Number.isFinite(value) || value <= 0 || value > 65535) {\n throw new Error(`Invalid port \"${raw}\". Port must be a number between 1 and 65535.`);\n }\n options.port = value;\n options.portExplicitlySet = true;\n continue;\n }\n\n if (token === '--widget-origin' || token === '--allowed-origin' || token === '--ws-origin') {\n const raw = readFlagValue(token, i);\n i += 1;\n const split = raw\n .split(',')\n .map((part) => part.trim())\n .filter(Boolean);\n\n if (split.length > 0) {\n options.allowedOrigins = split;\n }\n continue;\n }\n\n if (token === '--label') {\n options.label = readFlagValue(token, i);\n i += 1;\n continue;\n }\n\n if (token === '--workspace') {\n options.workspace = readFlagValue(token, i);\n i += 1;\n continue;\n }\n\n if (token === '--relay-id') {\n options.relayId = readFlagValue(token, i);\n i += 1;\n continue;\n }\n\n if (token === '--help' || token === '-h') {\n printHelp();\n process.exit(0);\n }\n\n if (token.startsWith('-')) {\n process.stderr.write(`[webmcp-local-relay] warn: unrecognized argument \"${token}\"\\n`);\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: unrecognized argument \"${token}\" (positional arguments are not supported)\\n`\n );\n }\n }\n\n return options;\n}\n\n/**\n * Prints CLI usage to stderr.\n */\nexport function printHelp(): void {\n process.stderr.write(\n [\n 'webmcp-local-relay',\n '',\n 'Usage:',\n ' webmcp-local-relay [--host 127.0.0.1] [--port 9333] [--widget-origin https://myapp.example.com]',\n '',\n 'Options:',\n ' --host, -H Bind host for local websocket relay (default: 127.0.0.1)',\n ' --port, -p Preferred root port for the local relay cluster (default: 9333)',\n ' --widget-origin Allowed host page origin(s), comma-separated (default: *)',\n ' --allowed-origin Alias for --widget-origin',\n ' --ws-origin Alias for --widget-origin',\n ' --label Human-readable relay label reported during discovery',\n ' --workspace Optional workspace name reported during discovery',\n ' --relay-id Stable relay identifier reported during discovery',\n ' --help, -h Show help',\n '',\n ].join('\\n')\n );\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nexport const DEFAULT_RELAY_PORT = 9333;\nexport const DEFAULT_RELAY_PORT_RANGE_END = 9348;\nexport const RELAY_PORT_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;\n\ninterface PersistedRelayPort {\n host: string;\n port: number;\n updatedAt: string;\n}\n\nexport interface PortStrategyOptions {\n fixedPort?: number;\n defaultPort: number;\n rangeEnd: number;\n host: string;\n persistPath?: string;\n}\n\nexport interface PortStrategyResult {\n port: number;\n wasFixed: boolean;\n fromCache: boolean;\n}\n\nexport function defaultRelayPortPersistPath(): string {\n return join(homedir(), '.webmcp', 'relay-port.json');\n}\n\nexport async function persistPort(\n port: number,\n path = defaultRelayPortPersistPath(),\n host = '127.0.0.1',\n now = Date.now()\n): Promise<void> {\n const payload: PersistedRelayPort = {\n port,\n host,\n updatedAt: new Date(now).toISOString(),\n };\n\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(payload, null, 2)}\\n`, 'utf8');\n}\n\nexport async function readPersistedPort(\n path = defaultRelayPortPersistPath(),\n options: {\n expectedHost?: string;\n maxAgeMs?: number;\n now?: number;\n } = {}\n): Promise<number | null> {\n try {\n const raw = await readFile(path, 'utf8');\n const parsed = JSON.parse(raw) as Partial<PersistedRelayPort>;\n if (\n typeof parsed.port !== 'number' ||\n !Number.isInteger(parsed.port) ||\n parsed.port < 1 ||\n parsed.port > 65535\n ) {\n return null;\n }\n if (typeof parsed.host !== 'string' || parsed.host.length === 0) {\n return null;\n }\n if (options.expectedHost && parsed.host !== options.expectedHost) {\n return null;\n }\n\n const maxAgeMs = options.maxAgeMs ?? RELAY_PORT_CACHE_MAX_AGE_MS;\n const now = options.now ?? Date.now();\n const updatedAtMs =\n typeof parsed.updatedAt === 'string' ? Date.parse(parsed.updatedAt) : Number.NaN;\n\n if (!Number.isFinite(updatedAtMs) || now - updatedAtMs > maxAgeMs) {\n return null;\n }\n\n return parsed.port;\n } catch {\n return null;\n }\n}\n\nexport async function buildPortCandidates(\n options: PortStrategyOptions\n): Promise<PortStrategyResult[]> {\n const {\n defaultPort,\n fixedPort,\n host,\n persistPath = defaultRelayPortPersistPath(),\n rangeEnd,\n } = options;\n\n if (fixedPort !== undefined) {\n return [{ port: fixedPort, wasFixed: true, fromCache: false }];\n }\n\n const cachedPort = await readPersistedPort(persistPath, { expectedHost: host });\n const seen = new Set<number>();\n const candidates: PortStrategyResult[] = [];\n\n const pushCandidate = (port: number, fromCache: boolean): void => {\n if (port < 1 || port > 65535 || seen.has(port)) {\n return;\n }\n seen.add(port);\n candidates.push({ port, wasFixed: false, fromCache });\n };\n\n if (cachedPort !== null && cachedPort >= defaultPort && cachedPort <= rangeEnd) {\n pushCandidate(cachedPort, true);\n }\n\n pushCandidate(defaultPort, false);\n\n for (let port = defaultPort + 1; port <= rangeEnd; port += 1) {\n pushCandidate(port, false);\n }\n\n return candidates;\n}\n","import {\n CallToolRequestParamsSchema,\n type CallToolResult,\n CallToolResultSchema,\n type Tool,\n type ToolAnnotations,\n ToolAnnotationsSchema,\n ToolSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod/v4';\n\n/**\n * SDK-derived canonical tool schema used internally by the relay.\n */\nexport const NormalizedToolSchema = ToolSchema.extend({\n name: ToolSchema.shape.name.min(1),\n});\n\n/**\n * Permissive inbound tool shape from browser/widget payloads.\n *\n * Only enforces a non-empty name at ingest. All other fields are normalized\n * against SDK schemas by {@link normalizeInboundTool}.\n */\nexport const InboundToolSchema = z.object({ name: z.string().min(1) }).passthrough();\n\n/**\n * SDK-derived argument schema for tool invocation payloads.\n */\nexport const RelayInvokeArgsSchema = CallToolRequestParamsSchema.shape.arguments;\n\n/**\n * Default input schema applied when inbound payload omits or provides\n * a non-object input schema.\n */\nexport const DEFAULT_TOOL_INPUT_SCHEMA: Tool['inputSchema'] = {\n type: 'object',\n properties: {},\n};\n\n/**\n * Canonical normalized relay tool shape.\n */\nexport type RelayTool = z.infer<typeof NormalizedToolSchema>;\n\n/**\n * Canonical relay tool annotations shape.\n */\nexport type RelayToolAnnotations = ToolAnnotations;\n\n/**\n * Canonical relay call result shape.\n */\nexport type RelayCallToolResult = CallToolResult;\n\n/**\n * Invocation argument object shape derived from MCP SDK request params.\n */\nexport type RelayInvokeArgs = Exclude<z.infer<typeof RelayInvokeArgsSchema>, undefined>;\n\n/**\n * Normalizes permissive inbound tool payloads into SDK-compliant Tool objects.\n *\n * Invalid optional metadata (description, output schema, annotations, etc.)\n * is dropped. Invalid/missing inputSchema falls back to an empty object schema.\n */\nexport function normalizeInboundTool(inbound: z.infer<typeof InboundToolSchema>): RelayTool {\n const inputSchemaParsed = ToolSchema.shape.inputSchema.safeParse(inbound.inputSchema);\n const outputSchemaParsed = ToolSchema.shape.outputSchema.safeParse(inbound.outputSchema);\n const annotationsParsed = ToolAnnotationsSchema.safeParse(inbound.annotations);\n\n const normalizedCandidate: Record<string, unknown> = {\n name: inbound.name,\n inputSchema: inputSchemaParsed.success ? inputSchemaParsed.data : DEFAULT_TOOL_INPUT_SCHEMA,\n };\n\n if (typeof inbound.description === 'string') {\n normalizedCandidate.description = inbound.description;\n }\n if (typeof inbound.title === 'string') {\n normalizedCandidate.title = inbound.title;\n }\n if (outputSchemaParsed.success && outputSchemaParsed.data !== undefined) {\n normalizedCandidate.outputSchema = outputSchemaParsed.data;\n }\n if (annotationsParsed.success && annotationsParsed.data !== undefined) {\n normalizedCandidate.annotations = annotationsParsed.data;\n }\n\n const normalizedParsed = NormalizedToolSchema.safeParse(normalizedCandidate);\n if (normalizedParsed.success) {\n return normalizedParsed.data;\n }\n\n return {\n name: inbound.name,\n inputSchema: DEFAULT_TOOL_INPUT_SCHEMA,\n };\n}\n\nexport { CallToolRequestParamsSchema, CallToolResultSchema, ToolAnnotationsSchema, ToolSchema };\n","/**\n * Maximum tool name length supported across MCP clients.\n */\nconst MAX_MCP_TOOL_NAME_LENGTH = 128;\n\n/**\n * Number of tab-id characters appended when disambiguation is required.\n */\nconst TAB_ID_DISAMBIGUATION_LENGTH = 4;\n\n/**\n * Converts arbitrary text into MCP-safe identifier characters.\n */\nexport function sanitizeName(value: string): string {\n return value.replace(/[^a-zA-Z0-9_]/g, '_');\n}\n\n/**\n * Extracts and sanitizes a domain label from an origin or URL.\n */\nexport function extractSanitizedDomain(originOrUrl?: string): string {\n if (!originOrUrl) {\n return 'unknown';\n }\n\n try {\n const parsed = new URL(originOrUrl);\n if (!parsed.hostname) {\n return 'unknown';\n }\n\n const isLocalhost =\n parsed.hostname === 'localhost' ||\n parsed.hostname === '127.0.0.1' ||\n parsed.hostname === '[::1]';\n\n const domain = isLocalhost ? `localhost_${parsed.port || '80'}` : parsed.hostname;\n return sanitizeName(domain);\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Builds a public tool name for MCP registration.\n *\n * Names can include a short tab-id suffix when multiple tabs publish the same\n * original tool name.\n */\nexport function buildPublicToolName(options: {\n originalToolName: string;\n tabId?: string;\n disambiguate?: boolean;\n}): string {\n const safeName = sanitizeName(options.originalToolName);\n\n if (!options.disambiguate || !options.tabId) {\n return safeName.slice(0, MAX_MCP_TOOL_NAME_LENGTH);\n }\n\n const shortTab = sanitizeName(options.tabId).slice(0, TAB_ID_DISAMBIGUATION_LENGTH);\n const suffix = `_${shortTab}`;\n const base = `${safeName}${suffix}`;\n\n if (base.length <= MAX_MCP_TOOL_NAME_LENGTH) {\n return base;\n }\n\n const available = MAX_MCP_TOOL_NAME_LENGTH - suffix.length;\n return `${safeName.slice(0, Math.max(1, available))}${suffix}`;\n}\n","import { buildPublicToolName } from './naming.js';\nimport type { RelayTool } from './protocol.js';\nimport type { BrowserHelloMessage } from './schemas.js';\n\n/**\n * Internal metadata tracked for each active browser source.\n */\ninterface SourceMetadata {\n sourceId: string;\n tabId: string;\n origin: string | undefined;\n url: string | undefined;\n title: string | undefined;\n iconUrl: string | undefined;\n connectedAt: number;\n lastSeenAt: number;\n}\n\n/**\n * Public source metadata exposed by registry APIs.\n */\nexport interface SourceInfo extends SourceMetadata {\n toolCount: number;\n}\n\n/**\n * Aggregated relayed tool metadata across one or more sources.\n *\n * Extends {@link RelayTool} with relay-specific fields so new SDK tool\n * properties are automatically inherited without manual duplication.\n */\nexport interface AggregatedTool extends RelayTool {\n originalName: string;\n sources: SourceInfo[];\n}\n\n/**\n * Internal record linking a source to a concrete tool implementation.\n */\ninterface ToolProvider {\n sourceId: string;\n tabId: string;\n tool: RelayTool;\n publicToolName: string;\n}\n\n/**\n * Invocation target selected by the registry.\n */\nexport interface ResolvedInvocation {\n connectionId: string;\n tool: RelayTool;\n publicToolName: string;\n}\n\n/**\n * Error thrown when a source attempts to register tools before `hello`.\n */\nexport class HelloRequiredError extends Error {\n readonly connectionId: string;\n\n constructor(connectionId: string) {\n super(`Connection ${connectionId} must send hello before tools`);\n this.name = 'HelloRequiredError';\n this.connectionId = connectionId;\n }\n}\n\n/**\n * In-memory source and tool registry used by the relay bridge.\n */\nexport class RelayRegistry {\n private readonly sourceByConnectionId = new Map<string, SourceMetadata>();\n private readonly toolsByConnectionId = new Map<string, ToolProvider[]>();\n private readonly providersByPublicToolName = new Map<string, ToolProvider[]>();\n\n /**\n * @param now Time provider used for recency ordering.\n */\n constructor(private readonly now: () => number = () => Date.now()) {}\n\n /**\n * Upserts source metadata from a browser `hello` message.\n *\n * `sourceId` currently matches `connectionId`, but remains conceptually distinct\n * so stable source identity can be introduced in the future.\n */\n upsertSource(connectionId: string, hello: BrowserHelloMessage): void {\n const now = this.now();\n const existing = this.sourceByConnectionId.get(connectionId);\n\n this.sourceByConnectionId.set(connectionId, {\n sourceId: connectionId,\n tabId: hello.tabId,\n origin: hello.origin ?? existing?.origin,\n url: hello.url ?? existing?.url,\n title: hello.title ?? existing?.title,\n iconUrl: hello.iconUrl ?? existing?.iconUrl,\n connectedAt: existing?.connectedAt ?? now,\n lastSeenAt: now,\n });\n }\n\n /**\n * Replaces the full tool set for a source connection.\n */\n registerTools(connectionId: string, tools: RelayTool[]): void {\n const source = this.sourceByConnectionId.get(connectionId);\n if (!source) {\n throw new HelloRequiredError(connectionId);\n }\n\n this.touchConnection(connectionId);\n this.removeConnectionTools(connectionId);\n\n const providers: ToolProvider[] = tools.map((tool) => ({\n sourceId: connectionId,\n tabId: source.tabId,\n tool,\n publicToolName: '',\n }));\n\n this.toolsByConnectionId.set(connectionId, providers);\n this.rebuildPublicNames();\n }\n\n /**\n * Removes a source and all of its tool registrations.\n */\n removeConnection(connectionId: string): void {\n this.removeConnectionTools(connectionId);\n this.sourceByConnectionId.delete(connectionId);\n this.rebuildPublicNames();\n }\n\n /**\n * Marks a source as recently seen without mutating identity metadata.\n */\n touchConnection(connectionId: string): void {\n const source = this.sourceByConnectionId.get(connectionId);\n if (!source) {\n return;\n }\n\n source.lastSeenAt = this.now();\n }\n\n /**\n * Lists active sources that currently publish at least one tool.\n */\n listSources(): SourceInfo[] {\n return Array.from(this.sourceByConnectionId.values())\n .map((source) => this.toSourceInfo(source))\n .filter((source) => source.toolCount > 0)\n .sort((a, b) => this.compareRecency(b.lastSeenAt, a.lastSeenAt, b.sourceId, a.sourceId));\n }\n\n /**\n * Lists aggregated tools ordered by public tool name.\n */\n listTools(): AggregatedTool[] {\n const result: AggregatedTool[] = [];\n\n for (const [publicToolName, providers] of this.providersByPublicToolName.entries()) {\n const rankedProviders = this.sortProvidersByRecency(providers);\n const primaryProvider = rankedProviders[0];\n if (!primaryProvider) {\n continue;\n }\n\n const sources = rankedProviders\n .map((provider) => {\n const source = this.sourceByConnectionId.get(provider.sourceId);\n return source ? this.toSourceInfo(source) : undefined;\n })\n .filter((source): source is SourceInfo => Boolean(source));\n\n result.push({\n ...primaryProvider.tool,\n name: publicToolName,\n originalName: primaryProvider.tool.name,\n sources,\n });\n }\n\n return result.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * Resolves a tool invocation to a concrete source provider.\n *\n * Resolution order:\n * 1. `sourceId`: exact source id, then fallback to tab id.\n * 2. `requestTabId`: strict tab id match with no fallback.\n * 3. Default: most recently seen provider.\n */\n resolveInvocation(options: {\n toolName: string;\n sourceId?: string;\n requestTabId?: string;\n }): ResolvedInvocation | null {\n const providers = this.providersByPublicToolName.get(options.toolName);\n if (!providers || providers.length === 0) {\n return null;\n }\n\n const rankedProviders = this.sortProvidersByRecency(providers);\n\n if (options.sourceId) {\n const byConnection = rankedProviders.find(\n (provider) => provider.sourceId === options.sourceId\n );\n if (byConnection) {\n return {\n connectionId: byConnection.sourceId,\n tool: byConnection.tool,\n publicToolName: byConnection.publicToolName,\n };\n }\n\n const byTabId = rankedProviders.find((provider) => provider.tabId === options.sourceId);\n if (byTabId) {\n return {\n connectionId: byTabId.sourceId,\n tool: byTabId.tool,\n publicToolName: byTabId.publicToolName,\n };\n }\n\n return null;\n }\n\n if (options.requestTabId) {\n const byRequestTab = rankedProviders.find(\n (provider) => provider.tabId === options.requestTabId\n );\n if (!byRequestTab) {\n return null;\n }\n\n return {\n connectionId: byRequestTab.sourceId,\n tool: byRequestTab.tool,\n publicToolName: byRequestTab.publicToolName,\n };\n }\n\n const provider = rankedProviders[0];\n if (!provider) {\n return null;\n }\n\n return {\n connectionId: provider.sourceId,\n tool: provider.tool,\n publicToolName: provider.publicToolName,\n };\n }\n\n /**\n * Converts internal source metadata to public source info.\n */\n private toSourceInfo(source: SourceMetadata): SourceInfo {\n return {\n ...source,\n toolCount: this.toolsByConnectionId.get(source.sourceId)?.length ?? 0,\n };\n }\n\n /**\n * Removes all tool providers for a connection.\n */\n private removeConnectionTools(connectionId: string): void {\n this.toolsByConnectionId.delete(connectionId);\n }\n\n /**\n * Rebuilds public tool names after source set changes.\n *\n * When the same original tool name appears in multiple tabs, names are\n * disambiguated with a short tab suffix.\n */\n private rebuildPublicNames(): void {\n const byOriginalName = new Map<string, ToolProvider[]>();\n for (const providers of this.toolsByConnectionId.values()) {\n for (const provider of providers) {\n const key = provider.tool.name;\n const group = byOriginalName.get(key) ?? [];\n group.push(provider);\n byOriginalName.set(key, group);\n }\n }\n\n this.providersByPublicToolName.clear();\n\n for (const [, providers] of byOriginalName) {\n const uniqueTabIds = new Set(providers.map((p) => p.tabId));\n const disambiguate = uniqueTabIds.size > 1;\n\n for (const provider of providers) {\n provider.publicToolName = buildPublicToolName({\n originalToolName: provider.tool.name,\n tabId: provider.tabId,\n disambiguate,\n });\n\n const existing = this.providersByPublicToolName.get(provider.publicToolName) ?? [];\n existing.push(provider);\n this.providersByPublicToolName.set(\n provider.publicToolName,\n this.sortProvidersByRecency(existing)\n );\n }\n }\n }\n\n /**\n * Returns providers sorted by source recency.\n */\n private sortProvidersByRecency(providers: ToolProvider[]): ToolProvider[] {\n return providers.slice().sort((a, b) => {\n const aSource = this.sourceByConnectionId.get(a.sourceId);\n const bSource = this.sourceByConnectionId.get(b.sourceId);\n const aLastSeen = aSource?.lastSeenAt ?? 0;\n const bLastSeen = bSource?.lastSeenAt ?? 0;\n\n return this.compareRecency(bLastSeen, aLastSeen, b.sourceId, a.sourceId);\n });\n }\n\n /**\n * Compares two source recency tuples and stabilizes order by source id.\n */\n private compareRecency(\n leftLastSeen: number,\n rightLastSeen: number,\n leftId: string,\n rightId: string\n ): number {\n const timeDiff = leftLastSeen - rightLastSeen;\n if (timeDiff !== 0) {\n return timeDiff;\n }\n return leftId.localeCompare(rightId);\n }\n}\n","import { z } from 'zod/v4';\nimport {\n CallToolResultSchema,\n InboundToolSchema,\n NormalizedToolSchema,\n normalizeInboundTool,\n RelayInvokeArgsSchema,\n type RelayTool,\n} from './protocol.js';\n\n/**\n * Schema for source identity bootstrap message.\n */\nexport const BrowserHelloMessageSchema = z.object({\n type: z.literal('hello'),\n tabId: z.string().min(1),\n origin: z.string().optional(),\n url: z.string().optional(),\n title: z.string().optional(),\n iconUrl: z.string().optional(),\n});\n\n/**\n * Schema for full tool-list synchronization message.\n *\n * Inbound tools are permissively parsed then normalized to SDK Tool shape.\n */\nexport const BrowserToolsListMessageSchema = z.object({\n type: z.literal('tools/list'),\n tools: z\n .array(InboundToolSchema)\n .transform((tools): RelayTool[] => tools.map(normalizeInboundTool)),\n});\n\n/**\n * Schema for tool-set replacement notification pushed after initial registration.\n *\n * Processing is identical to `tools/list` — the full tool set replaces any\n * previously registered tools — but the distinct type signals a dynamic update\n * rather than an initial handshake.\n */\nexport const BrowserToolsChangedMessageSchema = z.object({\n type: z.literal('tools/changed'),\n tools: z\n .array(InboundToolSchema)\n .transform((tools): RelayTool[] => tools.map(normalizeInboundTool)),\n});\n\n/**\n * Schema for invocation response payloads sent by browser sources.\n *\n * `result` remains permissive and is normalized later to preserve runtime\n * behavior for malformed tool responses.\n */\nexport const BrowserToolResultMessageSchema = z.object({\n type: z.literal('result'),\n callId: z.string().min(1),\n result: z.unknown(),\n});\n\n/**\n * Schema for browser heartbeat acknowledgement.\n */\nexport const BrowserPongMessageSchema = z.object({\n type: z.literal('pong'),\n});\n\n/**\n * Union schema for all browser-to-relay protocol messages.\n */\nexport const BrowserToRelayMessageSchema = z.discriminatedUnion('type', [\n BrowserHelloMessageSchema,\n BrowserToolsListMessageSchema,\n BrowserToolsChangedMessageSchema,\n BrowserToolResultMessageSchema,\n BrowserPongMessageSchema,\n]);\n\n/**\n * Browser-to-relay message payload.\n */\nexport type BrowserToRelayMessage = z.infer<typeof BrowserToRelayMessageSchema>;\n\n/**\n * Browser source bootstrap message.\n */\nexport type BrowserHelloMessage = z.infer<typeof BrowserHelloMessageSchema>;\n\n/**\n * Shared relay identity used for discovery and attach verification.\n */\nexport const RelayDescriptorSchema = z.object({\n host: z.string().min(1),\n instanceId: z.string().min(1),\n label: z.string().min(1).optional(),\n port: z.number().int().min(1).max(65535),\n relayId: z.string().min(1).optional(),\n workspace: z.string().min(1).optional(),\n});\n\n/**\n * Relay identity used for discovery and attach verification.\n */\nexport type RelayDescriptor = z.infer<typeof RelayDescriptorSchema>;\n\n/**\n * Schema for server hello messages sent immediately after WebSocket connect.\n */\nexport const ServerHelloMessageSchema = z.object({\n type: z.literal('server-hello'),\n service: z.literal('webmcp-local-relay'),\n version: z.literal(1),\n ...RelayDescriptorSchema.shape,\n});\n\n/**\n * Server hello payload.\n */\nexport type ServerHelloMessage = z.infer<typeof ServerHelloMessageSchema>;\n\n/**\n * Schema for relay invocation messages sent to browser sources.\n */\nexport const RelayInvokeMessageSchema = z.object({\n type: z.literal('invoke'),\n callId: z.string().min(1),\n toolName: z.string().min(1),\n args: RelayInvokeArgsSchema,\n});\n\n/**\n * Schema for relay heartbeat messages sent to browser sources.\n */\nexport const RelayPingMessageSchema = z.object({\n type: z.literal('ping'),\n});\n\n/**\n * Schema for relay reload messages sent to browser sources.\n */\nexport const RelayReloadMessageSchema = z.object({\n type: z.literal('reload'),\n});\n\n/**\n * Union schema for all relay-to-browser protocol messages.\n */\nexport const RelayToBrowserMessageSchema = z.discriminatedUnion('type', [\n ServerHelloMessageSchema,\n RelayInvokeMessageSchema,\n RelayPingMessageSchema,\n RelayReloadMessageSchema,\n]);\n\n/**\n * Relay-to-browser message payload.\n */\nexport type RelayToBrowserMessage = z.infer<typeof RelayToBrowserMessageSchema>;\n\n/**\n * Relay-to-relay protocol (client mode <-> server mode).\n */\n\n/**\n * Schema for source metadata transmitted in relay-to-relay messages.\n */\nexport const RelaySourceInfoSchema = z.object({\n sourceId: z.string(),\n tabId: z.string(),\n origin: z.string().optional(),\n url: z.string().optional(),\n title: z.string().optional(),\n iconUrl: z.string().optional(),\n connectedAt: z.number(),\n lastSeenAt: z.number(),\n toolCount: z.number(),\n});\n\n/**\n * Source metadata transmitted in relay-to-relay messages.\n */\nexport type RelaySourceInfo = z.infer<typeof RelaySourceInfoSchema>;\n\n/**\n * Schema for relay client identification message.\n */\nexport const RelayClientHelloSchema = z.object({\n type: z.literal('relay/hello'),\n});\n\n/**\n * Schema for relay client tool list request.\n */\nexport const RelayClientListToolsSchema = z.object({\n type: z.literal('relay/list-tools'),\n});\n\n/**\n * Schema for relay client tool invocation request.\n */\nexport const RelayClientInvokeSchema = z.object({\n type: z.literal('relay/invoke'),\n callId: z.string().min(1),\n toolName: z.string().min(1),\n args: RelayInvokeArgsSchema,\n});\n\n/**\n * Union schema for all relay-client-to-server messages.\n */\nexport const RelayClientToServerMessageSchema = z.discriminatedUnion('type', [\n RelayClientHelloSchema,\n RelayClientListToolsSchema,\n RelayClientInvokeSchema,\n]);\n\n/**\n * Relay client to server message payload.\n */\nexport type RelayClientToServerMessage = z.infer<typeof RelayClientToServerMessageSchema>;\n\nconst RelayToolsPayloadFields = {\n tools: z.array(NormalizedToolSchema),\n sources: z.array(RelaySourceInfoSchema).optional().default([]),\n toolSourceMap: z.record(z.string(), z.array(z.string())).optional().default({}),\n};\n\n/**\n * Schema for relay server tool list response.\n */\nexport const RelayServerToolsSchema = z.object({\n type: z.literal('relay/tools'),\n ...RelayToolsPayloadFields,\n});\n\n/**\n * Schema for relay server invocation result response.\n */\nexport const RelayServerResultSchema = z.object({\n type: z.literal('relay/result'),\n callId: z.string().min(1),\n result: CallToolResultSchema,\n});\n\n/**\n * Schema for relay server push notification when tools change.\n */\nexport const RelayServerToolsChangedSchema = z.object({\n type: z.literal('relay/tools-changed'),\n ...RelayToolsPayloadFields,\n});\n\n/**\n * Union schema for all relay-server-to-client messages.\n */\nexport const RelayServerToClientMessageSchema = z.discriminatedUnion('type', [\n ServerHelloMessageSchema,\n RelayServerToolsSchema,\n RelayServerResultSchema,\n RelayServerToolsChangedSchema,\n]);\n\n/**\n * Relay server to client message payload.\n */\nexport type RelayServerToClientMessage = z.infer<typeof RelayServerToClientMessageSchema>;\n","import { randomUUID } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\n\nimport WebSocket, { WebSocketServer } from 'ws';\nimport {\n buildPortCandidates,\n DEFAULT_RELAY_PORT,\n DEFAULT_RELAY_PORT_RANGE_END,\n defaultRelayPortPersistPath,\n persistPort,\n} from './portStrategy.js';\nimport {\n CallToolResultSchema,\n type RelayCallToolResult,\n type RelayInvokeArgs,\n type RelayTool,\n} from './protocol.js';\nimport { type AggregatedTool, HelloRequiredError, RelayRegistry } from './registry.js';\nimport {\n BrowserToRelayMessageSchema,\n type RelayClientToServerMessage,\n RelayClientToServerMessageSchema,\n type RelayServerToClientMessage,\n RelayServerToClientMessageSchema,\n type RelaySourceInfo,\n type RelayToBrowserMessage,\n type ServerHelloMessage,\n} from './schemas.js';\n\nconst RELAY_BROWSER_PROTOCOL = 'webmcp.v1';\nconst RELAY_DISCOVERY_PROTOCOL = 'webmcp-discovery.v1';\nconst RELAY_INTERNAL_PROTOCOL = 'webmcp-relay.v1';\nconst RELAY_SERVER_MESSAGE_TIMEOUT_MS = 750;\nconst HEARTBEAT_INTERVAL_MS = 15_000;\nconst HEARTBEAT_DEAD_THRESHOLD_MS = 25_000;\nconst SUPPORTED_SUBPROTOCOLS = new Set([\n RELAY_BROWSER_PROTOCOL,\n RELAY_DISCOVERY_PROTOCOL,\n RELAY_INTERNAL_PROTOCOL,\n]);\n\n/**\n * In-flight relay invocation waiting for a browser `result` message.\n */\ninterface PendingInvocation {\n callId: string;\n connectionId: string;\n timeoutId: ReturnType<typeof setTimeout>;\n resolve: (result: RelayCallToolResult) => void;\n reject: (error: Error) => void;\n}\n\n/**\n * Runtime options for {@link RelayBridgeServer}.\n */\nexport interface RelayBridgeServerOptions {\n /**\n * Network interface used by the local WebSocket server.\n * @defaultValue `\"127.0.0.1\"`\n */\n host?: string;\n /**\n * Preferred WebSocket port for browser widget connections.\n * @defaultValue `9333`\n */\n port?: number;\n /**\n * Whether the preferred port came from an explicit CLI/API override.\n * Explicit ports fail if occupied by a non-relay process.\n * @defaultValue `false`\n */\n portExplicitlySet?: boolean;\n /**\n * Inclusive upper bound for automatic port discovery.\n * @defaultValue `9348`\n */\n portRangeEnd?: number;\n /**\n * File path used to cache the last successful relay port.\n * @defaultValue `~/.webmcp/relay-port.json`\n */\n persistPath?: string;\n /**\n * Allowed host page origins reported by browser `hello` messages.\n * Use `[\"*\"]` to allow all origins.\n *\n * Permissive by default for zero-config developer experience — any browser\n * page can connect and register tools without additional setup. For production\n * or shared machines, restrict to trusted host origins via the\n * `--widget-origin` CLI flag or by passing explicit origins here.\n *\n * @defaultValue `[\"*\"]`\n */\n allowedOrigins?: string[];\n /**\n * Maximum WebSocket payload size in bytes.\n * @defaultValue `1000000`\n */\n maxPayloadBytes?: number;\n /**\n * Timeout used for browser tool invocations.\n * @defaultValue `25000`\n */\n invokeTimeoutMs?: number;\n /**\n * Human-readable relay label reported in discovery handshakes.\n */\n label?: string;\n /**\n * Optional workspace name reported in discovery handshakes.\n */\n workspace?: string;\n /**\n * Stable relay identifier used to select between multiple relays.\n */\n relayId?: string;\n}\n\n/**\n * WebSocket relay between browser widget frames and MCP server calls.\n *\n * Operates in two modes:\n * - **server** (default): Runs a WebSocket server, accepts browser and relay\n * client connections.\n * - **client** (fallback on EADDRINUSE): Connects as a WebSocket client to an\n * existing server relay and proxies tool operations through it.\n */\nexport class RelayBridgeServer extends EventEmitter {\n /**\n * Registry for connected sources and aggregated tool definitions.\n * Only actively used in server mode; remains empty when operating as a client.\n */\n readonly registry: RelayRegistry;\n\n private readonly host: string;\n private readonly preferredPort: number;\n private desiredPort: number;\n private readonly portExplicitlySet: boolean;\n private readonly portRangeEnd: number;\n private readonly persistPath: string;\n private readonly allowedOrigins: string[];\n private readonly maxPayloadBytes: number;\n private readonly invokeTimeoutMs: number;\n private readonly label: string | undefined;\n private readonly workspace: string | undefined;\n private readonly relayId: string | undefined;\n private readonly instanceId: string;\n\n private wss: WebSocketServer | null = null;\n private readonly socketByConnectionId = new Map<string, WebSocket>();\n private readonly pendingInvocations = new Map<string, PendingInvocation>();\n private readonly relayClientConnectionIds = new Set<string>();\n private readonly heartbeatIntervalByConnectionId = new Map<\n string,\n ReturnType<typeof setInterval>\n >();\n private readonly lastPongByConnectionId = new Map<string, number>();\n private readonly onStateChangedPushRelay = () => {\n this.pushToolsToRelayClients();\n };\n\n private _mode: 'server' | 'client' = 'server';\n private clientSocket: WebSocket | null = null;\n private clientReconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private clientReconnectDelay = 500;\n /**\n * Maximum delay for relay-to-relay reconnection backoff in client mode.\n */\n private readonly clientMaxReconnectDelay = 3_000;\n private readonly clientMaxReconnectAttempts = 100;\n private clientReconnectAttempts = 0;\n private readonly clientPendingInvocations = new Map<string, PendingInvocation>();\n private clientTools: RelayTool[] = [];\n private clientSources: RelaySourceInfo[] = [];\n private clientToolSourceMap: Record<string, string[]> = {};\n private stopping = false;\n\n /**\n * Creates a relay bridge server instance.\n */\n constructor(options: RelayBridgeServerOptions = {}, registry?: RelayRegistry) {\n super();\n this.registry = registry ?? new RelayRegistry();\n\n this.host = options.host ?? '127.0.0.1';\n this.preferredPort = options.port ?? DEFAULT_RELAY_PORT;\n this.desiredPort = this.preferredPort;\n this.portExplicitlySet = options.portExplicitlySet ?? false;\n this.portRangeEnd =\n options.portRangeEnd ?? Math.max(DEFAULT_RELAY_PORT_RANGE_END, this.preferredPort);\n this.persistPath = options.persistPath ?? defaultRelayPortPersistPath();\n this.allowedOrigins = options.allowedOrigins ?? ['*'];\n this.maxPayloadBytes = options.maxPayloadBytes ?? 1_000_000;\n this.invokeTimeoutMs = options.invokeTimeoutMs ?? 25_000;\n this.label = options.label;\n this.workspace = options.workspace;\n this.relayId = options.relayId;\n this.instanceId = randomUUID();\n\n if (this.preferredPort !== 0 && (this.preferredPort < 1 || this.preferredPort > 65535)) {\n throw new Error(\n `Invalid port ${this.preferredPort}. Port must be 0 (auto-assign) or between 1 and 65535.`\n );\n }\n if (this.portRangeEnd < this.preferredPort && this.preferredPort !== 0) {\n throw new Error(\n `Invalid port range ${this.preferredPort}-${this.portRangeEnd}. rangeEnd must be greater than or equal to the preferred port.`\n );\n }\n if (this.maxPayloadBytes <= 0) {\n throw new Error(`Invalid maxPayloadBytes ${this.maxPayloadBytes}. Must be greater than 0.`);\n }\n if (this.invokeTimeoutMs <= 0) {\n throw new Error(`Invalid invokeTimeoutMs ${this.invokeTimeoutMs}. Must be greater than 0.`);\n }\n }\n\n /**\n * Current operating mode.\n */\n get mode(): 'server' | 'client' {\n return this._mode;\n }\n\n /**\n * Resolved listening port. In client mode this is the port of the server\n * relay being proxied through.\n */\n get port(): number {\n return this.desiredPort;\n }\n\n /**\n * Tools received from the server relay (client mode only).\n * Returns an empty array in server mode.\n */\n listToolsFromRelay(): RelayTool[] {\n return this._mode === 'client' ? [...this.clientTools] : [];\n }\n\n /**\n * Source metadata received from the server relay (client mode only).\n * Returns an empty array in server mode.\n */\n listSourcesFromRelay(): RelaySourceInfo[] {\n return this._mode === 'client' ? [...this.clientSources] : [];\n }\n\n /**\n * Tool-to-source mapping received from the server relay (client mode only).\n * Maps public tool names to arrays of source IDs.\n * Returns an empty record in server mode.\n */\n getToolSourceMapFromRelay(): Record<string, string[]> {\n return this._mode === 'client' ? { ...this.clientToolSourceMap } : {};\n }\n\n /**\n * Starts the bridge. Attempts to bind a WebSocket server (server mode).\n * If a compatible relay already owns a candidate port, joins it in client mode.\n * If a non-relay process owns the port, continues searching the reserved range.\n */\n async start(): Promise<void> {\n if (this.wss || this.clientSocket) {\n return;\n }\n\n this.stopping = false;\n await this.startUsingPortStrategy();\n }\n\n private async startUsingPortStrategy(): Promise<void> {\n if (this.preferredPort === 0) {\n await this.startAsServer(0);\n return;\n }\n\n const candidates = await buildPortCandidates({\n defaultPort: this.preferredPort,\n ...(this.portExplicitlySet ? { fixedPort: this.preferredPort } : {}),\n host: this.host,\n persistPath: this.persistPath,\n rangeEnd: this.portRangeEnd,\n });\n\n for (const candidate of candidates) {\n try {\n await this.startAsServer(candidate.port);\n await persistPort(this.port, this.persistPath, this.host);\n return;\n } catch (err) {\n if (!this.isAddressInUseError(err)) {\n throw err;\n }\n\n const attached = await this.tryAttachToExistingRelay(candidate.port);\n if (attached) {\n await persistPort(this.port, this.persistPath, this.host);\n return;\n }\n\n if (candidate.wasFixed) {\n throw new Error(\n `Port ${candidate.port} is already in use by a non-WebMCP service and cannot be shared.`\n );\n }\n\n process.stderr.write(\n `[webmcp-local-relay] info: port ${candidate.port} is occupied by a non-relay service, trying next port\\n`\n );\n }\n }\n\n throw new Error(\n `No compatible relay port was available in the range ${this.preferredPort}-${this.portRangeEnd}.`\n );\n }\n\n private isAddressInUseError(error: unknown): boolean {\n return (\n error instanceof Error &&\n ('code' in error\n ? (error as NodeJS.ErrnoException).code === 'EADDRINUSE'\n : error.message.includes('EADDRINUSE'))\n );\n }\n\n private async tryAttachToExistingRelay(port: number): Promise<boolean> {\n try {\n await this.startAsClient(port);\n process.stderr.write(\n `[webmcp-local-relay] info: discovered compatible relay on port ${port}, switching to client mode\\n`\n );\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Stops all relay resources and rejects any pending invocations.\n */\n async stop(): Promise<void> {\n this.stopping = true;\n\n if (this.clientReconnectTimer) {\n clearTimeout(this.clientReconnectTimer);\n this.clientReconnectTimer = null;\n }\n\n if (this._mode === 'client') {\n for (const pending of this.clientPendingInvocations.values()) {\n clearTimeout(pending.timeoutId);\n pending.reject(new Error('Relay client stopped'));\n }\n this.clientPendingInvocations.clear();\n this.clientTools = [];\n this.clientSources = [];\n this.clientToolSourceMap = {};\n\n if (this.clientSocket) {\n try {\n this.clientSocket.close(1000, 'Relay client shutting down');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: error closing client socket during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n this.clientSocket = null;\n }\n return;\n }\n\n this.off('stateChanged', this.onStateChangedPushRelay);\n\n for (const socket of this.socketByConnectionId.values()) {\n try {\n socket.close(1001, 'Relay shutting down');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: error closing socket during shutdown: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n\n for (const intervalId of this.heartbeatIntervalByConnectionId.values()) {\n clearInterval(intervalId);\n }\n this.heartbeatIntervalByConnectionId.clear();\n this.lastPongByConnectionId.clear();\n\n this.socketByConnectionId.clear();\n this.relayClientConnectionIds.clear();\n\n for (const pending of this.pendingInvocations.values()) {\n clearTimeout(pending.timeoutId);\n pending.reject(new Error('Relay server stopped before tool invocation completed'));\n }\n this.pendingInvocations.clear();\n\n const wss = this.wss;\n this.wss = null;\n\n if (!wss) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n wss.close((err?: Error) => {\n if (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: WebSocket server close error: ${err.message}\\n`\n );\n }\n resolve();\n });\n });\n }\n\n /**\n * Sends a reload message to a connected browser source.\n * Only supported in server mode.\n */\n reloadSource(connectionId: string): void {\n if (this._mode !== 'server') {\n throw new Error('reloadSource is only supported in server mode');\n }\n const socket = this.socketByConnectionId.get(connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n throw new Error(`Source ${connectionId} is not connected`);\n }\n const message: RelayToBrowserMessage = { type: 'reload' };\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n throw new Error(`Failed to send reload: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n /**\n * Invokes a tool either locally (server mode) or through the upstream relay\n * (client mode).\n */\n async invokeTool(\n toolName: string,\n args: RelayInvokeArgs,\n options: {\n sourceId?: string;\n requestTabId?: string;\n } = {}\n ): Promise<RelayCallToolResult> {\n if (this._mode === 'client') {\n return this.invokeToolViaRelay(toolName, args);\n }\n\n return this.invokeToolLocally(toolName, args, options);\n }\n\n private async startAsServer(port = this.desiredPort): Promise<void> {\n const wss = await new Promise<WebSocketServer>((resolve, reject) => {\n const server = new WebSocketServer({\n handleProtocols: (protocols) => {\n for (const protocol of protocols) {\n if (SUPPORTED_SUBPROTOCOLS.has(protocol)) {\n return protocol;\n }\n }\n return false;\n },\n host: this.host,\n port,\n maxPayload: this.maxPayloadBytes,\n });\n\n const onListening = () => {\n server.off('error', onError);\n resolve(server);\n };\n const onError = (err: Error) => {\n server.off('listening', onListening);\n reject(err);\n };\n\n server.once('listening', onListening);\n server.once('error', onError);\n });\n\n wss.on('connection', (socket: WebSocket) => {\n const connectionId = randomUUID();\n this.socketByConnectionId.set(connectionId, socket);\n\n try {\n socket.send(JSON.stringify(this.buildServerHello()));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send server hello to connection ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n socket.close(1011, 'Failed to send server hello');\n return;\n }\n\n socket.on('message', (raw: WebSocket.RawData) => {\n this.onSocketMessage(connectionId, raw);\n });\n\n socket.on('close', () => {\n this.onSocketClose(connectionId);\n });\n\n socket.on('error', (err: Error) => {\n process.stderr.write(\n `[webmcp-local-relay] warn: socket error for connection ${connectionId}: ${err.message}\\n`\n );\n this.onSocketClose(connectionId);\n });\n\n this.startHeartbeat(connectionId);\n });\n\n wss.on('error', (err: Error) => {\n process.stderr.write(`[webmcp-local-relay] error: WebSocket server error: ${err.message}\\n`);\n this.emit('error', err);\n });\n\n this.wss = wss;\n this._mode = 'server';\n\n const address = wss.address();\n if (address && typeof address !== 'string') {\n this.desiredPort = address.port;\n }\n\n this.on('stateChanged', this.onStateChangedPushRelay);\n }\n\n private buildServerHello(): ServerHelloMessage {\n return {\n type: 'server-hello',\n service: 'webmcp-local-relay',\n version: 1,\n host: this.host,\n instanceId: this.instanceId,\n label: this.label,\n port: this.desiredPort,\n relayId: this.relayId ?? this.instanceId,\n workspace: this.workspace,\n };\n }\n\n private invokeToolLocally(\n toolName: string,\n args: RelayInvokeArgs,\n options: { sourceId?: string; requestTabId?: string }\n ): Promise<RelayCallToolResult> {\n const resolveOptions: { toolName: string; sourceId?: string; requestTabId?: string } = {\n toolName,\n };\n if (options.sourceId !== undefined) {\n resolveOptions.sourceId = options.sourceId;\n }\n if (options.requestTabId !== undefined) {\n resolveOptions.requestTabId = options.requestTabId;\n }\n\n const resolved = this.registry.resolveInvocation(resolveOptions);\n\n if (!resolved) {\n throw new Error(`No active browser source provides tool \"${toolName}\"`);\n }\n\n const socket = this.socketByConnectionId.get(resolved.connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n throw new Error(\n `Tool source ${resolved.connectionId} disconnected before invocation of \"${toolName}\"`\n );\n }\n\n const callId = randomUUID();\n\n return new Promise<RelayCallToolResult>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.pendingInvocations.delete(callId);\n reject(\n new Error(`Invocation for tool \"${toolName}\" timed out after ${this.invokeTimeoutMs}ms`)\n );\n }, this.invokeTimeoutMs);\n\n this.pendingInvocations.set(callId, {\n callId,\n connectionId: resolved.connectionId,\n timeoutId,\n resolve,\n reject,\n });\n\n const message: RelayToBrowserMessage = {\n type: 'invoke',\n callId,\n toolName: resolved.tool.name,\n args,\n };\n\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n clearTimeout(timeoutId);\n this.pendingInvocations.delete(callId);\n reject(\n new Error(\n `Failed to send invocation for tool \"${toolName}\": ${err instanceof Error ? err.message : err}`\n )\n );\n }\n });\n }\n\n /**\n * Handles a raw WebSocket message from a connected source.\n *\n * Routes relay-protocol messages (`relay/*`) to the relay client handler\n * and browser-protocol messages to the existing browser handler.\n */\n private onSocketMessage(connectionId: string, raw: WebSocket.RawData): void {\n const text = this.rawDataToUtf8(raw);\n\n let parsedJson: unknown;\n try {\n parsedJson = JSON.parse(text);\n } catch (err) {\n const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid JSON from connection ${connectionId} (${err instanceof Error ? err.message : 'parse error'}): ${preview}\\n`\n );\n return;\n }\n\n const typeField =\n typeof parsedJson === 'object' && parsedJson !== null\n ? (parsedJson as Record<string, unknown>).type\n : undefined;\n\n if (typeof typeField === 'string' && typeField.startsWith('relay/')) {\n const relayMsg = RelayClientToServerMessageSchema.safeParse(parsedJson);\n if (relayMsg.success) {\n this.onRelayClientMessage(connectionId, relayMsg.data);\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid relay message from ${connectionId}: ${relayMsg.error.message}\\n`\n );\n }\n return;\n }\n\n this.registry.touchConnection(connectionId);\n\n const parsedMessage = BrowserToRelayMessageSchema.safeParse(parsedJson);\n if (!parsedMessage.success) {\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid message from connection ${connectionId} (type=${typeField}): ${parsedMessage.error.message}\\n`\n );\n return;\n }\n\n const message = parsedMessage.data;\n\n switch (message.type) {\n case 'hello':\n try {\n if (!this.isHostOriginAllowed(message.origin)) {\n process.stderr.write(\n `[webmcp-local-relay] warn: rejecting source ${connectionId} with disallowed host origin: ${message.origin ?? 'missing'}\\n`\n );\n const socket = this.socketByConnectionId.get(connectionId);\n socket?.close(1008, 'Host origin not allowed');\n break;\n }\n this.registry.upsertSource(connectionId, message);\n this.emit('stateChanged');\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to process hello from connection ${connectionId}: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n }\n break;\n\n case 'tools/list':\n case 'tools/changed':\n try {\n this.registry.registerTools(connectionId, message.tools);\n this.emit('stateChanged');\n } catch (err) {\n if (err instanceof HelloRequiredError) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} sent tools before hello, ignoring\\n`\n );\n } else {\n process.stderr.write(\n `[webmcp-local-relay] error: failed to register tools for connection ${connectionId}: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n const socket = this.socketByConnectionId.get(connectionId);\n socket?.close(1011, 'Failed to register tools');\n }\n }\n break;\n\n case 'result': {\n const pending = this.pendingInvocations.get(message.callId);\n if (!pending) {\n process.stderr.write(\n `[webmcp-local-relay] warn: received result for unknown callId ${message.callId}\\n`\n );\n break;\n }\n\n clearTimeout(pending.timeoutId);\n this.pendingInvocations.delete(message.callId);\n pending.resolve(this.normalizeCallToolResult(message.result));\n break;\n }\n\n case 'pong':\n this.lastPongByConnectionId.set(connectionId, Date.now());\n break;\n }\n }\n\n /**\n * Handles relay-protocol messages from relay client connections.\n */\n private onRelayClientMessage(connectionId: string, message: RelayClientToServerMessage): void {\n switch (message.type) {\n case 'relay/hello':\n this.relayClientConnectionIds.add(connectionId);\n break;\n\n case 'relay/list-tools': {\n const response = this.buildRelayToolsPayload('relay/tools');\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(JSON.stringify(response));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay tools response to ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n break;\n }\n\n case 'relay/invoke': {\n const { callId, toolName, args } = message;\n void (async () => {\n try {\n const result = await this.invokeToolLocally(toolName, args ?? {}, {});\n const response: RelayServerToClientMessage = {\n type: 'relay/result',\n callId,\n result,\n };\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(JSON.stringify(response));\n } catch (sendErr) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay result to ${connectionId}: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}\\n`\n );\n }\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client ${connectionId} disconnected before result for callId ${callId} could be delivered\\n`\n );\n }\n } catch (err) {\n const response: RelayServerToClientMessage = {\n type: 'relay/result',\n callId,\n result: {\n content: [\n {\n type: 'text',\n text: `Relay invocation failed: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n },\n };\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(JSON.stringify(response));\n } catch (sendErr) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send relay error result to ${connectionId}: ${sendErr instanceof Error ? sendErr.message : String(sendErr)}\\n`\n );\n }\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client ${connectionId} disconnected before error result for callId ${callId} could be delivered\\n`\n );\n }\n }\n })();\n break;\n }\n }\n }\n\n /**\n * Pushes current tool state to all connected relay clients.\n */\n private pushToolsToRelayClients(): void {\n if (this.relayClientConnectionIds.size === 0) {\n return;\n }\n\n const message = this.buildRelayToolsPayload('relay/tools-changed');\n const payload = JSON.stringify(message);\n\n for (const connectionId of this.relayClientConnectionIds) {\n const socket = this.socketByConnectionId.get(connectionId);\n if (socket?.readyState === WebSocket.OPEN) {\n try {\n socket.send(payload);\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to push tool update to relay client ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n }\n }\n\n /**\n * Handles source disconnection and rejects in-flight calls owned by that source.\n */\n private onSocketClose(connectionId: string): void {\n this.stopHeartbeat(connectionId);\n this.relayClientConnectionIds.delete(connectionId);\n this.registry.removeConnection(connectionId);\n this.socketByConnectionId.delete(connectionId);\n this.emit('stateChanged');\n\n for (const [callId, pending] of this.pendingInvocations.entries()) {\n if (pending.connectionId !== connectionId) {\n continue;\n }\n\n clearTimeout(pending.timeoutId);\n this.pendingInvocations.delete(callId);\n pending.reject(new Error(`Tool source ${connectionId} disconnected during invocation`));\n }\n }\n\n private startHeartbeat(connectionId: string): void {\n this.lastPongByConnectionId.set(connectionId, Date.now());\n\n const intervalId = setInterval(() => {\n const socket = this.socketByConnectionId.get(connectionId);\n if (!socket || socket.readyState !== WebSocket.OPEN) {\n this.stopHeartbeat(connectionId);\n return;\n }\n\n // Skip heartbeat for relay client connections — they have their own reconnect logic.\n if (this.relayClientConnectionIds.has(connectionId)) {\n return;\n }\n\n const lastPong = this.lastPongByConnectionId.get(connectionId) ?? 0;\n if (Date.now() - lastPong > HEARTBEAT_DEAD_THRESHOLD_MS) {\n process.stderr.write(\n `[webmcp-local-relay] warn: connection ${connectionId} missed heartbeat, closing\\n`\n );\n this.stopHeartbeat(connectionId);\n socket.close(1001, 'Heartbeat timeout');\n return;\n }\n\n try {\n socket.send(JSON.stringify({ type: 'ping' }));\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send heartbeat ping to ${connectionId}: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }, HEARTBEAT_INTERVAL_MS);\n\n this.heartbeatIntervalByConnectionId.set(connectionId, intervalId);\n }\n\n private stopHeartbeat(connectionId: string): void {\n const intervalId = this.heartbeatIntervalByConnectionId.get(connectionId);\n if (intervalId !== undefined) {\n clearInterval(intervalId);\n this.heartbeatIntervalByConnectionId.delete(connectionId);\n }\n this.lastPongByConnectionId.delete(connectionId);\n }\n\n private async startAsClient(port = this.desiredPort): Promise<void> {\n this.stopping = false;\n\n const previousMode = this._mode;\n const previousPort = this.desiredPort;\n this._mode = 'client';\n this.desiredPort = port;\n\n try {\n const { bufferedMessages, socket } = await this.connectToRelayServer(port);\n this.clientSocket = socket;\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n this.setupClientHandlers(socket, bufferedMessages);\n } catch (error) {\n this._mode = previousMode;\n this.desiredPort = previousPort;\n throw error;\n }\n }\n\n private async connectToRelayServer(\n port: number\n ): Promise<{ bufferedMessages: RelayServerToClientMessage[]; socket: WebSocket }> {\n this.stopping = false;\n\n return new Promise((resolve, reject) => {\n const wsUrl = `ws://${this.host}:${port}`;\n const ws = new WebSocket(wsUrl, [RELAY_INTERNAL_PROTOCOL, RELAY_BROWSER_PROTOCOL]);\n const bufferedMessages: RelayServerToClientMessage[] = [];\n\n const cleanup = () => {\n clearTimeout(timeoutId);\n ws.off('close', onCloseBeforeReady);\n ws.off('error', onErrorBeforeReady);\n ws.off('message', onMessageBeforeReady);\n ws.off('open', onOpen);\n };\n\n const rejectWith = (error: Error) => {\n ws.once('error', () => {\n // Ignore late socket errors from ports that failed relay verification.\n });\n cleanup();\n reject(error);\n };\n\n const finish = () => {\n cleanup();\n resolve({ bufferedMessages, socket: ws });\n };\n\n const onOpen = () => {\n try {\n this.sendRelayClientHandshake(ws);\n } catch (err) {\n rejectWith(\n new Error(\n `Failed to send handshake to relay server at ${wsUrl}: ${err instanceof Error ? err.message : String(err)}`\n )\n );\n }\n };\n\n const onErrorBeforeReady = (err: Error) => {\n rejectWith(new Error(`Failed to connect to relay server at ${wsUrl}: ${err.message}`));\n };\n\n const onCloseBeforeReady = () => {\n rejectWith(new Error(`Connection to ${wsUrl} closed before relay verification completed`));\n };\n\n const onMessageBeforeReady = (raw: WebSocket.RawData) => {\n const message = this.parseRelayServerMessage(raw);\n if (!message) {\n rejectWith(new Error(`Received a non-relay response while probing ${wsUrl}`));\n return;\n }\n\n bufferedMessages.push(message);\n\n if (message.type === 'server-hello') {\n if (message.service !== 'webmcp-local-relay') {\n rejectWith(new Error(`Unexpected relay service \"${message.service}\" at ${wsUrl}`));\n return;\n }\n finish();\n return;\n }\n\n if (message.type === 'relay/tools' || message.type === 'relay/tools-changed') {\n finish();\n }\n };\n\n const timeoutId = setTimeout(() => {\n rejectWith(new Error(`Timed out waiting for relay hello from ${wsUrl}`));\n }, RELAY_SERVER_MESSAGE_TIMEOUT_MS);\n\n ws.once('open', onOpen);\n ws.once('error', onErrorBeforeReady);\n ws.once('close', onCloseBeforeReady);\n ws.on('message', onMessageBeforeReady);\n });\n }\n\n private setupClientHandlers(\n ws: WebSocket,\n bufferedMessages: RelayServerToClientMessage[] = []\n ): void {\n for (const message of bufferedMessages) {\n this.processRelayServerMessage(message);\n }\n\n ws.on('message', (raw: WebSocket.RawData) => {\n const message = this.parseRelayServerMessage(raw);\n if (!message) {\n return;\n }\n\n this.processRelayServerMessage(message);\n });\n\n ws.on('close', () => {\n this.clientSocket = null;\n for (const [callId, pending] of this.clientPendingInvocations) {\n clearTimeout(pending.timeoutId);\n this.clientPendingInvocations.delete(callId);\n pending.reject(new Error('Relay server connection lost during invocation'));\n }\n\n this.clientTools = [];\n this.clientSources = [];\n this.clientToolSourceMap = {};\n this.emit('stateChanged');\n\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n });\n\n ws.on('error', (err: Error) => {\n process.stderr.write(\n `[webmcp-local-relay] warn: relay client socket error: ${err.message}\\n`\n );\n });\n }\n\n private sendRelayClientHandshake(ws: WebSocket): void {\n ws.send(JSON.stringify({ type: 'relay/hello' }));\n ws.send(JSON.stringify({ type: 'relay/list-tools' }));\n }\n\n private parseRelayServerMessage(raw: WebSocket.RawData): RelayServerToClientMessage | null {\n const text = this.rawDataToUtf8(raw);\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (err) {\n const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid JSON from relay server (${err instanceof Error ? err.message : 'parse error'}): ${preview}\\n`\n );\n return null;\n }\n\n const message = RelayServerToClientMessageSchema.safeParse(parsed);\n if (!message.success) {\n const typeField =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>).type\n : 'unknown';\n process.stderr.write(\n `[webmcp-local-relay] warn: invalid relay server message (type=${typeField}): ${message.error.message}\\n`\n );\n return null;\n }\n\n return message.data;\n }\n\n private processRelayServerMessage(message: RelayServerToClientMessage): void {\n switch (message.type) {\n case 'server-hello':\n break;\n\n case 'relay/tools':\n case 'relay/tools-changed':\n this.clientTools = message.tools;\n this.clientSources = message.sources;\n this.clientToolSourceMap = message.toolSourceMap;\n this.emit('stateChanged');\n break;\n\n case 'relay/result': {\n const pending = this.clientPendingInvocations.get(message.callId);\n if (!pending) {\n process.stderr.write(\n `[webmcp-local-relay] warn: received relay result for unknown callId ${message.callId}\\n`\n );\n break;\n }\n clearTimeout(pending.timeoutId);\n this.clientPendingInvocations.delete(message.callId);\n pending.resolve(message.result);\n break;\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.clientReconnectTimer || this.stopping) {\n return;\n }\n\n this.clientReconnectAttempts++;\n if (this.clientReconnectAttempts >= this.clientMaxReconnectAttempts) {\n process.stderr.write(\n `[webmcp-local-relay] error: giving up reconnection after ${this.clientReconnectAttempts} attempts\\n`\n );\n return;\n }\n\n const delay = this.clientReconnectDelay;\n this.clientReconnectDelay = Math.min(\n this.clientReconnectDelay * 1.5,\n this.clientMaxReconnectDelay\n );\n\n this.clientReconnectTimer = setTimeout(() => {\n this.clientReconnectTimer = null;\n if (this.stopping) {\n return;\n }\n\n void this.reconnectWithModePromotion().catch((err) => {\n process.stderr.write(\n `[webmcp-local-relay] error: unexpected failure during reconnection: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n });\n }, delay);\n }\n\n /**\n * Attempts to promote from client to server mode when reconnecting.\n * Re-runs the same attach-or-bind strategy used during startup.\n */\n private async reconnectWithModePromotion(): Promise<void> {\n try {\n await this.startUsingPortStrategy();\n this.clientReconnectDelay = 500;\n this.clientReconnectAttempts = 0;\n if (this._mode === 'server') {\n process.stderr.write('[webmcp-local-relay] info: promoted from client to server mode\\n');\n this.emit('stateChanged');\n }\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: reconnection failed: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n if (!this.stopping) {\n this.scheduleReconnect();\n }\n }\n }\n\n private invokeToolViaRelay(\n toolName: string,\n args: RelayInvokeArgs\n ): Promise<RelayCallToolResult> {\n if (!this.clientSocket || this.clientSocket.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected to relay server');\n }\n\n const callId = randomUUID();\n const socket = this.clientSocket;\n\n return new Promise<RelayCallToolResult>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.clientPendingInvocations.delete(callId);\n reject(\n new Error(\n `Proxied invocation for tool \"${toolName}\" timed out after ${this.invokeTimeoutMs}ms`\n )\n );\n }, this.invokeTimeoutMs);\n\n this.clientPendingInvocations.set(callId, {\n callId,\n connectionId: 'relay-server',\n timeoutId,\n resolve,\n reject,\n });\n\n const message: RelayClientToServerMessage = {\n type: 'relay/invoke',\n callId,\n toolName,\n args,\n };\n\n try {\n socket.send(JSON.stringify(message));\n } catch (err) {\n clearTimeout(timeoutId);\n this.clientPendingInvocations.delete(callId);\n reject(\n new Error(`Failed to send relay invocation: ${err instanceof Error ? err.message : err}`)\n );\n }\n });\n }\n\n /**\n * Builds the complete relay response payload including tools and source metadata.\n */\n private buildRelayToolsPayload(\n type: 'relay/tools' | 'relay/tools-changed'\n ): RelayServerToClientMessage {\n const tools = this.registry.listTools();\n const sources = this.registry.listSources();\n const toolSourceMap: Record<string, string[]> = {};\n\n for (const tool of tools) {\n toolSourceMap[tool.name] = tool.sources.map((s) => s.sourceId);\n }\n\n return {\n type,\n tools: this.toWireTools(tools),\n sources,\n toolSourceMap,\n };\n }\n\n /**\n * Maps aggregated tools to the wire format used by the relay protocol.\n */\n private toWireTools(tools: AggregatedTool[]): RelayTool[] {\n return tools.map(({ originalName: _originalName, sources: _sources, ...tool }) => tool);\n }\n\n private isHostOriginAllowed(origin: string | undefined): boolean {\n if (this.allowedOrigins.includes('*')) {\n return true;\n }\n\n if (!origin) {\n return false;\n }\n\n return this.allowedOrigins.includes(origin);\n }\n\n /**\n * Validates browser tool results against CallToolResultSchema.\n * Non-conforming payloads are wrapped as error results with diagnostic text.\n */\n private normalizeCallToolResult(result: unknown): RelayCallToolResult {\n const parsed = CallToolResultSchema.safeParse(result);\n if (parsed.success) {\n return parsed.data;\n }\n\n const preview = JSON.stringify(result)?.slice(0, 500) ?? 'undefined';\n process.stderr.write(\n `[webmcp-local-relay] warn: tool returned invalid CallToolResult (${parsed.error.message}), wrapping as error: ${preview}\\n`\n );\n\n return {\n content: [\n {\n type: 'text',\n text: `Tool returned an invalid result (expected {content: [...]}): ${preview}`,\n },\n ],\n isError: true,\n };\n }\n\n /**\n * Converts WebSocket raw data variants to a UTF-8 string payload.\n */\n private rawDataToUtf8(raw: WebSocket.RawData): string {\n if (typeof raw === 'string') {\n return raw;\n }\n\n if (Buffer.isBuffer(raw)) {\n return raw.toString('utf8');\n }\n\n if (raw instanceof ArrayBuffer) {\n return Buffer.from(raw).toString('utf8');\n }\n\n if (Array.isArray(raw)) {\n return Buffer.concat(raw).toString('utf8');\n }\n\n return String(raw);\n }\n}\n","import { toJsonSchemaCompat } from '@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';\nimport { z } from 'zod/v4';\n\nexport type StaticToolInputShape = Record<string, z.ZodTypeAny>;\n\nexport const EMPTY_STATIC_TOOL_INPUT_SHAPE = {} satisfies StaticToolInputShape;\n\nexport const WEBMCP_CALL_TOOL_INPUT_SHAPE = {\n name: z\n .string()\n .describe('The tool name to call. Use webmcp_list_tools to see available tool names.'),\n arguments: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\n 'Arguments to pass to the tool as a JSON object. Check the tool inputSchema from webmcp_list_tools for expected fields.'\n ),\n} satisfies StaticToolInputShape;\n\nexport const WEBMCP_OPEN_PAGE_INPUT_SHAPE = {\n url: z.string().describe('URL to open or match for refresh.'),\n refresh: z\n .boolean()\n .optional()\n .describe(\n 'If true, refresh the connected source matching this URL instead of opening a new tab.'\n ),\n} satisfies StaticToolInputShape;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction stripJsonSchemaMetadata(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => stripJsonSchemaMetadata(item));\n }\n\n if (!isRecord(value)) {\n return value;\n }\n\n const next: Record<string, unknown> = {};\n for (const [key, nestedValue] of Object.entries(value)) {\n if (key === '$schema') {\n continue;\n }\n next[key] = stripJsonSchemaMetadata(nestedValue);\n }\n return next;\n}\n\nexport function publicInputSchemaFromZodShape(\n inputShape: StaticToolInputShape\n): Record<string, unknown> {\n return stripJsonSchemaMetadata(\n toJsonSchemaCompat(z.object(inputShape), {\n strictUnions: true,\n pipeStrategy: 'input',\n }) as Record<string, unknown>\n ) as Record<string, unknown>;\n}\n","import { execFile } from 'node:child_process';\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod/v4';\n\nimport { RelayBridgeServer, type RelayBridgeServerOptions } from './bridgeServer.js';\nimport type { AggregatedTool, SourceInfo } from './registry.js';\nimport {\n EMPTY_STATIC_TOOL_INPUT_SHAPE,\n publicInputSchemaFromZodShape,\n type StaticToolInputShape,\n WEBMCP_CALL_TOOL_INPUT_SHAPE,\n WEBMCP_OPEN_PAGE_INPUT_SHAPE,\n} from './staticToolSchemas.js';\n\n/**\n * Handle returned by MCP tool registration.\n */\ninterface RegisteredToolHandle {\n remove: () => void;\n}\n\ninterface StaticToolRegistration<T extends StaticToolInputShape = StaticToolInputShape> {\n name: string;\n description: string;\n inputShape: T;\n annotations?: unknown;\n handler: (args: z.output<z.ZodObject<T>>) => Promise<Record<string, unknown>>;\n}\n\ninterface PublicToolMetadata {\n name: string;\n title?: string;\n description?: string;\n inputSchema: Record<string, unknown>;\n outputSchema?: Record<string, unknown>;\n annotations?: unknown;\n}\n\n/**\n * Base options shared by all {@link LocalRelayMcpServer} configurations.\n */\ninterface LocalRelayMcpServerBaseOptions {\n /**\n * MCP server name reported during initialization.\n */\n serverName?: string;\n /**\n * MCP server version reported during initialization.\n */\n serverVersion?: string;\n}\n\n/**\n * Construction options for {@link LocalRelayMcpServer}.\n *\n * Provide either an existing `bridge` instance OR `bridgeOptions` to create\n * one internally — not both.\n */\nexport type LocalRelayMcpServerOptions = LocalRelayMcpServerBaseOptions &\n (\n | { bridge: RelayBridgeServer; bridgeOptions?: never }\n | { bridge?: never; bridgeOptions?: RelayBridgeServerOptions }\n );\n\n/**\n * MCP server facade that exposes browser-relayed tools over MCP transport.\n */\nexport class LocalRelayMcpServer {\n /**\n * Underlying WebSocket bridge used for browser communication.\n */\n readonly bridge: RelayBridgeServer;\n\n private readonly mcpServer: McpServer;\n private readonly staticToolData = new Map<string, PublicToolMetadata>();\n private readonly dynamicToolHandles = new Map<string, RegisteredToolHandle>();\n private readonly dynamicToolData = new Map<string, AggregatedTool>();\n private readonly dynamicToolSignature = new Map<string, string>();\n\n private syncing = false;\n private syncRequested = false;\n private connected = false;\n\n /**\n * Creates a local relay MCP server with static and dynamic tool registration.\n */\n constructor(options: LocalRelayMcpServerOptions = {}) {\n this.bridge = options.bridge ?? new RelayBridgeServer(options.bridgeOptions);\n\n this.mcpServer = new McpServer({\n name: options.serverName ?? 'webmcp-local-relay',\n version: options.serverVersion ?? '0.0.0',\n });\n\n this.bridge.on('stateChanged', () => {\n void this.syncDynamicTools().catch((err) => {\n this.logSyncError(err);\n });\n });\n\n this.registerStaticTools();\n this.overrideListToolsHandler();\n }\n\n /**\n * Starts the browser bridge and synchronizes dynamic MCP tools.\n */\n async start(): Promise<void> {\n await this.bridge.start();\n await this.syncDynamicTools();\n }\n\n /**\n * Connects the MCP server to a transport.\n *\n * This may be called exactly once per instance lifecycle.\n */\n async connect(transport: Transport): Promise<void> {\n if (this.connected) {\n throw new Error('MCP server transport already connected');\n }\n\n await this.mcpServer.connect(transport);\n this.connected = true;\n }\n\n /**\n * Convenience helper that connects the server over stdio transport.\n */\n async startStdio(): Promise<void> {\n await this.connect(new StdioServerTransport());\n }\n\n /**\n * Stops MCP transport and bridge resources.\n */\n async stop(): Promise<void> {\n this.connected = false;\n let mcpCloseError: unknown;\n try {\n await this.mcpServer.close();\n } catch (err) {\n mcpCloseError = err;\n process.stderr.write(\n `[webmcp-local-relay] error: failed to close MCP server: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\\n`\n );\n }\n await this.bridge.stop();\n if (mcpCloseError) {\n process.stderr.write(\n '[webmcp-local-relay] warn: shutdown completed with errors (MCP server close failed)\\n'\n );\n }\n }\n\n /**\n * Returns dynamic tool names currently registered in MCP.\n */\n listDynamicToolNames(): string[] {\n return Array.from(this.dynamicToolHandles.keys()).sort();\n }\n\n /**\n * Overrides the SDK's ListTools handler to return real JSON Schema\n * for dynamic tools instead of the empty `z.object({}).passthrough()`\n * schema that the SDK would otherwise convert to `{ type: 'object' }`.\n *\n * The MCP SDK's McpServer internally runs `toJsonSchemaCompat()` on\n * registered inputSchema values, which strips plain JSON Schema objects\n * (non-Zod) to empty schemas. This override bypasses that conversion\n * for dynamic (relayed) tools while preserving static tools as-is.\n */\n private overrideListToolsHandler(): void {\n this.mcpServer.server.setRequestHandler(ListToolsRequestSchema, () => {\n const tools: Array<Record<string, unknown>> = [\n ...Array.from(this.staticToolData.values()).map((tool) => ({ ...tool })),\n ...Array.from(this.dynamicToolData.entries()).map(([name, tool]) => ({\n name,\n description: this.dynamicToolDescription(tool),\n inputSchema: tool.inputSchema,\n ...(tool.outputSchema ? { outputSchema: tool.outputSchema } : {}),\n ...(tool.annotations ? { annotations: tool.annotations } : {}),\n })),\n ].sort((left, right) => String(left.name).localeCompare(String(right.name)));\n\n return { tools };\n });\n }\n\n private registerStaticTool<T extends StaticToolInputShape>({\n name,\n description,\n inputShape,\n annotations,\n handler,\n }: StaticToolRegistration<T>): void {\n this.mcpServer.registerTool(\n name,\n {\n description,\n inputSchema: inputShape,\n ...(annotations ? { annotations } : {}),\n } as Parameters<McpServer['registerTool']>[1],\n handler as Parameters<McpServer['registerTool']>[2]\n );\n this.staticToolData.set(name, {\n name,\n description,\n inputSchema: publicInputSchemaFromZodShape(inputShape),\n ...(annotations ? { annotations } : {}),\n });\n }\n\n /**\n * Registers built-in management tools exposed by the relay.\n */\n private registerStaticTools(): void {\n this.registerStaticTool({\n name: 'webmcp_list_sources',\n description: 'List connected browser tool sources and their metadata.',\n inputShape: EMPTY_STATIC_TOOL_INPUT_SHAPE,\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n handler: async () => {\n if (this.bridge.mode === 'client') {\n const sources = this.bridge.listSourcesFromRelay();\n const info = {\n mode: 'client' as const,\n count: sources.length,\n sources,\n };\n return {\n content: [{ type: 'text', text: JSON.stringify(info, null, 2) }],\n structuredContent: info,\n };\n }\n const sources = this.bridge.registry.listSources();\n return {\n content: [\n { type: 'text', text: JSON.stringify({ count: sources.length, sources }, null, 2) },\n ],\n structuredContent: {\n count: sources.length,\n sources,\n },\n };\n },\n });\n\n this.registerStaticTool({\n name: 'webmcp_list_tools',\n description:\n 'List WebMCP tools available from connected browser sources. Returns tool definitions including name, description, input schema, and source info. Use webmcp_call_tool to invoke a tool by name.',\n inputShape: EMPTY_STATIC_TOOL_INPUT_SHAPE,\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n },\n handler: async () => {\n const tools = this.listAggregatedTools();\n return {\n content: [\n { type: 'text', text: JSON.stringify({ count: tools.length, tools }, null, 2) },\n ],\n structuredContent: {\n count: tools.length,\n tools,\n },\n };\n },\n });\n\n this.registerStaticTool({\n name: 'webmcp_call_tool',\n description:\n 'Call a WebMCP tool registered by a connected browser page. Use webmcp_list_tools first to see available tools and their input schemas.',\n inputShape: WEBMCP_CALL_TOOL_INPUT_SHAPE,\n annotations: {\n readOnlyHint: false,\n },\n handler: async ({ name, arguments: args }) => {\n const tools = this.listAggregatedTools();\n const toolSummary = this.buildToolSummary(tools);\n\n const matched = tools.find((t) => t.name === name);\n if (!matched) {\n const errorLines = [`Tool \"${name}\" not found.`];\n if (toolSummary) {\n errorLines.push('', 'Available tools:', toolSummary);\n } else {\n errorLines.push(\n '',\n 'No tools are currently available. Ensure a browser page with WebMCP is connected.'\n );\n }\n return {\n content: [{ type: 'text' as const, text: errorLines.join('\\n') }],\n isError: true,\n };\n }\n\n try {\n const result = await this.bridge.invokeTool(name, args ?? {});\n\n const updatedTools =\n this.bridge.mode === 'client'\n ? this.buildAggregatedToolsFromRelay()\n : this.bridge.registry.listTools();\n const updatedSummary = this.buildToolSummary(updatedTools);\n if (updatedSummary) {\n result.content = [\n ...result.content,\n { type: 'text' as const, text: `\\n---\\nAvailable tools:\\n${updatedSummary}` },\n ];\n }\n\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const errorLines = [`Failed to call tool \"${name}\": ${message}`];\n if (toolSummary) {\n errorLines.push('', 'Available tools:', toolSummary);\n }\n return {\n content: [{ type: 'text' as const, text: errorLines.join('\\n') }],\n isError: true,\n };\n }\n },\n });\n\n this.registerStaticTool({\n name: 'webmcp_open_page',\n description:\n \"Open a URL in the user's default browser, or refresh a connected source page. Use to launch WebMCP-enabled pages or reload stale connections.\",\n inputShape: WEBMCP_OPEN_PAGE_INPUT_SHAPE,\n annotations: { readOnlyHint: false },\n handler: async ({ url, refresh }) => {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return {\n content: [{ type: 'text' as const, text: `Invalid URL: ${url}` }],\n isError: true,\n };\n }\n\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Only http: and https: URLs are allowed. Got: ${parsed.protocol}`,\n },\n ],\n isError: true,\n };\n }\n\n if (refresh) {\n if (this.bridge.mode === 'client') {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Refresh is not supported in client mode. Only the server relay can reload sources.',\n },\n ],\n isError: true,\n };\n }\n\n const sources = this.bridge.registry.listSources();\n const matched = sources.find((s) => {\n if (!s.url) return false;\n try {\n return new URL(s.url).origin === parsed.origin;\n } catch {\n process.stderr.write(\n `[webmcp-local-relay] warn: source ${s.sourceId} has unparseable URL: ${s.url}\\n`\n );\n return false;\n }\n });\n\n if (!matched) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No connected source matches origin ${parsed.origin}. The page may not be open or connected.`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n this.bridge.reloadSource(matched.sourceId);\n return {\n content: [\n {\n type: 'text' as const,\n text: `Reload sent to source ${matched.sourceId} (${matched.url ?? matched.origin}).`,\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to reload source: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n try {\n await this.openInBrowser(url);\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to open browser: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n\n if (this.bridge.mode === 'server') {\n const sources = this.bridge.registry.listSources();\n const existing = sources.find((s) => {\n if (!s.url) return false;\n try {\n return new URL(s.url).origin === parsed.origin;\n } catch {\n process.stderr.write(\n `[webmcp-local-relay] warn: source ${s.sourceId} has unparseable URL: ${s.url}\\n`\n );\n return false;\n }\n });\n\n if (existing) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Opened ${url} in the default browser. Note: a source from ${existing.url ?? existing.origin} is already connected.`,\n },\n ],\n };\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: `Opened ${url} in the default browser.` }],\n };\n },\n });\n }\n\n /**\n * Builds a concise plain-text list of available tools.\n */\n private buildToolSummary(tools: AggregatedTool[]): string | null {\n if (tools.length === 0) {\n return null;\n }\n\n return tools\n .map((t) => {\n const desc = t.description ? ` - ${t.description.split('\\n')[0]}` : '';\n return ` ${t.name}${desc}`;\n })\n .join('\\n');\n }\n\n /**\n * Opens a URL in the user's default browser using the platform open command.\n *\n * Uses the re-serialized `URL.href` to prevent injection of shell metacharacters.\n * On Windows, PowerShell `Start-Process` is used instead of `cmd /c start`\n * because cmd.exe interprets `&`, `|`, and other metacharacters in URLs.\n */\n private openInBrowser(url: string): Promise<void> {\n const safeUrl = new URL(url).href;\n return new Promise((resolve, reject) => {\n const platform = process.platform;\n let command: string;\n let args: string[];\n if (platform === 'darwin') {\n command = 'open';\n args = [safeUrl];\n } else if (platform === 'win32') {\n command = 'powershell.exe';\n args = ['-NoProfile', '-Command', `Start-Process \"${safeUrl}\"`];\n } else {\n command = 'xdg-open';\n args = [safeUrl];\n }\n execFile(command, args, (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n /**\n * Coalesces concurrent sync requests into a single serialized sync loop.\n */\n private async syncDynamicTools(): Promise<void> {\n if (this.syncing) {\n this.syncRequested = true;\n return;\n }\n\n this.syncing = true;\n\n try {\n do {\n this.syncRequested = false;\n const tools = this.listAggregatedTools();\n this.applyDynamicTools(tools);\n } while (this.syncRequested);\n } finally {\n const retryRequested = this.syncRequested;\n this.syncRequested = false;\n this.syncing = false;\n if (retryRequested) {\n void this.syncDynamicTools().catch((err) => {\n this.logSyncError(err);\n });\n }\n }\n }\n\n /**\n * Returns the current aggregated tool list, dispatching based on bridge mode.\n */\n private listAggregatedTools(): AggregatedTool[] {\n return this.bridge.mode === 'client'\n ? this.buildAggregatedToolsFromRelay()\n : this.bridge.registry.listTools();\n }\n\n /**\n * Converts relay client tool descriptors to aggregated tool shape.\n * Populates per-tool source metadata from the relay's tool-source mapping.\n */\n private buildAggregatedToolsFromRelay(): AggregatedTool[] {\n const toolSourceMap = this.bridge.getToolSourceMapFromRelay();\n const allSources = this.bridge.listSourcesFromRelay();\n const sourceById = new Map(allSources.map((s) => [s.sourceId, s]));\n\n return this.bridge\n .listToolsFromRelay()\n .map((tool) => {\n const sourceIds = toolSourceMap[tool.name] ?? [];\n const sources: SourceInfo[] = [];\n for (const id of sourceIds) {\n const source = sourceById.get(id);\n if (source) {\n sources.push(source as SourceInfo);\n } else {\n process.stderr.write(\n `[webmcp-local-relay] warn: tool \"${tool.name}\" references unknown sourceId \"${id}\"\\n`\n );\n }\n }\n\n return {\n ...tool,\n originalName: tool.name,\n sources,\n };\n })\n .sort((left, right) => left.name.localeCompare(right.name));\n }\n\n /**\n * Applies registry tool state to MCP dynamic registrations.\n */\n private applyDynamicTools(tools: AggregatedTool[]): void {\n const nextNames = new Set(tools.map((tool) => tool.name));\n let changed = false;\n\n for (const [name, handle] of this.dynamicToolHandles.entries()) {\n if (nextNames.has(name)) {\n continue;\n }\n\n handle.remove();\n this.dynamicToolHandles.delete(name);\n this.dynamicToolData.delete(name);\n this.dynamicToolSignature.delete(name);\n changed = true;\n }\n\n for (const tool of tools) {\n const signature = this.toolSignature(tool);\n const currentSignature = this.dynamicToolSignature.get(tool.name);\n\n if (currentSignature === signature && this.dynamicToolHandles.has(tool.name)) {\n continue;\n }\n\n const existing = this.dynamicToolHandles.get(tool.name);\n if (existing) {\n existing.remove();\n this.dynamicToolHandles.delete(tool.name);\n }\n\n const handle = this.registerDynamicTool(tool);\n this.dynamicToolHandles.set(tool.name, handle);\n this.dynamicToolData.set(tool.name, tool);\n this.dynamicToolSignature.set(tool.name, signature);\n changed = true;\n }\n\n if (changed && this.connected) {\n try {\n this.mcpServer.sendToolListChanged();\n } catch (err) {\n process.stderr.write(\n `[webmcp-local-relay] warn: failed to send tool list changed notification: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n }\n }\n }\n\n /**\n * Registers a single dynamic tool and returns a removal handle.\n */\n private registerDynamicTool(tool: AggregatedTool): RegisteredToolHandle {\n const inputSchema = z.object({}).passthrough();\n const config = {\n description: this.dynamicToolDescription(tool),\n inputSchema,\n };\n\n const handle = this.mcpServer.registerTool(\n tool.name,\n tool.annotations ? { ...config, annotations: tool.annotations } : config,\n async (args: Record<string, unknown>) => {\n try {\n return await this.bridge.invokeTool(tool.name, args);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(\n `[webmcp-local-relay] error: dynamic tool \"${tool.name}\" invocation failed: ${details}\\n`\n );\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to invoke relayed tool \"${tool.name}\": ${message}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n\n return {\n remove: () => {\n handle.remove();\n },\n };\n }\n\n /**\n * Builds a display description for relayed tools including source context.\n * In client mode, source metadata is unavailable, so tools are labeled `[WebMCP relay]`.\n */\n private dynamicToolDescription(tool: AggregatedTool): string {\n const source = tool.sources[0];\n const sourceLabel = source\n ? `[WebMCP ${source.tabId}${source.title ? ` • ${source.title}` : ''}]`\n : '[WebMCP relay]';\n\n return `${sourceLabel} ${tool.description ?? `Relayed tool ${tool.originalName}`}`;\n }\n\n /**\n * Produces a stable signature for change detection of dynamic tool metadata.\n */\n private toolSignature(tool: AggregatedTool): string {\n return JSON.stringify({\n name: tool.name,\n originalName: tool.originalName,\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n annotations: tool.annotations,\n execution: tool.execution,\n _meta: tool._meta,\n icons: tool.icons,\n sources: tool.sources.map((source) => ({\n sourceId: source.sourceId,\n tabId: source.tabId,\n })),\n });\n }\n\n /**\n * Logs tool sync errors with full detail.\n */\n private logSyncError(err: unknown): void {\n const details = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(`[webmcp-local-relay] error: failed to sync dynamic tools: ${details}\\n`);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,SAAgB,gBAAgB,MAA4B;CAC1D,MAAMA,UAAsB;EAC1B,MAAM;EACN,MAAM;EACN,mBAAmB;EAGnB,gBAAgB,CAAC,IAAI;EACtB;CAED,MAAM,iBAAiB,MAAc,UAA0B;EAC7D,MAAM,OAAO,KAAK,QAAQ;AAC1B,MAAI,CAAC,QAAQ,KAAK,WAAW,IAAI,CAC/B,OAAM,IAAI,MAAM,qBAAqB,OAAO;AAE9C,SAAO;;AAGT,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,MACH;AAGF,MAAI,UAAU,YAAY,UAAU,MAAM;AACxC,WAAQ,OAAO,cAAc,OAAO,EAAE;AACtC,QAAK;AACL;;AAGF,MAAI,UAAU,YAAY,UAAU,MAAM;GACxC,MAAM,MAAM,cAAc,OAAO,EAAE;AACnC,QAAK;GACL,MAAM,QAAQ,OAAO,SAAS,KAAK,GAAG;AACtC,OAAI,CAAC,OAAO,SAAS,MAAM,IAAI,SAAS,KAAK,QAAQ,MACnD,OAAM,IAAI,MAAM,iBAAiB,IAAI,+CAA+C;AAEtF,WAAQ,OAAO;AACf,WAAQ,oBAAoB;AAC5B;;AAGF,MAAI,UAAU,qBAAqB,UAAU,sBAAsB,UAAU,eAAe;GAC1F,MAAM,MAAM,cAAc,OAAO,EAAE;AACnC,QAAK;GACL,MAAM,QAAQ,IACX,MAAM,IAAI,CACV,KAAK,SAAS,KAAK,MAAM,CAAC,CAC1B,OAAO,QAAQ;AAElB,OAAI,MAAM,SAAS,EACjB,SAAQ,iBAAiB;AAE3B;;AAGF,MAAI,UAAU,WAAW;AACvB,WAAQ,QAAQ,cAAc,OAAO,EAAE;AACvC,QAAK;AACL;;AAGF,MAAI,UAAU,eAAe;AAC3B,WAAQ,YAAY,cAAc,OAAO,EAAE;AAC3C,QAAK;AACL;;AAGF,MAAI,UAAU,cAAc;AAC1B,WAAQ,UAAU,cAAc,OAAO,EAAE;AACzC,QAAK;AACL;;AAGF,MAAI,UAAU,YAAY,UAAU,MAAM;AACxC,cAAW;AACX,WAAQ,KAAK,EAAE;;AAGjB,MAAI,MAAM,WAAW,IAAI,CACvB,SAAQ,OAAO,MAAM,qDAAqD,MAAM,KAAK;MAErF,SAAQ,OAAO,MACb,qDAAqD,MAAM,8CAC5D;;AAIL,QAAO;;;;;AAMT,SAAgB,YAAkB;AAChC,SAAQ,OAAO,MACb;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,KAAK,KAAK,CACb;;;;;AC9HH,MAAa,qBAAqB;AAClC,MAAa,+BAA+B;AAC5C,MAAa,8BAA8B,OAAU,KAAK;AAsB1D,SAAgB,8BAAsC;AACpD,QAAO,KAAK,SAAS,EAAE,WAAW,kBAAkB;;AAGtD,eAAsB,YACpB,MACA,OAAO,6BAA6B,EACpC,OAAO,aACP,MAAM,KAAK,KAAK,EACD;CACf,MAAMC,UAA8B;EAClC;EACA;EACA,WAAW,IAAI,KAAK,IAAI,CAAC,aAAa;EACvC;AAED,OAAM,MAAM,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC/C,OAAM,UAAU,MAAM,GAAG,KAAK,UAAU,SAAS,MAAM,EAAE,CAAC,KAAK,OAAO;;AAGxE,eAAsB,kBACpB,OAAO,6BAA6B,EACpC,UAII,EAAE,EACkB;AACxB,KAAI;EACF,MAAM,MAAM,MAAM,SAAS,MAAM,OAAO;EACxC,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,MACE,OAAO,OAAO,SAAS,YACvB,CAAC,OAAO,UAAU,OAAO,KAAK,IAC9B,OAAO,OAAO,KACd,OAAO,OAAO,MAEd,QAAO;AAET,MAAI,OAAO,OAAO,SAAS,YAAY,OAAO,KAAK,WAAW,EAC5D,QAAO;AAET,MAAI,QAAQ,gBAAgB,OAAO,SAAS,QAAQ,aAClD,QAAO;EAGT,MAAM,WAAW,QAAQ,YAAY;EACrC,MAAM,MAAM,QAAQ,OAAO,KAAK,KAAK;EACrC,MAAM,cACJ,OAAO,OAAO,cAAc,WAAW,KAAK,MAAM,OAAO,UAAU,GAAG;AAExE,MAAI,CAAC,OAAO,SAAS,YAAY,IAAI,MAAM,cAAc,SACvD,QAAO;AAGT,SAAO,OAAO;SACR;AACN,SAAO;;;AAIX,eAAsB,oBACpB,SAC+B;CAC/B,MAAM,EACJ,aACA,WACA,MACA,cAAc,6BAA6B,EAC3C,aACE;AAEJ,KAAI,cAAc,OAChB,QAAO,CAAC;EAAE,MAAM;EAAW,UAAU;EAAM,WAAW;EAAO,CAAC;CAGhE,MAAM,aAAa,MAAM,kBAAkB,aAAa,EAAE,cAAc,MAAM,CAAC;CAC/E,MAAM,uBAAO,IAAI,KAAa;CAC9B,MAAMC,aAAmC,EAAE;CAE3C,MAAM,iBAAiB,MAAc,cAA6B;AAChE,MAAI,OAAO,KAAK,OAAO,SAAS,KAAK,IAAI,KAAK,CAC5C;AAEF,OAAK,IAAI,KAAK;AACd,aAAW,KAAK;GAAE;GAAM,UAAU;GAAO;GAAW,CAAC;;AAGvD,KAAI,eAAe,QAAQ,cAAc,eAAe,cAAc,SACpE,eAAc,YAAY,KAAK;AAGjC,eAAc,aAAa,MAAM;AAEjC,MAAK,IAAI,OAAO,cAAc,GAAG,QAAQ,UAAU,QAAQ,EACzD,eAAc,MAAM,MAAM;AAG5B,QAAO;;;;;;;;AChHT,MAAa,uBAAuB,WAAW,OAAO,EACpD,MAAM,WAAW,MAAM,KAAK,IAAI,EAAE,EACnC,CAAC;;;;;;;AAQF,MAAa,oBAAoB,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,aAAa;;;;AAKpF,MAAa,wBAAwB,4BAA4B,MAAM;;;;;AAMvE,MAAaC,4BAAiD;CAC5D,MAAM;CACN,YAAY,EAAE;CACf;;;;;;;AA4BD,SAAgB,qBAAqB,SAAuD;CAC1F,MAAM,oBAAoB,WAAW,MAAM,YAAY,UAAU,QAAQ,YAAY;CACrF,MAAM,qBAAqB,WAAW,MAAM,aAAa,UAAU,QAAQ,aAAa;CACxF,MAAM,oBAAoB,sBAAsB,UAAU,QAAQ,YAAY;CAE9E,MAAMC,sBAA+C;EACnD,MAAM,QAAQ;EACd,aAAa,kBAAkB,UAAU,kBAAkB,OAAO;EACnE;AAED,KAAI,OAAO,QAAQ,gBAAgB,SACjC,qBAAoB,cAAc,QAAQ;AAE5C,KAAI,OAAO,QAAQ,UAAU,SAC3B,qBAAoB,QAAQ,QAAQ;AAEtC,KAAI,mBAAmB,WAAW,mBAAmB,SAAS,OAC5D,qBAAoB,eAAe,mBAAmB;AAExD,KAAI,kBAAkB,WAAW,kBAAkB,SAAS,OAC1D,qBAAoB,cAAc,kBAAkB;CAGtD,MAAM,mBAAmB,qBAAqB,UAAU,oBAAoB;AAC5E,KAAI,iBAAiB,QACnB,QAAO,iBAAiB;AAG1B,QAAO;EACL,MAAM,QAAQ;EACd,aAAa;EACd;;;;;;;;AC9FH,MAAM,2BAA2B;;;;AAKjC,MAAM,+BAA+B;;;;AAKrC,SAAgB,aAAa,OAAuB;AAClD,QAAO,MAAM,QAAQ,kBAAkB,IAAI;;;;;AAM7C,SAAgB,uBAAuB,aAA8B;AACnE,KAAI,CAAC,YACH,QAAO;AAGT,KAAI;EACF,MAAM,SAAS,IAAI,IAAI,YAAY;AACnC,MAAI,CAAC,OAAO,SACV,QAAO;AAST,SAAO,aALL,OAAO,aAAa,eACpB,OAAO,aAAa,eACpB,OAAO,aAAa,UAEO,aAAa,OAAO,QAAQ,SAAS,OAAO,SAC9C;SACrB;AACN,SAAO;;;;;;;;;AAUX,SAAgB,oBAAoB,SAIzB;CACT,MAAM,WAAW,aAAa,QAAQ,iBAAiB;AAEvD,KAAI,CAAC,QAAQ,gBAAgB,CAAC,QAAQ,MACpC,QAAO,SAAS,MAAM,GAAG,yBAAyB;CAIpD,MAAM,SAAS,IADE,aAAa,QAAQ,MAAM,CAAC,MAAM,GAAG,6BAA6B;CAEnF,MAAM,OAAO,GAAG,WAAW;AAE3B,KAAI,KAAK,UAAU,yBACjB,QAAO;CAGT,MAAM,YAAY,2BAA2B,OAAO;AACpD,QAAO,GAAG,SAAS,MAAM,GAAG,KAAK,IAAI,GAAG,UAAU,CAAC,GAAG;;;;;;;;ACXxD,IAAa,qBAAb,cAAwC,MAAM;CAC5C,AAAS;CAET,YAAY,cAAsB;AAChC,QAAM,cAAc,aAAa,+BAA+B;AAChE,OAAK,OAAO;AACZ,OAAK,eAAe;;;;;;AAOxB,IAAa,gBAAb,MAA2B;CACzB,AAAiB,uCAAuB,IAAI,KAA6B;CACzE,AAAiB,sCAAsB,IAAI,KAA6B;CACxE,AAAiB,4CAA4B,IAAI,KAA6B;;;;CAK9E,YAAY,AAAiBC,YAA0B,KAAK,KAAK,EAAE;EAAtC;;;;;;;;CAQ7B,aAAa,cAAsB,OAAkC;EACnE,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,WAAW,KAAK,qBAAqB,IAAI,aAAa;AAE5D,OAAK,qBAAqB,IAAI,cAAc;GAC1C,UAAU;GACV,OAAO,MAAM;GACb,QAAQ,MAAM,UAAU,UAAU;GAClC,KAAK,MAAM,OAAO,UAAU;GAC5B,OAAO,MAAM,SAAS,UAAU;GAChC,SAAS,MAAM,WAAW,UAAU;GACpC,aAAa,UAAU,eAAe;GACtC,YAAY;GACb,CAAC;;;;;CAMJ,cAAc,cAAsB,OAA0B;EAC5D,MAAM,SAAS,KAAK,qBAAqB,IAAI,aAAa;AAC1D,MAAI,CAAC,OACH,OAAM,IAAI,mBAAmB,aAAa;AAG5C,OAAK,gBAAgB,aAAa;AAClC,OAAK,sBAAsB,aAAa;EAExC,MAAMC,YAA4B,MAAM,KAAK,UAAU;GACrD,UAAU;GACV,OAAO,OAAO;GACd;GACA,gBAAgB;GACjB,EAAE;AAEH,OAAK,oBAAoB,IAAI,cAAc,UAAU;AACrD,OAAK,oBAAoB;;;;;CAM3B,iBAAiB,cAA4B;AAC3C,OAAK,sBAAsB,aAAa;AACxC,OAAK,qBAAqB,OAAO,aAAa;AAC9C,OAAK,oBAAoB;;;;;CAM3B,gBAAgB,cAA4B;EAC1C,MAAM,SAAS,KAAK,qBAAqB,IAAI,aAAa;AAC1D,MAAI,CAAC,OACH;AAGF,SAAO,aAAa,KAAK,KAAK;;;;;CAMhC,cAA4B;AAC1B,SAAO,MAAM,KAAK,KAAK,qBAAqB,QAAQ,CAAC,CAClD,KAAK,WAAW,KAAK,aAAa,OAAO,CAAC,CAC1C,QAAQ,WAAW,OAAO,YAAY,EAAE,CACxC,MAAM,GAAG,MAAM,KAAK,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,CAAC;;;;;CAM5F,YAA8B;EAC5B,MAAMC,SAA2B,EAAE;AAEnC,OAAK,MAAM,CAAC,gBAAgB,cAAc,KAAK,0BAA0B,SAAS,EAAE;GAClF,MAAM,kBAAkB,KAAK,uBAAuB,UAAU;GAC9D,MAAM,kBAAkB,gBAAgB;AACxC,OAAI,CAAC,gBACH;GAGF,MAAM,UAAU,gBACb,KAAK,aAAa;IACjB,MAAM,SAAS,KAAK,qBAAqB,IAAI,SAAS,SAAS;AAC/D,WAAO,SAAS,KAAK,aAAa,OAAO,GAAG;KAC5C,CACD,QAAQ,WAAiC,QAAQ,OAAO,CAAC;AAE5D,UAAO,KAAK;IACV,GAAG,gBAAgB;IACnB,MAAM;IACN,cAAc,gBAAgB,KAAK;IACnC;IACD,CAAC;;AAGJ,SAAO,OAAO,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;;;;;;;CAW5D,kBAAkB,SAIY;EAC5B,MAAM,YAAY,KAAK,0BAA0B,IAAI,QAAQ,SAAS;AACtE,MAAI,CAAC,aAAa,UAAU,WAAW,EACrC,QAAO;EAGT,MAAM,kBAAkB,KAAK,uBAAuB,UAAU;AAE9D,MAAI,QAAQ,UAAU;GACpB,MAAM,eAAe,gBAAgB,MAClC,eAAaC,WAAS,aAAa,QAAQ,SAC7C;AACD,OAAI,aACF,QAAO;IACL,cAAc,aAAa;IAC3B,MAAM,aAAa;IACnB,gBAAgB,aAAa;IAC9B;GAGH,MAAM,UAAU,gBAAgB,MAAM,eAAaA,WAAS,UAAU,QAAQ,SAAS;AACvF,OAAI,QACF,QAAO;IACL,cAAc,QAAQ;IACtB,MAAM,QAAQ;IACd,gBAAgB,QAAQ;IACzB;AAGH,UAAO;;AAGT,MAAI,QAAQ,cAAc;GACxB,MAAM,eAAe,gBAAgB,MAClC,eAAaA,WAAS,UAAU,QAAQ,aAC1C;AACD,OAAI,CAAC,aACH,QAAO;AAGT,UAAO;IACL,cAAc,aAAa;IAC3B,MAAM,aAAa;IACnB,gBAAgB,aAAa;IAC9B;;EAGH,MAAM,WAAW,gBAAgB;AACjC,MAAI,CAAC,SACH,QAAO;AAGT,SAAO;GACL,cAAc,SAAS;GACvB,MAAM,SAAS;GACf,gBAAgB,SAAS;GAC1B;;;;;CAMH,AAAQ,aAAa,QAAoC;AACvD,SAAO;GACL,GAAG;GACH,WAAW,KAAK,oBAAoB,IAAI,OAAO,SAAS,EAAE,UAAU;GACrE;;;;;CAMH,AAAQ,sBAAsB,cAA4B;AACxD,OAAK,oBAAoB,OAAO,aAAa;;;;;;;;CAS/C,AAAQ,qBAA2B;EACjC,MAAM,iCAAiB,IAAI,KAA6B;AACxD,OAAK,MAAM,aAAa,KAAK,oBAAoB,QAAQ,CACvD,MAAK,MAAM,YAAY,WAAW;GAChC,MAAM,MAAM,SAAS,KAAK;GAC1B,MAAM,QAAQ,eAAe,IAAI,IAAI,IAAI,EAAE;AAC3C,SAAM,KAAK,SAAS;AACpB,kBAAe,IAAI,KAAK,MAAM;;AAIlC,OAAK,0BAA0B,OAAO;AAEtC,OAAK,MAAM,GAAG,cAAc,gBAAgB;GAE1C,MAAM,eADe,IAAI,IAAI,UAAU,KAAK,MAAM,EAAE,MAAM,CAAC,CACzB,OAAO;AAEzC,QAAK,MAAM,YAAY,WAAW;AAChC,aAAS,iBAAiB,oBAAoB;KAC5C,kBAAkB,SAAS,KAAK;KAChC,OAAO,SAAS;KAChB;KACD,CAAC;IAEF,MAAM,WAAW,KAAK,0BAA0B,IAAI,SAAS,eAAe,IAAI,EAAE;AAClF,aAAS,KAAK,SAAS;AACvB,SAAK,0BAA0B,IAC7B,SAAS,gBACT,KAAK,uBAAuB,SAAS,CACtC;;;;;;;CAQP,AAAQ,uBAAuB,WAA2C;AACxE,SAAO,UAAU,OAAO,CAAC,MAAM,GAAG,MAAM;GACtC,MAAM,UAAU,KAAK,qBAAqB,IAAI,EAAE,SAAS;GACzD,MAAM,UAAU,KAAK,qBAAqB,IAAI,EAAE,SAAS;GACzD,MAAM,YAAY,SAAS,cAAc;GACzC,MAAM,YAAY,SAAS,cAAc;AAEzC,UAAO,KAAK,eAAe,WAAW,WAAW,EAAE,UAAU,EAAE,SAAS;IACxE;;;;;CAMJ,AAAQ,eACN,cACA,eACA,QACA,SACQ;EACR,MAAM,WAAW,eAAe;AAChC,MAAI,aAAa,EACf,QAAO;AAET,SAAO,OAAO,cAAc,QAAQ;;;;;;;;;AC1UxC,MAAa,4BAA4B,EAAE,OAAO;CAChD,MAAM,EAAE,QAAQ,QAAQ;CACxB,OAAO,EAAE,QAAQ,CAAC,IAAI,EAAE;CACxB,QAAQ,EAAE,QAAQ,CAAC,UAAU;CAC7B,KAAK,EAAE,QAAQ,CAAC,UAAU;CAC1B,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,SAAS,EAAE,QAAQ,CAAC,UAAU;CAC/B,CAAC;;;;;;AAOF,MAAa,gCAAgC,EAAE,OAAO;CACpD,MAAM,EAAE,QAAQ,aAAa;CAC7B,OAAO,EACJ,MAAM,kBAAkB,CACxB,WAAW,UAAuB,MAAM,IAAI,qBAAqB,CAAC;CACtE,CAAC;;;;;;;;AASF,MAAa,mCAAmC,EAAE,OAAO;CACvD,MAAM,EAAE,QAAQ,gBAAgB;CAChC,OAAO,EACJ,MAAM,kBAAkB,CACxB,WAAW,UAAuB,MAAM,IAAI,qBAAqB,CAAC;CACtE,CAAC;;;;;;;AAQF,MAAa,iCAAiC,EAAE,OAAO;CACrD,MAAM,EAAE,QAAQ,SAAS;CACzB,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE;CACzB,QAAQ,EAAE,SAAS;CACpB,CAAC;;;;AAKF,MAAa,2BAA2B,EAAE,OAAO,EAC/C,MAAM,EAAE,QAAQ,OAAO,EACxB,CAAC;;;;AAKF,MAAa,8BAA8B,EAAE,mBAAmB,QAAQ;CACtE;CACA;CACA;CACA;CACA;CACD,CAAC;;;;AAeF,MAAa,wBAAwB,EAAE,OAAO;CAC5C,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE;CACvB,YAAY,EAAE,QAAQ,CAAC,IAAI,EAAE;CAC7B,OAAO,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU;CACnC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,MAAM;CACxC,SAAS,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU;CACrC,WAAW,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU;CACxC,CAAC;;;;AAUF,MAAa,2BAA2B,EAAE,OAAO;CAC/C,MAAM,EAAE,QAAQ,eAAe;CAC/B,SAAS,EAAE,QAAQ,qBAAqB;CACxC,SAAS,EAAE,QAAQ,EAAE;CACrB,GAAG,sBAAsB;CAC1B,CAAC;;;;AAUF,MAAa,2BAA2B,EAAE,OAAO;CAC/C,MAAM,EAAE,QAAQ,SAAS;CACzB,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE;CACzB,UAAU,EAAE,QAAQ,CAAC,IAAI,EAAE;CAC3B,MAAM;CACP,CAAC;;;;AAKF,MAAa,yBAAyB,EAAE,OAAO,EAC7C,MAAM,EAAE,QAAQ,OAAO,EACxB,CAAC;;;;AAKF,MAAa,2BAA2B,EAAE,OAAO,EAC/C,MAAM,EAAE,QAAQ,SAAS,EAC1B,CAAC;;;;AAKF,MAAa,8BAA8B,EAAE,mBAAmB,QAAQ;CACtE;CACA;CACA;CACA;CACD,CAAC;;;;;;;AAcF,MAAa,wBAAwB,EAAE,OAAO;CAC5C,UAAU,EAAE,QAAQ;CACpB,OAAO,EAAE,QAAQ;CACjB,QAAQ,EAAE,QAAQ,CAAC,UAAU;CAC7B,KAAK,EAAE,QAAQ,CAAC,UAAU;CAC1B,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,SAAS,EAAE,QAAQ,CAAC,UAAU;CAC9B,aAAa,EAAE,QAAQ;CACvB,YAAY,EAAE,QAAQ;CACtB,WAAW,EAAE,QAAQ;CACtB,CAAC;;;;AAUF,MAAa,yBAAyB,EAAE,OAAO,EAC7C,MAAM,EAAE,QAAQ,cAAc,EAC/B,CAAC;;;;AAKF,MAAa,6BAA6B,EAAE,OAAO,EACjD,MAAM,EAAE,QAAQ,mBAAmB,EACpC,CAAC;;;;AAKF,MAAa,0BAA0B,EAAE,OAAO;CAC9C,MAAM,EAAE,QAAQ,eAAe;CAC/B,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE;CACzB,UAAU,EAAE,QAAQ,CAAC,IAAI,EAAE;CAC3B,MAAM;CACP,CAAC;;;;AAKF,MAAa,mCAAmC,EAAE,mBAAmB,QAAQ;CAC3E;CACA;CACA;CACD,CAAC;AAOF,MAAM,0BAA0B;CAC9B,OAAO,EAAE,MAAM,qBAAqB;CACpC,SAAS,EAAE,MAAM,sBAAsB,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;CAC9D,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;CAChF;;;;AAKD,MAAa,yBAAyB,EAAE,OAAO;CAC7C,MAAM,EAAE,QAAQ,cAAc;CAC9B,GAAG;CACJ,CAAC;;;;AAKF,MAAa,0BAA0B,EAAE,OAAO;CAC9C,MAAM,EAAE,QAAQ,eAAe;CAC/B,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE;CACzB,QAAQ;CACT,CAAC;;;;AAKF,MAAa,gCAAgC,EAAE,OAAO;CACpD,MAAM,EAAE,QAAQ,sBAAsB;CACtC,GAAG;CACJ,CAAC;;;;AAKF,MAAa,mCAAmC,EAAE,mBAAmB,QAAQ;CAC3E;CACA;CACA;CACA;CACD,CAAC;;;;ACvOF,MAAM,yBAAyB;AAC/B,MAAM,2BAA2B;AACjC,MAAM,0BAA0B;AAChC,MAAM,kCAAkC;AACxC,MAAM,wBAAwB;AAC9B,MAAM,8BAA8B;AACpC,MAAM,yBAAyB,IAAI,IAAI;CACrC;CACA;CACA;CACD,CAAC;;;;;;;;;;AAwFF,IAAa,oBAAb,cAAuC,aAAa;;;;;CAKlD,AAAS;CAET,AAAiB;CACjB,AAAiB;CACjB,AAAQ;CACR,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,AAAQ,MAA8B;CACtC,AAAiB,uCAAuB,IAAI,KAAwB;CACpE,AAAiB,qCAAqB,IAAI,KAAgC;CAC1E,AAAiB,2CAA2B,IAAI,KAAa;CAC7D,AAAiB,kDAAkC,IAAI,KAGpD;CACH,AAAiB,yCAAyB,IAAI,KAAqB;CACnE,AAAiB,gCAAgC;AAC/C,OAAK,yBAAyB;;CAGhC,AAAQ,QAA6B;CACrC,AAAQ,eAAiC;CACzC,AAAQ,uBAA6D;CACrE,AAAQ,uBAAuB;;;;CAI/B,AAAiB,0BAA0B;CAC3C,AAAiB,6BAA6B;CAC9C,AAAQ,0BAA0B;CAClC,AAAiB,2CAA2B,IAAI,KAAgC;CAChF,AAAQ,cAA2B,EAAE;CACrC,AAAQ,gBAAmC,EAAE;CAC7C,AAAQ,sBAAgD,EAAE;CAC1D,AAAQ,WAAW;;;;CAKnB,YAAY,UAAoC,EAAE,EAAE,UAA0B;AAC5E,SAAO;AACP,OAAK,WAAW,YAAY,IAAI,eAAe;AAE/C,OAAK,OAAO,QAAQ,QAAQ;AAC5B,OAAK,gBAAgB,QAAQ,QAAQ;AACrC,OAAK,cAAc,KAAK;AACxB,OAAK,oBAAoB,QAAQ,qBAAqB;AACtD,OAAK,eACH,QAAQ,gBAAgB,KAAK,IAAI,8BAA8B,KAAK,cAAc;AACpF,OAAK,cAAc,QAAQ,eAAe,6BAA6B;AACvE,OAAK,iBAAiB,QAAQ,kBAAkB,CAAC,IAAI;AACrD,OAAK,kBAAkB,QAAQ,mBAAmB;AAClD,OAAK,kBAAkB,QAAQ,mBAAmB;AAClD,OAAK,QAAQ,QAAQ;AACrB,OAAK,YAAY,QAAQ;AACzB,OAAK,UAAU,QAAQ;AACvB,OAAK,aAAa,YAAY;AAE9B,MAAI,KAAK,kBAAkB,MAAM,KAAK,gBAAgB,KAAK,KAAK,gBAAgB,OAC9E,OAAM,IAAI,MACR,gBAAgB,KAAK,cAAc,wDACpC;AAEH,MAAI,KAAK,eAAe,KAAK,iBAAiB,KAAK,kBAAkB,EACnE,OAAM,IAAI,MACR,sBAAsB,KAAK,cAAc,GAAG,KAAK,aAAa,iEAC/D;AAEH,MAAI,KAAK,mBAAmB,EAC1B,OAAM,IAAI,MAAM,2BAA2B,KAAK,gBAAgB,2BAA2B;AAE7F,MAAI,KAAK,mBAAmB,EAC1B,OAAM,IAAI,MAAM,2BAA2B,KAAK,gBAAgB,2BAA2B;;;;;CAO/F,IAAI,OAA4B;AAC9B,SAAO,KAAK;;;;;;CAOd,IAAI,OAAe;AACjB,SAAO,KAAK;;;;;;CAOd,qBAAkC;AAChC,SAAO,KAAK,UAAU,WAAW,CAAC,GAAG,KAAK,YAAY,GAAG,EAAE;;;;;;CAO7D,uBAA0C;AACxC,SAAO,KAAK,UAAU,WAAW,CAAC,GAAG,KAAK,cAAc,GAAG,EAAE;;;;;;;CAQ/D,4BAAsD;AACpD,SAAO,KAAK,UAAU,WAAW,EAAE,GAAG,KAAK,qBAAqB,GAAG,EAAE;;;;;;;CAQvE,MAAM,QAAuB;AAC3B,MAAI,KAAK,OAAO,KAAK,aACnB;AAGF,OAAK,WAAW;AAChB,QAAM,KAAK,wBAAwB;;CAGrC,MAAc,yBAAwC;AACpD,MAAI,KAAK,kBAAkB,GAAG;AAC5B,SAAM,KAAK,cAAc,EAAE;AAC3B;;EAGF,MAAM,aAAa,MAAM,oBAAoB;GAC3C,aAAa,KAAK;GAClB,GAAI,KAAK,oBAAoB,EAAE,WAAW,KAAK,eAAe,GAAG,EAAE;GACnE,MAAM,KAAK;GACX,aAAa,KAAK;GAClB,UAAU,KAAK;GAChB,CAAC;AAEF,OAAK,MAAM,aAAa,WACtB,KAAI;AACF,SAAM,KAAK,cAAc,UAAU,KAAK;AACxC,SAAM,YAAY,KAAK,MAAM,KAAK,aAAa,KAAK,KAAK;AACzD;WACO,KAAK;AACZ,OAAI,CAAC,KAAK,oBAAoB,IAAI,CAChC,OAAM;AAIR,OADiB,MAAM,KAAK,yBAAyB,UAAU,KAAK,EACtD;AACZ,UAAM,YAAY,KAAK,MAAM,KAAK,aAAa,KAAK,KAAK;AACzD;;AAGF,OAAI,UAAU,SACZ,OAAM,IAAI,MACR,QAAQ,UAAU,KAAK,kEACxB;AAGH,WAAQ,OAAO,MACb,mCAAmC,UAAU,KAAK,yDACnD;;AAIL,QAAM,IAAI,MACR,uDAAuD,KAAK,cAAc,GAAG,KAAK,aAAa,GAChG;;CAGH,AAAQ,oBAAoB,OAAyB;AACnD,SACE,iBAAiB,UAChB,UAAU,QACN,MAAgC,SAAS,eAC1C,MAAM,QAAQ,SAAS,aAAa;;CAI5C,MAAc,yBAAyB,MAAgC;AACrE,MAAI;AACF,SAAM,KAAK,cAAc,KAAK;AAC9B,WAAQ,OAAO,MACb,kEAAkE,KAAK,8BACxE;AACD,UAAO;UACD;AACN,UAAO;;;;;;CAOX,MAAM,OAAsB;AAC1B,OAAK,WAAW;AAEhB,MAAI,KAAK,sBAAsB;AAC7B,gBAAa,KAAK,qBAAqB;AACvC,QAAK,uBAAuB;;AAG9B,MAAI,KAAK,UAAU,UAAU;AAC3B,QAAK,MAAM,WAAW,KAAK,yBAAyB,QAAQ,EAAE;AAC5D,iBAAa,QAAQ,UAAU;AAC/B,YAAQ,uBAAO,IAAI,MAAM,uBAAuB,CAAC;;AAEnD,QAAK,yBAAyB,OAAO;AACrC,QAAK,cAAc,EAAE;AACrB,QAAK,gBAAgB,EAAE;AACvB,QAAK,sBAAsB,EAAE;AAE7B,OAAI,KAAK,cAAc;AACrB,QAAI;AACF,UAAK,aAAa,MAAM,KAAM,6BAA6B;aACpD,KAAK;AACZ,aAAQ,OAAO,MACb,2EAA2E,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,IAC7H;;AAEH,SAAK,eAAe;;AAEtB;;AAGF,OAAK,IAAI,gBAAgB,KAAK,wBAAwB;AAEtD,OAAK,MAAM,UAAU,KAAK,qBAAqB,QAAQ,CACrD,KAAI;AACF,UAAO,MAAM,MAAM,sBAAsB;WAClC,KAAK;AACZ,WAAQ,OAAO,MACb,oEAAoE,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,IACtH;;AAIL,OAAK,MAAM,cAAc,KAAK,gCAAgC,QAAQ,CACpE,eAAc,WAAW;AAE3B,OAAK,gCAAgC,OAAO;AAC5C,OAAK,uBAAuB,OAAO;AAEnC,OAAK,qBAAqB,OAAO;AACjC,OAAK,yBAAyB,OAAO;AAErC,OAAK,MAAM,WAAW,KAAK,mBAAmB,QAAQ,EAAE;AACtD,gBAAa,QAAQ,UAAU;AAC/B,WAAQ,uBAAO,IAAI,MAAM,wDAAwD,CAAC;;AAEpF,OAAK,mBAAmB,OAAO;EAE/B,MAAM,MAAM,KAAK;AACjB,OAAK,MAAM;AAEX,MAAI,CAAC,IACH;AAGF,QAAM,IAAI,SAAe,YAAY;AACnC,OAAI,OAAO,QAAgB;AACzB,QAAI,IACF,SAAQ,OAAO,MACb,4DAA4D,IAAI,QAAQ,IACzE;AAEH,aAAS;KACT;IACF;;;;;;CAOJ,aAAa,cAA4B;AACvC,MAAI,KAAK,UAAU,SACjB,OAAM,IAAI,MAAM,gDAAgD;EAElE,MAAM,SAAS,KAAK,qBAAqB,IAAI,aAAa;AAC1D,MAAI,CAAC,UAAU,OAAO,eAAe,UAAU,KAC7C,OAAM,IAAI,MAAM,UAAU,aAAa,mBAAmB;EAE5D,MAAMC,UAAiC,EAAE,MAAM,UAAU;AACzD,MAAI;AACF,UAAO,KAAK,KAAK,UAAU,QAAQ,CAAC;WAC7B,KAAK;AACZ,SAAM,IAAI,MAAM,0BAA0B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;;;;;;;CAQjG,MAAM,WACJ,UACA,MACA,UAGI,EAAE,EACwB;AAC9B,MAAI,KAAK,UAAU,SACjB,QAAO,KAAK,mBAAmB,UAAU,KAAK;AAGhD,SAAO,KAAK,kBAAkB,UAAU,MAAM,QAAQ;;CAGxD,MAAc,cAAc,OAAO,KAAK,aAA4B;EAClE,MAAM,MAAM,MAAM,IAAI,SAA0B,SAAS,WAAW;GAClE,MAAM,SAAS,IAAI,gBAAgB;IACjC,kBAAkB,cAAc;AAC9B,UAAK,MAAM,YAAY,UACrB,KAAI,uBAAuB,IAAI,SAAS,CACtC,QAAO;AAGX,YAAO;;IAET,MAAM,KAAK;IACX;IACA,YAAY,KAAK;IAClB,CAAC;GAEF,MAAM,oBAAoB;AACxB,WAAO,IAAI,SAAS,QAAQ;AAC5B,YAAQ,OAAO;;GAEjB,MAAM,WAAW,QAAe;AAC9B,WAAO,IAAI,aAAa,YAAY;AACpC,WAAO,IAAI;;AAGb,UAAO,KAAK,aAAa,YAAY;AACrC,UAAO,KAAK,SAAS,QAAQ;IAC7B;AAEF,MAAI,GAAG,eAAe,WAAsB;GAC1C,MAAM,eAAe,YAAY;AACjC,QAAK,qBAAqB,IAAI,cAAc,OAAO;AAEnD,OAAI;AACF,WAAO,KAAK,KAAK,UAAU,KAAK,kBAAkB,CAAC,CAAC;YAC7C,KAAK;AACZ,YAAQ,OAAO,MACb,wEAAwE,aAAa,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,IAC3I;AACD,WAAO,MAAM,MAAM,8BAA8B;AACjD;;AAGF,UAAO,GAAG,YAAY,QAA2B;AAC/C,SAAK,gBAAgB,cAAc,IAAI;KACvC;AAEF,UAAO,GAAG,eAAe;AACvB,SAAK,cAAc,aAAa;KAChC;AAEF,UAAO,GAAG,UAAU,QAAe;AACjC,YAAQ,OAAO,MACb,0DAA0D,aAAa,IAAI,IAAI,QAAQ,IACxF;AACD,SAAK,cAAc,aAAa;KAChC;AAEF,QAAK,eAAe,aAAa;IACjC;AAEF,MAAI,GAAG,UAAU,QAAe;AAC9B,WAAQ,OAAO,MAAM,uDAAuD,IAAI,QAAQ,IAAI;AAC5F,QAAK,KAAK,SAAS,IAAI;IACvB;AAEF,OAAK,MAAM;AACX,OAAK,QAAQ;EAEb,MAAM,UAAU,IAAI,SAAS;AAC7B,MAAI,WAAW,OAAO,YAAY,SAChC,MAAK,cAAc,QAAQ;AAG7B,OAAK,GAAG,gBAAgB,KAAK,wBAAwB;;CAGvD,AAAQ,mBAAuC;AAC7C,SAAO;GACL,MAAM;GACN,SAAS;GACT,SAAS;GACT,MAAM,KAAK;GACX,YAAY,KAAK;GACjB,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,SAAS,KAAK,WAAW,KAAK;GAC9B,WAAW,KAAK;GACjB;;CAGH,AAAQ,kBACN,UACA,MACA,SAC8B;EAC9B,MAAMC,iBAAiF,EACrF,UACD;AACD,MAAI,QAAQ,aAAa,OACvB,gBAAe,WAAW,QAAQ;AAEpC,MAAI,QAAQ,iBAAiB,OAC3B,gBAAe,eAAe,QAAQ;EAGxC,MAAM,WAAW,KAAK,SAAS,kBAAkB,eAAe;AAEhE,MAAI,CAAC,SACH,OAAM,IAAI,MAAM,2CAA2C,SAAS,GAAG;EAGzE,MAAM,SAAS,KAAK,qBAAqB,IAAI,SAAS,aAAa;AACnE,MAAI,CAAC,UAAU,OAAO,eAAe,UAAU,KAC7C,OAAM,IAAI,MACR,eAAe,SAAS,aAAa,sCAAsC,SAAS,GACrF;EAGH,MAAM,SAAS,YAAY;AAE3B,SAAO,IAAI,SAA8B,SAAS,WAAW;GAC3D,MAAM,YAAY,iBAAiB;AACjC,SAAK,mBAAmB,OAAO,OAAO;AACtC,2BACE,IAAI,MAAM,wBAAwB,SAAS,oBAAoB,KAAK,gBAAgB,IAAI,CACzF;MACA,KAAK,gBAAgB;AAExB,QAAK,mBAAmB,IAAI,QAAQ;IAClC;IACA,cAAc,SAAS;IACvB;IACA;IACA;IACD,CAAC;GAEF,MAAMD,UAAiC;IACrC,MAAM;IACN;IACA,UAAU,SAAS,KAAK;IACxB;IACD;AAED,OAAI;AACF,WAAO,KAAK,KAAK,UAAU,QAAQ,CAAC;YAC7B,KAAK;AACZ,iBAAa,UAAU;AACvB,SAAK,mBAAmB,OAAO,OAAO;AACtC,2BACE,IAAI,MACF,uCAAuC,SAAS,KAAK,eAAe,QAAQ,IAAI,UAAU,MAC3F,CACF;;IAEH;;;;;;;;CASJ,AAAQ,gBAAgB,cAAsB,KAA8B;EAC1E,MAAM,OAAO,KAAK,cAAc,IAAI;EAEpC,IAAIE;AACJ,MAAI;AACF,gBAAa,KAAK,MAAM,KAAK;WACtB,KAAK;GACZ,MAAM,UAAU,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO;AACjE,WAAQ,OAAO,MACb,2DAA2D,aAAa,IAAI,eAAe,QAAQ,IAAI,UAAU,cAAc,KAAK,QAAQ,IAC7I;AACD;;EAGF,MAAM,YACJ,OAAO,eAAe,YAAY,eAAe,OAC5C,WAAuC,OACxC;AAEN,MAAI,OAAO,cAAc,YAAY,UAAU,WAAW,SAAS,EAAE;GACnE,MAAM,WAAW,iCAAiC,UAAU,WAAW;AACvE,OAAI,SAAS,QACX,MAAK,qBAAqB,cAAc,SAAS,KAAK;OAEtD,SAAQ,OAAO,MACb,yDAAyD,aAAa,IAAI,SAAS,MAAM,QAAQ,IAClG;AAEH;;AAGF,OAAK,SAAS,gBAAgB,aAAa;EAE3C,MAAM,gBAAgB,4BAA4B,UAAU,WAAW;AACvE,MAAI,CAAC,cAAc,SAAS;AAC1B,WAAQ,OAAO,MACb,8DAA8D,aAAa,SAAS,UAAU,KAAK,cAAc,MAAM,QAAQ,IAChI;AACD;;EAGF,MAAM,UAAU,cAAc;AAE9B,UAAQ,QAAQ,MAAhB;GACE,KAAK;AACH,QAAI;AACF,SAAI,CAAC,KAAK,oBAAoB,QAAQ,OAAO,EAAE;AAC7C,cAAQ,OAAO,MACb,+CAA+C,aAAa,gCAAgC,QAAQ,UAAU,UAAU,IACzH;AAED,MADe,KAAK,qBAAqB,IAAI,aAAa,EAClD,MAAM,MAAM,0BAA0B;AAC9C;;AAEF,UAAK,SAAS,aAAa,cAAc,QAAQ;AACjD,UAAK,KAAK,eAAe;aAClB,KAAK;AACZ,aAAQ,OAAO,MACb,uEAAuE,aAAa,IAAI,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,IAAI,CAAC,IACzJ;;AAEH;GAEF,KAAK;GACL,KAAK;AACH,QAAI;AACF,UAAK,SAAS,cAAc,cAAc,QAAQ,MAAM;AACxD,UAAK,KAAK,eAAe;aAClB,KAAK;AACZ,SAAI,eAAe,mBACjB,SAAQ,OAAO,MACb,yCAAyC,aAAa,sCACvD;UACI;AACL,cAAQ,OAAO,MACb,uEAAuE,aAAa,IAAI,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,IAAI,CAAC,IACzJ;AAED,MADe,KAAK,qBAAqB,IAAI,aAAa,EAClD,MAAM,MAAM,2BAA2B;;;AAGnD;GAEF,KAAK,UAAU;IACb,MAAM,UAAU,KAAK,mBAAmB,IAAI,QAAQ,OAAO;AAC3D,QAAI,CAAC,SAAS;AACZ,aAAQ,OAAO,MACb,iEAAiE,QAAQ,OAAO,IACjF;AACD;;AAGF,iBAAa,QAAQ,UAAU;AAC/B,SAAK,mBAAmB,OAAO,QAAQ,OAAO;AAC9C,YAAQ,QAAQ,KAAK,wBAAwB,QAAQ,OAAO,CAAC;AAC7D;;GAGF,KAAK;AACH,SAAK,uBAAuB,IAAI,cAAc,KAAK,KAAK,CAAC;AACzD;;;;;;CAON,AAAQ,qBAAqB,cAAsB,SAA2C;AAC5F,UAAQ,QAAQ,MAAhB;GACE,KAAK;AACH,SAAK,yBAAyB,IAAI,aAAa;AAC/C;GAEF,KAAK,oBAAoB;IACvB,MAAM,WAAW,KAAK,uBAAuB,cAAc;IAC3D,MAAM,SAAS,KAAK,qBAAqB,IAAI,aAAa;AAC1D,QAAI,QAAQ,eAAe,UAAU,KACnC,KAAI;AACF,YAAO,KAAK,KAAK,UAAU,SAAS,CAAC;aAC9B,KAAK;AACZ,aAAQ,OAAO,MACb,qEAAqE,aAAa,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,IACxI;;AAGL;;GAGF,KAAK,gBAAgB;IACnB,MAAM,EAAE,QAAQ,UAAU,SAAS;AACnC,KAAM,YAAY;AAChB,SAAI;MAEF,MAAMC,WAAuC;OAC3C,MAAM;OACN;OACA,QAJa,MAAM,KAAK,kBAAkB,UAAU,QAAQ,EAAE,EAAE,EAAE,CAAC;OAKpE;MACD,MAAM,SAAS,KAAK,qBAAqB,IAAI,aAAa;AAC1D,UAAI,QAAQ,eAAe,UAAU,KACnC,KAAI;AACF,cAAO,KAAK,KAAK,UAAU,SAAS,CAAC;eAC9B,SAAS;AAChB,eAAQ,OAAO,MACb,6DAA6D,aAAa,IAAI,mBAAmB,QAAQ,QAAQ,UAAU,OAAO,QAAQ,CAAC,IAC5I;;UAGH,SAAQ,OAAO,MACb,2CAA2C,aAAa,yCAAyC,OAAO,uBACzG;cAEI,KAAK;MACZ,MAAMA,WAAuC;OAC3C,MAAM;OACN;OACA,QAAQ;QACN,SAAS,CACP;SACE,MAAM;SACN,MAAM,4BAA4B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;SACnF,CACF;QACD,SAAS;QACV;OACF;MACD,MAAM,SAAS,KAAK,qBAAqB,IAAI,aAAa;AAC1D,UAAI,QAAQ,eAAe,UAAU,KACnC,KAAI;AACF,cAAO,KAAK,KAAK,UAAU,SAAS,CAAC;eAC9B,SAAS;AAChB,eAAQ,OAAO,MACb,mEAAmE,aAAa,IAAI,mBAAmB,QAAQ,QAAQ,UAAU,OAAO,QAAQ,CAAC,IAClJ;;UAGH,SAAQ,OAAO,MACb,2CAA2C,aAAa,+CAA+C,OAAO,uBAC/G;;QAGH;AACJ;;;;;;;CAQN,AAAQ,0BAAgC;AACtC,MAAI,KAAK,yBAAyB,SAAS,EACzC;EAGF,MAAM,UAAU,KAAK,uBAAuB,sBAAsB;EAClE,MAAM,UAAU,KAAK,UAAU,QAAQ;AAEvC,OAAK,MAAM,gBAAgB,KAAK,0BAA0B;GACxD,MAAM,SAAS,KAAK,qBAAqB,IAAI,aAAa;AAC1D,OAAI,QAAQ,eAAe,UAAU,KACnC,KAAI;AACF,WAAO,KAAK,QAAQ;YACb,KAAK;AACZ,YAAQ,OAAO,MACb,yEAAyE,aAAa,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,IAC5I;;;;;;;CAST,AAAQ,cAAc,cAA4B;AAChD,OAAK,cAAc,aAAa;AAChC,OAAK,yBAAyB,OAAO,aAAa;AAClD,OAAK,SAAS,iBAAiB,aAAa;AAC5C,OAAK,qBAAqB,OAAO,aAAa;AAC9C,OAAK,KAAK,eAAe;AAEzB,OAAK,MAAM,CAAC,QAAQ,YAAY,KAAK,mBAAmB,SAAS,EAAE;AACjE,OAAI,QAAQ,iBAAiB,aAC3B;AAGF,gBAAa,QAAQ,UAAU;AAC/B,QAAK,mBAAmB,OAAO,OAAO;AACtC,WAAQ,uBAAO,IAAI,MAAM,eAAe,aAAa,iCAAiC,CAAC;;;CAI3F,AAAQ,eAAe,cAA4B;AACjD,OAAK,uBAAuB,IAAI,cAAc,KAAK,KAAK,CAAC;EAEzD,MAAM,aAAa,kBAAkB;GACnC,MAAM,SAAS,KAAK,qBAAqB,IAAI,aAAa;AAC1D,OAAI,CAAC,UAAU,OAAO,eAAe,UAAU,MAAM;AACnD,SAAK,cAAc,aAAa;AAChC;;AAIF,OAAI,KAAK,yBAAyB,IAAI,aAAa,CACjD;GAGF,MAAM,WAAW,KAAK,uBAAuB,IAAI,aAAa,IAAI;AAClE,OAAI,KAAK,KAAK,GAAG,WAAW,6BAA6B;AACvD,YAAQ,OAAO,MACb,yCAAyC,aAAa,8BACvD;AACD,SAAK,cAAc,aAAa;AAChC,WAAO,MAAM,MAAM,oBAAoB;AACvC;;AAGF,OAAI;AACF,WAAO,KAAK,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC,CAAC;YACtC,KAAK;AACZ,YAAQ,OAAO,MACb,+DAA+D,aAAa,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,IAClI;;KAEF,sBAAsB;AAEzB,OAAK,gCAAgC,IAAI,cAAc,WAAW;;CAGpE,AAAQ,cAAc,cAA4B;EAChD,MAAM,aAAa,KAAK,gCAAgC,IAAI,aAAa;AACzE,MAAI,eAAe,QAAW;AAC5B,iBAAc,WAAW;AACzB,QAAK,gCAAgC,OAAO,aAAa;;AAE3D,OAAK,uBAAuB,OAAO,aAAa;;CAGlD,MAAc,cAAc,OAAO,KAAK,aAA4B;AAClE,OAAK,WAAW;EAEhB,MAAM,eAAe,KAAK;EAC1B,MAAM,eAAe,KAAK;AAC1B,OAAK,QAAQ;AACb,OAAK,cAAc;AAEnB,MAAI;GACF,MAAM,EAAE,kBAAkB,WAAW,MAAM,KAAK,qBAAqB,KAAK;AAC1E,QAAK,eAAe;AACpB,QAAK,uBAAuB;AAC5B,QAAK,0BAA0B;AAC/B,QAAK,oBAAoB,QAAQ,iBAAiB;WAC3C,OAAO;AACd,QAAK,QAAQ;AACb,QAAK,cAAc;AACnB,SAAM;;;CAIV,MAAc,qBACZ,MACgF;AAChF,OAAK,WAAW;AAEhB,SAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,QAAQ,QAAQ,KAAK,KAAK,GAAG;GACnC,MAAM,KAAK,IAAI,UAAU,OAAO,CAAC,yBAAyB,uBAAuB,CAAC;GAClF,MAAMC,mBAAiD,EAAE;GAEzD,MAAM,gBAAgB;AACpB,iBAAa,UAAU;AACvB,OAAG,IAAI,SAAS,mBAAmB;AACnC,OAAG,IAAI,SAAS,mBAAmB;AACnC,OAAG,IAAI,WAAW,qBAAqB;AACvC,OAAG,IAAI,QAAQ,OAAO;;GAGxB,MAAM,cAAc,UAAiB;AACnC,OAAG,KAAK,eAAe,GAErB;AACF,aAAS;AACT,WAAO,MAAM;;GAGf,MAAM,eAAe;AACnB,aAAS;AACT,YAAQ;KAAE;KAAkB,QAAQ;KAAI,CAAC;;GAG3C,MAAM,eAAe;AACnB,QAAI;AACF,UAAK,yBAAyB,GAAG;aAC1B,KAAK;AACZ,gCACE,IAAI,MACF,+CAA+C,MAAM,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAC1G,CACF;;;GAIL,MAAM,sBAAsB,QAAe;AACzC,+BAAW,IAAI,MAAM,wCAAwC,MAAM,IAAI,IAAI,UAAU,CAAC;;GAGxF,MAAM,2BAA2B;AAC/B,+BAAW,IAAI,MAAM,iBAAiB,MAAM,6CAA6C,CAAC;;GAG5F,MAAM,wBAAwB,QAA2B;IACvD,MAAM,UAAU,KAAK,wBAAwB,IAAI;AACjD,QAAI,CAAC,SAAS;AACZ,gCAAW,IAAI,MAAM,+CAA+C,QAAQ,CAAC;AAC7E;;AAGF,qBAAiB,KAAK,QAAQ;AAE9B,QAAI,QAAQ,SAAS,gBAAgB;AACnC,SAAI,QAAQ,YAAY,sBAAsB;AAC5C,iCAAW,IAAI,MAAM,6BAA6B,QAAQ,QAAQ,OAAO,QAAQ,CAAC;AAClF;;AAEF,aAAQ;AACR;;AAGF,QAAI,QAAQ,SAAS,iBAAiB,QAAQ,SAAS,sBACrD,SAAQ;;GAIZ,MAAM,YAAY,iBAAiB;AACjC,+BAAW,IAAI,MAAM,0CAA0C,QAAQ,CAAC;MACvE,gCAAgC;AAEnC,MAAG,KAAK,QAAQ,OAAO;AACvB,MAAG,KAAK,SAAS,mBAAmB;AACpC,MAAG,KAAK,SAAS,mBAAmB;AACpC,MAAG,GAAG,WAAW,qBAAqB;IACtC;;CAGJ,AAAQ,oBACN,IACA,mBAAiD,EAAE,EAC7C;AACN,OAAK,MAAM,WAAW,iBACpB,MAAK,0BAA0B,QAAQ;AAGzC,KAAG,GAAG,YAAY,QAA2B;GAC3C,MAAM,UAAU,KAAK,wBAAwB,IAAI;AACjD,OAAI,CAAC,QACH;AAGF,QAAK,0BAA0B,QAAQ;IACvC;AAEF,KAAG,GAAG,eAAe;AACnB,QAAK,eAAe;AACpB,QAAK,MAAM,CAAC,QAAQ,YAAY,KAAK,0BAA0B;AAC7D,iBAAa,QAAQ,UAAU;AAC/B,SAAK,yBAAyB,OAAO,OAAO;AAC5C,YAAQ,uBAAO,IAAI,MAAM,iDAAiD,CAAC;;AAG7E,QAAK,cAAc,EAAE;AACrB,QAAK,gBAAgB,EAAE;AACvB,QAAK,sBAAsB,EAAE;AAC7B,QAAK,KAAK,eAAe;AAEzB,OAAI,CAAC,KAAK,SACR,MAAK,mBAAmB;IAE1B;AAEF,KAAG,GAAG,UAAU,QAAe;AAC7B,WAAQ,OAAO,MACb,yDAAyD,IAAI,QAAQ,IACtE;IACD;;CAGJ,AAAQ,yBAAyB,IAAqB;AACpD,KAAG,KAAK,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC,CAAC;AAChD,KAAG,KAAK,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC,CAAC;;CAGvD,AAAQ,wBAAwB,KAA2D;EACzF,MAAM,OAAO,KAAK,cAAc,IAAI;EACpC,IAAIC;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,KAAK;WAClB,KAAK;GACZ,MAAM,UAAU,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO;AACjE,WAAQ,OAAO,MACb,8DAA8D,eAAe,QAAQ,IAAI,UAAU,cAAc,KAAK,QAAQ,IAC/H;AACD,UAAO;;EAGT,MAAM,UAAU,iCAAiC,UAAU,OAAO;AAClE,MAAI,CAAC,QAAQ,SAAS;GACpB,MAAM,YACJ,OAAO,WAAW,YAAY,WAAW,OACpC,OAAmC,OACpC;AACN,WAAQ,OAAO,MACb,iEAAiE,UAAU,KAAK,QAAQ,MAAM,QAAQ,IACvG;AACD,UAAO;;AAGT,SAAO,QAAQ;;CAGjB,AAAQ,0BAA0B,SAA2C;AAC3E,UAAQ,QAAQ,MAAhB;GACE,KAAK,eACH;GAEF,KAAK;GACL,KAAK;AACH,SAAK,cAAc,QAAQ;AAC3B,SAAK,gBAAgB,QAAQ;AAC7B,SAAK,sBAAsB,QAAQ;AACnC,SAAK,KAAK,eAAe;AACzB;GAEF,KAAK,gBAAgB;IACnB,MAAM,UAAU,KAAK,yBAAyB,IAAI,QAAQ,OAAO;AACjE,QAAI,CAAC,SAAS;AACZ,aAAQ,OAAO,MACb,uEAAuE,QAAQ,OAAO,IACvF;AACD;;AAEF,iBAAa,QAAQ,UAAU;AAC/B,SAAK,yBAAyB,OAAO,QAAQ,OAAO;AACpD,YAAQ,QAAQ,QAAQ,OAAO;AAC/B;;;;CAKN,AAAQ,oBAA0B;AAChC,MAAI,KAAK,wBAAwB,KAAK,SACpC;AAGF,OAAK;AACL,MAAI,KAAK,2BAA2B,KAAK,4BAA4B;AACnE,WAAQ,OAAO,MACb,4DAA4D,KAAK,wBAAwB,aAC1F;AACD;;EAGF,MAAM,QAAQ,KAAK;AACnB,OAAK,uBAAuB,KAAK,IAC/B,KAAK,uBAAuB,KAC5B,KAAK,wBACN;AAED,OAAK,uBAAuB,iBAAiB;AAC3C,QAAK,uBAAuB;AAC5B,OAAI,KAAK,SACP;AAGF,GAAK,KAAK,4BAA4B,CAAC,OAAO,QAAQ;AACpD,YAAQ,OAAO,MACb,uEAAuE,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,IAAI,CAAC,IACxI;AACD,QAAI,CAAC,KAAK,SACR,MAAK,mBAAmB;KAE1B;KACD,MAAM;;;;;;CAOX,MAAc,6BAA4C;AACxD,MAAI;AACF,SAAM,KAAK,wBAAwB;AACnC,QAAK,uBAAuB;AAC5B,QAAK,0BAA0B;AAC/B,OAAI,KAAK,UAAU,UAAU;AAC3B,YAAQ,OAAO,MAAM,mEAAmE;AACxF,SAAK,KAAK,eAAe;;WAEpB,KAAK;AACZ,WAAQ,OAAO,MACb,mDAAmD,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,IACrG;AACD,OAAI,CAAC,KAAK,SACR,MAAK,mBAAmB;;;CAK9B,AAAQ,mBACN,UACA,MAC8B;AAC9B,MAAI,CAAC,KAAK,gBAAgB,KAAK,aAAa,eAAe,UAAU,KACnE,OAAM,IAAI,MAAM,gCAAgC;EAGlD,MAAM,SAAS,YAAY;EAC3B,MAAM,SAAS,KAAK;AAEpB,SAAO,IAAI,SAA8B,SAAS,WAAW;GAC3D,MAAM,YAAY,iBAAiB;AACjC,SAAK,yBAAyB,OAAO,OAAO;AAC5C,2BACE,IAAI,MACF,gCAAgC,SAAS,oBAAoB,KAAK,gBAAgB,IACnF,CACF;MACA,KAAK,gBAAgB;AAExB,QAAK,yBAAyB,IAAI,QAAQ;IACxC;IACA,cAAc;IACd;IACA;IACA;IACD,CAAC;GAEF,MAAMC,UAAsC;IAC1C,MAAM;IACN;IACA;IACA;IACD;AAED,OAAI;AACF,WAAO,KAAK,KAAK,UAAU,QAAQ,CAAC;YAC7B,KAAK;AACZ,iBAAa,UAAU;AACvB,SAAK,yBAAyB,OAAO,OAAO;AAC5C,2BACE,IAAI,MAAM,oCAAoC,eAAe,QAAQ,IAAI,UAAU,MAAM,CAC1F;;IAEH;;;;;CAMJ,AAAQ,uBACN,MAC4B;EAC5B,MAAM,QAAQ,KAAK,SAAS,WAAW;EACvC,MAAM,UAAU,KAAK,SAAS,aAAa;EAC3C,MAAMC,gBAA0C,EAAE;AAElD,OAAK,MAAM,QAAQ,MACjB,eAAc,KAAK,QAAQ,KAAK,QAAQ,KAAK,MAAM,EAAE,SAAS;AAGhE,SAAO;GACL;GACA,OAAO,KAAK,YAAY,MAAM;GAC9B;GACA;GACD;;;;;CAMH,AAAQ,YAAY,OAAsC;AACxD,SAAO,MAAM,KAAK,EAAE,cAAc,eAAe,SAAS,SAAU,GAAG,WAAW,KAAK;;CAGzF,AAAQ,oBAAoB,QAAqC;AAC/D,MAAI,KAAK,eAAe,SAAS,IAAI,CACnC,QAAO;AAGT,MAAI,CAAC,OACH,QAAO;AAGT,SAAO,KAAK,eAAe,SAAS,OAAO;;;;;;CAO7C,AAAQ,wBAAwB,QAAsC;EACpE,MAAM,SAAS,qBAAqB,UAAU,OAAO;AACrD,MAAI,OAAO,QACT,QAAO,OAAO;EAGhB,MAAM,UAAU,KAAK,UAAU,OAAO,EAAE,MAAM,GAAG,IAAI,IAAI;AACzD,UAAQ,OAAO,MACb,oEAAoE,OAAO,MAAM,QAAQ,wBAAwB,QAAQ,IAC1H;AAED,SAAO;GACL,SAAS,CACP;IACE,MAAM;IACN,MAAM,gEAAgE;IACvE,CACF;GACD,SAAS;GACV;;;;;CAMH,AAAQ,cAAc,KAAgC;AACpD,MAAI,OAAO,QAAQ,SACjB,QAAO;AAGT,MAAI,OAAO,SAAS,IAAI,CACtB,QAAO,IAAI,SAAS,OAAO;AAG7B,MAAI,eAAe,YACjB,QAAO,OAAO,KAAK,IAAI,CAAC,SAAS,OAAO;AAG1C,MAAI,MAAM,QAAQ,IAAI,CACpB,QAAO,OAAO,OAAO,IAAI,CAAC,SAAS,OAAO;AAG5C,SAAO,OAAO,IAAI;;;;;;ACpxCtB,MAAa,gCAAgC,EAAE;AAE/C,MAAa,+BAA+B;CAC1C,MAAM,EACH,QAAQ,CACR,SAAS,4EAA4E;CACxF,WAAW,EACR,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,CAAC,CAC/B,UAAU,CACV,SACC,yHACD;CACJ;AAED,MAAa,+BAA+B;CAC1C,KAAK,EAAE,QAAQ,CAAC,SAAS,oCAAoC;CAC7D,SAAS,EACN,SAAS,CACT,UAAU,CACV,SACC,wFACD;CACJ;AAED,SAAS,SAAS,OAAkD;AAClE,QAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM;;AAG7E,SAAS,wBAAwB,OAAyB;AACxD,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MAAM,KAAK,SAAS,wBAAwB,KAAK,CAAC;AAG3D,KAAI,CAAC,SAAS,MAAM,CAClB,QAAO;CAGT,MAAMC,OAAgC,EAAE;AACxC,MAAK,MAAM,CAAC,KAAK,gBAAgB,OAAO,QAAQ,MAAM,EAAE;AACtD,MAAI,QAAQ,UACV;AAEF,OAAK,OAAO,wBAAwB,YAAY;;AAElD,QAAO;;AAGT,SAAgB,8BACd,YACyB;AACzB,QAAO,wBACL,mBAAmB,EAAE,OAAO,WAAW,EAAE;EACvC,cAAc;EACd,cAAc;EACf,CAAC,CACH;;;;;;;;ACWH,IAAa,sBAAb,MAAiC;;;;CAI/B,AAAS;CAET,AAAiB;CACjB,AAAiB,iCAAiB,IAAI,KAAiC;CACvE,AAAiB,qCAAqB,IAAI,KAAmC;CAC7E,AAAiB,kCAAkB,IAAI,KAA6B;CACpE,AAAiB,uCAAuB,IAAI,KAAqB;CAEjE,AAAQ,UAAU;CAClB,AAAQ,gBAAgB;CACxB,AAAQ,YAAY;;;;CAKpB,YAAY,UAAsC,EAAE,EAAE;AACpD,OAAK,SAAS,QAAQ,UAAU,IAAI,kBAAkB,QAAQ,cAAc;AAE5E,OAAK,YAAY,IAAI,UAAU;GAC7B,MAAM,QAAQ,cAAc;GAC5B,SAAS,QAAQ,iBAAiB;GACnC,CAAC;AAEF,OAAK,OAAO,GAAG,sBAAsB;AACnC,GAAK,KAAK,kBAAkB,CAAC,OAAO,QAAQ;AAC1C,SAAK,aAAa,IAAI;KACtB;IACF;AAEF,OAAK,qBAAqB;AAC1B,OAAK,0BAA0B;;;;;CAMjC,MAAM,QAAuB;AAC3B,QAAM,KAAK,OAAO,OAAO;AACzB,QAAM,KAAK,kBAAkB;;;;;;;CAQ/B,MAAM,QAAQ,WAAqC;AACjD,MAAI,KAAK,UACP,OAAM,IAAI,MAAM,yCAAyC;AAG3D,QAAM,KAAK,UAAU,QAAQ,UAAU;AACvC,OAAK,YAAY;;;;;CAMnB,MAAM,aAA4B;AAChC,QAAM,KAAK,QAAQ,IAAI,sBAAsB,CAAC;;;;;CAMhD,MAAM,OAAsB;AAC1B,OAAK,YAAY;EACjB,IAAIC;AACJ,MAAI;AACF,SAAM,KAAK,UAAU,OAAO;WACrB,KAAK;AACZ,mBAAgB;AAChB,WAAQ,OAAO,MACb,2DAA2D,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,IAAI,CAAC,IAC5H;;AAEH,QAAM,KAAK,OAAO,MAAM;AACxB,MAAI,cACF,SAAQ,OAAO,MACb,wFACD;;;;;CAOL,uBAAiC;AAC/B,SAAO,MAAM,KAAK,KAAK,mBAAmB,MAAM,CAAC,CAAC,MAAM;;;;;;;;;;;;CAa1D,AAAQ,2BAAiC;AACvC,OAAK,UAAU,OAAO,kBAAkB,8BAA8B;AAYpE,UAAO,EAAE,OAXqC,CAC5C,GAAG,MAAM,KAAK,KAAK,eAAe,QAAQ,CAAC,CAAC,KAAK,UAAU,EAAE,GAAG,MAAM,EAAE,EACxE,GAAG,MAAM,KAAK,KAAK,gBAAgB,SAAS,CAAC,CAAC,KAAK,CAAC,MAAM,WAAW;IACnE;IACA,aAAa,KAAK,uBAAuB,KAAK;IAC9C,aAAa,KAAK;IAClB,GAAI,KAAK,eAAe,EAAE,cAAc,KAAK,cAAc,GAAG,EAAE;IAChE,GAAI,KAAK,cAAc,EAAE,aAAa,KAAK,aAAa,GAAG,EAAE;IAC9D,EAAE,CACJ,CAAC,MAAM,MAAM,UAAU,OAAO,KAAK,KAAK,CAAC,cAAc,OAAO,MAAM,KAAK,CAAC,CAAC,EAE5D;IAChB;;CAGJ,AAAQ,mBAAmD,EACzD,MACA,aACA,YACA,aACA,WACkC;AAClC,OAAK,UAAU,aACb,MACA;GACE;GACA,aAAa;GACb,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;GACvC,EACD,QACD;AACD,OAAK,eAAe,IAAI,MAAM;GAC5B;GACA;GACA,aAAa,8BAA8B,WAAW;GACtD,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;GACvC,CAAC;;;;;CAMJ,AAAQ,sBAA4B;AAClC,OAAK,mBAAmB;GACtB,MAAM;GACN,aAAa;GACb,YAAY;GACZ,aAAa;IACX,cAAc;IACd,gBAAgB;IACjB;GACD,SAAS,YAAY;AACnB,QAAI,KAAK,OAAO,SAAS,UAAU;KACjC,MAAMC,YAAU,KAAK,OAAO,sBAAsB;KAClD,MAAM,OAAO;MACX,MAAM;MACN,OAAOA,UAAQ;MACf;MACD;AACD,YAAO;MACL,SAAS,CAAC;OAAE,MAAM;OAAQ,MAAM,KAAK,UAAU,MAAM,MAAM,EAAE;OAAE,CAAC;MAChE,mBAAmB;MACpB;;IAEH,MAAM,UAAU,KAAK,OAAO,SAAS,aAAa;AAClD,WAAO;KACL,SAAS,CACP;MAAE,MAAM;MAAQ,MAAM,KAAK,UAAU;OAAE,OAAO,QAAQ;OAAQ;OAAS,EAAE,MAAM,EAAE;MAAE,CACpF;KACD,mBAAmB;MACjB,OAAO,QAAQ;MACf;MACD;KACF;;GAEJ,CAAC;AAEF,OAAK,mBAAmB;GACtB,MAAM;GACN,aACE;GACF,YAAY;GACZ,aAAa;IACX,cAAc;IACd,gBAAgB;IACjB;GACD,SAAS,YAAY;IACnB,MAAM,QAAQ,KAAK,qBAAqB;AACxC,WAAO;KACL,SAAS,CACP;MAAE,MAAM;MAAQ,MAAM,KAAK,UAAU;OAAE,OAAO,MAAM;OAAQ;OAAO,EAAE,MAAM,EAAE;MAAE,CAChF;KACD,mBAAmB;MACjB,OAAO,MAAM;MACb;MACD;KACF;;GAEJ,CAAC;AAEF,OAAK,mBAAmB;GACtB,MAAM;GACN,aACE;GACF,YAAY;GACZ,aAAa,EACX,cAAc,OACf;GACD,SAAS,OAAO,EAAE,MAAM,WAAW,WAAW;IAC5C,MAAM,QAAQ,KAAK,qBAAqB;IACxC,MAAM,cAAc,KAAK,iBAAiB,MAAM;AAGhD,QAAI,CADY,MAAM,MAAM,MAAM,EAAE,SAAS,KAAK,EACpC;KACZ,MAAM,aAAa,CAAC,SAAS,KAAK,cAAc;AAChD,SAAI,YACF,YAAW,KAAK,IAAI,oBAAoB,YAAY;SAEpD,YAAW,KACT,IACA,oFACD;AAEH,YAAO;MACL,SAAS,CAAC;OAAE,MAAM;OAAiB,MAAM,WAAW,KAAK,KAAK;OAAE,CAAC;MACjE,SAAS;MACV;;AAGH,QAAI;KACF,MAAM,SAAS,MAAM,KAAK,OAAO,WAAW,MAAM,QAAQ,EAAE,CAAC;KAE7D,MAAM,eACJ,KAAK,OAAO,SAAS,WACjB,KAAK,+BAA+B,GACpC,KAAK,OAAO,SAAS,WAAW;KACtC,MAAM,iBAAiB,KAAK,iBAAiB,aAAa;AAC1D,SAAI,eACF,QAAO,UAAU,CACf,GAAG,OAAO,SACV;MAAE,MAAM;MAAiB,MAAM,4BAA4B;MAAkB,CAC9E;AAGH,YAAO;aACA,KAAK;KAEZ,MAAM,aAAa,CAAC,wBAAwB,KAAK,KADjC,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GACA;AAChE,SAAI,YACF,YAAW,KAAK,IAAI,oBAAoB,YAAY;AAEtD,YAAO;MACL,SAAS,CAAC;OAAE,MAAM;OAAiB,MAAM,WAAW,KAAK,KAAK;OAAE,CAAC;MACjE,SAAS;MACV;;;GAGN,CAAC;AAEF,OAAK,mBAAmB;GACtB,MAAM;GACN,aACE;GACF,YAAY;GACZ,aAAa,EAAE,cAAc,OAAO;GACpC,SAAS,OAAO,EAAE,KAAK,cAAc;IACnC,IAAIC;AACJ,QAAI;AACF,cAAS,IAAI,IAAI,IAAI;YACf;AACN,YAAO;MACL,SAAS,CAAC;OAAE,MAAM;OAAiB,MAAM,gBAAgB;OAAO,CAAC;MACjE,SAAS;MACV;;AAGH,QAAI,OAAO,aAAa,WAAW,OAAO,aAAa,SACrD,QAAO;KACL,SAAS,CACP;MACE,MAAM;MACN,MAAM,gDAAgD,OAAO;MAC9D,CACF;KACD,SAAS;KACV;AAGH,QAAI,SAAS;AACX,SAAI,KAAK,OAAO,SAAS,SACvB,QAAO;MACL,SAAS,CACP;OACE,MAAM;OACN,MAAM;OACP,CACF;MACD,SAAS;MACV;KAIH,MAAM,UADU,KAAK,OAAO,SAAS,aAAa,CAC1B,MAAM,MAAM;AAClC,UAAI,CAAC,EAAE,IAAK,QAAO;AACnB,UAAI;AACF,cAAO,IAAI,IAAI,EAAE,IAAI,CAAC,WAAW,OAAO;cAClC;AACN,eAAQ,OAAO,MACb,qCAAqC,EAAE,SAAS,wBAAwB,EAAE,IAAI,IAC/E;AACD,cAAO;;OAET;AAEF,SAAI,CAAC,QACH,QAAO;MACL,SAAS,CACP;OACE,MAAM;OACN,MAAM,sCAAsC,OAAO,OAAO;OAC3D,CACF;MACD,SAAS;MACV;AAGH,SAAI;AACF,WAAK,OAAO,aAAa,QAAQ,SAAS;AAC1C,aAAO,EACL,SAAS,CACP;OACE,MAAM;OACN,MAAM,yBAAyB,QAAQ,SAAS,IAAI,QAAQ,OAAO,QAAQ,OAAO;OACnF,CACF,EACF;cACM,KAAK;AACZ,aAAO;OACL,SAAS,CACP;QACE,MAAM;QACN,MAAM,4BAA4B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;QACnF,CACF;OACD,SAAS;OACV;;;AAIL,QAAI;AACF,WAAM,KAAK,cAAc,IAAI;aACtB,KAAK;AACZ,YAAO;MACL,SAAS,CACP;OACE,MAAM;OACN,MAAM,2BAA2B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;OAClF,CACF;MACD,SAAS;MACV;;AAGH,QAAI,KAAK,OAAO,SAAS,UAAU;KAEjC,MAAM,WADU,KAAK,OAAO,SAAS,aAAa,CACzB,MAAM,MAAM;AACnC,UAAI,CAAC,EAAE,IAAK,QAAO;AACnB,UAAI;AACF,cAAO,IAAI,IAAI,EAAE,IAAI,CAAC,WAAW,OAAO;cAClC;AACN,eAAQ,OAAO,MACb,qCAAqC,EAAE,SAAS,wBAAwB,EAAE,IAAI,IAC/E;AACD,cAAO;;OAET;AAEF,SAAI,SACF,QAAO,EACL,SAAS,CACP;MACE,MAAM;MACN,MAAM,UAAU,IAAI,+CAA+C,SAAS,OAAO,SAAS,OAAO;MACpG,CACF,EACF;;AAIL,WAAO,EACL,SAAS,CAAC;KAAE,MAAM;KAAiB,MAAM,UAAU,IAAI;KAA2B,CAAC,EACpF;;GAEJ,CAAC;;;;;CAMJ,AAAQ,iBAAiB,OAAwC;AAC/D,MAAI,MAAM,WAAW,EACnB,QAAO;AAGT,SAAO,MACJ,KAAK,MAAM;GACV,MAAM,OAAO,EAAE,cAAc,MAAM,EAAE,YAAY,MAAM,KAAK,CAAC,OAAO;AACpE,UAAO,KAAK,EAAE,OAAO;IACrB,CACD,KAAK,KAAK;;;;;;;;;CAUf,AAAQ,cAAc,KAA4B;EAChD,MAAM,UAAU,IAAI,IAAI,IAAI,CAAC;AAC7B,SAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,WAAW,QAAQ;GACzB,IAAIC;GACJ,IAAIC;AACJ,OAAI,aAAa,UAAU;AACzB,cAAU;AACV,WAAO,CAAC,QAAQ;cACP,aAAa,SAAS;AAC/B,cAAU;AACV,WAAO;KAAC;KAAc;KAAY,kBAAkB,QAAQ;KAAG;UAC1D;AACL,cAAU;AACV,WAAO,CAAC,QAAQ;;AAElB,YAAS,SAAS,OAAO,QAAQ;AAC/B,QAAI,IAAK,QAAO,IAAI;QACf,UAAS;KACd;IACF;;;;;CAMJ,MAAc,mBAAkC;AAC9C,MAAI,KAAK,SAAS;AAChB,QAAK,gBAAgB;AACrB;;AAGF,OAAK,UAAU;AAEf,MAAI;AACF,MAAG;AACD,SAAK,gBAAgB;IACrB,MAAM,QAAQ,KAAK,qBAAqB;AACxC,SAAK,kBAAkB,MAAM;YACtB,KAAK;YACN;GACR,MAAM,iBAAiB,KAAK;AAC5B,QAAK,gBAAgB;AACrB,QAAK,UAAU;AACf,OAAI,eACF,CAAK,KAAK,kBAAkB,CAAC,OAAO,QAAQ;AAC1C,SAAK,aAAa,IAAI;KACtB;;;;;;CAQR,AAAQ,sBAAwC;AAC9C,SAAO,KAAK,OAAO,SAAS,WACxB,KAAK,+BAA+B,GACpC,KAAK,OAAO,SAAS,WAAW;;;;;;CAOtC,AAAQ,gCAAkD;EACxD,MAAM,gBAAgB,KAAK,OAAO,2BAA2B;EAC7D,MAAM,aAAa,KAAK,OAAO,sBAAsB;EACrD,MAAM,aAAa,IAAI,IAAI,WAAW,KAAK,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;AAElE,SAAO,KAAK,OACT,oBAAoB,CACpB,KAAK,SAAS;GACb,MAAM,YAAY,cAAc,KAAK,SAAS,EAAE;GAChD,MAAMC,UAAwB,EAAE;AAChC,QAAK,MAAM,MAAM,WAAW;IAC1B,MAAM,SAAS,WAAW,IAAI,GAAG;AACjC,QAAI,OACF,SAAQ,KAAK,OAAqB;QAElC,SAAQ,OAAO,MACb,oCAAoC,KAAK,KAAK,iCAAiC,GAAG,KACnF;;AAIL,UAAO;IACL,GAAG;IACH,cAAc,KAAK;IACnB;IACD;IACD,CACD,MAAM,MAAM,UAAU,KAAK,KAAK,cAAc,MAAM,KAAK,CAAC;;;;;CAM/D,AAAQ,kBAAkB,OAA+B;EACvD,MAAM,YAAY,IAAI,IAAI,MAAM,KAAK,SAAS,KAAK,KAAK,CAAC;EACzD,IAAI,UAAU;AAEd,OAAK,MAAM,CAAC,MAAM,WAAW,KAAK,mBAAmB,SAAS,EAAE;AAC9D,OAAI,UAAU,IAAI,KAAK,CACrB;AAGF,UAAO,QAAQ;AACf,QAAK,mBAAmB,OAAO,KAAK;AACpC,QAAK,gBAAgB,OAAO,KAAK;AACjC,QAAK,qBAAqB,OAAO,KAAK;AACtC,aAAU;;AAGZ,OAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,YAAY,KAAK,cAAc,KAAK;AAG1C,OAFyB,KAAK,qBAAqB,IAAI,KAAK,KAAK,KAExC,aAAa,KAAK,mBAAmB,IAAI,KAAK,KAAK,CAC1E;GAGF,MAAM,WAAW,KAAK,mBAAmB,IAAI,KAAK,KAAK;AACvD,OAAI,UAAU;AACZ,aAAS,QAAQ;AACjB,SAAK,mBAAmB,OAAO,KAAK,KAAK;;GAG3C,MAAM,SAAS,KAAK,oBAAoB,KAAK;AAC7C,QAAK,mBAAmB,IAAI,KAAK,MAAM,OAAO;AAC9C,QAAK,gBAAgB,IAAI,KAAK,MAAM,KAAK;AACzC,QAAK,qBAAqB,IAAI,KAAK,MAAM,UAAU;AACnD,aAAU;;AAGZ,MAAI,WAAW,KAAK,UAClB,KAAI;AACF,QAAK,UAAU,qBAAqB;WAC7B,KAAK;AACZ,WAAQ,OAAO,MACb,6EAA6E,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,IAC/H;;;;;;CAQP,AAAQ,oBAAoB,MAA4C;EACtE,MAAM,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,aAAa;EAC9C,MAAM,SAAS;GACb,aAAa,KAAK,uBAAuB,KAAK;GAC9C;GACD;EAED,MAAM,SAAS,KAAK,UAAU,aAC5B,KAAK,MACL,KAAK,cAAc;GAAE,GAAG;GAAQ,aAAa,KAAK;GAAa,GAAG,QAClE,OAAO,SAAkC;AACvC,OAAI;AACF,WAAO,MAAM,KAAK,OAAO,WAAW,KAAK,MAAM,KAAK;YAC7C,KAAK;IACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;IAChE,MAAM,UAAU,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,IAAI;AAC/E,YAAQ,OAAO,MACb,6CAA6C,KAAK,KAAK,uBAAuB,QAAQ,IACvF;AACD,WAAO;KACL,SAAS,CACP;MACE,MAAM;MACN,MAAM,kCAAkC,KAAK,KAAK,KAAK;MACxD,CACF;KACD,SAAS;KACV;;IAGN;AAED,SAAO,EACL,cAAc;AACZ,UAAO,QAAQ;KAElB;;;;;;CAOH,AAAQ,uBAAuB,MAA8B;EAC3D,MAAM,SAAS,KAAK,QAAQ;AAK5B,SAAO,GAJa,SAChB,WAAW,OAAO,QAAQ,OAAO,QAAQ,MAAM,OAAO,UAAU,GAAG,KACnE,iBAEkB,GAAG,KAAK,eAAe,gBAAgB,KAAK;;;;;CAMpE,AAAQ,cAAc,MAA8B;AAClD,SAAO,KAAK,UAAU;GACpB,MAAM,KAAK;GACX,cAAc,KAAK;GACnB,OAAO,KAAK;GACZ,aAAa,KAAK;GAClB,aAAa,KAAK;GAClB,cAAc,KAAK;GACnB,aAAa,KAAK;GAClB,WAAW,KAAK;GAChB,OAAO,KAAK;GACZ,OAAO,KAAK;GACZ,SAAS,KAAK,QAAQ,KAAK,YAAY;IACrC,UAAU,OAAO;IACjB,OAAO,OAAO;IACf,EAAE;GACJ,CAAC;;;;;CAMJ,AAAQ,aAAa,KAAoB;EACvC,MAAM,UAAU,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,IAAI;AAC/E,UAAQ,OAAO,MAAM,6DAA6D,QAAQ,IAAI"}
|
|
File without changes
|