{"version":3,"file":"languageSource.mjs","names":["useState","useEffect","useCallback","useRef","AxiosError","AxiosResponse","apiMutate","isI18nStackEnabled","ConfigService","SwitcherLanguage","languageId","languageCode","languageLabel","UdpLanguageRow","code","label","LanguageLoader","Promise","normalizeProductLanguage","row","activeLanguageLoader","setLanguageLoader","loader","createDefaultLanguageLoader","baseUrl","waitForConfig","base","productV1ApiUrl","Error","response","method","Array","isArray","data","map","tenantV1ApiUrl","cachedLanguages","inflightLanguages","fetchAvailableLanguages","force","resolve","then","result","finally","UseAvailableLanguagesResult","loading","error","useAvailableLanguages","skip","setData","undefined","setLoading","setError","requestRef","load","requestId","current","err","refetch"],"sources":["../../../src/utilities/i18n/languageSource.ts"],"sourcesContent":["import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { AxiosError, AxiosResponse } from 'axios';\nimport { apiMutate } from '../useAxiosMutate';\nimport { isI18nStackEnabled } from './i18nEnabled';\nimport { ConfigService } from '../../configService';\n\n/** The shape the shell's LanguageSwitcher consumes (legacy Unity shape;\n *  generated udp.Language rows are normalized to it). */\nexport interface SwitcherLanguage {\n  languageId: string;\n  languageCode: string;\n  languageLabel: string;\n}\n\n/** A generated udp.Language row as served by the UdpLanguage endpoint. */\ninterface UdpLanguageRow {\n  languageId: string;\n  code: string;\n  label?: string | null;\n}\n\n/**\n * Fetches the languages offered by the shell's language switcher. Injected via\n * initializeUnityI18n's options.loadLanguages (mirroring loadTranslations);\n * createDefaultLanguageLoader builds the stock implementation.\n */\nexport type LanguageLoader = () => Promise<SwitcherLanguage[]>;\n\nconst normalizeProductLanguage = (row: UdpLanguageRow): SwitcherLanguage => ({\n  languageId: row.languageId,\n  languageCode: row.code,\n  languageLabel: row.label || row.code\n});\n\n// The loader configured at init (initializeUnityI18n's options.loadLanguages)\n// for apps whose generated stack is not on the product API - e.g. Unity Client\n// pointing at the tenants V2 API, matching its loadTranslations override.\nlet activeLanguageLoader: LanguageLoader | null = null;\n\n/** Record the app-configured language loader (called from initializeUnityI18n). */\nexport const setLanguageLoader = (loader?: LanguageLoader | null): void => {\n  activeLanguageLoader = loader ?? null;\n};\n\n/**\n * Build the stock language loader.\n *\n * Endpoint-as-probe, mirroring the translation loader's base URL resolution:\n * the PRODUCT application's generated language list (UdpLanguage) is\n * authoritative when the product hosts the generated translation stack; any\n * failure (no base configured, stack not generated there, SPA fallback\n * answering with index.html) falls back to the legacy Unity language list on\n * the tenant API. Only a validated array response counts as success.\n * @param baseUrl\n *   Base API URL hosting the generated UdpLanguage endpoint. Omit to try the\n *   product API.\n */\nexport const createDefaultLanguageLoader =\n  (baseUrl?: string): LanguageLoader =>\n  async () => {\n    await ConfigService.waitForConfig?.();\n\n    try {\n      const base = baseUrl || ConfigService.productV1ApiUrl;\n      if (!base) {\n        throw new Error('Languages base URL is not configured');\n      }\n      const response = (await apiMutate(base, 'UdpLanguage', {\n        method: 'get'\n      })) as AxiosResponse<UdpLanguageRow[]>;\n      if (!Array.isArray(response?.data)) {\n        throw new Error('Unexpected languages response (not the API)');\n      }\n      return response.data.map(normalizeProductLanguage);\n    } catch {\n      const response = (await apiMutate(\n        ConfigService.tenantV1ApiUrl,\n        'language',\n        { method: 'get' }\n      )) as AxiosResponse<SwitcherLanguage[]>;\n      return Array.isArray(response?.data) ? response.data : [];\n    }\n  };\n\n// Session cache + in-flight dedupe: the language list rarely changes and the\n// hook mounts several times at startup (StrictMode double-effects, the skip\n// flip when the user resolves), so concurrent callers share ONE request and\n// later mounts reuse the result. Failures are never cached; a manual refetch\n// (refetchAll) forces a refresh.\nlet cachedLanguages: SwitcherLanguage[] | null = null;\nlet inflightLanguages: Promise<SwitcherLanguage[]> | null = null;\n\n/**\n * Run the app-configured language loader (falling back to the stock one when\n * no loadLanguages override was supplied at init).\n * @param force Bypass the session cache and refetch.\n */\nexport const fetchAvailableLanguages = (\n  force = false\n): Promise<SwitcherLanguage[]> => {\n  // Opted out: no switcher is rendered, so the probe would be a request whose\n  // result nothing consumes. Empty (not null) - callers treat it as \"no choice\n  // of language\", which is exactly the state.\n  if (!isI18nStackEnabled()) {\n    return Promise.resolve<SwitcherLanguage[]>([]);\n  }\n  if (!force && cachedLanguages !== null) {\n    return Promise.resolve(cachedLanguages);\n  }\n  if (inflightLanguages !== null) {\n    return inflightLanguages;\n  }\n  const loader = activeLanguageLoader ?? createDefaultLanguageLoader();\n  inflightLanguages = loader()\n    .then((result) => {\n      cachedLanguages = result;\n      return result;\n    })\n    .finally(() => {\n      inflightLanguages = null;\n    });\n  return inflightLanguages;\n};\n\nexport type UseAvailableLanguagesResult = [\n  {\n    data?: SwitcherLanguage[];\n    loading: boolean;\n    error?: AxiosError<unknown>;\n  },\n  () => Promise<SwitcherLanguage[] | undefined>\n];\n\n/**\n * Hook wrapper around the language loader with the same\n * [{ data, loading, error }, refetch] tuple shape as useAxiosGet.\n *\n * Deliberately NOT built on useAxiosGet: the stock loader needs try-product-\n * then-fallback control flow with response validation (a 200 serving the SPA's\n * index.html must not count as languages), and the product attempt FAILING is\n * the expected path on apps without the generated stack - useAxiosGet would\n * route that expected failure through handleResponseErrors.\n * @param skip Skip fetching (e.g. until the user resolves).\n * @param loader Override the loader for this consumer only; MUST be\n *   referentially stable (memoized) - a new identity per render refetches.\n *   Omit to use the init-configured loadLanguages / stock probe.\n */\nexport const useAvailableLanguages = (\n  skip = false,\n  loader?: LanguageLoader\n): UseAvailableLanguagesResult => {\n  const [data, setData] = useState<SwitcherLanguage[] | undefined>(undefined);\n  const [loading, setLoading] = useState(false);\n  const [error, setError] = useState<AxiosError<unknown> | undefined>(\n    undefined\n  );\n  // Monotonic request id: a stale response (unmount-adjacent or superseded by\n  // a newer refetch) must not clobber state.\n  const requestRef = useRef(0);\n\n  const load = useCallback(\n    async (force: boolean): Promise<SwitcherLanguage[] | undefined> => {\n      if (skip) {\n        return undefined;\n      }\n      const requestId = ++requestRef.current;\n      setLoading(true);\n      setError(undefined);\n      try {\n        const result = await (loader\n          ? loader()\n          : fetchAvailableLanguages(force));\n        if (requestRef.current === requestId) {\n          setData(result);\n        }\n        return result;\n      } catch (err) {\n        if (requestRef.current === requestId) {\n          setError(err as AxiosError<unknown>);\n        }\n        return undefined;\n      } finally {\n        if (requestRef.current === requestId) {\n          setLoading(false);\n        }\n      }\n    },\n    [skip, loader]\n  );\n\n  // Mount/skip-flip loads go through the session cache (StrictMode double\n  // effects and remounts reuse one request); the returned refetch is the\n  // deliberate-refresh path (refetchAll) and forces past it.\n  const refetch = useCallback(() => load(true), [load]);\n\n  useEffect(() => {\n    void load(false);\n    // Invalidate in-flight responses on unmount/skip-change.\n    return () => {\n      requestRef.current += 1;\n    };\n  }, [load]);\n\n  return [{ data, loading, error }, refetch];\n};\n"],"mappings":";;;;;AA4BA,MAAMkB,4BAA4BC,SAA2C;CAC3ET,YAAYS,IAAIT;CAChBC,cAAcQ,IAAIL;CAClBF,eAAeO,IAAIJ,SAASI,IAAIL;CACjC;AAKD,IAAIM,uBAA8C;;AAGlD,MAAaC,qBAAqBC,WAAyC;AACzEF,wBAAuBE,UAAU;;;;;;;;;;;;;;;AAgBnC,MAAaC,+BACVC,YACD,YAAY;AACV,OAAMhB,cAAciB,iBAAiB;AAErC,KAAI;EACF,MAAMC,OAAOF,WAAWhB,cAAcmB;AACtC,MAAI,CAACD,KACH,OAAM,IAAIE,MAAM,uCAAuC;EAEzD,MAAMC,WAAY,MAAMvB,UAAUoB,MAAM,eAAe,EACrDI,QAAQ,OACT,CAAC;AACF,MAAI,CAACC,MAAMC,QAAQH,UAAUI,KAAK,CAChC,OAAM,IAAIL,MAAM,8CAA8C;AAEhE,SAAOC,SAASI,KAAKC,IAAIhB,yBAAyB;SAC5C;EACN,MAAMW,WAAY,MAAMvB,UACtBE,cAAc2B,gBACd,YACA,EAAEL,QAAQ,OACZ,CAAC;AACD,SAAOC,MAAMC,QAAQH,UAAUI,KAAK,GAAGJ,SAASI,OAAO,EAAE;;;AAS/D,IAAIG,kBAA6C;AACjD,IAAIC,oBAAwD;;;;;;AAO5D,MAAaC,2BACXC,QAAQ,UACwB;AAIhC,KAAI,CAAChC,oBAAoB,CACvB,QAAOU,QAAQuB,QAA4B,EAAE,CAAC;AAEhD,KAAI,CAACD,SAASH,oBAAoB,KAChC,QAAOnB,QAAQuB,QAAQJ,gBAAgB;AAEzC,KAAIC,sBAAsB,KACxB,QAAOA;AAGTA,sBADejB,wBAAwBG,6BAA6B,GACxC,CACzBkB,MAAMC,WAAW;AAChBN,oBAAkBM;AAClB,SAAOA;GACP,CACDC,cAAc;AACbN,sBAAoB;GACpB;AACJ,QAAOA;;;;;;;;;;;;;;;;AA0BT,MAAaU,yBACXC,OAAO,OACP1B,WACgC;CAChC,MAAM,CAACW,MAAMgB,WAAWjD,SAAyCkD,KAAAA,EAAU;CAC3E,MAAM,CAACL,SAASM,cAAcnD,SAAS,MAAM;CAC7C,MAAM,CAAC8C,OAAOM,YAAYpD,SACxBkD,KAAAA,EACD;CAGD,MAAMG,aAAalD,OAAO,EAAE;CAE5B,MAAMmD,OAAOpD,YACX,OAAOqC,UAA4D;AACjE,MAAIS,KACF;EAEF,MAAMO,YAAY,EAAEF,WAAWG;AAC/BL,aAAW,KAAK;AAChBC,WAASF,KAAAA,EAAU;AACnB,MAAI;GACF,MAAMR,SAAS,OAAOpB,SAClBA,QAAQ,GACRgB,wBAAwBC,MAAM;AAClC,OAAIc,WAAWG,YAAYD,UACzBN,SAAQP,OAAO;AAEjB,UAAOA;WACAe,KAAK;AACZ,OAAIJ,WAAWG,YAAYD,UACzBH,UAASK,IAA2B;AAEtC;YACQ;AACR,OAAIJ,WAAWG,YAAYD,UACzBJ,YAAW,MAAM;;IAIvB,CAACH,MAAM1B,OACT,CAAC;CAKD,MAAMoC,UAAUxD,kBAAkBoD,KAAK,KAAK,EAAE,CAACA,KAAK,CAAC;AAErDrD,iBAAgB;AACTqD,OAAK,MAAM;AAEhB,eAAa;AACXD,cAAWG,WAAW;;IAEvB,CAACF,KAAK,CAAC;AAEV,QAAO,CAAC;EAAErB;EAAMY;EAASC;EAAO,EAAEY,QAAQ"}