meteoswiss-mcp 2.0.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 (85) hide show
  1. package/README.md +199 -0
  2. package/dist/data/meteoswiss-content-data.js +356 -0
  3. package/dist/data/meteoswiss-content-data.js.map +1 -0
  4. package/dist/data/meteoswiss-search-data.js +215 -0
  5. package/dist/data/meteoswiss-search-data.js.map +1 -0
  6. package/dist/data/ogd-current-weather.js +95 -0
  7. package/dist/data/ogd-current-weather.js.map +1 -0
  8. package/dist/data/ogd-data-store.js +160 -0
  9. package/dist/data/ogd-data-store.js.map +1 -0
  10. package/dist/data/ogd-local-forecast.js +199 -0
  11. package/dist/data/ogd-local-forecast.js.map +1 -0
  12. package/dist/data/ogd-pollen-data.js +94 -0
  13. package/dist/data/ogd-pollen-data.js.map +1 -0
  14. package/dist/data/ogd-smn-stations.js +106 -0
  15. package/dist/data/ogd-smn-stations.js.map +1 -0
  16. package/dist/data/ogd-stac-client.js +95 -0
  17. package/dist/data/ogd-stac-client.js.map +1 -0
  18. package/dist/data/ogd-station-list.js +39 -0
  19. package/dist/data/ogd-station-list.js.map +1 -0
  20. package/dist/data/ogd-station-resolver.js +126 -0
  21. package/dist/data/ogd-station-resolver.js.map +1 -0
  22. package/dist/index.js +138 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/schemas/meteoswiss-fetch.js +25 -0
  25. package/dist/schemas/meteoswiss-fetch.js.map +1 -0
  26. package/dist/schemas/meteoswiss-search.js +46 -0
  27. package/dist/schemas/meteoswiss-search.js.map +1 -0
  28. package/dist/schemas/ogd-current-weather.js +19 -0
  29. package/dist/schemas/ogd-current-weather.js.map +1 -0
  30. package/dist/schemas/ogd-local-forecast.js +19 -0
  31. package/dist/schemas/ogd-local-forecast.js.map +1 -0
  32. package/dist/schemas/ogd-pollen-data.js +11 -0
  33. package/dist/schemas/ogd-pollen-data.js.map +1 -0
  34. package/dist/schemas/ogd-shared.js +78 -0
  35. package/dist/schemas/ogd-shared.js.map +1 -0
  36. package/dist/schemas/ogd-station-list.js +21 -0
  37. package/dist/schemas/ogd-station-list.js.map +1 -0
  38. package/dist/server.js +307 -0
  39. package/dist/server.js.map +1 -0
  40. package/dist/support/environment-validation.js +129 -0
  41. package/dist/support/environment-validation.js.map +1 -0
  42. package/dist/support/geocode.js +81 -0
  43. package/dist/support/geocode.js.map +1 -0
  44. package/dist/support/haversine.js +46 -0
  45. package/dist/support/haversine.js.map +1 -0
  46. package/dist/support/http-cache.js +170 -0
  47. package/dist/support/http-cache.js.map +1 -0
  48. package/dist/support/http-communication.js +215 -0
  49. package/dist/support/http-communication.js.map +1 -0
  50. package/dist/support/logging.js +81 -0
  51. package/dist/support/logging.js.map +1 -0
  52. package/dist/support/markdown-rendering.js +62 -0
  53. package/dist/support/markdown-rendering.js.map +1 -0
  54. package/dist/support/normalize.js +17 -0
  55. package/dist/support/normalize.js.map +1 -0
  56. package/dist/support/ogd-csv-parser.js +58 -0
  57. package/dist/support/ogd-csv-parser.js.map +1 -0
  58. package/dist/support/reverse-geocode.js +71 -0
  59. package/dist/support/reverse-geocode.js.map +1 -0
  60. package/dist/support/session-management.js +117 -0
  61. package/dist/support/session-management.js.map +1 -0
  62. package/dist/support/styles.js +5 -0
  63. package/dist/support/styles.js.map +1 -0
  64. package/dist/support/test-fixtures.js +12 -0
  65. package/dist/support/test-fixtures.js.map +1 -0
  66. package/dist/support/url-generation.js +32 -0
  67. package/dist/support/url-generation.js.map +1 -0
  68. package/dist/support/validation-errors.js +38 -0
  69. package/dist/support/validation-errors.js.map +1 -0
  70. package/dist/support/version.js +25 -0
  71. package/dist/support/version.js.map +1 -0
  72. package/dist/support/weather-icons.js +101 -0
  73. package/dist/support/weather-icons.js.map +1 -0
  74. package/dist/tools/meteoswiss-fetch.js +30 -0
  75. package/dist/tools/meteoswiss-fetch.js.map +1 -0
  76. package/dist/tools/meteoswiss-search.js +29 -0
  77. package/dist/tools/meteoswiss-search.js.map +1 -0
  78. package/dist/transports/streamable-http.js +282 -0
  79. package/dist/transports/streamable-http.js.map +1 -0
  80. package/dist/types/mcp-prompts.js +71 -0
  81. package/dist/types/mcp-prompts.js.map +1 -0
  82. package/package.json +101 -0
  83. package/src/views/homepage/overview.md +21 -0
  84. package/src/views/homepage/tools.md +63 -0
  85. package/src/views/homepage/usage.md +44 -0
