{
  "version": 3,
  "sources": ["pwa_version_check.js"],
  "sourcesContent": ["/*\n * static/js/pwa_version_check.js \u2014 Client-side version-check gate (SNOW-374).\n *\n * Spec \u00A73.4, \u00A712.10 (non-negotiable). Layers a forced-update gate on top of\n * the soft ``#sw-update-banner`` (SNOW-331 / sw_register.js). The server is\n * the source of truth via one header stamped on every response by\n * ``core.middleware.AppVersionHeaderMiddleware`` (SNOW-369):\n *\n *   X-App-Version \u2014 the build the server is serving right now.\n *\n * Contract\n * --------\n * The client's \"current build\" is baked into the page it was delivered on\n * (``<meta name=\"pwa-app-version\">``). When ``X-App-Version`` on any\n * response differs from it, one authoritative ``/api/version`` round trip\n * decides between two outcomes:\n *\n *   1. body ``update_required: true`` \u2192 **Update Required** state. The\n *      blocking modal opens and stays open; nothing is wiped and nothing\n *      reloads until the user clicks \"Reload now\". No dismiss control.\n *\n *   2. body ``update_available: true`` \u2192 OFFER the soft\n *      ``#sw-update-banner`` to ``sw_register.js``, which reveals it only\n *      when the worker is stuck on a stale shell (SNOW-952, SNOW-1025 \u2014\n *      see ``showSoftBanner``). A routine update is applied silently and\n *      never shows it. Since SNOW-869 the offer keys off the server's\n *      boolean rather than a client-side comparison of ``current`` against\n *      the shell's build: the server holds both strings anyway (the\n *      request carried ``X-Client-Version``), so one authority answers\n *      both verdicts.\n *\n * The body is published as ``window.pwaVersionInfo.verified()`` for\n * ``sw_register.js``, whose banner gate reads the server's shell from it.\n * Both callers share one round trip.\n *\n * The client performs NO version arithmetic (SNOW-609)\n * ----------------------------------------------------\n * This module used to compare the shell's build against a server-declared\n * ``min_supported`` floor with ``differs()`` \u2014 string inequality, not\n * ordering. ``APP_VERSION`` resolves to a git SHA, and SHAs have no order,\n * so every client compared as \"below the floor\" and any real floor value\n * would have swept every client, newest included, into the forced-update\n * path. A floor is not expressible on either side of the wire, so the\n * concept is gone: the server names the specific builds it refuses to keep\n * serving (``settings.APP_BLOCKED_VERSIONS``) and returns the verdict as a\n * single boolean. ``differs()`` survives only as the soft-banner hint \u2014\n * \"not the same string\" is all that check ever needed.\n *\n * Note the practical consequence: the verdict is only read when an\n * ``X-App-Version`` drift schedules the round trip, so blocking the build\n * the server is itself serving does nothing. That matches how a block is\n * actually issued \u2014 deploy the replacement first, then list the build being\n * retired.\n *\n * Header drift is a HINT, not a verdict (staging stuck-banner fix)\n * ----------------------------------------------------------------\n * The version header rides on every response \u2014 including responses the\n * browser HTTP cache or the service worker's stale-while-revalidate cache\n * replays from before a deploy (``/api/ratings/`` and the geo feeds carry\n * ``Cache-Control: public, max-age=300``/``3600``). Right after a deploy\n * those replayed responses still carry the PREVIOUS build's\n * ``X-App-Version``, which looks exactly like a real drift. Acting on the\n * header directly showed a phantom \"Update available\" banner that the\n * Reload button could never clear \u2014 clearing Cache Storage does not touch\n * the browser HTTP cache, so the stale header came straight back after\n * the reload.\n *\n * So an observed drift now only *schedules a verification*: one\n * authoritative ``fetch('/api/version', {cache: 'no-store'})`` (via the\n * pristine pre-wrap fetch, so it cannot recurse into this check), acting\n * on the response *body* \u2014 which is by definition as fresh as its own\n * headers. Only a confirmed drift reveals the banner or modal; a\n * confirmed-clean check memoises the stale header value so replays of the\n * same cached response don't re-trigger the round trip.\n *\n * Sources checked\n * ---------------\n * * ``window.fetch`` \u2014 wrapped so every JS-issued request participates.\n * * ``htmx:afterOnLoad`` \u2014 HTMX uses XMLHttpRequest, not fetch, so its\n *   responses are inspected via the DOM event.\n *\n * Failure modes\n * -------------\n * * An unreachable ``/api/version`` reveals nothing. \"Cannot confirm\" must\n *   never read as \"confirmed\" \u2014 a blocking modal on unverifiable evidence\n *   would strand offline users.\n * * If the ``<meta>`` tag is missing (a stale template), the wrapper is a\n *   no-op \u2014 safer than firing spurious update prompts when we don't know\n *   what version the shell was delivered on.\n */\n\n(function () {\n  'use strict';\n\n  const CURRENT_BUILD = readMeta('pwa-app-version');\n  if (!CURRENT_BUILD) return; // Meta tag absent \u2014 bail safely.\n\n  const VERSION_ENDPOINT = '/api/version';\n\n  // The pristine fetch, captured BEFORE wrapFetch() replaces window.fetch.\n  // Verification requests go out through this so they can never recurse\n  // back into inspectHeaders (and never re-observe their own headers).\n  const pristineFetch =\n    typeof window.fetch === 'function' ? window.fetch.bind(window) : null;\n\n  // Latch \u2014 once set, we've triggered a forced update and further\n  // responses should not re-run the flow.\n  let forcedUpdateTriggered = false;\n  // Header values already resolved by a verification round trip, so a\n  // replay of the same cached response doesn't fetch /api/version again:\n  // ``staleConfirmed`` \u2014 the server said we're current, the header was a\n  // cache artefact; ``driftConfirmed`` \u2014 the server confirmed a real\n  // drift (the soft banner is already showing, and it is sticky).\n  const staleConfirmed = new Set();\n  const driftConfirmed = new Set();\n  // Single-flight guard, on the FETCH rather than on the drift\n  // verification (SNOW-869): the verification and the banner gate's read\n  // both want the same body, and concurrent callers must share one round\n  // trip rather than issue two.\n  let fetchInFlight = null;\n  // The most recent body a round trip actually returned, or null while\n  // none has. Read by ``window.pwaVersionInfo.verified()`` so the banner\n  // gate, which runs immediately after the verification that offered the\n  // banner, does not go back to the network for a body we are already\n  // holding. Deliberately NOT a permanent memo of the fetch:\n  // a later, distinct header drift still gets its own round trip, which\n  // is what makes a second deploy in one session verifiable.\n  let lastVerdict = null;\n  // SNOW-384: separate latch so pwa.forced_update.triggered fires exactly\n  // once regardless of which caller reaches showBlockingModal() first \u2014\n  // the caller already gates on forcedUpdateTriggered before calling, so\n  // this is defence in depth rather than a load-bearing guard.\n  let modalTelemetryEmitted = false;\n\n  /**\n   * Read a ``content`` value from a ``<meta name=\"\u2026\">`` tag or return ``\"\"``\n   * when the tag is missing.\n   *\n   * @param {string} name\n   * @returns {string}\n   */\n  function readMeta(name) {\n    const el = document.querySelector(`meta[name=\"${name}\"]`);\n    return el ? (el.getAttribute('content') || '').trim() : '';\n  }\n\n  /**\n   * Reveal the blocking modal. Idempotent \u2014 safe to call twice.\n   *\n   * @param {'blocked_build'} trigger What caused the forced update.\n   *   Stamped on the telemetry event for dashboard breakdown. SNOW-609\n   *   left exactly one value: the server listed this build in\n   *   ``APP_BLOCKED_VERSIONS``. The previous ``'min_version'`` and\n   *   ``'escalation'`` triggers are both gone.\n   */\n  function showBlockingModal(trigger) {\n    const modal = document.getElementById('pwa-update-modal');\n    if (!modal) return;\n    modal.classList.remove('hidden');\n    // Prevent the underlying page from scrolling while the modal owns\n    // focus. Restored on reload; we never intentionally hide the modal\n    // once shown.\n    document.documentElement.style.overflow = 'hidden';\n    // SNOW-384: pwa.forced_update.triggered is critical (telemetry.js\n    // CRITICAL_EVENTS) \u2014 sendBeacon fires immediately.\n    if (!modalTelemetryEmitted) {\n      modalTelemetryEmitted = true;\n      try {\n        window.pwaTelemetry?.emit('pwa.forced_update.triggered', {\n          trigger: trigger || 'unknown',\n        });\n      } catch (_err) {\n        // Ignore \u2014 telemetry must never block the forced-update flow.\n      }\n    }\n  }\n\n  /**\n   * Reveal the existing soft update banner. Delegates to whatever\n   * sw_register.js has wired up \u2014 we do not manage the banner's DOM\n   * ourselves so the two flows stay in sync. The public partial reveals\n   * via ``hidden`` class toggle; the admin fallback (data-fallback=\"1\",\n   * inline-styled) uses ``display: flex`` instead.\n   *\n   * SNOW-623: that fork is ``sw_register.js``'s, reached through\n   * ``window.pwaUpdateBanner``. This file used to carry its own copy, under\n   * a docstring telling the reader to \"mirror\" the other one \u2014 which is\n   * how two owners of one element stay in step only as long as somebody\n   * remembers. The dev-bypass suppression is checked there too, so the\n   * banner cannot be revealed by either path under the bypass.\n   *\n   * SNOW-952 / SNOW-1025: the delegation is also a GATE. A server-build\n   * drift says the server has redeployed; it does not say this device\n   * needs anyone to act. HTML is network-first and static assets are\n   * hashed, so an online client is already current, and a new worker is\n   * applied silently. The one case a person must act on is a worker stuck\n   * on a stale shell, and only ``sw_register.js`` can ask the controlling\n   * worker and the registration about that. So this hands the offer over\n   * and lets those two gates decide.\n   *\n   * SNOW-609: nothing else stays here. This function used to also stamp\n   * ``localStorage['pwa.update.first_shown_at']``, the clock behind the\n   * 24h escalation to the blocking modal. That escalation is gone \u2014 it\n   * blocked the app on elapsed time rather than on any server statement\n   * that the build was unacceptable \u2014 and the stamp had no other reader,\n   * so revealing the banner is now the whole job.\n   */\n  function showSoftBanner() {\n    if (!window.pwaUpdateBanner) return;\n    window.pwaUpdateBanner.reveal();\n  }\n\n  /**\n   * Drop the SW shell caches, then reload \u2014 the forced update's whole\n   * effect on local state.\n   *\n   * SNOW-615 routed this through ``window.pwaResetLocalData(true)``, the\n   * six-step everything-goes wipe, to settle a mismatch between this\n   * function's name and what it did. SNOW-609 settles the same mismatch\n   * the other way for this path, and the name changed with it: a code\n   * update must not destroy user data. The full wipe also takes\n   * IndexedDB \u2014 the mutation queue, the offline favourites roster, the\n   * cached ratings \u2014 and every pinned basemap bucket, which can be\n   * hundreds of megabytes the user deliberately downloaded. None of that\n   * is code, and none of it is why the build is blocked.\n   *\n   * ``sw_register.js:657-661`` already made exactly this trade for the\n   * kill switch: caches are left alone there because ops flipping a\n   * temporary switch should not cost a user their downloads. The\n   * forced-update path now agrees with its neighbour. If you are here to\n   * \"fix\" this back to a full wipe, that is the argument to answer first.\n   *\n   * ``window.pwaResetLocalData`` (pwa_reset.js) remains the\n   * everything-goes path, reachable from /offline/ and the offline\n   * fallback page, and it asks for confirmation first.\n   *\n   * Falls back to a plain reload when the global is absent (``sw_register.js``\n   * returns early on a browser with no ``navigator.serviceWorker``) \u2014\n   * this runs on a page that may already be part-broken, so the reload is\n   * the floor rather than the plan.\n   *\n   * @returns {Promise<void>}\n   */\n  async function clearShellAndReload() {\n    if (typeof window.pwaClearShellCachesAndReload !== 'function') {\n      window.location.reload();\n      return;\n    }\n    await window.pwaClearShellCachesAndReload();\n  }\n\n  /**\n   * Compare two version strings for inequality.\n   *\n   * The soft-banner hint only, since SNOW-609 \u2014 \"the server is serving a\n   * different string than the one this shell was delivered on\" is the\n   * whole question there, and inequality answers it exactly. It is NOT\n   * used to decide whether a build is acceptable; that is\n   * ``update_required``, which the server computes.\n   *\n   * @param {string} a\n   * @param {string} b\n   * @returns {boolean}\n   */\n  function differs(a, b) {\n    return String(a || '').trim() !== String(b || '').trim();\n  }\n\n  /**\n   * Fetch the authoritative version verdict from ``/api/version``,\n   * bypassing every cache layer (``cache: 'no-store'`` skips the browser\n   * HTTP cache; the SW classifies the path as network-only so Cache\n   * Storage never sees it). Uses the pristine pre-wrap fetch so the\n   * request cannot recurse into ``inspectHeaders``.\n   *\n   * Single-flight: concurrent callers share one round trip, and the\n   * resolved body is held in ``lastVerdict`` for the banner gate's read\n   * that follows an offer.\n   *\n   * @returns {Promise<{current: string, shell: string,\n   *   update_required: boolean, update_available: boolean,\n   *   today: string} | null>}\n   *   The trimmed identifiers and both server verdicts, or ``null`` when\n   *   the endpoint is unreachable / non-2xx \u2014 \"cannot confirm\" must never\n   *   be treated as \"confirmed\".\n   */\n  function fetchAuthoritativeVersion() {\n    if (!pristineFetch) return Promise.resolve(null);\n    if (fetchInFlight) return fetchInFlight;\n    fetchInFlight = (async () => {\n      try {\n        const res = await pristineFetch(VERSION_ENDPOINT, { cache: 'no-store' });\n        if (!res || !res.ok) return null;\n        const json = await res.json();\n        return {\n          current: String(json.current || '').trim(),\n          // SNOW-952: the shell cache name this build would serve. Carried\n          // through untouched for ``sw_register.js``'s staleness gate,\n          // which compares it against the controlling worker's own. Empty\n          // from a server that predates the field, which the gate reads as\n          // \"cannot confirm\" and therefore reveals \u2014 see ``shellIsStale``.\n          shell: String(json.shell || '').trim(),\n          // Strict ``=== true`` on both verdicts: a server that predates\n          // the field omits it entirely, and an absent verdict must read\n          // as \"not blocked\" / \"nothing to pick up\" rather than as\n          // truthiness on ``undefined``.\n          update_required: json.update_required === true,\n          update_available: json.update_available === true,\n          // SNOW-920: the server's day (``YYYY-MM-DD``), for\n          // ``stale_day.js``. Empty from a server that predates the field,\n          // which that module reads as \"cannot confirm\" and says nothing.\n          today: String(json.today || '').trim(),\n        };\n      } catch (_err) {\n        return null;\n      }\n    })()\n      .then((verdict) => {\n        if (verdict) lastVerdict = verdict;\n        return verdict;\n      })\n      .finally(() => {\n        fetchInFlight = null;\n      });\n    return fetchInFlight;\n  }\n\n  /**\n   * Resolve an observed header drift into one of the three outcomes,\n   * using the authoritative body rather than the (possibly cache-stale)\n   * header that triggered us:\n   *\n   *   * forced-update \u2192 open the modal and leave it open.\n   *   * soft-update   \u2192 reveal the sticky banner.\n   *   * fresh         \u2192 memoise the stale header value; no-op.\n   *\n   * Single-flight: concurrent observations share one round trip. The\n   * guard sits on ``fetchAuthoritativeVersion`` since SNOW-869, so the\n   * banner gate's read shares it too.\n   *\n   * @param {string} observed The drifting header value that prompted this\n   *   verification, memoised under whichever verdict comes back.\n   * @returns {Promise<void>}\n   */\n  function verifyObservedDrift(observed) {\n    return fetchAuthoritativeVersion()\n      .then((verdict) => {\n        if (!verdict || forcedUpdateTriggered) return;\n\n        // The server's verdict wins. Spec \u00A73.4 is deliberate that this\n        // must not be dismissable \u2014 and SNOW-609 is deliberate that it\n        // must not act on its own either: nothing is cleared and nothing\n        // reloads until the user clicks \"Reload now\" (``bindModal``).\n        // The old auto-reset raced the reveal, so the copy explaining\n        // what was about to happen was torn down before it could be\n        // read.\n        if (verdict.update_required) {\n          forcedUpdateTriggered = true;\n          showBlockingModal('blocked_build');\n          return;\n        }\n\n        // SNOW-869: the soft-update verdict is the server's too. It used\n        // to be ``differs(verdict.current, CURRENT_BUILD)`` \u2014 a client\n        // comparison of the same two strings the server now compares\n        // itself, against the ``X-Client-Version`` this very request\n        // carried. Reading it from the body puts both verdicts on the\n        // same authority. ``differs()`` stays as the header-drift HINT in\n        // ``inspectHeaders``, which is a comparison of two strings the\n        // client already holds.\n        if (verdict.update_available) {\n          driftConfirmed.add(observed);\n          showSoftBanner();\n          return;\n        }\n\n        // Server says we ARE current \u2014 the observed header was replayed\n        // from a pre-deploy cache entry. Remember it so the same cached\n        // responses don't re-trigger the round trip.\n        staleConfirmed.add(observed);\n      });\n  }\n\n  /**\n   * Consume the version header from any completed response. A drift\n   * against the shell's build is only a HINT that an update might exist \u2014\n   * the response may have been replayed from the browser HTTP cache or\n   * the SW's stale-while-revalidate cache with pre-deploy headers \u2014 so\n   * the outcome is decided by ``verifyObservedDrift`` against the\n   * authoritative ``/api/version`` body, never by the header alone.\n   *\n   * @param {(name: string) => string | null} getHeader\n   */\n  function inspectHeaders(getHeader) {\n    if (forcedUpdateTriggered) return;\n    // SNOW-609: ``X-App-Min-Version`` was the other header read here. The\n    // server no longer sends it, and there is no floor left to compare\n    // against.\n    const serverVer = (getHeader('X-App-Version') || '').trim();\n    if (!serverVer || !differs(serverVer, CURRENT_BUILD)) return;\n    if (staleConfirmed.has(serverVer)) return;\n    if (driftConfirmed.has(serverVer)) {\n      // Already verified as a real drift \u2014 keep the sticky banner visible\n      // without another round trip.\n      showSoftBanner();\n      return;\n    }\n    verifyObservedDrift(serverVer);\n  }\n\n  /**\n   * Wrap ``window.fetch`` so every JS-issued request participates in the\n   * version check. The wrapper is a passthrough \u2014 the original response\n   * is always returned unchanged; the version check runs as a\n   * side-effect.\n   */\n  function wrapFetch() {\n    if (typeof window.fetch !== 'function') return;\n    const original = window.fetch.bind(window);\n    window.fetch = async function (...args) {\n      const response = await original(...args);\n      try {\n        inspectHeaders((name) => response.headers.get(name));\n      } catch (_err) {\n        // Never let the version check break the caller's fetch chain.\n      }\n      return response;\n    };\n  }\n\n  /**\n   * Hook the HTMX post-response event so XHR-driven traffic (the bulk of\n   * Snowdesk's mutations) also participates.\n   */\n  function wrapHtmx() {\n    document.body?.addEventListener('htmx:afterOnLoad', (evt) => {\n      try {\n        const xhr = evt?.detail?.xhr;\n        if (!xhr || typeof xhr.getResponseHeader !== 'function') return;\n        inspectHeaders((name) => xhr.getResponseHeader(name));\n      } catch (_err) {\n        // Ignore.\n      }\n    });\n  }\n\n  /**\n   * Bind the modal's Reload button. Idempotent.\n   *\n   * SNOW-609: this is now the only caller of ``clearShellAndReload``. It\n   * was dead code before \u2014 the forced-update branch reset and reloaded\n   * under the open modal, so the page was gone before the button could be\n   * clicked.\n   */\n  function bindModal() {\n    const btn = document.getElementById('pwa-update-modal-reload');\n    if (!btn || btn.dataset.bound === '1') return;\n    btn.dataset.bound = '1';\n    btn.addEventListener('click', () => {\n      clearShellAndReload().catch(() => window.location.reload());\n    });\n  }\n\n  /**\n   * The verified server body, or ``null`` when it cannot be had.\n   *\n   * Returns the body the last round trip produced when we are holding\n   * one, which is the normal case for the banner gate, since the offer it\n   * is judging was itself caused by that round trip. Otherwise issues one\n   * (sharing any fetch already in flight).\n   *\n   * SNOW-1025 removed the ``{refresh: true}`` option. Its one caller was\n   * the waiting-worker banner path, which no longer exists: a waiting\n   * worker is applied silently.\n   *\n   * @returns {Promise<{current: string, shell: string,\n   *   update_required: boolean, update_available: boolean} | null>}\n   */\n  function verified() {\n    if (lastVerdict) return Promise.resolve(lastVerdict);\n    return fetchAuthoritativeVersion();\n  }\n\n  // The server's verdict, for ``sw_register.js``'s banner gate. SNOW-869\n  // also published the shell's own build and release here for the banner's\n  // versioned copy; SNOW-1025 removed that copy, and SNOW-1026 removed the\n  // ``release`` field from ``/api/version``.\n  //\n  // SNOW-920: ``refresh`` is a round trip that is never answered from\n  // ``lastVerdict`` \u2014 ``stale_day.js`` asks what day it is NOW, on a page\n  // that may have been open since yesterday, and a held body would answer\n  // with the day of the last drift check. It still shares any fetch already\n  // in flight.\n  window.pwaVersionInfo = Object.freeze({\n    verified: verified,\n    refresh: fetchAuthoritativeVersion,\n    reload: clearShellAndReload,\n  });\n\n  // Kick off. The pwa-app-version meta tag being present is our signal\n  // that the shell knows about this contract.\n  bindModal();\n  wrapFetch();\n  wrapHtmx();\n})();\n"],
  "mappings": "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",
  "names": ["CURRENT_BUILD", "readMeta", "VERSION_ENDPOINT", "pristineFetch", "forcedUpdateTriggered", "staleConfirmed", "driftConfirmed", "fetchInFlight", "lastVerdict", "modalTelemetryEmitted", "name", "el", "showBlockingModal", "trigger", "modal", "showSoftBanner", "clearShellAndReload", "differs", "a", "b", "fetchAuthoritativeVersion", "res", "json", "verdict", "verifyObservedDrift", "observed", "inspectHeaders", "getHeader", "serverVer", "wrapFetch", "original", "args", "response", "wrapHtmx", "evt", "xhr", "bindModal", "btn", "verified"]
}
