From 1b8ab89758ea0b280facd8097225af0068d8eb15 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Thu, 18 Jun 2026 22:25:12 +0200 Subject: [PATCH 1/2] frontend: isolate init steps so one failure doesn't abort the chain Co-Authored-By: Claude Sonnet 4.6 --- src/Elastic.Documentation.Site/Assets/main.ts | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/src/Elastic.Documentation.Site/Assets/main.ts b/src/Elastic.Documentation.Site/Assets/main.ts index 74a5d8405..8024816a2 100644 --- a/src/Elastic.Documentation.Site/Assets/main.ts +++ b/src/Elastic.Documentation.Site/Assets/main.ts @@ -11,6 +11,7 @@ import { initNav } from './pages-nav' import { initSmoothScroll } from './smooth-scroll' import { initTabs } from './tabs' import { initializeOtel } from './telemetry/instrumentation' +import { logError } from './telemetry/logging' import { initTocNav } from './toc-nav' import 'htmx-ext-head-support' import 'htmx-ext-preload' @@ -54,6 +55,29 @@ const { getOS } = new UAParser() // eslint-disable-next-line @typescript-eslint/no-explicit-any type HtmxEvent = any +// Run each init step in isolation so a failure in one does not abort the rest. +function runInitSteps(steps: Array<[string, () => void]>) { + for (const [name, init] of steps) { + try { + init() + } catch (error) { + console.error(`Init step "${name}" failed:`, error) + logError(`Init step failed: ${name}`, { + 'init.step': name, + 'error.message': + error instanceof Error ? error.message : String(error), + }) + } + } +} + +function applyEditParam() { + const urlParams = new URLSearchParams(window.location.search) + if (urlParams.has('edit')) { + $optional('.edit-this-page.hidden')?.classList.remove('hidden') + } +} + /** * Initialize KaTeX math rendering for elements with class 'math' */ @@ -104,31 +128,29 @@ function initMath() { // Initialize on initial page load document.addEventListener('DOMContentLoaded', function () { - initMath() - initMermaid() + runInitSteps([ + ['initMath', initMath], + ['initMermaid', initMermaid], + ]) }) document.addEventListener('htmx:load', function () { - initTocNav() - initHighlight() - initCopyButton() - initAgentSkillCopy() - initTabs() - initAppliesSwitch() - initMath() - initMermaid() - initNav() - - initSmoothScroll() - openDetailsWithAnchor() - initImageCarousel() - initApiDocs() - - const urlParams = new URLSearchParams(window.location.search) - const editParam = urlParams.has('edit') - if (editParam) { - $optional('.edit-this-page.hidden')?.classList.remove('hidden') - } + runInitSteps([ + ['initTocNav', initTocNav], + ['initHighlight', initHighlight], + ['initCopyButton', initCopyButton], + ['initAgentSkillCopy', initAgentSkillCopy], + ['initTabs', initTabs], + ['initAppliesSwitch', initAppliesSwitch], + ['initMath', initMath], + ['initMermaid', initMermaid], + ['initNav', initNav], + ['initSmoothScroll', initSmoothScroll], + ['openDetailsWithAnchor', openDetailsWithAnchor], + ['initImageCarousel', initImageCarousel], + ['initApiDocs', initApiDocs], + ['applyEditParam', applyEditParam], + ]) }) // Don't remove style tags because they are used by the elastic global nav. From d6b6f9d4ed96599af3080a5c2fa0df642f40d646 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Thu, 18 Jun 2026 22:40:53 +0200 Subject: [PATCH 2/2] frontend: await async init steps in runInitSteps to catch promise rejections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit initMermaid is async; without await the try/catch silently misses its rejected promise. Event handlers are left synchronous — the browser ignores listener return values and runInitSteps never rejects externally. Co-Authored-By: Claude Sonnet 4.6 --- src/Elastic.Documentation.Site/Assets/main.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Elastic.Documentation.Site/Assets/main.ts b/src/Elastic.Documentation.Site/Assets/main.ts index 8024816a2..41c8effed 100644 --- a/src/Elastic.Documentation.Site/Assets/main.ts +++ b/src/Elastic.Documentation.Site/Assets/main.ts @@ -56,10 +56,12 @@ const { getOS } = new UAParser() type HtmxEvent = any // Run each init step in isolation so a failure in one does not abort the rest. -function runInitSteps(steps: Array<[string, () => void]>) { +async function runInitSteps( + steps: Array<[string, () => void | Promise]> +) { for (const [name, init] of steps) { try { - init() + await init() } catch (error) { console.error(`Init step "${name}" failed:`, error) logError(`Init step failed: ${name}`, {