package/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # MeteoSwiss MCP Server
2
+
3
+ [![npm version](https://img.shields.io/npm/v/meteoswiss-mcp)](https://www.npmjs.com/package/meteoswiss-mcp)
4
+ [![License: CC0-1.0](https://img.shields.io/badge/license-CC0--1.0-blue)](../../LICENSE)
5
+ [![Node.js >= 22](https://img.shields.io/badge/node-%3E%3D22-brightgreen)](https://nodejs.org/)
6
+
7
+ MCP server for MeteoSwiss weather data — powered by [MeteoSwiss Open Data](https://opendatadocs.meteoswiss.ch/), the same data behind the MeteoSwiss app and website.
8
+
9
+ - **Multi-day forecasts** for ~6000 Swiss locations (postal codes, stations, place names)
10
+ - **Real-time measurements** from ~160 automatic weather stations, updated every 10 minutes
11
+ - **Station discovery** by name, canton, or GPS coordinates
12
+ - **Pollen monitoring** from ~15 stations across Switzerland
13
+ - **MeteoSwiss website** search and content retrieval
14
+
15
+ ## Use the Hosted Service
16
+
17
+ No installation required — the server is hosted at `https://meteoswiss-mcp.ars.is`.
18
+
19
+ ### Claude Code
20
+
21
+ ```bash
22
+ claude mcp add meteoswiss https://meteoswiss-mcp.ars.is/mcp
23
+ ```
24
+
25
+ ### Claude Desktop
26
+
27
+ Add to your configuration file (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "meteoswiss": {
33
+ "command": "npx",
34
+ "args": ["mcp-remote", "https://meteoswiss-mcp.ars.is/mcp"]
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ ### Claude.ai
41
+
42
+ Go to **Settings** → **Integrations** → **Add MCP Server** → paste `https://meteoswiss-mcp.ars.is/mcp`.
43
+
44
+ ### Example Questions
45
+
46
+ - "What's the weather forecast for Zurich this week?"
47
+ - "Wie wird das Wetter in Bern morgen?"
48
+ - "Quelle est la météo à Genève?"
49
+ - "Che tempo fa a Lugano?"
50
+ - "How windy is it at Jungfraujoch right now?"
51
+ - "Show me pollen data for Zurich"
52
+
53
+ ## Available Tools
54
+
55
+ | Tool | Description |
56
+ |------|-------------|
57
+ | `meteoswissLocalForecast` | Multi-day forecasts for any Swiss location by postal code, station, or place name |
58
+ | `meteoswissCurrentWeather` | Real-time measurements from automatic weather stations (temperature, wind, humidity, pressure) |
59
+ | `meteoswissStations` | Search and browse the MeteoSwiss station network by name, canton, or coordinates |
60
+ | `meteoswissPollenData` | Current pollen concentration data from monitoring stations |
61
+ | `search` | Search MeteoSwiss website content (DE, FR, IT, EN) |
62
+ | `fetch` | Fetch full content from MeteoSwiss pages in markdown, text, or HTML |
63
+
64
+ ## Prompts
65
+
66
+ Pre-configured prompts for common weather queries:
67
+
68
+ | Prompt | Language | Description |
69
+ |--------|----------|-------------|
70
+ | `wetterNordschweiz` | German | Forecast and current weather for Northern Switzerland |
71
+ | `wetterSchweiz` | German | Weather for any Swiss location |
72
+ | `meteoSuisseRomande` | French | Forecast and current weather for Western Switzerland |
73
+ | `meteoTicino` | Italian | Forecast and current weather for Southern Switzerland |
74
+
75
+ ---
76
+
77
+ ## Self-Hosting
78
+
79
+ ### Docker
80
+
81
+ ```bash
82
+ docker run -p 3000:3000 ghcr.io/eins78/meteoswiss-mcp:latest
83
+ ```
84
+
85
+ With custom URL:
86
+
87
+ ```bash
88
+ docker run -p 3000:3000 -e PUBLIC_URL=https://your-domain.com ghcr.io/eins78/meteoswiss-mcp:latest
89
+ ```
90
+
91
+ ### Docker Compose
92
+
93
+ ```bash
94
+ docker compose up
95
+ ```
96
+
97
+ ### From Source
98
+
99
+ ```bash
100
+ git clone https://github.com/eins78/meteoswiss-llm-tools.git
101
+ cd meteoswiss-llm-tools/packages/meteoswiss-mcp
102
+ pnpm install
103
+ pnpm build
104
+ pnpm start
105
+ ```
106
+
107
+ The server will be available at `http://localhost:3000`. MCP endpoint: `http://localhost:3000/mcp`.
108
+
109
+ ### Local MCP Configuration
110
+
111
+ ```json
112
+ {
113
+ "mcpServers": {
114
+ "meteoswiss-local": {
115
+ "command": "npx",
116
+ "args": ["mcp-remote", "http://localhost:3000/mcp"]
117
+ }
118
+ }
119
+ }
120
+ ```
121
+
122
+ ## Environment Variables
123
+
124
+ | Variable | Default | Description |
125
+ |----------|---------|-------------|
126
+ | `PORT` | `3000` | Server port |
127
+ | `PUBLIC_URL` | — | Full public URL for URL generation (e.g. `https://meteoswiss-mcp.ars.is`) |
128
+ | `USE_TEST_FIXTURES` | `false` | Use local test data instead of live API |
129
+ | `BIND_ADDRESS` | `0.0.0.0` | Interface to bind to |
130
+ | `MAX_SESSIONS` | `100` | Maximum concurrent sessions |
131
+ | `SESSION_TIMEOUT_MS` | `300000` | Session timeout in milliseconds |
132
+ | `DEBUG` | — | Debug namespaces (e.g. `mcp:*`) |
133
+
134
+ ---
135
+
136
+ ## Development
137
+
138
+ ### Prerequisites
139
+
140
+ - Node.js v22+ ([nvm](https://github.com/nvm-sh/nvm) recommended)
141
+ - [pnpm](https://pnpm.io/)
142
+
143
+ ### Setup
144
+
145
+ ```bash
146
+ nvm use
147
+ pnpm install
148
+ ```
149
+
150
+ ### Commands
151
+
152
+ | Command | Description |
153
+ |---------|-------------|
154
+ | `pnpm dev` | Start with hot reloading |
155
+ | `pnpm start` | Start the server |
156
+ | `pnpm build` | Build TypeScript |
157
+ | `pnpm test` | Run tests |
158
+ | `pnpm run lint` | Type checking + ESLint |
159
+ | `pnpm run fix` | Auto-fix lint errors |
160
+ | `pnpm run ci` | Full CI (lint + build + test) |
161
+ | `pnpm run dev:inspect` | Test with MCP Inspector |
162
+
163
+ ### Project Structure
164
+
165
+ ```
166
+ src/
167
+ index.ts # Entry point
168
+ server.ts # MCP server (factory pattern)
169
+ data/ # Data access and transformation
170
+ schemas/ # Zod schemas for validation
171
+ tools/ # MCP tool implementations
172
+ support/ # Logging, validation, HTTP, sessions
173
+ views/homepage/ # Server homepage content
174
+ test/
175
+ integration/ # Integration tests for all tools
176
+ __fixtures__/ # Test data
177
+ ```
178
+
179
+ ### Debugging
180
+
181
+ Enable debug output with the `DEBUG` environment variable:
182
+
183
+ ```bash
184
+ DEBUG=mcp:* pnpm dev # All debug output
185
+ DEBUG=mcp:tools pnpm dev # Tool execution only
186
+ DEBUG=mcp:transport pnpm dev # Transport layer only
187
+ ```
188
+
189
+ See [docs/debugging-guide.md](docs/debugging-guide.md) for more.
190
+
191
+ ## Contributing
192
+
193
+ Contributions are welcome! Run `pnpm run fix && pnpm run ci` before committing.
194
+
195
+ See [docs/releasing.md](docs/releasing.md) for the release process.
196
+
197
+ ## License
198
+
199
+ [CC0-1.0](../../LICENSE) — public domain
@@ -0,0 +1,356 @@
1
+ import fs from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { JSDOM } from 'jsdom';
6
+ import TurndownService from 'turndown';
7
+ import { gfm } from 'turndown-plugin-gfm';
8
+ import { fetchHtml, HttpRequestError } from '../support/http-communication.js';
9
+ import { debugData } from '../support/logging.js';
10
+ // Test fixtures location
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
+ const TEST_FIXTURES_DEV_PATH = path.resolve(__dirname, '../../test/__fixtures__/content');
13
+ const TEST_FIXTURES_PROD_PATH = path.resolve(__dirname, '../test/__fixtures__/content');
14
+ const TEST_FIXTURES_ROOT = existsSync(TEST_FIXTURES_DEV_PATH)
15
+ ? TEST_FIXTURES_DEV_PATH
16
+ : TEST_FIXTURES_PROD_PATH;
17
+ const USE_TEST_FIXTURES = process.env.USE_TEST_FIXTURES === 'true';
18
+ // Allowed MeteoSwiss domains
19
+ const ALLOWED_DOMAINS = [
20
+ 'www.meteoschweiz.admin.ch',
21
+ 'www.meteosuisse.admin.ch',
22
+ 'www.meteosvizzera.admin.ch',
23
+ 'www.meteoswiss.admin.ch',
24
+ 'meteoschweiz.admin.ch',
25
+ 'meteosuisse.admin.ch',
26
+ 'meteosvizzera.admin.ch',
27
+ 'meteoswiss.admin.ch',
28
+ ];
29
+ // Initialize Turndown for HTML to Markdown conversion
30
+ const turndownService = new TurndownService({
31
+ headingStyle: 'atx',
32
+ bulletListMarker: '-',
33
+ codeBlockStyle: 'fenced',
34
+ });
35
+ // Add GFM plugin for better markdown support (tables, strikethrough, task lists)
36
+ turndownService.use(gfm);
37
+ /**
38
+ * Fetch MeteoSwiss content by ID
39
+ *
40
+ * @param params Fetch parameters
41
+ * @returns Content response
42
+ */
43
+ export async function fetchMeteoSwissContent(params) {
44
+ const { id, format = 'markdown', includeMetadata = true } = params;
45
+ debugData('fetchMeteoSwissContent called with params: %o', {
46
+ id,
47
+ format,
48
+ includeMetadata,
49
+ });
50
+ if (USE_TEST_FIXTURES) {
51
+ debugData('Using test fixtures for content fetch');
52
+ return fetchFromTestFixtures(id, format, includeMetadata);
53
+ }
54
+ debugData('Using live API for content fetch');
55
+ return fetchFromWeb(id, format, includeMetadata);
56
+ }
57
+ /**
58
+ * Fetch content from the web
59
+ */
60
+ async function fetchFromWeb(id, format, includeMetadata) {
61
+ // The ID should now be a full URL from the search tool
62
+ const url = id.startsWith('http')
63
+ ? id
64
+ : `https://www.meteoswiss.admin.ch${id.startsWith('/') ? id : '/' + id}`; // Fallback for backward compatibility
65
+ debugData('Fetching content from URL: %s', url);
66
+ // Validate the URL is from an allowed MeteoSwiss domain
67
+ try {
68
+ const parsedUrl = new URL(url);
69
+ if (!ALLOWED_DOMAINS.includes(parsedUrl.hostname)) {
70
+ throw new Error(`Invalid domain: ${parsedUrl.hostname}. Only MeteoSwiss domains are allowed.`);
71
+ }
72
+ }
73
+ catch (error) {
74
+ if (error instanceof TypeError) {
75
+ throw new Error(`Invalid URL: ${url}`, { cause: error });
76
+ }
77
+ throw error;
78
+ }
79
+ try {
80
+ debugData('Making HTTP request to fetch content');
81
+ const html = await fetchHtml(url);
82
+ debugData('Content fetched successfully, size: %d bytes', html.length);
83
+ // Add timeout protection for HTML processing
84
+ const processingTimeout = new Promise((_, reject) => {
85
+ setTimeout(() => reject(new Error('HTML processing timeout after 10 seconds')), 10000);
86
+ });
87
+ const contentProcessing = processHtmlContent(html, id, url, format, includeMetadata);
88
+ return await Promise.race([contentProcessing, processingTimeout]);
89
+ }
90
+ catch (error) {
91
+ debugData('Content fetch error: %o', error);
92
+ if (error instanceof HttpRequestError && error.statusCode === 404) {
93
+ throw new Error(`Content not found: ${id}`, { cause: error });
94
+ }
95
+ throw new Error(`Failed to fetch content: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
96
+ }
97
+ }
98
+ /**
99
+ * Fetch content from test fixtures
100
+ */
101
+ async function fetchFromTestFixtures(id, format, includeMetadata) {
102
+ debugData('Looking for test fixture for ID: %s', id);
103
+ // Extract language from URL if it's a full URL
104
+ let detectedLang = 'de';
105
+ let urlPath = id;
106
+ if (id.startsWith('http')) {
107
+ const url = new URL(id);
108
+ urlPath = url.pathname;
109
+ // Detect language from domain
110
+ if (url.hostname.includes('meteoschweiz')) {
111
+ detectedLang = 'de';
112
+ }
113
+ else if (url.hostname.includes('meteosuisse')) {
114
+ detectedLang = 'fr';
115
+ }
116
+ else if (url.hostname.includes('meteosvizzera')) {
117
+ detectedLang = 'it';
118
+ }
119
+ else if (url.hostname.includes('meteoswiss')) {
120
+ detectedLang = 'en';
121
+ }
122
+ }
123
+ // Extract filename from path
124
+ const fileName = urlPath.split('/').pop() || 'index.html';
125
+ const baseName = fileName.replace(/\.[^.]+$/, '');
126
+ // Try to find the fixture file
127
+ const languages = [detectedLang, 'de', 'fr', 'it', 'en'];
128
+ for (const lang of languages) {
129
+ const fixtureFile = path.join(TEST_FIXTURES_ROOT, lang, `${baseName}.html`);
130
+ debugData('Checking for fixture file: %s', fixtureFile);
131
+ if (existsSync(fixtureFile)) {
132
+ debugData('Loading test fixture from: %s', fixtureFile);
133
+ const html = await fs.readFile(fixtureFile, 'utf-8');
134
+ const url = id.startsWith('http') ? id : `https://www.meteoswiss.admin.ch${id}`;
135
+ return processHtmlContent(html, id, url, format, includeMetadata);
136
+ }
137
+ }
138
+ debugData('No test fixture found for ID: %s', id);
139
+ throw new Error(`Content not found: ${id}`);
140
+ }
141
+ /**
142
+ * Process HTML content and convert to requested format
143
+ */
144
+ function processHtmlContent(html, id, url, format, includeMetadata) {
145
+ debugData('Processing HTML content, format: %s, includeMetadata: %s, size: %d bytes', format, includeMetadata, html.length);
146
+ // Warn if HTML is very large
147
+ if (html.length > 500000) {
148
+ debugData('WARNING: Large HTML document (%d bytes), processing may be slow', html.length);
149
+ }
150
+ const dom = new JSDOM(html);
151
+ const document = dom.window.document;
152
+ // Extract main content
153
+ const mainContent = extractMainContent(document);
154
+ // Extract title - try multiple selectors
155
+ const title = document.querySelector('h1')?.textContent?.trim() ||
156
+ document.querySelector('mch-title[level="1"]')?.textContent?.trim() ||
157
+ document.querySelector('[heading]')?.getAttribute('heading') ||
158
+ document.querySelector('title')?.textContent?.trim() ||
159
+ 'Untitled';
160
+ // Extract metadata
161
+ const metadata = includeMetadata
162
+ ? {
163
+ url,
164
+ language: detectLanguage(document),
165
+ lastModified: extractLastModified(document),
166
+ contentType: extractContentType(document),
167
+ keywords: extractKeywords(document),
168
+ description: extractDescription(document),
169
+ }
170
+ : undefined;
171
+ // Convert content to requested format
172
+ let content;
173
+ switch (format) {
174
+ case 'markdown':
175
+ content = turndownService.turndown(mainContent);
176
+ // Prepend title as H1 if we have one and it's not already in the content
177
+ if (title && title !== 'Untitled' && !content.includes(`# ${title}`)) {
178
+ content = `# ${title}\n\n${content}`;
179
+ }
180
+ break;
181
+ case 'text':
182
+ content = extractTextContent(mainContent);
183
+ // Prepend title for text format too
184
+ if (title && title !== 'Untitled') {
185
+ content = `${title}\n\n${content}`;
186
+ }
187
+ break;
188
+ default:
189
+ throw new Error(`Invalid format: ${format}`);
190
+ }
191
+ debugData('Content processed successfully, content length: %d characters', content.length);
192
+ return {
193
+ id,
194
+ title,
195
+ content,
196
+ format,
197
+ metadata,
198
+ };
199
+ }
200
+ /**
201
+ * Extract main content from the page
202
+ */
203
+ function extractMainContent(document) {
204
+ // Remove screenreader titles in all languages
205
+ const screenreaderTitles = [
206
+ 'Inhaltsbereich', // German
207
+ 'Contenu principal', // French
208
+ 'Contenuto principale', // Italian
209
+ 'Main content', // English
210
+ ];
211
+ // Remove elements with screenreader-only titles
212
+ document.querySelectorAll('h1, h2, h3').forEach((heading) => {
213
+ const text = heading.textContent?.trim() || '';
214
+ if (screenreaderTitles.includes(text) ||
215
+ heading.classList.contains('a11y-description--hidden') ||
216
+ heading.classList.contains('sr-only') ||
217
+ heading.classList.contains('visually-hidden')) {
218
+ heading.remove();
219
+ }
220
+ });
221
+ // Remove share widgets and dialogs
222
+ const shareSelectors = [
223
+ 'dialog.share-dialog', // Share dialog container
224
+ '.share-dialog', // Share dialog elements
225
+ '.share-dialog-button__share--dark', // Share button
226
+ '[data-share]',
227
+ '.share-widget',
228
+ '.social-share',
229
+ 'mch-share-dialog',
230
+ '.mch-page-intro__controls', // Container that includes share button
231
+ ];
232
+ shareSelectors.forEach((selector) => {
233
+ document.querySelectorAll(selector).forEach((el) => {
234
+ el.remove();
235
+ });
236
+ });
237
+ // Also remove elements that contain share-related text but use generic classes
238
+ document.querySelectorAll('[class*="share"], [id*="share"]').forEach((el) => {
239
+ const text = el.textContent?.toLowerCase() || '';
240
+ if (text.includes('seite teilen') ||
241
+ text.includes('partager') ||
242
+ text.includes('condividi') ||
243
+ text.includes('share') ||
244
+ text.includes('copy link') ||
245
+ text.includes('link kopieren')) {
246
+ el.remove();
247
+ }
248
+ });
249
+ // First, expand any shadow DOM content by looking for slot elements
250
+ // and moving their assigned content into the main DOM
251
+ // Note: JSDOM doesn't fully support shadow DOM, so this is a best-effort approach
252
+ document.querySelectorAll('slot[name="main"]').forEach((slot) => {
253
+ const parent = slot.parentElement;
254
+ if (parent) {
255
+ // In JSDOM, slots don't have assignedNodes, so we'll just look for child elements
256
+ // that might be slotted content
257
+ const children = Array.from(slot.children);
258
+ children.forEach((child) => {
259
+ parent.appendChild(child.cloneNode(true));
260
+ });
261
+ }
262
+ });
263
+ // Try different selectors for main content
264
+ const selectors = [
265
+ 'main',
266
+ '[role="main"]',
267
+ '.main-content',
268
+ '.content',
269
+ 'article',
270
+ '.mch-article',
271
+ '#content',
272
+ '.page-main__wrapper', // MeteoSwiss specific
273
+ 'mch-detail-page', // MeteoSwiss component
274
+ ];
275
+ for (const selector of selectors) {
276
+ const element = document.querySelector(selector);
277
+ if (element) {
278
+ // Also include any slot content that might be after the main content
279
+ const slotContent = element.querySelectorAll('slot[slot="main"], [slot="main"]');
280
+ let additionalContent = '';
281
+ slotContent.forEach((el) => {
282
+ additionalContent += el.innerHTML || el.textContent || '';
283
+ });
284
+ return element.innerHTML + additionalContent;
285
+ }
286
+ }
287
+ // Fallback to body content
288
+ const body = document.querySelector('body');
289
+ if (body) {
290
+ // Remove navigation, header, footer, scripts, styles
291
+ const toRemove = body.querySelectorAll('nav, header, footer, script, style, noscript');
292
+ toRemove.forEach((el) => el.remove());
293
+ return body.innerHTML;
294
+ }
295
+ return '';
296
+ }
297
+ /**
298
+ * Extract text content from HTML
299
+ */
300
+ function extractTextContent(html) {
301
+ const tempDom = new JSDOM(html);
302
+ const text = tempDom.window.document.body.textContent || '';
303
+ // Clean up whitespace
304
+ return text
305
+ .split('\n')
306
+ .map((line) => line.trim())
307
+ .filter((line) => line.length > 0)
308
+ .join('\n');
309
+ }
310
+ /**
311
+ * Detect language from the document
312
+ */
313
+ function detectLanguage(document) {
314
+ const lang = document.documentElement.getAttribute('lang') ||
315
+ document.querySelector('meta[property="og:locale"]')?.getAttribute('content') ||
316
+ 'de';
317
+ return lang.substring(0, 2).toLowerCase();
318
+ }
319
+ /**
320
+ * Extract last modified date
321
+ */
322
+ function extractLastModified(document) {
323
+ const lastModified = document.querySelector('meta[property="article:modified_time"]')?.getAttribute('content') ||
324
+ document.querySelector('meta[name="DC.date.modified"]')?.getAttribute('content');
325
+ return lastModified || undefined;
326
+ }
327
+ /**
328
+ * Extract content type
329
+ */
330
+ function extractContentType(document) {
331
+ const type = document.querySelector('meta[property="og:type"]')?.getAttribute('content') ||
332
+ document.querySelector('meta[name="DC.type"]')?.getAttribute('content') ||
333
+ 'article';
334
+ return type;
335
+ }
336
+ /**
337
+ * Extract keywords
338
+ */
339
+ function extractKeywords(document) {
340
+ const keywordsStr = document.querySelector('meta[name="keywords"]')?.getAttribute('content') ||
341
+ document.querySelector('meta[property="article:tag"]')?.getAttribute('content') ||
342
+ '';
343
+ return keywordsStr
344
+ .split(',')
345
+ .map((k) => k.trim())
346
+ .filter((k) => k.length > 0);
347
+ }
348
+ /**
349
+ * Extract description
350
+ */
351
+ function extractDescription(document) {
352
+ const description = document.querySelector('meta[name="description"]')?.getAttribute('content') ||
353
+ document.querySelector('meta[property="og:description"]')?.getAttribute('content');
354
+ return description || undefined;
355
+ }
356
+ //# sourceMappingURL=meteoswiss-content-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meteoswiss-content-data.js","sourceRoot":"","sources":["../../src/data/meteoswiss-content-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,eAAe,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGlD,yBAAyB;AACzB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,MAAM,sBAAsB,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,iCAAiC,CAAC,CAAC;AAC1F,MAAM,uBAAuB,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,8BAA8B,CAAC,CAAC;AACxF,MAAM,kBAAkB,GAAG,UAAU,CAAC,sBAAsB,CAAC;IAC3D,CAAC,CAAC,sBAAsB;IACxB,CAAC,CAAC,uBAAuB,CAAC;AAE5B,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,MAAM,CAAC;AAKnE,6BAA6B;AAC7B,MAAM,eAAe,GAAG;IACtB,2BAA2B;IAC3B,0BAA0B;IAC1B,4BAA4B;IAC5B,yBAAyB;IACzB,uBAAuB;IACvB,sBAAsB;IACtB,wBAAwB;IACxB,qBAAqB;CACtB,CAAC;AAEF,sDAAsD;AACtD,MAAM,eAAe,GAAG,IAAI,eAAe,CAAC;IAC1C,YAAY,EAAE,KAAK;IACnB,gBAAgB,EAAE,GAAG;IACrB,cAAc,EAAE,QAAQ;CACzB,CAAC,CAAC;AAEH,iFAAiF;AACjF,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAoBzB;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAmC;IAEnC,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,EAAE,eAAe,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAEnE,SAAS,CAAC,+CAA+C,EAAE;QACzD,EAAE;QACF,MAAM;QACN,eAAe;KAChB,CAAC,CAAC;IAEH,IAAI,iBAAiB,EAAE,CAAC;QACtB,SAAS,CAAC,uCAAuC,CAAC,CAAC;QACnD,OAAO,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC;IAC5D,CAAC;IAED,SAAS,CAAC,kCAAkC,CAAC,CAAC;IAC9C,OAAO,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC;AACnD,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,YAAY,CACzB,EAAU,EACV,MAA2B,EAC3B,eAAwB;IAExB,uDAAuD;IACvD,MAAM,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QAC/B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,kCAAkC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,CAAC,sCAAsC;IAElH,SAAS,CAAC,+BAA+B,EAAE,GAAG,CAAC,CAAC;IAEhD,wDAAwD;IACxD,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CACb,mBAAmB,SAAS,CAAC,QAAQ,wCAAwC,CAC9E,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,gBAAgB,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,SAAS,CAAC,sCAAsC,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;QAClC,SAAS,CAAC,8CAA8C,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAEvE,6CAA6C;QAC7C,MAAM,iBAAiB,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;YACzD,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACzF,CAAC,CAAC,CAAC;QAEH,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC;QAErF,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACpE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,SAAS,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;QAC5C,IAAI,KAAK,YAAY,gBAAgB,IAAI,KAAK,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;YAClE,MAAM,IAAI,KAAK,CAAC,sBAAsB,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,IAAI,KAAK,CACb,4BAA4B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EACpF,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,qBAAqB,CAClC,EAAU,EACV,MAA2B,EAC3B,eAAwB;IAExB,SAAS,CAAC,qCAAqC,EAAE,EAAE,CAAC,CAAC;IAErD,+CAA+C;IAC/C,IAAI,YAAY,GAAG,IAAI,CAAC;IACxB,IAAI,OAAO,GAAG,EAAE,CAAC;IAEjB,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC;QACxB,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC;QAEvB,8BAA8B;QAC9B,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YAC1C,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;aAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YAChD,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;aAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;YAClD,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;aAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YAC/C,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IAED,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,YAAY,CAAC;IAC1D,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAElD,+BAA+B;IAC/B,MAAM,SAAS,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACzD,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,IAAI,EAAE,GAAG,QAAQ,OAAO,CAAC,CAAC;QAC5E,SAAS,CAAC,+BAA+B,EAAE,WAAW,CAAC,CAAC;QACxD,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5B,SAAS,CAAC,+BAA+B,EAAE,WAAW,CAAC,CAAC;YACxD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACrD,MAAM,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kCAAkC,EAAE,EAAE,CAAC;YAEhF,OAAO,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,SAAS,CAAC,kCAAkC,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,IAAI,KAAK,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CACzB,IAAY,EACZ,EAAU,EACV,GAAW,EACX,MAA2B,EAC3B,eAAwB;IAExB,SAAS,CACP,0EAA0E,EAC1E,MAAM,EACN,eAAe,EACf,IAAI,CAAC,MAAM,CACZ,CAAC;IAEF,6BAA6B;IAC7B,IAAI,IAAI,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;QACzB,SAAS,CAAC,iEAAiE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5F,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;IAErC,uBAAuB;IACvB,MAAM,WAAW,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAEjD,yCAAyC;IACzC,MAAM,KAAK,GACT,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE;QACjD,QAAQ,CAAC,aAAa,CAAC,sBAAsB,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE;QACnE,QAAQ,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC;QAC5D,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE;QACpD,UAAU,CAAC;IAEb,mBAAmB;IACnB,MAAM,QAAQ,GAAG,eAAe;QAC9B,CAAC,CAAC;YACE,GAAG;YACH,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC;YAClC,YAAY,EAAE,mBAAmB,CAAC,QAAQ,CAAC;YAC3C,WAAW,EAAE,kBAAkB,CAAC,QAAQ,CAAC;YACzC,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC;YACnC,WAAW,EAAE,kBAAkB,CAAC,QAAQ,CAAC;SAC1C;QACH,CAAC,CAAC,SAAS,CAAC;IAEd,sCAAsC;IACtC,IAAI,OAAe,CAAC;IACpB,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,UAAU;YACb,OAAO,GAAG,eAAe,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YAChD,yEAAyE;YACzE,IAAI,KAAK,IAAI,KAAK,KAAK,UAAU,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,KAAK,EAAE,CAAC,EAAE,CAAC;gBACrE,OAAO,GAAG,KAAK,KAAK,OAAO,OAAO,EAAE,CAAC;YACvC,CAAC;YACD,MAAM;QACR,KAAK,MAAM;YACT,OAAO,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;YAC1C,oCAAoC;YACpC,IAAI,KAAK,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;gBAClC,OAAO,GAAG,GAAG,KAAK,OAAO,OAAO,EAAE,CAAC;YACrC,CAAC;YACD,MAAM;QACR;YACE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,SAAS,CAAC,+DAA+D,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAE3F,OAAO;QACL,EAAE;QACF,KAAK;QACL,OAAO;QACP,MAAM;QACN,QAAQ;KACT,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,QAAkB;IAC5C,8CAA8C;IAC9C,MAAM,kBAAkB,GAAG;QACzB,gBAAgB,EAAE,SAAS;QAC3B,mBAAmB,EAAE,SAAS;QAC9B,sBAAsB,EAAE,UAAU;QAClC,cAAc,EAAE,UAAU;KAC3B,CAAC;IAEF,gDAAgD;IAChD,QAAQ,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC1D,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAC/C,IACE,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,0BAA0B,CAAC;YACtD,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;YACrC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAC7C,CAAC;YACD,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,mCAAmC;IACnC,MAAM,cAAc,GAAG;QACrB,qBAAqB,EAAE,yBAAyB;QAChD,eAAe,EAAE,wBAAwB;QACzC,mCAAmC,EAAE,eAAe;QACpD,cAAc;QACd,eAAe;QACf,eAAe;QACf,kBAAkB;QAClB,2BAA2B,EAAE,uCAAuC;KACrE,CAAC;IAEF,cAAc,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;QAClC,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE;YACjD,EAAE,CAAC,MAAM,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,+EAA+E;IAC/E,QAAQ,CAAC,gBAAgB,CAAC,iCAAiC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE;QAC1E,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QACjD,IACE,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;YAC7B,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;YAC1B,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YACtB,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;YAC1B,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,EAC9B,CAAC;YACD,EAAE,CAAC,MAAM,EAAE,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,oEAAoE;IACpE,sDAAsD;IACtD,kFAAkF;IAClF,QAAQ,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC9D,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC;QAClC,IAAI,MAAM,EAAE,CAAC;YACX,kFAAkF;YAClF,gCAAgC;YAChC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3C,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;gBACzB,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5C,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,2CAA2C;IAC3C,MAAM,SAAS,GAAG;QAChB,MAAM;QACN,eAAe;QACf,eAAe;QACf,UAAU;QACV,SAAS;QACT,cAAc;QACd,UAAU;QACV,qBAAqB,EAAE,sBAAsB;QAC7C,iBAAiB,EAAE,uBAAuB;KAC3C,CAAC;IAEF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACjD,IAAI,OAAO,EAAE,CAAC;YACZ,qEAAqE;YACrE,MAAM,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,kCAAkC,CAAC,CAAC;YACjF,IAAI,iBAAiB,GAAG,EAAE,CAAC;YAC3B,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE;gBACzB,iBAAiB,IAAI,EAAE,CAAC,SAAS,IAAI,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC;YAC5D,CAAC,CAAC,CAAC;YACH,OAAO,OAAO,CAAC,SAAS,GAAG,iBAAiB,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,2BAA2B;IAC3B,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,IAAI,EAAE,CAAC;QACT,qDAAqD;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,8CAA8C,CAAC,CAAC;QACvF,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,IAAY;IACtC,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;IAE5D,sBAAsB;IACtB,OAAO,IAAI;SACR,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAClC,MAAM,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;SACzC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,QAAkB;IACxC,MAAM,IAAI,GACR,QAAQ,CAAC,eAAe,CAAC,YAAY,CAAC,MAAM,CAAC;QAC7C,QAAQ,CAAC,aAAa,CAAC,4BAA4B,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC;QAC7E,IAAI,CAAC;IAEP,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAAC,QAAkB;IAC7C,MAAM,YAAY,GAChB,QAAQ,CAAC,aAAa,CAAC,wCAAwC,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC;QACzF,QAAQ,CAAC,aAAa,CAAC,+BAA+B,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAEnF,OAAO,YAAY,IAAI,SAAS,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,QAAkB;IAC5C,MAAM,IAAI,GACR,QAAQ,CAAC,aAAa,CAAC,0BAA0B,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC;QAC3E,QAAQ,CAAC,aAAa,CAAC,sBAAsB,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC;QACvE,SAAS,CAAC;IAEZ,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,QAAkB;IACzC,MAAM,WAAW,GACf,QAAQ,CAAC,aAAa,CAAC,uBAAuB,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC;QACxE,QAAQ,CAAC,aAAa,CAAC,8BAA8B,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC;QAC/E,EAAE,CAAC;IAEL,OAAO,WAAW;SACf,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,QAAkB;IAC5C,MAAM,WAAW,GACf,QAAQ,CAAC,aAAa,CAAC,0BAA0B,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC;QAC3E,QAAQ,CAAC,aAAa,CAAC,iCAAiC,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAErF,OAAO,WAAW,IAAI,SAAS,CAAC;AAClC,CAAC"}