safari-devtools-mcp 0.1.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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +268 -0
  3. package/build/src/SafariDriver.d.ts +56 -0
  4. package/build/src/SafariDriver.d.ts.map +1 -0
  5. package/build/src/SafariDriver.js +475 -0
  6. package/build/src/SafariDriver.js.map +1 -0
  7. package/build/src/bin/safari-devtools-mcp.d.ts +9 -0
  8. package/build/src/bin/safari-devtools-mcp.d.ts.map +1 -0
  9. package/build/src/bin/safari-devtools-mcp.js +42 -0
  10. package/build/src/bin/safari-devtools-mcp.js.map +1 -0
  11. package/build/src/formatters/ConsoleFormatter.d.ts +7 -0
  12. package/build/src/formatters/ConsoleFormatter.d.ts.map +1 -0
  13. package/build/src/formatters/ConsoleFormatter.js +47 -0
  14. package/build/src/formatters/ConsoleFormatter.js.map +1 -0
  15. package/build/src/formatters/NetworkFormatter.d.ts +7 -0
  16. package/build/src/formatters/NetworkFormatter.d.ts.map +1 -0
  17. package/build/src/formatters/NetworkFormatter.js +70 -0
  18. package/build/src/formatters/NetworkFormatter.js.map +1 -0
  19. package/build/src/formatters/SnapshotFormatter.d.ts +6 -0
  20. package/build/src/formatters/SnapshotFormatter.d.ts.map +1 -0
  21. package/build/src/formatters/SnapshotFormatter.js +34 -0
  22. package/build/src/formatters/SnapshotFormatter.js.map +1 -0
  23. package/build/src/index.d.ts +13 -0
  24. package/build/src/index.d.ts.map +1 -0
  25. package/build/src/index.js +81 -0
  26. package/build/src/index.js.map +1 -0
  27. package/build/src/injected/console.d.ts +9 -0
  28. package/build/src/injected/console.d.ts.map +1 -0
  29. package/build/src/injected/console.js +119 -0
  30. package/build/src/injected/console.js.map +1 -0
  31. package/build/src/injected/network.d.ts +14 -0
  32. package/build/src/injected/network.d.ts.map +1 -0
  33. package/build/src/injected/network.js +273 -0
  34. package/build/src/injected/network.js.map +1 -0
  35. package/build/src/injected/snapshot.d.ts +9 -0
  36. package/build/src/injected/snapshot.d.ts.map +1 -0
  37. package/build/src/injected/snapshot.js +217 -0
  38. package/build/src/injected/snapshot.js.map +1 -0
  39. package/build/src/tools/console.d.ts +17 -0
  40. package/build/src/tools/console.d.ts.map +1 -0
  41. package/build/src/tools/console.js +76 -0
  42. package/build/src/tools/console.js.map +1 -0
  43. package/build/src/tools/input.d.ts +59 -0
  44. package/build/src/tools/input.d.ts.map +1 -0
  45. package/build/src/tools/input.js +220 -0
  46. package/build/src/tools/input.js.map +1 -0
  47. package/build/src/tools/network.d.ts +17 -0
  48. package/build/src/tools/network.d.ts.map +1 -0
  49. package/build/src/tools/network.js +74 -0
  50. package/build/src/tools/network.js.map +1 -0
  51. package/build/src/tools/pages.d.ts +36 -0
  52. package/build/src/tools/pages.d.ts.map +1 -0
  53. package/build/src/tools/pages.js +236 -0
  54. package/build/src/tools/pages.js.map +1 -0
  55. package/build/src/tools/screenshot.d.ts +15 -0
  56. package/build/src/tools/screenshot.d.ts.map +1 -0
  57. package/build/src/tools/screenshot.js +70 -0
  58. package/build/src/tools/screenshot.js.map +1 -0
  59. package/build/src/tools/script.d.ts +12 -0
  60. package/build/src/tools/script.d.ts.map +1 -0
  61. package/build/src/tools/script.js +48 -0
  62. package/build/src/tools/script.js.map +1 -0
  63. package/build/src/tools/snapshot.d.ts +17 -0
  64. package/build/src/tools/snapshot.d.ts.map +1 -0
  65. package/build/src/tools/snapshot.js +75 -0
  66. package/build/src/tools/snapshot.js.map +1 -0
  67. package/build/src/tools/types.d.ts +17 -0
  68. package/build/src/tools/types.d.ts.map +1 -0
  69. package/build/src/tools/types.js +5 -0
  70. package/build/src/tools/types.js.map +1 -0
  71. package/build/src/types.d.ts +74 -0
  72. package/build/src/types.d.ts.map +1 -0
  73. package/build/src/types.js +11 -0
  74. package/build/src/types.js.map +1 -0
  75. package/package.json +66 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HayoDev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,268 @@
