1<!doctype html> 2<html lang="de"> 3 <head> 4 <meta charset="UTF-8" /> 5 <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> 6 <meta name="generator" content="Hostinger Horizons" /> 7 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 8 <title>Terra-Pie - Therapeutische & Pädagogische Praxis</title> 9 <meta name="description" content="Therapeutische Beratung, Life Coaching und wingwave in Kiel und online. Ãber 20 Jahre Erfahrung, evidenzbasierte Methoden. Termin bei Torsten Gottschall." /> 10 <meta name="keywords" content="therapeutische Beratung Kiel, Life Coaching Kiel, wingwave Coaching, Coaching online, Psychotherapie Beratung, Torsten Gottschall, Terra-Pie, Easy Weight, Supervision, Konfliktberatung, Mobbingberatung, Trauerbegleitung, Paarberatung, Seminare, Mental Health" /> 11 <meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" /> 12 <link rel="canonical" href="https://torsten-gottschall.de/" /> 13 <meta property="og:type" content="website" /> 14 <meta property="og:locale" content="de_DE" /> 15 <meta property="og:site_name" content="Terra-Pie â Torsten Gottschall" /> 16 <meta property="og:title" content="Therapeutische Beratung & Coaching in Kiel | Terra-Pie" /> 17 <meta property="og:description" content="Therapeutische Beratung, Life Coaching und wingwave in Kiel und online. Ãber 20 Jahre Erfahrung, evidenzbasierte Methoden. Termin bei Torsten Gottschall." /> 18 <meta property="og:url" content="https://torsten-gottschall.de/" /> 19 <meta property="og:image" content="https://horizons-cdn.hostinger.com/5afb7969-abff-4799-b3f5-b7b6f9dda281/2589_2-032full-c-soulpicture-NqBSb.jpg" /> 20 <meta name="twitter:card" content="summary_large_image" /> 21 <meta name="twitter:title" content="Therapeutische Beratung & Coaching in Kiel | Terra-Pie" /> 22 <meta name="twitter:description" content="Therapeutische Beratung, Life Coaching und wingwave in Kiel und online. Ãber 20 Jahre Erfahrung, evidenzbasierte Methoden. Termin bei Torsten Gottschall." /> 23 <meta name="twitter:image" content="https://horizons-cdn.hostinger.com/5afb7969-abff-4799-b3f5-b7b6f9dda281/2589_2-032full-c-soulpicture-NqBSb.jpg" /> 24 <link rel="preconnect" href="https://fonts.googleapis.com"> 25 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 26 <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> 27
27<script type="module" crossorigin src="/assets/index-4Y6CaQow.js"></script>
27 28 <link rel="stylesheet" crossorigin href="/assets/index-qP4x9G2Y.css"> 29
29<script type="module"> 30window.onerror = (message, source, lineno, colno, errorObj) => { 31 const errorDetails = errorObj ? JSON.stringify({ 32 name: errorObj.name, 33 message: errorObj.message, 34 stack: errorObj.stack, 35 source, 36 lineno, 37 colno, 38 }) : null; 39 40 window.parent.postMessage({ 41 type: 'horizons-runtime-error', 42 message, 43 error: errorDetails 44 }, '*'); 45}; 46</script>
46 47
47<script type="module"> 48const observer = new MutationObserver((mutations) => { 49 for (const mutation of mutations) { 50 for (const addedNode of mutation.addedNodes) { 51 if ( 52 addedNode.nodeType === Node.ELEMENT_NODE && 53 ( 54 addedNode.tagName?.toLowerCase() === 'vite-error-overlay' || 55 addedNode.classList?.contains('backdrop') 56 ) 57 ) { 58 handleViteOverlay(addedNode); 59 } 60 } 61 } 62}); 63 64observer.observe(document.documentElement, { 65 childList: true, 66 subtree: true 67}); 68 69function handleViteOverlay(node) { 70 if (!node.shadowRoot) { 71 return; 72 } 73 74 const backdrop = node.shadowRoot.querySelector('.backdrop'); 75 76 if (backdrop) { 77 const overlayHtml = backdrop.outerHTML; 78 const parser = new DOMParser(); 79 const doc = parser.parseFromString(overlayHtml, 'text/html'); 80 const messageBodyElement = doc.querySelector('.message-body'); 81 const fileElement = doc.querySelector('.file'); 82 const messageText = messageBodyElement ? messageBodyElement.textContent.trim() : ''; 83 const fileText = fileElement ? fileElement.textContent.trim() : ''; 84 const error = messageText + (fileText ? ' File:' + fileText : ''); 85 86 window.parent.postMessage({ 87 type: 'horizons-vite-error', 88 error, 89 }, '*'); 90 } 91} 92</script>
92 93
93<script type="module"> 94const originalConsoleError = console.error; 95const MATCH_LINE_COL_REGEX = /:(\d+):(\d+)\)?\s*$/; // regex to match the :lineNum:colNum 96const MATCH_AT_REGEX = /^\s*at\s+(?:async\s+)?(?:.*?\s+)?\(?/; // regex to remove the 'at' keyword and any 'async' or function name 97const MATCH_PATH_REGEX = /^\//; // regex to remove the leading slash 98 99function parseStackFrameLine(line) { 100 const lineColMatch = line.match(MATCH_LINE_COL_REGEX); 101 if (!lineColMatch) return null; 102 const [, lineNum, colNum] = lineColMatch; 103 const suffix = `:${lineNum}:${colNum}`; 104 const idx = line.lastIndexOf(suffix); 105 if (idx === -1) return null; 106 const before = line.substring(0, idx); 107 const path = before.replace(MATCH_AT_REGEX, '').trim(); 108 109 if (!path) return null; 110 111 try { 112 const pathname = new URL(path).pathname; 113 const filePath = pathname.replace(MATCH_PATH_REGEX, '') || pathname; 114 return `${filePath}:${lineNum}:${colNum}`; 115 } catch (e) { 116 const filePath = path.replace(MATCH_PATH_REGEX, '') || path; 117 return `${filePath}:${lineNum}:${colNum}`; 118 } 119} 120 121function getFilePathFromStack(stack, skipFrames = 0) { 122 if (!stack || typeof stack !== 'string') return null; 123 const lines = stack.split('\n').slice(1); 124 125 const frames = lines.map(line => parseStackFrameLine(line.replace(/\r$/, ''))).filter(Boolean); 126 127 return frames[skipFrames] ?? null; 128} 129 130function formatConsoleMessage(args, skipStackFrames = 1) { 131 let messageString = ''; 132 let filePath = null; 133 134 for (let i = 0; i < args.length; i++) { 135 const arg = args[i]; 136 if (arg instanceof Error) { 137 filePath = getFilePathFromStack(arg.stack, 0); 138 messageString = `${arg.name}: ${arg.message}`; 139 if (filePath) { 140 messageString = `${messageString} at ${filePath}`; 141 } 142 break; 143 } 144 } 145 146 if (!messageString) { 147 messageString = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' '); 148 const stack = new Error().stack; 149 filePath = getFilePathFromStack(stack, skipStackFrames); 150 if (filePath) { 151 messageString = `${messageString} at ${filePath}`; 152 } 153 } 154 155 return messageString; 156} 157 158console.error = function(...args) { 159 originalConsoleError.apply(console, args); 160 161 window.parent.postMessage({ 162 type: 'horizons-console-error', 163 error: formatConsoleMessage(args, 1) 164 }, '*'); 165}; 166 167const originalConsoleWarn = console.warn; 168 169console.warn = function(...args) { 170 originalConsoleWarn.apply(console, args); 171 172 window.parent.postMessage({ 173 type: 'horizons-console-warn', 174 warning: formatConsoleMessage(args, 1) 175 }, '*'); 176}; 177</script>
177 178
178<script type="module"> 179// Prevents authentication failures (PB returns 400) from crashing the app â use console.info, not console.error/PM2. 180// [urlPattern, bodyPattern] tuples; align with sandboxErrorUtils ERROR_BLACKLIST. 181const BENIGN_FETCH_ERRORS = [ 182 [/hcgi\/platform\/api\/collections\/.*auth-with-password.*/i, /Failed to authenticate/i], 183 [/hcgi\/api\//i, /Insufficient credits/i], 184 // A user-supplied integration secret (Stripe/PayPal/Twilio/...) is not set yet â an expected setup state, not a bug to fix. 185 [/hcgi\/api\//i, /INTEGRATION_NOT_CONFIGURED/i], 186]; 187 188function isBenignFetchError(url, body) { 189 return BENIGN_FETCH_ERRORS.some(([urlPattern, bodyPattern]) => 190 urlPattern.test(url) && (!bodyPattern || bodyPattern.test(body))); 191} 192 193const PLATFORM_URL_PATTERN = /hcgi\/platform\//i; 194const VALIDATION_CODE_TEXT_PATTERN = /validation_/; 195 196function hasValidationCode(value) { 197 if (value == null) { 198 return false; 199 } 200 if (typeof value === 'string') { 201 return value.startsWith('validation_'); 202 } 203 if (Array.isArray(value)) { 204 return value.some(hasValidationCode); 205 } 206 if (typeof value === 'object') { 207 return Object.values(value).some(hasValidationCode); 208 } 209 return false; 210} 211 212function isValidationFetchWarning(url, body) { 213 if (!PLATFORM_URL_PATTERN.test(url)) { 214 return false; 215 } 216 try { 217 return hasValidationCode(JSON.parse(body)); 218 } catch { 219 return VALIDATION_CODE_TEXT_PATTERN.test(body); 220 } 221} 222 223const originalFetch = window.fetch; 224 225window.fetch = function(...args) { 226 const url = args[0] instanceof Request ? args[0].url : args[0]; 227 228 // Skip WebSocket URLs 229 if (url.startsWith('ws:') || url.startsWith('wss:')) { 230 return originalFetch.apply(this, args); 231 } 232 233 return originalFetch.apply(this, args) 234 .then(async response => { 235 const contentType = response.headers.get('Content-Type') || ''; 236 237 // Exclude HTML document responses 238 const isDocumentResponse = 239 contentType.includes('text/html') || 240 contentType.includes('application/xhtml+xml'); 241 242 // no-cors requests (e.g. Google Analytics/Ads beacons) always return an opaque 243 const isOpaqueResponse = response.type === 'opaque'; 244 245 if (!response.ok && !isDocumentResponse && !isOpaqueResponse) { 246 const responseClone = response.clone(); 247 const errorFromRes = await responseClone.text(); 248 const requestUrl = response.url; 249 const errorMessage = `Fetch error from ${requestUrl}: ${errorFromRes}`; 250 251 if (isBenignFetchError(requestUrl, errorFromRes)) { 252 console.info(errorMessage); 253 } else if (isValidationFetchWarning(requestUrl, errorFromRes)) { 254 console.warn(errorMessage); 255 } else { 256 console.error(errorMessage); 257 } 258 } 259 260 return response; 261 }) 262 .catch(error => { 263 if (!url.match(/.html?$/i)) { 264 // Cancelled in-flight fetch (e.g. preview reload) â not a server failure. 265 if (error?.name === 'AbortError') { 266 console.info(error); 267 } else { 268 console.error(error); 269 } 270 } 271 272 throw error; 273 }); 274}; 275</script>
275 276
276<script type="module"> 277if (window.navigation && window.self !== window.top) { 278 window.navigation.addEventListener('navigate', (event) => { 279 const url = event.destination.url; 280 281 try { 282 const destinationUrl = new URL(url); 283 const destinationOrigin = destinationUrl.origin; 284 const currentOrigin = window.location.origin; 285 286 if (destinationOrigin === currentOrigin) { 287 return; 288 } 289 } catch (error) { 290 return; 291 } 292 293 window.parent.postMessage({ 294 type: 'horizons-navigation-error', 295 url, 296 }, '*'); 297 }); 298} 299</script>
299 300 </head> 301 <body> 302 <div id="root"></div> 303 </body> 304</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.