/** * Bifrost docs — Base URL persistence * * The OpenAPI spec exposes the gateway base URL as a server variable * (`{baseUrl}`), so Mintlify's API Reference playground renders an * editable input for it. This script: * * 1. Preloads that input from localStorage on every page load / * SPA route change, so the user only has to type their URL once. * 2. Persists any edit the user makes back to localStorage. * 3. Rewrites every `` block in the MDX docs that mentions the * default `http://localhost:8080`, so curl/SDK examples on the * regular doc pages also use the configured URL. * * Mintlify auto-injects any `.js` file in the docs root on every page, * so no docs.json wiring is required. */ (function () { if (typeof window === "undefined" || typeof document === "undefined") return; if (window.__bifrostBaseUrlSwitcherLoaded) return; window.__bifrostBaseUrlSwitcherLoaded = true; var DEFAULT_URL = "http://localhost:8080"; var STORAGE_KEY = "bifrost_base_url"; // Per-element snapshot of original text-node values, keyed via a // WeakMap so detached DOM nodes get GC'd cleanly. var snapshots = new WeakMap(); function readStoredUrl() { try { var v = window.localStorage.getItem(STORAGE_KEY); return v && v.trim() ? v.trim() : DEFAULT_URL; } catch (e) { return DEFAULT_URL; } } function writeStoredUrl(url) { try { window.localStorage.setItem(STORAGE_KEY, url); } catch (e) { /* ignore quota / private mode */ } } function normalizeUrl(input) { if (!input) return DEFAULT_URL; var url = String(input).trim(); if (!url) return DEFAULT_URL; if (!/^https?:\/\//i.test(url)) url = "http://" + url; return url.replace(/\/+$/, ""); } /** * Snapshot every text node inside `el` and remember the original * value, so subsequent URL changes can always rewrite from the * canonical source. Returns the snapshot, or null if the block has * no localhost reference (so we never visit it again). */ function snapshotTextNodes(el) { var walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null); var entries = []; var hasMatch = false; var node; while ((node = walker.nextNode())) { var text = node.nodeValue || ""; if (text.indexOf("localhost:8080") !== -1) hasMatch = true; entries.push({ node: node, original: text }); } return hasMatch ? entries : null; } /** * Rewrite every `` block that mentions the default localhost * URL. We only touch text nodes (`nodeValue`) — never `innerHTML` — * so there is no path where a string is reinterpreted as HTML. */ function rewriteCodeBlocks(currentUrl) { var blocks = document.querySelectorAll("pre code, code"); var bareUrl = currentUrl.replace(/^https?:\/\//, ""); for (var i = 0; i < blocks.length; i++) { var el = blocks[i]; var entries = snapshots.get(el); if (entries === undefined) { entries = snapshotTextNodes(el); // Cache the result either way (null = "scanned, no match") // so we don't re-walk this element on every observer tick. snapshots.set(el, entries); } if (!entries) continue; for (var j = 0; j < entries.length; j++) { var entry = entries[j]; var next; if (currentUrl === DEFAULT_URL) { next = entry.original; } else { next = entry.original .replace(/https?:\/\/localhost:8080/g, currentUrl) .replace(/localhost:8080/g, bareUrl); } if (entry.node.nodeValue !== next) entry.node.nodeValue = next; } } } // ---------- API Reference playground sync ---------- /** * Mintlify renders the server-variable field with a stable id of * `api-playground-input`, so we can scope directly to it instead of * heuristically scanning every text input on the page. */ function findPlaygroundUrlInputs() { var el = document.getElementById("api-playground-input"); if (!el || el.__bifrostPlaygroundBound) return []; return [el]; } function setNativeValue(el, value) { // React overrides the input's value setter; bypass it so React's // controlled state picks up the programmatic change. var proto = Object.getPrototypeOf(el); var descriptor = Object.getOwnPropertyDescriptor(proto, "value"); if (descriptor && descriptor.set) descriptor.set.call(el, value); else el.value = value; el.dispatchEvent(new Event("input", { bubbles: true })); el.dispatchEvent(new Event("change", { bubbles: true })); } function syncPlaygroundInputs(state) { var inputs = findPlaygroundUrlInputs(); for (var i = 0; i < inputs.length; i++) { var el = inputs[i]; if (el.__bifrostPlaygroundBound) continue; el.__bifrostPlaygroundBound = true; // Persist on blur / change. Using `change` (not `input`) avoids // fighting the user mid-keystroke. el.addEventListener("change", function (e) { var v = normalizeUrl(e.target.value); state.currentUrl = v; writeStoredUrl(v); rewriteCodeBlocks(v); }); // Preload from storage exactly once. After this, the input is // user-owned — we never write to it again, otherwise typing would // get clobbered by the next MutationObserver tick. if (state.currentUrl !== DEFAULT_URL && el.value !== state.currentUrl) { setNativeValue(el, state.currentUrl); } } } // ---------- Boot ---------- function boot() { var state = { currentUrl: normalizeUrl(readStoredUrl()) }; rewriteCodeBlocks(state.currentUrl); syncPlaygroundInputs(state); // Mintlify is an SPA — re-run on any DOM mutation (debounced). var pending = false; var observer = new MutationObserver(function () { if (pending) return; pending = true; window.requestAnimationFrame(function () { pending = false; rewriteCodeBlocks(state.currentUrl); syncPlaygroundInputs(state); }); }); observer.observe(document.body, { childList: true, subtree: true }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", boot); } else { boot(); } })();