{"version":3,"file":"authApi-Bkze8VZU.cjs","names":[],"sources":["../src/services/apiConfig.js","../src/services/authApi.js"],"sourcesContent":["// Central API base URLs.\n//\n// When CORE-THEME runs standalone (npm run dev), the values come from\n// src/services/.env (Vite VITE_* vars, baked in at build time).\n//\n// When CORE-THEME is consumed as the published @realtek/core-theme package,\n// host apps can override these values at RUNTIME by setting\n// `window.__CORE_THEME_CONFIG__` before the app renders:\n//\n//   <script>\n//     window.__CORE_THEME_CONFIG__ = {\n//       VITE_AUTH_URL:        'http://192.168.1.66/authapi/v1',\n//       VITE_SUBMISSIONS_URL: 'http://192.168.1.66/submissionsapi/v1',\n//       VITE_JOBS_URL:        'http://192.168.1.66/jobsapi/v1',\n//       VITE_CANDIDATES_URL:  'http://192.168.1.66/candidatesapi/v1',\n//       VITE_APP_ID:          'zinnext',   // sent as the `X-App-Id` header on every API call\n//       VITE_ADMIN_APP_ID:    'zinnext',   // optional admin override\n//     };\n//   </script>\n//\n// Priority: runtime window config  >  build-time .env fallback.\n\nconst BUILD_TIME_FALLBACK = {\n  VITE_AUTH_URL: import.meta.env.VITE_AUTH_URL,\n  VITE_ADMIN_API_URL: import.meta.env.VITE_ADMIN_API_URL,\n  VITE_ADMIN_APP_ID: import.meta.env.VITE_ADMIN_APP_ID,\n  VITE_SUBMISSIONS_URL: import.meta.env.VITE_SUBMISSIONS_URL,\n  VITE_JOBS_URL: import.meta.env.VITE_JOBS_URL,\n  VITE_CANDIDATES_URL: import.meta.env.VITE_CANDIDATES_URL,\n  VITE_PYOPS_URL: import.meta.env.VITE_PYOPS_URL,\n  VITE_APP_ID: import.meta.env.VITE_APP_ID,\n};\n\nfunction resolve(key) {\n  if (\n    typeof window !== 'undefined' &&\n    window.__CORE_THEME_CONFIG__ &&\n    window.__CORE_THEME_CONFIG__[key] != null\n  ) {\n    return window.__CORE_THEME_CONFIG__[key];\n  }\n  return BUILD_TIME_FALLBACK[key];\n}\n\nfunction runtimeValue(key, fallback = '') {\n  return {\n    toString: () => resolve(key) || (typeof fallback === 'function' ? fallback() : fallback),\n    valueOf: () => resolve(key) || (typeof fallback === 'function' ? fallback() : fallback),\n    [Symbol.toPrimitive]: () => resolve(key) || (typeof fallback === 'function' ? fallback() : fallback),\n  };\n}\n\n// These are intentionally dynamic. Many service files use them inside template\n// literals (`${AUTH_URL}/path`) or pass them to helpers that later build a URL.\n// Resolving at string-conversion time prevents a published package from\n// freezing CORE-THEME's own .env values into consuming apps and Docker builds.\nexport const AUTH_URL = runtimeValue('VITE_AUTH_URL');\nexport const ADMIN_API_URL = runtimeValue(\n  'VITE_ADMIN_API_URL',\n  () => resolve('VITE_AUTH_URL') || 'http://localhost:9009',\n);\nexport const ADMIN_APP_ID = runtimeValue('VITE_ADMIN_APP_ID', () => resolve('VITE_APP_ID') || 'zinnext-dev-api-v1');\nexport const SUBMISSIONS_URL = runtimeValue('VITE_SUBMISSIONS_URL');\nexport const JOBS_URL = runtimeValue('VITE_JOBS_URL');\nexport const CANDIDATES_URL = runtimeValue('VITE_CANDIDATES_URL');\nexport const PYOPS_URL = runtimeValue('VITE_PYOPS_URL', 'http://192.168.1.66/pyOps/v1');\n\n// App identifier sent as the `X-App-Id` header on every API request (e.g.\n// 'zinnext', 'lms'). Configured in ONE place — the host's window config (or the\n// build-time .env fallback) — and applied to all calls via the interceptor below.\nexport const APP_ID = runtimeValue('VITE_APP_ID');\n\n// Helper for code that builds headers explicitly: merges `X-App-Id` into a\n// header object without clobbering anything else. (The interceptor below already\n// covers existing calls; this is just a convenience for new code.)\nexport function withAppId(headers = {}) {\n  const appId = resolve('VITE_APP_ID');\n  return appId ? { 'X-App-Id': appId, ...headers } : { ...headers };\n}\n\nexport function withAdminAppId(headers = {}) {\n  const appId = resolve('VITE_ADMIN_APP_ID') || resolve('VITE_APP_ID') || 'zinnext-dev-api-v1';\n  return appId ? { 'X-App-Id': appId, ...headers } : { ...headers };\n}\n\n// ── X-App-Id interceptor ──────────────────────────────────────────────────────\n//\n// Rather than editing every inline `headers: {...}` across the service files\n// (and risking new calls forgetting it), we patch `fetch` ONCE so every request\n// to a configured API base URL carries `X-App-Id`. This is scoped strictly to the\n// four API origins, so third-party/asset requests are never touched, and any\n// caller that already set `X-App-Id` explicitly is left as-is.\nfunction installAppIdInterceptor() {\n  if (typeof window === 'undefined' || typeof window.fetch !== 'function') return;\n  // Guard against double-patching (repeated imports / Vite HMR).\n  if (window.__CORE_THEME_APP_ID_PATCHED__) return;\n\n  const apiBaseUrls = [AUTH_URL, ADMIN_API_URL, SUBMISSIONS_URL, JOBS_URL, CANDIDATES_URL, PYOPS_URL];\n  const originalFetch = window.fetch.bind(window);\n\n  const urlOf = (input) => {\n    if (typeof input === 'string') return input;\n    if (input instanceof URL) return input.href;\n    if (input && typeof input.url === 'string') return input.url; // Request object\n    return '';\n  };\n  const isApiRequest = (url) =>\n    !!url && apiBaseUrls.some((base) => {\n      const value = String(base);\n      return value && url.startsWith(value);\n    });\n\n  window.fetch = function patchedFetch(input, init) {\n    try {\n      const url = urlOf(input);\n      const isAdminRequest = String(ADMIN_API_URL) && url.startsWith(String(ADMIN_API_URL));\n      const appId = isAdminRequest\n        ? (resolve('VITE_ADMIN_APP_ID') || resolve('VITE_APP_ID') || 'zinnext-dev-api-v1')\n        : resolve('VITE_APP_ID');\n      if (appId && isApiRequest(url)) {\n        const isRequestObj = typeof Request !== 'undefined' && input instanceof Request;\n        const headers = new Headers(\n          (init && init.headers) || (isRequestObj ? input.headers : undefined),\n        );\n        if (!headers.has('X-App-Id')) headers.set('X-App-Id', appId);\n        if (isRequestObj && !(init && init.headers)) {\n          return originalFetch(new Request(input, { headers }), init);\n        }\n        return originalFetch(input, { ...(init || {}), headers });\n      }\n    } catch {\n      // Any unexpected argument shape: fall through to the untouched request.\n    }\n    return originalFetch(input, init);\n  };\n\n  window.__CORE_THEME_APP_ID_PATCHED__ = true;\n}\n\ninstallAppIdInterceptor();\n\n// Optional JS helper (alternative to the index.html <script>). Because the\n// exports above are resolved when this module first loads, call this BEFORE the\n// first import of @realtek/core-theme — e.g. in the host's index.html, not in\n// main.jsx (which runs after imports are evaluated). The index.html <script>\n// approach is the reliable one.\nexport function configureCoreTheme(config = {}) {\n  if (typeof window === 'undefined') return;\n  window.__CORE_THEME_CONFIG__ = { ...(window.__CORE_THEME_CONFIG__ || {}), ...config };\n  installAppIdInterceptor();\n}\n","import { AUTH_URL, withAppId } from './apiConfig';\n\nconst AUTH_TOKEN_KEY = 'authToken';\nconst AUTH_USER_KEY = 'authUser';\nconst AUTH_LOGIN_SESSION_KEY = 'authLoginSession';\nconst CT_PROJECT_ID_KEY = 'ct_project_id';\nconst CT_PROJECT_NAME_KEY = 'ct_project_name';\nconst LOGIN_STORAGE_KEYS = [\n  'access_token', 'refresh_token', 'id_token', 'token_type', 'expires_in',\n  'refresh_expires_in', 'menu', 'footermenu', 'menuPermission', 'default_values',\n  'roleId', 'companyName', 'preferred_Username', 'user_Email', 'userName',\n  'userId', 'tenantId', 'businessId', 'businessUnitId', 'activeKey',\n  'ct_project_id', 'ct_project_name',\n];\n\nlet loginPromise = null;\nlet refreshPromise = null;\nlet runtimeAuthToken = null;\n\nexport function setAuthTokenOverride(token) {\n  runtimeAuthToken = typeof token === 'string' && token.trim() ? token.trim() : null;\n}\n\nexport function clearAuthTokenOverride() {\n  runtimeAuthToken = null;\n}\n\nfunction extractToken(json) {\n  return (\n    json?.token ?? json?.access_token ?? json?.accessToken ?? json?.jwt ??\n    json?.data?.token ?? json?.data?.access_token ?? json?.data?.accessToken ?? json?.data?.jwt\n  );\n}\n\nfunction extractUser(json) {\n  return json?.user ?? json?.data?.user ?? json?.data?.profile ?? null;\n}\n\nfunction decodeJwtPayload(token) {\n  if (!token || typeof token !== 'string') return null;\n  try {\n    const payload = token.split('.')[1];\n    if (!payload) return null;\n    const normalized = payload.replace(/-/g, '+').replace(/_/g, '/');\n    return JSON.parse(atob(normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '=')));\n  } catch {\n    return null;\n  }\n}\n\nfunction setLocalStorageJson(key, value) {\n  if (value === undefined || value === null) return;\n  localStorage.setItem(key, JSON.stringify(value));\n}\n\nfunction getLoginMenus(json, data) {\n  return data?.menus ?? json?.menus ?? {\n    menu: data?.menu ?? json?.menu,\n    footermenu: data?.footermenu ?? json?.footermenu,\n  };\n}\n\nfunction persistLoginResponse(json, token, user) {\n  // Menu/permission data is login/role scoped. Never let values from the\n  // previous authenticated user survive when the next login response omits a\n  // field; otherwise two users with the same role can render different sidebars\n  // purely because one browser still has stale localStorage.\n  ['menu', 'footermenu', 'menuPermission', 'default_values', 'activeKey'].forEach((key) => {\n    localStorage.removeItem(key);\n  });\n\n  const data = json?.data ?? json;\n  const tokenPayload = decodeJwtPayload(token) ?? {};\n  const menus = getLoginMenus(json, data);\n  const userName = (\n    user?.userName ?? user?.name ?? data?.userName ?? data?.name ??\n    tokenPayload.userName ?? tokenPayload.name ?? tokenPayload.preferred_username ?? tokenPayload.email\n  );\n  const preferredUsername = (\n    user?.preferred_Username ?? user?.preferred_username ??\n    data?.preferred_Username ?? data?.preferred_username ??\n    tokenPayload.preferred_Username ?? tokenPayload.preferred_username ??\n    tokenPayload.email ?? userName\n  );\n  const userEmail = user?.email ?? data?.email ?? tokenPayload.email ?? preferredUsername;\n\n  localStorage.setItem('access_token', token);\n  if (data?.refresh_token) localStorage.setItem('refresh_token', data.refresh_token);\n  if (data?.id_token !== undefined) localStorage.setItem('id_token', data.id_token);\n  if (data?.token_type) localStorage.setItem('token_type', data.token_type);\n  if (data?.expires_in !== undefined) localStorage.setItem('expires_in', String(data.expires_in));\n  if (data?.refresh_expires_in !== undefined) localStorage.setItem('refresh_expires_in', String(data.refresh_expires_in));\n  if (userName) localStorage.setItem('userName', userName);\n  if (preferredUsername) localStorage.setItem('preferred_Username', preferredUsername);\n  if (userEmail) localStorage.setItem('user_Email', userEmail);\n  if (data?.roleId ?? tokenPayload.role_id) localStorage.setItem('roleId', data?.roleId ?? tokenPayload.role_id);\n  if (data?.companyName) localStorage.setItem('companyName', data.companyName);\n  if (tokenPayload.userId !== undefined) localStorage.setItem('userId', String(tokenPayload.userId));\n  if (tokenPayload.tenantId) localStorage.setItem('tenantId', tokenPayload.tenantId);\n  if (tokenPayload.businessId) localStorage.setItem('businessId', tokenPayload.businessId);\n  if (tokenPayload.businessUnitId) localStorage.setItem('businessUnitId', tokenPayload.businessUnitId);\n\n  setLocalStorageJson('menu', menus.menu);\n  setLocalStorageJson('footermenu', menus.footermenu);\n  setLocalStorageJson('menuPermission', data?.menuPermission);\n  setLocalStorageJson('default_values', data?.default_values);\n}\n\n// ── Token & Session ──────────────────────────────────────────────────────────\n\nexport function getStoredToken() {\n  if (runtimeAuthToken) return runtimeAuthToken;\n\n  // localStorage is shared by every tab/window for the same origin.\n  // Do not gate the persisted token with sessionStorage: sessionStorage is\n  // isolated per tab/window, which made a newly opened window look logged out\n  // even though a valid auth token already existed.\n  return localStorage.getItem(AUTH_TOKEN_KEY);\n}\n\nexport function getStoredUser() {\n  try {\n    const stored = localStorage.getItem(AUTH_USER_KEY);\n    if (stored) return JSON.parse(stored);\n  } catch {\n    // Fall through to the legacy login fields below.\n  }\n  return {\n    userId: localStorage.getItem('userId'),\n    username: localStorage.getItem('userName') || localStorage.getItem('preferred_Username'),\n    email: localStorage.getItem('user_Email'),\n    roleId: localStorage.getItem('roleId'),\n  };\n}\n\nexport function logout() {\n  // No response may outlive the session that was entitled to it.\n  invalidateGetCache();\n  localStorage.removeItem(AUTH_TOKEN_KEY);\n  localStorage.removeItem(AUTH_USER_KEY);\n  LOGIN_STORAGE_KEYS.forEach((key) => localStorage.removeItem(key));\n  sessionStorage.removeItem(AUTH_LOGIN_SESSION_KEY);\n  window.dispatchEvent(new Event('auth:logout'));\n}\n\n// ── Role Helpers ─────────────────────────────────────────────────────────────\n\nexport function getCurrentRoleName() {\n  const stored = String(localStorage.getItem('roleId') ?? '').trim().toLowerCase();\n  if (stored) return stored;\n  const payload = decodeJwtPayload(getStoredToken()) ?? {};\n  return String(payload.role ?? payload.roleName ?? payload.role_name ?? payload.role_id ?? payload.roleId ?? '').trim().toLowerCase();\n}\n\nexport function getUserRoles() {\n  const token = getStoredToken() ?? localStorage.getItem(AUTH_TOKEN_KEY);\n  const payload = decodeJwtPayload(token);\n  const roles = new Set();\n  const stored = getCurrentRoleName();\n  if (stored) roles.add(stored);\n  if (payload) {\n    (payload?.realm_access?.roles ?? []).forEach((role) => roles.add(role));\n    Object.values(payload?.resource_access ?? {}).forEach((client) => {\n      (client?.roles ?? []).forEach((role) => roles.add(role));\n    });\n    [payload?.role, payload?.roleName, payload?.role_name].forEach((role) => {\n      if (role) roles.add(role);\n    });\n  }\n  return Array.from(roles).map((role) => String(role).toLowerCase());\n}\n\nexport function isTester() {\n  return getCurrentRoleName() === 'tester' || getUserRoles().includes('tester');\n}\n\n// getActingIdentity returns the numeric {roleId, userId} of the logged-in user,\n// read from the auth token (falling back to the stored roleId). Used to evaluate\n// admin-configured field autoValueRules for UX only — the gateway re-evaluates\n// the same rules from the token and has the final say, so nothing here is a\n// security boundary.\nexport function getActingIdentity() {\n  const payload = decodeJwtPayload(getStoredToken() ?? localStorage.getItem(AUTH_TOKEN_KEY)) ?? {};\n  const num = (value) => {\n    if (value === undefined || value === null || value === '') return undefined;\n    const parsed = Number(value);\n    return Number.isFinite(parsed) ? parsed : undefined;\n  };\n  return {\n    roleId: num(payload.role_id ?? payload.roleId) ?? num(localStorage.getItem('roleId')),\n    userId: num(payload.userId ?? payload.user_id),\n  };\n}\n\n// ── Project Context ───────────────────────────────────────────────────────────\n\nexport function setSelectedProject(project) {\n  if (!project) return;\n  localStorage.setItem(CT_PROJECT_ID_KEY, String(project.id));\n  localStorage.setItem(CT_PROJECT_NAME_KEY, String(project.name));\n}\n\nexport function getSelectedProject() {\n  const id = localStorage.getItem(CT_PROJECT_ID_KEY);\n  const name = localStorage.getItem(CT_PROJECT_NAME_KEY);\n  if (!id || !name) return null;\n  return { id: Number(id), name };\n}\n\nexport function clearSelectedProject() {\n  localStorage.removeItem(CT_PROJECT_ID_KEY);\n  localStorage.removeItem(CT_PROJECT_NAME_KEY);\n}\n\n// ── Login ────────────────────────────────────────────────────────────────────\n\nexport async function login(credentials) {\n  if (loginPromise) return loginPromise;\n\n  loginPromise = fetch(`${AUTH_URL}/login`, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify(credentials),\n  })\n    .then(async (res) => {\n      const json = await res.json();\n      if (!res.ok) {\n        const error = new Error(json?.message ?? json?.error ?? `Login failed: ${res.status}`);\n        error.status = res.status;\n        throw error;\n      }\n      const token = extractToken(json);\n      if (!token) throw new Error('No token in login response');\n      const user = extractUser(json);\n      localStorage.setItem(AUTH_TOKEN_KEY, token);\n      sessionStorage.setItem(AUTH_LOGIN_SESSION_KEY, '1');\n      if (user) localStorage.setItem(AUTH_USER_KEY, JSON.stringify(user));\n      persistLoginResponse(json, token, user);\n      // Let any mounted AuthGuard re-check and reveal the protected content.\n      window.dispatchEvent(new Event('auth:login'));\n      return { token, user, response: json };\n    })\n    .finally(() => { loginPromise = null; });\n\n  return loginPromise;\n}\n\n\nfunction tokenExpiresSoon(token, skewSeconds = 30) {\n  const payload = decodeJwtPayload(token);\n  const exp = Number(payload?.exp);\n  if (!Number.isFinite(exp) || exp <= 0) return false;\n  return (exp * 1000) <= (Date.now() + (skewSeconds * 1000));\n}\n\nfunction persistRefreshedAccessToken(json) {\n  const token = extractToken(json);\n  if (!token) return '';\n\n  localStorage.setItem(AUTH_TOKEN_KEY, token);\n  localStorage.setItem('access_token', token);\n\n  const data = json?.data ?? json;\n  const expiresIn = data?.expires_in ?? data?.accessTokenExpiresIn;\n  if (expiresIn !== undefined) localStorage.setItem('expires_in', String(expiresIn));\n\n  window.dispatchEvent(new Event('auth:token-refreshed'));\n  return token;\n}\n\nexport async function refreshAccessToken() {\n  if (refreshPromise) return refreshPromise;\n\n  const refreshToken = String(localStorage.getItem('refresh_token') ?? '').trim();\n  if (!refreshToken) {\n    const error = new Error('Session expired');\n    error.status = 401;\n    throw error;\n  }\n\n  refreshPromise = fetch(`${AUTH_URL}/generate-refresh-token`, {\n    method: 'POST',\n    headers: withAppId({ 'Content-Type': 'application/json' }),\n    body: JSON.stringify({ refresh_token: refreshToken }),\n  })\n    .then(async (res) => {\n      let json = {};\n      try {\n        json = await res.json();\n      } catch {\n        json = {};\n      }\n\n      if (!res.ok) {\n        const error = new Error(json?.error ?? json?.message ?? `Token refresh failed: ${res.status}`);\n        error.status = res.status;\n        throw error;\n      }\n\n      const token = persistRefreshedAccessToken(json);\n      if (!token) {\n        const error = new Error('Token refresh response did not contain an access token');\n        error.status = 401;\n        throw error;\n      }\n      return token;\n    })\n    .finally(() => {\n      refreshPromise = null;\n    });\n\n  return refreshPromise;\n}\n\n// ── HTTP Helpers (exported for use by other service files) ───────────────────\n\nexport async function ensureToken() {\n  const token = getStoredToken();\n  if (token && !tokenExpiresSoon(token)) return token;\n\n  if (localStorage.getItem('refresh_token')) {\n    try {\n      return await refreshAccessToken();\n    } catch {\n      // The request helper will handle a genuine refresh failure.\n    }\n  }\n\n  if (token) return token;\n  const error = new Error('Authentication required');\n  error.status = 401;\n  throw error;\n}\n\nasync function authHeaders() {\n  const token = await ensureToken();\n  // Normal application API calls must always target the currently selected\n  // project database. Do not let the global fetch interceptor infer this from\n  // the URL because AUTH_URL and ADMIN_API_URL may point to the same service.\n  // Explicit X-App-Id wins in the backend middleware and keeps runtime module\n  // data/master dropdowns scoped to VITE_APP_ID.\n  return withAppId({\n    'Content-Type': 'application/json',\n    Authorization: `Bearer ${token}`,\n  });\n}\n\n// ── GET in-flight coalescing ────────────────────────────────────────────────\n//\n// A page routinely asks for the SAME data several times over, because the parts\n// that need it are independent and none of them can know the others exist. Six\n// form fields that look up the same collection issue six identical requests;\n// the header and the body of a detail page each fetch the same record. Measured\n// on an ORDINARY edit form: 13 requests for 8 distinct URLs, one of them fired\n// six times.\n//\n// The honest fix is not to make each caller aware of the others — that couples\n// components that have no business knowing about each other, and breaks the\n// moment a new one is added. It is to make the REQUEST idempotent: a GET that\n// is already in the air is JOINED rather than reissued.\n//\n// Deliberately in-flight ONLY, with no time-to-live:\n//   • It cannot serve stale data. The answer a joiner receives is the answer to\n//     a request that had not yet returned when it asked — by definition no\n//     staler than issuing its own.\n//   • It needs no invalidation, so a write can never be shadowed by a cache\n//     entry, and no caller has to remember to clear anything.\n// A TTL would catch a few more (a child mounting just after its parent's fetch\n// resolved) at the cost of a staleness window on every endpoint in the app.\n// `formGroupsCache` already makes that trade deliberately for one endpoint it\n// understands; this layer, which knows nothing about what it is fetching,\n// should not make it for everything.\n//\n// Each joiner gets its OWN deep copy of the payload. Callers legitimately mutate\n// what they receive (dropdownApi attaches `recordMeta` to the groups array), and\n// handing several callers one shared object would turn a saved request into a\n// cross-component data bug — far worse than the duplicate it removed.\n//\n// A SHORT TIME-TO-LIVE sits on top of the coalescing, because not every\n// duplicate overlaps: a child that mounts just after its parent's fetch\n// resolved asks a moment too late to join it. That is the remaining half of the\n// \"same data, fetched again\" problem, and it is the half a pure in-flight join\n// cannot reach.\n//\n// This part CAN serve stale data, so it is bounded hard:\n//   • a few seconds only — long enough to cover one page's mount burst, short\n//     enough that no realistic user action fits inside it;\n//   • ANY non-GET clears the whole GET cache. A write is the one event that\n//     makes previously-fetched data wrong, so nothing a user saves can be\n//     shadowed by a stale entry;\n//   • logging out clears it too, so no response outlives the session that was\n//     entitled to it.\n// This mirrors the trade `formGroupsCache` already makes deliberately for one\n// endpoint; the difference is that the invalidation here is automatic rather\n// than something each caller has to remember.\nconst GET_TTL_MS = 4000;\n\nconst inFlightGets = new Map(); // url -> Promise<json>\nconst settledGets = new Map();  // url -> { at, json }\n\n// Cleared on every write and on logout — see the note above.\nexport function invalidateGetCache() {\n  settledGets.clear();\n}\n\nfunction clonePayload(json) {\n  // structuredClone is present in every browser this app supports and in Node\n  // 17+. Falling back to a JSON round-trip keeps older test environments\n  // working; both produce a copy no other caller can observe.\n  try {\n    return typeof structuredClone === 'function' ? structuredClone(json) : JSON.parse(JSON.stringify(json));\n  } catch {\n    // Non-cloneable payload (rare). Sharing the original is still correct for\n    // every caller that does not mutate, and is better than failing the call.\n    return json;\n  }\n}\n\nfunction isGet(options) {\n  const method = (options?.method ?? 'GET').toUpperCase();\n  return method === 'GET';\n}\n\nexport async function fetchJsonWithAuth(baseUrl, path = '', options = {}) {\n  if (path && typeof path === 'object') {\n    options = path;\n    path = '';\n  }\n\n  const url = `${baseUrl}${path || ''}`;\n  if (isGet(options)) {\n    const pending = inFlightGets.get(url);\n    if (pending) return clonePayload(await pending);\n    const settled = settledGets.get(url);\n    if (settled && Date.now() - settled.at < GET_TTL_MS) return clonePayload(settled.json);\n    // Expired: drop it now rather than leaving the map to grow for the rest of\n    // the session on a long-lived page.\n    if (settled) settledGets.delete(url);\n  }\n\n  if (!isGet(options)) {\n    // A write invalidates BEFORE it runs and again after it settles: before, so\n    // a GET fired concurrently with the save cannot repopulate the cache from\n    // pre-write state; after, so anything read during the write is dropped too.\n    invalidateGetCache();\n    try {\n      return await performFetch(url, options);\n    } finally {\n      invalidateGetCache();\n    }\n  }\n\n  const run = performFetch(url, options);\n  inFlightGets.set(url, run);\n  try {\n    // The ORIGINATING caller is cloned too, so no caller can tell whether it\n    // issued the request or joined one — otherwise a mutation would be safe or\n    // unsafe depending on render order, which is the worst kind of bug.\n    const json = await run;\n    settledGets.set(url, { at: Date.now(), json });\n    return clonePayload(json);\n  } finally {\n    // Cleared on success AND failure: a failed GET must not be inherited by\n    // anyone who asks next, they retry for real.\n    inFlightGets.delete(url);\n  }\n}\n\nasync function performFetch(url, options = {}) {\n  const request = async (token) => fetch(url, {\n    ...options,\n    headers: {\n      ...withAppId({\n        'Content-Type': 'application/json',\n        Authorization: `Bearer ${token}`,\n      }),\n      ...options.headers,\n    },\n  });\n\n  let token = await ensureToken();\n  let res = await request(token);\n\n  // Do not destroy the session on the first 401. A role-permission change or\n  // an expired access token can make the current token stale while the stored\n  // refresh token is still valid. Refresh and retry the exact request once.\n  if (res.status === 401 && localStorage.getItem('refresh_token')) {\n    try {\n      token = await refreshAccessToken();\n      res = await request(token);\n    } catch {\n      // Fall through; a final 401 below means the session really is invalid.\n    }\n  }\n\n  if (res.status === 401) logout();\n\n  const text = await res.text();\n  let json = {};\n  if (text) {\n    try {\n      json = JSON.parse(text);\n    } catch {\n      json = { error: text };\n    }\n  }\n  if (!res.ok) {\n    const responseError = json?.error ?? json?.message;\n    const detail = typeof responseError === 'string'\n      ? responseError\n      : responseError && Object.keys(responseError).length > 0\n        ? JSON.stringify(responseError)\n        : `API ${res.status}: ${res.statusText}`;\n    const error = new Error(detail);\n    error.status = res.status;\n    throw error;\n  }\n  return json;\n}\n\nexport async function apiGetWithAuth(baseUrl, path) {\n  const json = await fetchJsonWithAuth(baseUrl, path);\n  return json.data ?? json;\n}\n"],"mappings":"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"}