1
+ # Safari DevTools MCP
2
+
3
+ A Model Context Protocol (MCP) server that provides Safari browser debugging and automation tools for AI coding agents on macOS. Tool names and schemas mirror [chrome-devtools-mcp](https://github.com/ChromeDevTools/chrome-devtools-mcp) for cross-browser compatibility.
4
+
5
+ ## Key Features
6
+
7
+ - **Console debugging** — capture logs, errors, warnings with stack traces
8
+ - **Network monitoring** — inspect requests/responses with hybrid capture (Performance API + Fetch/XHR interception)
9
+ - **JavaScript evaluation** — run scripts in the browser context
10
+ - **DOM snapshots** — accessibility-tree snapshots with UIDs for element targeting
11
+ - **Screenshots** — capture page or element screenshots
12
+ - **Tab management** — native macOS tab management via AppleScript
13
+ - **Input automation** — click, type, fill forms, drag and drop, keyboard shortcuts
14
+ - **Cross-browser compatible** — matching tool names with chrome-devtools-mcp
15
+
16
+ ## Requirements
17
+
18
+ - **macOS** (Safari and SafariDriver are Apple-exclusive)
19
+ - **Node.js 18+** (22+ recommended, see `.nvmrc`)
20
+ - **Safari** with Developer menu enabled
21
+
22
+ ### Safari Setup
23
+
24
+ 1. Open Safari → Settings → Advanced → check **"Show features for web developers"**
25
+ 2. In the Develop menu → check **"Allow Remote Automation"**
26
+ 3. Authorize SafariDriver:
27
+ ```bash
28
+ sudo safaridriver --enable
29
+ ```
30
+
31
+ ## Getting Started
32
+
33
+ ### Install from npm
34
+
35
+ ```bash
36
+ npx safari-devtools-mcp
37
+ ```
38
+
39
+ ### Install from source
40
+
41
+ ```bash
42
+ git clone https://github.com/HayoDev/safari-devtools-mcp.git
43
+ cd safari-devtools-mcp
44
+ npm install
45
+ npm run build
46
+ ```
47
+
48
+ ### MCP Client Configuration
49
+
50
+ <details>
51
+ <summary>Claude Code</summary>
52
+
53
+ Run this command:
54
+
55
+ ```bash
56
+ claude mcp add safari-devtools -- npx safari-devtools-mcp
57
+ ```
58
+
59
+ Or add to your project's `.mcp.json`:
60
+
61
+ ```json
62
+ {
63
+ "mcpServers": {
64
+ "safari-devtools": {
65
+ "command": "npx",
66
+ "args": ["safari-devtools-mcp"]
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ </details>
73
+
74
+ <details>
75
+ <summary>Claude Desktop</summary>
76
+
77
+ Add to your `claude_desktop_config.json`:
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "safari-devtools": {
83
+ "command": "npx",
84
+ "args": ["safari-devtools-mcp"]
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ Config file location:
91
+
92
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
93
+
94
+ </details>
95
+
96
+ <details>
97
+ <summary>Cursor</summary>
98
+
99
+ Add to your Cursor MCP settings:
100
+
101
+ ```json
102
+ {
103
+ "mcpServers": {
104
+ "safari-devtools": {
105
+ "command": "npx",
106
+ "args": ["safari-devtools-mcp"]
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ </details>
113
+
114
+ <details>
115
+ <summary>VS Code</summary>
116
+
117
+ Add to your VS Code settings (`.vscode/mcp.json`):
118
+
119
+ ```json
120
+ {
121
+ "mcpServers": {
122
+ "safari-devtools": {
123
+ "command": "npx",
124
+ "args": ["safari-devtools-mcp"]
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ </details>
131
+
132
+ <details>
133
+ <summary>From source (any client)</summary>
134
+
135
+ If you cloned the repo, point to the built entry point:
136
+
137
+ ```json
138
+ {
139
+ "mcpServers": {
140
+ "safari-devtools": {
141
+ "command": "node",
142
+ "args": ["/path/to/safari-devtools-mcp/build/bin/safari-devtools-mcp.js"]
143
+ }
144
+ }
145
+ }
146
+ ```
147
+
148
+ </details>
149
+
150
+ ### Your First Prompt
151
+
152
+ > Navigate to https://example.com, take a snapshot, and list any console errors.
153
+
154
+ ## Tools
155
+
156
+ ### Debugging (Core)
157
+
158
+ | Tool | Description |
159
+ | ----------------------- | ------------------------------------------------- |
160
+ | `list_console_messages` | List console messages with filtering by level |
161
+ | `get_console_message` | Get detailed message with stack trace |
162
+ | `list_network_requests` | Monitor network requests (Fetch, XHR, resources) |
163
+ | `get_network_request` | Get full request/response with headers |
164
+ | `evaluate_script` | Execute JavaScript in the browser context |
165
+ | `take_screenshot` | Capture page or element screenshots (PNG) |
166
+ | `take_snapshot` | DOM tree snapshot with UIDs for element targeting |
167
+
168
+ ### Navigation
169
+
170
+ | Tool | Description |
171
+ | --------------- | ----------------------------------------- |
172
+ | `list_pages` | List open Safari tabs (via AppleScript) |
173
+ | `select_page` | Switch to a specific tab |
174
+ | `new_page` | Open a new tab with URL |
175
+ | `close_page` | Close a tab |
176
+ | `navigate_page` | Navigate to URL, back, forward, or reload |
177
+ | `wait_for` | Wait for text to appear on page |
178
+ | `resize_page` | Resize the browser window |
179
+ | `handle_dialog` | Accept or dismiss browser dialogs |
180
+
181
+ ### Input Automation
182
+
183
+ | Tool | Description |
184
+ | ------------- | ------------------------------------------- |
185
+ | `click` | Click elements by UID from snapshot |
186
+ | `click_at` | Click at coordinates |
187
+ | `hover` | Hover over elements |
188
+ | `fill` | Type into inputs or select from dropdowns |
189
+ | `fill_form` | Fill multiple form fields at once |
190
+ | `type_text` | Type text into focused input |
191
+ | `drag` | Drag and drop elements |
192
+ | `press_key` | Press keys or combinations (e.g., `Meta+A`) |
193
+ | `upload_file` | Upload files through file inputs |
194
+
195
+ ## Architecture
196
+
197
+ ```
198
+ MCP Client (Claude, Cursor, etc.)
199
+ | stdio (MCP protocol)
200
+ v
201
+ Safari DevTools MCP Server
202
+ |
203
+ v
204
+ +-------------------------------+
205
+ | SafariDriver |
206
+ | +-- Selenium WebDriver | <-- Browser automation
207
+ | +-- JS Injection | <-- Console/Network capture
208
+ | +-- AppleScript (osascript) | <-- Native tab management
209
+ +-------------------------------+
210
+ |
211
+ v
212
+ Safari Browser
213
+ ```
214
+
215
+ ### Network Monitoring Strategy
216
+
217
+ Network request capture uses a 4-layer hybrid approach to minimize the pre-injection gap:
218
+
219
+ 1. **Performance API backfill** — `performance.getEntriesByType('resource')` captures historical requests made before script injection (timing and size data, but no headers/status)
220
+ 2. **Fetch interception** — Wraps `window.fetch()` for ongoing requests with full details
221
+ 3. **XHR interception** — Wraps `XMLHttpRequest` for the same
222
+ 4. **PerformanceObserver** — Catches resource loads that interceptors miss
223
+
224
+ Historical entries are flagged so the AI knows they have limited detail compared to intercepted requests.
225
+
226
+ ### Tab Management
227
+
228
+ Uses AppleScript (`osascript`) for native macOS tab management — listing all Safari tabs across windows, switching tabs, opening/closing tabs. Falls back to WebDriver-only mode if AppleScript access is denied.
229
+
230
+ ## Compatibility with chrome-devtools-mcp
231
+
232
+ Tool names and parameter schemas intentionally mirror chrome-devtools-mcp. The same `take_screenshot`, `evaluate_script`, `list_console_messages`, `click`, `fill`, etc. calls work across both servers, making it easy to switch between Chrome and Safari.
233
+
234
+ ## Features NOT Available (vs Chrome DevTools MCP)
235
+
236
+ The following chrome-devtools-mcp features **cannot** be ported to Safari due to platform differences:
237
+
238
+ | Feature | Reason |
239
+ | ------------------------------- | ------------------------------------------------------ |
240
+ | **Lighthouse audits** | Chrome-specific tool, not available for Safari |
241
+ | **Memory heap snapshots** | Requires Chrome DevTools Protocol (CDP) |
242
+ | **Full performance traces** | Chrome's Tracing API is CDP-specific |
243
+ | **Chrome extension management** | Safari extensions use a completely different system |
244
+ | **Screencast/video recording** | Requires CDP screencast API |
245
+ | **Network throttling** | CDP-specific network emulation |
246
+ | **Geolocation emulation** | CDP-specific emulation |
247
+ | **Device emulation** | CDP-specific (Safari has no equivalent via WebDriver) |
248
+ | **Screenshot formats** | Safari WebDriver only supports PNG (no JPEG/WebP) |
249
+ | **Full-page screenshots** | Safari WebDriver does not support full-page capture |
250
+ | **initScript** | Requires CDP's `Page.addScriptToEvaluateOnNewDocument` |
251
+ | **Isolated browser contexts** | Safari WebDriver only supports a single session |
252
+
253
+ ## Known Limitations
254
+
255
+ - **Single session**: Safari only allows one WebDriver session at a time
256
+ - **Network pre-injection gap**: Requests before script injection are captured via Performance API with limited detail (no headers, no status code)
257
+ - **Console pre-injection gap**: Console messages logged before script injection are not captured
258
+ - **AppleScript permissions**: Tab management requires macOS Accessibility permissions
259
+ - **No headless mode**: Safari does not support headless operation
260
+ - **macOS only**: Safari and SafariDriver are Apple-exclusive
261
+
262
+ ## Contributing
263
+
264
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
265
+
266
+ ## License
267
+
268
+ [MIT](LICENSE)
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Safari WebDriver manager.
3
+ * Handles browser automation via safaridriver + Selenium WebDriver.
4
+ */
5
+ import { type ThenableWebDriver } from 'selenium-webdriver';
6
+ import type { ConsoleLogEntry, NetworkLogEntry, PageInfo, SnapshotNode } from './types.js';
7
+ export declare class SafariDriver {
8
+ private driver;
9
+ private initialized;
10
+ private injectedOnPages;
11
+ ensureDriver(): Promise<ThenableWebDriver>;
12
+ isReady(): Promise<boolean>;
13
+ /**
14
+ * Inject console and network capture scripts into the current page.
15
+ * Uses a page-URL-based guard so we re-inject after navigations.
16
+ */
17
+ injectCaptureScripts(): Promise<void>;
18
+ navigate(url: string): Promise<void>;
19
+ goBack(): Promise<void>;
20
+ goForward(): Promise<void>;
21
+ reload(): Promise<void>;
22
+ getCurrentUrl(): Promise<string>;
23
+ getTitle(): Promise<string>;
24
+ getConsoleLogs(options?: {
25
+ types?: string[];
26
+ pageSize?: number;
27
+ pageIdx?: number;
28
+ }): Promise<ConsoleLogEntry[]>;
29
+ getConsoleMessage(msgid: number): Promise<ConsoleLogEntry | null>;
30
+ getNetworkLogs(options?: {
31
+ resourceTypes?: string[];
32
+ pageSize?: number;
33
+ pageIdx?: number;
34
+ }): Promise<NetworkLogEntry[]>;
35
+ getNetworkRequest(reqid: number): Promise<NetworkLogEntry | null>;
36
+ evaluateScript(fn: string, args?: string[]): Promise<string>;
37
+ takeScreenshot(): Promise<string>;
38
+ takeElementScreenshot(uid: string): Promise<string>;
39
+ takeSnapshot(verbose?: boolean): Promise<SnapshotNode>;
40
+ clickElement(uid: string, dblClick?: boolean): Promise<void>;
41
+ clickAtCoordinates(x: number, y: number, dblClick?: boolean): Promise<void>;
42
+ hoverElement(uid: string): Promise<void>;
43
+ fillElement(uid: string, value: string): Promise<void>;
44
+ typeText(text: string, submitKey?: string): Promise<void>;
45
+ pressKey(key: string): Promise<void>;
46
+ dragElement(fromUid: string, toUid: string): Promise<void>;
47
+ uploadFile(uid: string, filePath: string): Promise<void>;
48
+ resizePage(width: number, height: number): Promise<void>;
49
+ waitForText(texts: string[], timeout?: number): Promise<string>;
50
+ listPages(): Promise<PageInfo[]>;
51
+ selectPage(pageId: number): Promise<void>;
52
+ newPage(url: string): Promise<void>;
53
+ closePage(pageId: number): Promise<void>;
54
+ close(): Promise<void>;
55
+ }
56
+ //# sourceMappingURL=SafariDriver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SafariDriver.d.ts","sourceRoot":"","sources":["../../src/SafariDriver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,KAAK,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAKnE,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,QAAQ,EACR,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAqB;IAEtC,YAAY,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAY1C,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAIjC;;;OAGG;IACG,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBrC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAQvB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAO1B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAOvB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAKhC,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAO3B,cAAc,CAAC,OAAO,CAAC,EAAE;QAC7B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAqBxB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAYjE,cAAc,CAAC,OAAO,CAAC,EAAE;QAC7B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAqBxB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAYjE,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAuB5D,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAKjC,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUnD,YAAY,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC;IAUtD,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D,kBAAkB,CACtB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,QAAQ,CAAC,EAAE,OAAO,GACjB,OAAO,CAAC,IAAI,CAAC;IAgBV,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASxC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BtD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASzD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0EpC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1D,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUxD,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxD,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwB/D,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IA+ChC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BzC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBnC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoCxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAY7B"}