@favish/staffbase-utils 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +3 -1
  2. package/dist/api.cjs.js +2 -0
  3. package/dist/api.cjs.js.map +1 -0
  4. package/dist/api.es.mjs +28 -0
  5. package/dist/api.es.mjs.map +1 -0
  6. package/dist/content.cjs.js +2 -0
  7. package/dist/content.cjs.js.map +1 -0
  8. package/dist/content.es.mjs +26 -0
  9. package/dist/content.es.mjs.map +1 -0
  10. package/dist/src/api/ApiError.d.ts +15 -0
  11. package/dist/src/api/ApiError.d.ts.map +1 -0
  12. package/dist/src/api/fetchAllPaginated.d.ts +16 -0
  13. package/dist/src/api/fetchAllPaginated.d.ts.map +1 -0
  14. package/dist/src/api/fetchJson.d.ts +14 -0
  15. package/dist/src/api/fetchJson.d.ts.map +1 -0
  16. package/dist/src/api/index.d.ts +4 -0
  17. package/dist/src/api/index.d.ts.map +1 -0
  18. package/dist/src/content/detectEditorLanguage.d.ts +8 -0
  19. package/dist/src/content/detectEditorLanguage.d.ts.map +1 -0
  20. package/dist/src/content/detectPreviewLanguage.d.ts +8 -0
  21. package/dist/src/content/detectPreviewLanguage.d.ts.map +1 -0
  22. package/dist/src/content/index.d.ts +6 -0
  23. package/dist/src/content/index.d.ts.map +1 -0
  24. package/dist/src/content/resolveActiveLanguage.d.ts +13 -0
  25. package/dist/src/content/resolveActiveLanguage.d.ts.map +1 -0
  26. package/dist/src/content/resolveLocalizedContent.d.ts +15 -0
  27. package/dist/src/content/resolveLocalizedContent.d.ts.map +1 -0
  28. package/dist/src/types/api/FetchAllPaginatedOptions.d.ts +31 -0
  29. package/dist/src/types/api/FetchAllPaginatedOptions.d.ts.map +1 -0
  30. package/dist/src/types/content/ArticleImage.d.ts +17 -0
  31. package/dist/src/types/content/ArticleImage.d.ts.map +1 -0
  32. package/dist/src/types/content/ArticleImageVariant.d.ts +13 -0
  33. package/dist/src/types/content/ArticleImageVariant.d.ts.map +1 -0
  34. package/dist/src/types/content/LocalizedContent.d.ts +16 -0
  35. package/dist/src/types/content/LocalizedContent.d.ts.map +1 -0
  36. package/dist/src/types/content/ResolveLocalizedContentOptions.d.ts +24 -0
  37. package/dist/src/types/content/ResolveLocalizedContentOptions.d.ts.map +1 -0
  38. package/dist/src/types/content/index.d.ts +3 -0
  39. package/dist/src/types/content/index.d.ts.map +1 -1
  40. package/package.json +11 -1
package/README.md CHANGED
@@ -23,9 +23,11 @@ minimumReleaseAgeExclude:
23
23
 
24
24
  | Subpath | Exports |
25
25
  | --- | --- |
26
+ | `@favish/staffbase-utils/api` | `fetchJson`, `fetchAllPaginated`, `ApiError` |
27
+ | `@favish/staffbase-utils/content` | `resolveLocalizedContent`, `resolveActiveLanguage`, `detectEditorLanguage`, `detectPreviewLanguage` |
26
28
  | `@favish/staffbase-utils/log` | `logError`, `logWarn`, `logDebug`, `setLoggingEnabled` |
27
29
  | `@favish/staffbase-utils/dom` | `getDynamicClasses` |
28
- | `@favish/staffbase-utils/types` | `Channel`, `ChannelLink`, `ChannelLinkParameter`, `DropdownOption` (type-only) |
30
+ | `@favish/staffbase-utils/types` | `Channel`, `ChannelLink`, `ChannelLinkParameter`, `DropdownOption`, `LocalizedContent`, `ArticleImage`, `ArticleImageVariant` (type-only) |
29
31
 
30
32
  More modules (`/env`, `/device`, `/html`, `/links`, `/widgets`) are added per the
31
33
  delivery roadmap; each is its own subpath so consumers only bundle what they import.
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class extends Error{status;constructor(e,t){super(t),this.name=`ApiError`,this.status=e}},t=async(t,n)=>{let r=await fetch(t,{credentials:`include`,...n});if(!r.ok)throw new e(r.status,`API request failed with status: ${r.status}`);return await r.json()},n=50,r=1e3,i=async(e,i={})=>{let{mapItem:a,limit:o=n,includeDrafts:s=!1,maxPages:c=r,onError:l}=i,u=[],d=0,f=0;for(;f<c;){let n=(await t(`${e}${e.includes(`?`)?`&`:`?`}limit=${o}&offset=${d}${s?`&includeDrafts=true`:``}`)).data??[];if(n.length===0)break;let r=a?n.map(a).filter(e=>e!==null):n;if(u.push(...r),n.length<o)break;d+=o,f+=1}return f>=c&&l?.(`Pagination cap (${c} pages) reached for ${e}; results may be truncated.`),u};exports.ApiError=e,exports.fetchAllPaginated=i,exports.fetchJson=t;
2
+ //# sourceMappingURL=api.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.cjs.js","names":[],"sources":["../src/api/ApiError.ts","../src/api/fetchJson.ts","../src/api/fetchAllPaginated.ts"],"sourcesContent":["/**\n * Error thrown by the API layer when a request resolves with a non-2xx status.\n * Carries the HTTP status so callers can branch (e.g. distinguish 401/403 from\n * 5xx) instead of collapsing every failure into one generic message.\n */\nexport class ApiError extends Error {\n public readonly status: number\n\n /**\n * Creates an ApiError carrying the failed response status.\n * @param {number} status - The HTTP status code of the failed response.\n * @param {string} message - A human-readable error message.\n */\n public constructor(status: number, message: string) {\n super(message)\n this.name = 'ApiError'\n this.status = status\n }\n}\n","import { ApiError } from './ApiError'\n\n/**\n * Fetches JSON from a URL using Staffbase session credentials.\n *\n * Throws {@link ApiError} (carrying the HTTP status) on a non-2xx response so\n * callers can branch on the status. Network/parse errors propagate as-is. This\n * helper does not log; the calling service layer owns error logging.\n * @template T The expected shape of the parsed JSON body.\n * @param {string} url - The API URL to fetch.\n * @param {RequestInit} [init] - Optional fetch overrides (merged after credentials).\n * @returns {Promise<T>} The parsed JSON body.\n * @throws {ApiError} When the response status is not ok.\n */\nexport const fetchJson = async <T>(\n url: string,\n init?: RequestInit,\n): Promise<T> => {\n const response = await fetch(url, { credentials: 'include', ...init })\n\n if (!response.ok) {\n throw new ApiError(\n response.status,\n `API request failed with status: ${response.status}`,\n )\n }\n\n return (await response.json()) as T\n}\n","import type { FetchAllPaginatedOptions } from '../types/api/FetchAllPaginatedOptions'\nimport { fetchJson } from './fetchJson'\n\nconst DEFAULT_LIMIT = 50\nconst DEFAULT_MAX_PAGES = 1000\n\n/**\n * Fetches every page of a Staffbase `{ data, total }` collection endpoint.\n *\n * Termination is driven by page contents (a short page ends the loop) rather\n * than the API's reported `total`, which avoids under-fetching when `total` is\n * under-reported. A `maxPages` cap bounds worst-case latency/memory; reaching it\n * invokes `onError` so the truncation is never silent.\n * @template TItem The mapped item type returned to the caller.\n * @template TSource The raw item type returned by the API before mapping.\n * @param {string} baseUrl - The base API URL (without limit/offset parameters).\n * @param {FetchAllPaginatedOptions<TItem, TSource>} [options] - Mapping and pagination options.\n * @returns {Promise<TItem[]>} All fetched (and optionally mapped) items.\n */\nexport const fetchAllPaginated = async <TItem, TSource = TItem>(\n baseUrl: string,\n options: FetchAllPaginatedOptions<TItem, TSource> = {},\n): Promise<TItem[]> => {\n const {\n mapItem,\n limit = DEFAULT_LIMIT,\n includeDrafts = false,\n maxPages = DEFAULT_MAX_PAGES,\n onError,\n } = options\n\n const results: TItem[] = []\n let offset = 0\n let page = 0\n\n while (page < maxPages) {\n const separator = baseUrl.includes('?') ? '&' : '?'\n const draftsParam = includeDrafts ? '&includeDrafts=true' : ''\n const url = `${baseUrl}${separator}limit=${limit}&offset=${offset}${draftsParam}`\n\n const data = await fetchJson<{ data: TSource[]; total?: number }>(url)\n const rawItems = data.data ?? []\n\n if (rawItems.length === 0) break\n\n const mapped = mapItem\n ? rawItems.map(mapItem).filter((item): item is TItem => item !== null)\n : (rawItems as unknown as TItem[])\n\n results.push(...mapped)\n\n // A short page means we reached the end of the collection.\n if (rawItems.length < limit) break\n\n offset += limit\n page += 1\n }\n\n if (page >= maxPages) {\n onError?.(\n `Pagination cap (${maxPages} pages) reached for ${baseUrl}; results may be truncated.`,\n )\n }\n\n return results\n}\n"],"mappings":"mEAKA,IAAa,EAAb,cAA8B,KAAM,CAClC,OAOA,YAAmB,EAAgB,EAAiB,CAClD,MAAM,CAAO,EACb,KAAK,KAAO,WACZ,KAAK,OAAS,CAChB,CACF,ECJa,EAAY,MACvB,EACA,IACe,CACf,IAAM,EAAW,MAAM,MAAM,EAAK,CAAE,YAAa,UAAW,GAAG,CAAK,CAAC,EAErE,GAAI,CAAC,EAAS,GACZ,MAAM,IAAI,EACR,EAAS,OACT,mCAAmC,EAAS,QAC9C,EAGF,OAAQ,MAAM,EAAS,KAAK,CAC9B,ECzBM,EAAgB,GAChB,EAAoB,IAeb,EAAoB,MAC/B,EACA,EAAoD,CAAC,IAChC,CACrB,GAAM,CACJ,UACA,QAAQ,EACR,gBAAgB,GAChB,WAAW,EACX,WACE,EAEE,EAAmB,CAAC,EACtB,EAAS,EACT,EAAO,EAEX,KAAO,EAAO,GAAU,CAMtB,IAAM,GAAW,MADE,EAA+C,GAFnD,IAFG,EAAQ,SAAS,GAAG,EAAI,IAAM,IAEb,QAAQ,EAAM,UAAU,IADvC,EAAgB,sBAAwB,IAGS,GAC/C,MAAQ,CAAC,EAE/B,GAAI,EAAS,SAAW,EAAG,MAE3B,IAAM,EAAS,EACX,EAAS,IAAI,CAAO,EAAE,OAAQ,GAAwB,IAAS,IAAI,EAClE,EAKL,GAHA,EAAQ,KAAK,GAAG,CAAM,EAGlB,EAAS,OAAS,EAAO,MAE7B,GAAU,EACV,GAAQ,CACV,CAQA,OANI,GAAQ,GACV,IACE,mBAAmB,EAAS,sBAAsB,EAAQ,4BAC5D,EAGK,CACT"}
@@ -0,0 +1,28 @@
1
+ //#region src/api/ApiError.ts
2
+ var e = class extends Error {
3
+ status;
4
+ constructor(e, t) {
5
+ super(t), this.name = "ApiError", this.status = e;
6
+ }
7
+ }, t = async (t, n) => {
8
+ let r = await fetch(t, {
9
+ credentials: "include",
10
+ ...n
11
+ });
12
+ if (!r.ok) throw new e(r.status, `API request failed with status: ${r.status}`);
13
+ return await r.json();
14
+ }, n = 50, r = 1e3, i = async (e, i = {}) => {
15
+ let { mapItem: a, limit: o = n, includeDrafts: s = !1, maxPages: c = r, onError: l } = i, u = [], d = 0, f = 0;
16
+ for (; f < c;) {
17
+ let n = (await t(`${e}${e.includes("?") ? "&" : "?"}limit=${o}&offset=${d}${s ? "&includeDrafts=true" : ""}`)).data ?? [];
18
+ if (n.length === 0) break;
19
+ let r = a ? n.map(a).filter((e) => e !== null) : n;
20
+ if (u.push(...r), n.length < o) break;
21
+ d += o, f += 1;
22
+ }
23
+ return f >= c && l?.(`Pagination cap (${c} pages) reached for ${e}; results may be truncated.`), u;
24
+ };
25
+ //#endregion
26
+ export { e as ApiError, i as fetchAllPaginated, t as fetchJson };
27
+
28
+ //# sourceMappingURL=api.es.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.es.mjs","names":[],"sources":["../src/api/ApiError.ts","../src/api/fetchJson.ts","../src/api/fetchAllPaginated.ts"],"sourcesContent":["/**\n * Error thrown by the API layer when a request resolves with a non-2xx status.\n * Carries the HTTP status so callers can branch (e.g. distinguish 401/403 from\n * 5xx) instead of collapsing every failure into one generic message.\n */\nexport class ApiError extends Error {\n public readonly status: number\n\n /**\n * Creates an ApiError carrying the failed response status.\n * @param {number} status - The HTTP status code of the failed response.\n * @param {string} message - A human-readable error message.\n */\n public constructor(status: number, message: string) {\n super(message)\n this.name = 'ApiError'\n this.status = status\n }\n}\n","import { ApiError } from './ApiError'\n\n/**\n * Fetches JSON from a URL using Staffbase session credentials.\n *\n * Throws {@link ApiError} (carrying the HTTP status) on a non-2xx response so\n * callers can branch on the status. Network/parse errors propagate as-is. This\n * helper does not log; the calling service layer owns error logging.\n * @template T The expected shape of the parsed JSON body.\n * @param {string} url - The API URL to fetch.\n * @param {RequestInit} [init] - Optional fetch overrides (merged after credentials).\n * @returns {Promise<T>} The parsed JSON body.\n * @throws {ApiError} When the response status is not ok.\n */\nexport const fetchJson = async <T>(\n url: string,\n init?: RequestInit,\n): Promise<T> => {\n const response = await fetch(url, { credentials: 'include', ...init })\n\n if (!response.ok) {\n throw new ApiError(\n response.status,\n `API request failed with status: ${response.status}`,\n )\n }\n\n return (await response.json()) as T\n}\n","import type { FetchAllPaginatedOptions } from '../types/api/FetchAllPaginatedOptions'\nimport { fetchJson } from './fetchJson'\n\nconst DEFAULT_LIMIT = 50\nconst DEFAULT_MAX_PAGES = 1000\n\n/**\n * Fetches every page of a Staffbase `{ data, total }` collection endpoint.\n *\n * Termination is driven by page contents (a short page ends the loop) rather\n * than the API's reported `total`, which avoids under-fetching when `total` is\n * under-reported. A `maxPages` cap bounds worst-case latency/memory; reaching it\n * invokes `onError` so the truncation is never silent.\n * @template TItem The mapped item type returned to the caller.\n * @template TSource The raw item type returned by the API before mapping.\n * @param {string} baseUrl - The base API URL (without limit/offset parameters).\n * @param {FetchAllPaginatedOptions<TItem, TSource>} [options] - Mapping and pagination options.\n * @returns {Promise<TItem[]>} All fetched (and optionally mapped) items.\n */\nexport const fetchAllPaginated = async <TItem, TSource = TItem>(\n baseUrl: string,\n options: FetchAllPaginatedOptions<TItem, TSource> = {},\n): Promise<TItem[]> => {\n const {\n mapItem,\n limit = DEFAULT_LIMIT,\n includeDrafts = false,\n maxPages = DEFAULT_MAX_PAGES,\n onError,\n } = options\n\n const results: TItem[] = []\n let offset = 0\n let page = 0\n\n while (page < maxPages) {\n const separator = baseUrl.includes('?') ? '&' : '?'\n const draftsParam = includeDrafts ? '&includeDrafts=true' : ''\n const url = `${baseUrl}${separator}limit=${limit}&offset=${offset}${draftsParam}`\n\n const data = await fetchJson<{ data: TSource[]; total?: number }>(url)\n const rawItems = data.data ?? []\n\n if (rawItems.length === 0) break\n\n const mapped = mapItem\n ? rawItems.map(mapItem).filter((item): item is TItem => item !== null)\n : (rawItems as unknown as TItem[])\n\n results.push(...mapped)\n\n // A short page means we reached the end of the collection.\n if (rawItems.length < limit) break\n\n offset += limit\n page += 1\n }\n\n if (page >= maxPages) {\n onError?.(\n `Pagination cap (${maxPages} pages) reached for ${baseUrl}; results may be truncated.`,\n )\n }\n\n return results\n}\n"],"mappings":";AAKA,IAAa,IAAb,cAA8B,MAAM;CAClC;CAOA,YAAmB,GAAgB,GAAiB;EAGlD,AAFA,MAAM,CAAO,GACb,KAAK,OAAO,YACZ,KAAK,SAAS;CAChB;AACF,GCJa,IAAY,OACvB,GACA,MACe;CACf,IAAM,IAAW,MAAM,MAAM,GAAK;EAAE,aAAa;EAAW,GAAG;CAAK,CAAC;CAErE,IAAI,CAAC,EAAS,IACZ,MAAM,IAAI,EACR,EAAS,QACT,mCAAmC,EAAS,QAC9C;CAGF,OAAQ,MAAM,EAAS,KAAK;AAC9B,GCzBM,IAAgB,IAChB,IAAoB,KAeb,IAAoB,OAC/B,GACA,IAAoD,CAAC,MAChC;CACrB,IAAM,EACJ,YACA,WAAQ,GACR,mBAAgB,IAChB,cAAW,GACX,eACE,GAEE,IAAmB,CAAC,GACtB,IAAS,GACT,IAAO;CAEX,OAAO,IAAO,IAAU;EAMtB,IAAM,KAAW,MADE,EAA+C,GAFnD,IAFG,EAAQ,SAAS,GAAG,IAAI,MAAM,IAEb,QAAQ,EAAM,UAAU,IADvC,IAAgB,wBAAwB,IAGS,GAC/C,QAAQ,CAAC;EAE/B,IAAI,EAAS,WAAW,GAAG;EAE3B,IAAM,IAAS,IACX,EAAS,IAAI,CAAO,EAAE,QAAQ,MAAwB,MAAS,IAAI,IAClE;EAKL,IAHA,EAAQ,KAAK,GAAG,CAAM,GAGlB,EAAS,SAAS,GAAO;EAG7B,AADA,KAAU,GACV,KAAQ;CACV;CAQA,OANI,KAAQ,KACV,IACE,mBAAmB,EAAS,sBAAsB,EAAQ,4BAC5D,GAGK;AACT"}
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=()=>typeof document>`u`?null:(document.querySelector(`[aria-selected="true"]`)?.getAttribute(`data-testid`))?.split(`-`).pop()??null,t=()=>{if(typeof window>`u`)return null;let e=window.App?._urlParameters;return e?new URLSearchParams(e).get(`language`):null},n=n=>{let{contentLanguage:r,defaultLanguage:i,isEditor:a}=n,o=r??i;return a?e()??o:r??t()??o},r=(e,t)=>{let{defaultLanguage:r,onError:i}=t;if(!e)return i?.(`Invalid article data or missing contents`),null;let a=n(t),o=e[a]||e[r]||null;if(!o)return i?.(`Content unavailable for language: ${a} or fallback: ${r}`),null;let s=e[r];return{title:o.title||s?.title||``,teaser:o.teaser||s?.teaser||``,content:o.content||s?.content||``,image:o.image||s?.image||``,feedImage:o.feedImage||s?.feedImage||``}};exports.detectEditorLanguage=e,exports.detectPreviewLanguage=t,exports.resolveActiveLanguage=n,exports.resolveLocalizedContent=r;
2
+ //# sourceMappingURL=content.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.cjs.js","names":[],"sources":["../src/content/detectEditorLanguage.ts","../src/content/detectPreviewLanguage.ts","../src/content/resolveActiveLanguage.ts","../src/content/resolveLocalizedContent.ts"],"sourcesContent":["/**\n * Reads the active language from the Staffbase editor's selected language tab\n * (`[aria-selected=\"true\"]` with a `data-testid` suffixed by the language code).\n * Returns null when no tab is active or there is no DOM, so callers can fall back.\n * @returns {string | null} The detected editor language, or null.\n */\nexport const detectEditorLanguage = (): string | null => {\n if (typeof document === 'undefined') return null\n\n const activeTab = document.querySelector('[aria-selected=\"true\"]')\n const testId = activeTab?.getAttribute('data-testid')\n\n return testId?.split('-').pop() ?? null\n}\n","/**\n * Reads the `language` URL parameter the Staffbase preview shell exposes on\n * `window.App._urlParameters`. Returns null when the shell or parameter is\n * absent (or there is no DOM), so callers can fall back.\n * @returns {string | null} The preview language, or null.\n */\nexport const detectPreviewLanguage = (): string | null => {\n if (typeof window === 'undefined') return null\n\n const appConfig = (window as { App?: { _urlParameters?: string } }).App\n ?._urlParameters\n if (!appConfig) return null\n\n return new URLSearchParams(appConfig).get('language')\n}\n","import type { ResolveLocalizedContentOptions } from '../types/content/ResolveLocalizedContentOptions'\nimport { detectEditorLanguage } from './detectEditorLanguage'\nimport { detectPreviewLanguage } from './detectPreviewLanguage'\n\n/**\n * Resolves the language to render content in.\n *\n * Editor path: the selected language tab wins. Runtime path: the explicit\n * `contentLanguage` is trusted first so the article language matches the rest of\n * the UI, with preview-URL sniffing only as a fallback. Both paths fall back to\n * `contentLanguage ?? defaultLanguage`.\n * @param {ResolveLocalizedContentOptions} options - The resolution options.\n * @returns {string} The resolved active language code.\n */\nexport const resolveActiveLanguage = (\n options: ResolveLocalizedContentOptions,\n): string => {\n const { contentLanguage, defaultLanguage, isEditor } = options\n const fallback = contentLanguage ?? defaultLanguage\n\n if (isEditor) return detectEditorLanguage() ?? fallback\n\n return contentLanguage ?? detectPreviewLanguage() ?? fallback\n}\n","import type { LocalizedContent } from '../types/content/LocalizedContent'\nimport type { ResolveLocalizedContentOptions } from '../types/content/ResolveLocalizedContentOptions'\nimport { resolveActiveLanguage } from './resolveActiveLanguage'\n\n/**\n * Selects and field-merges the localized article content for the active language.\n *\n * Resolves the active language (see resolveActiveLanguage), picks that language's\n * content (falling back to the default language), then fills each field from the\n * default-language entry when the active one is empty. Returns null and reports\n * via `onError` when contents are missing or unavailable in both languages.\n * @param {Record<string, LocalizedContent> | undefined} contents - The article's per-language contents.\n * @param {ResolveLocalizedContentOptions} options - Language and diagnostics options.\n * @returns {LocalizedContent | null} The merged localized content, or null.\n */\nexport const resolveLocalizedContent = (\n contents: Record<string, LocalizedContent> | undefined,\n options: ResolveLocalizedContentOptions,\n): LocalizedContent | null => {\n const { defaultLanguage, onError } = options\n\n if (!contents) {\n onError?.('Invalid article data or missing contents')\n return null\n }\n\n const language = resolveActiveLanguage(options)\n const localized = contents[language] || contents[defaultLanguage] || null\n\n if (!localized) {\n onError?.(\n `Content unavailable for language: ${language} or fallback: ${defaultLanguage}`,\n )\n return null\n }\n\n const fallback = contents[defaultLanguage]\n\n return {\n title: localized.title || fallback?.title || '',\n teaser: localized.teaser || fallback?.teaser || '',\n content: localized.content || fallback?.content || '',\n image: localized.image || fallback?.image || '',\n feedImage: localized.feedImage || fallback?.feedImage || '',\n }\n}\n"],"mappings":"mEAMA,IAAa,MACP,OAAO,SAAa,IAAoB,MAE1B,SAAS,cAAc,wBAC1B,GAAW,aAAa,aAAa,IAErC,MAAM,GAAG,EAAE,IAAI,GAAK,KCNxB,MAA6C,CACxD,GAAI,OAAO,OAAW,IAAa,OAAO,KAE1C,IAAM,EAAa,OAAiD,KAChE,eAGJ,OAFK,EAEE,IAAI,gBAAgB,CAAS,EAAE,IAAI,UAAU,EAF7B,IAGzB,ECAa,EACX,GACW,CACX,GAAM,CAAE,kBAAiB,kBAAiB,YAAa,EACjD,EAAW,GAAmB,EAIpC,OAFI,EAAiB,EAAqB,GAAK,EAExC,GAAmB,EAAsB,GAAK,CACvD,ECRa,GACX,EACA,IAC4B,CAC5B,GAAM,CAAE,kBAAiB,WAAY,EAErC,GAAI,CAAC,EAEH,OADA,IAAU,0CAA0C,EAC7C,KAGT,IAAM,EAAW,EAAsB,CAAO,EACxC,EAAY,EAAS,IAAa,EAAS,IAAoB,KAErE,GAAI,CAAC,EAIH,OAHA,IACE,qCAAqC,EAAS,gBAAgB,GAChE,EACO,KAGT,IAAM,EAAW,EAAS,GAE1B,MAAO,CACL,MAAO,EAAU,OAAS,GAAU,OAAS,GAC7C,OAAQ,EAAU,QAAU,GAAU,QAAU,GAChD,QAAS,EAAU,SAAW,GAAU,SAAW,GACnD,MAAO,EAAU,OAAS,GAAU,OAAS,GAC7C,UAAW,EAAU,WAAa,GAAU,WAAa,EAC3D,CACF"}
@@ -0,0 +1,26 @@
1
+ //#region src/content/detectEditorLanguage.ts
2
+ var e = () => typeof document > "u" ? null : (document.querySelector("[aria-selected=\"true\"]")?.getAttribute("data-testid"))?.split("-").pop() ?? null, t = () => {
3
+ if (typeof window > "u") return null;
4
+ let e = window.App?._urlParameters;
5
+ return e ? new URLSearchParams(e).get("language") : null;
6
+ }, n = (n) => {
7
+ let { contentLanguage: r, defaultLanguage: i, isEditor: a } = n, o = r ?? i;
8
+ return a ? e() ?? o : r ?? t() ?? o;
9
+ }, r = (e, t) => {
10
+ let { defaultLanguage: r, onError: i } = t;
11
+ if (!e) return i?.("Invalid article data or missing contents"), null;
12
+ let a = n(t), o = e[a] || e[r] || null;
13
+ if (!o) return i?.(`Content unavailable for language: ${a} or fallback: ${r}`), null;
14
+ let s = e[r];
15
+ return {
16
+ title: o.title || s?.title || "",
17
+ teaser: o.teaser || s?.teaser || "",
18
+ content: o.content || s?.content || "",
19
+ image: o.image || s?.image || "",
20
+ feedImage: o.feedImage || s?.feedImage || ""
21
+ };
22
+ };
23
+ //#endregion
24
+ export { e as detectEditorLanguage, t as detectPreviewLanguage, n as resolveActiveLanguage, r as resolveLocalizedContent };
25
+
26
+ //# sourceMappingURL=content.es.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.es.mjs","names":[],"sources":["../src/content/detectEditorLanguage.ts","../src/content/detectPreviewLanguage.ts","../src/content/resolveActiveLanguage.ts","../src/content/resolveLocalizedContent.ts"],"sourcesContent":["/**\n * Reads the active language from the Staffbase editor's selected language tab\n * (`[aria-selected=\"true\"]` with a `data-testid` suffixed by the language code).\n * Returns null when no tab is active or there is no DOM, so callers can fall back.\n * @returns {string | null} The detected editor language, or null.\n */\nexport const detectEditorLanguage = (): string | null => {\n if (typeof document === 'undefined') return null\n\n const activeTab = document.querySelector('[aria-selected=\"true\"]')\n const testId = activeTab?.getAttribute('data-testid')\n\n return testId?.split('-').pop() ?? null\n}\n","/**\n * Reads the `language` URL parameter the Staffbase preview shell exposes on\n * `window.App._urlParameters`. Returns null when the shell or parameter is\n * absent (or there is no DOM), so callers can fall back.\n * @returns {string | null} The preview language, or null.\n */\nexport const detectPreviewLanguage = (): string | null => {\n if (typeof window === 'undefined') return null\n\n const appConfig = (window as { App?: { _urlParameters?: string } }).App\n ?._urlParameters\n if (!appConfig) return null\n\n return new URLSearchParams(appConfig).get('language')\n}\n","import type { ResolveLocalizedContentOptions } from '../types/content/ResolveLocalizedContentOptions'\nimport { detectEditorLanguage } from './detectEditorLanguage'\nimport { detectPreviewLanguage } from './detectPreviewLanguage'\n\n/**\n * Resolves the language to render content in.\n *\n * Editor path: the selected language tab wins. Runtime path: the explicit\n * `contentLanguage` is trusted first so the article language matches the rest of\n * the UI, with preview-URL sniffing only as a fallback. Both paths fall back to\n * `contentLanguage ?? defaultLanguage`.\n * @param {ResolveLocalizedContentOptions} options - The resolution options.\n * @returns {string} The resolved active language code.\n */\nexport const resolveActiveLanguage = (\n options: ResolveLocalizedContentOptions,\n): string => {\n const { contentLanguage, defaultLanguage, isEditor } = options\n const fallback = contentLanguage ?? defaultLanguage\n\n if (isEditor) return detectEditorLanguage() ?? fallback\n\n return contentLanguage ?? detectPreviewLanguage() ?? fallback\n}\n","import type { LocalizedContent } from '../types/content/LocalizedContent'\nimport type { ResolveLocalizedContentOptions } from '../types/content/ResolveLocalizedContentOptions'\nimport { resolveActiveLanguage } from './resolveActiveLanguage'\n\n/**\n * Selects and field-merges the localized article content for the active language.\n *\n * Resolves the active language (see resolveActiveLanguage), picks that language's\n * content (falling back to the default language), then fills each field from the\n * default-language entry when the active one is empty. Returns null and reports\n * via `onError` when contents are missing or unavailable in both languages.\n * @param {Record<string, LocalizedContent> | undefined} contents - The article's per-language contents.\n * @param {ResolveLocalizedContentOptions} options - Language and diagnostics options.\n * @returns {LocalizedContent | null} The merged localized content, or null.\n */\nexport const resolveLocalizedContent = (\n contents: Record<string, LocalizedContent> | undefined,\n options: ResolveLocalizedContentOptions,\n): LocalizedContent | null => {\n const { defaultLanguage, onError } = options\n\n if (!contents) {\n onError?.('Invalid article data or missing contents')\n return null\n }\n\n const language = resolveActiveLanguage(options)\n const localized = contents[language] || contents[defaultLanguage] || null\n\n if (!localized) {\n onError?.(\n `Content unavailable for language: ${language} or fallback: ${defaultLanguage}`,\n )\n return null\n }\n\n const fallback = contents[defaultLanguage]\n\n return {\n title: localized.title || fallback?.title || '',\n teaser: localized.teaser || fallback?.teaser || '',\n content: localized.content || fallback?.content || '',\n image: localized.image || fallback?.image || '',\n feedImage: localized.feedImage || fallback?.feedImage || '',\n }\n}\n"],"mappings":";AAMA,IAAa,UACP,OAAO,WAAa,MAAoB,QAE1B,SAAS,cAAc,0BAC1B,GAAW,aAAa,aAAa,IAErC,MAAM,GAAG,EAAE,IAAI,KAAK,MCNxB,UAA6C;CACxD,IAAI,OAAO,SAAW,KAAa,OAAO;CAE1C,IAAM,IAAa,OAAiD,KAChE;CAGJ,OAFK,IAEE,IAAI,gBAAgB,CAAS,EAAE,IAAI,UAAU,IAF7B;AAGzB,GCAa,KACX,MACW;CACX,IAAM,EAAE,oBAAiB,oBAAiB,gBAAa,GACjD,IAAW,KAAmB;CAIpC,OAFI,IAAiB,EAAqB,KAAK,IAExC,KAAmB,EAAsB,KAAK;AACvD,GCRa,KACX,GACA,MAC4B;CAC5B,IAAM,EAAE,oBAAiB,eAAY;CAErC,IAAI,CAAC,GAEH,OADA,IAAU,0CAA0C,GAC7C;CAGT,IAAM,IAAW,EAAsB,CAAO,GACxC,IAAY,EAAS,MAAa,EAAS,MAAoB;CAErE,IAAI,CAAC,GAIH,OAHA,IACE,qCAAqC,EAAS,gBAAgB,GAChE,GACO;CAGT,IAAM,IAAW,EAAS;CAE1B,OAAO;EACL,OAAO,EAAU,SAAS,GAAU,SAAS;EAC7C,QAAQ,EAAU,UAAU,GAAU,UAAU;EAChD,SAAS,EAAU,WAAW,GAAU,WAAW;EACnD,OAAO,EAAU,SAAS,GAAU,SAAS;EAC7C,WAAW,EAAU,aAAa,GAAU,aAAa;CAC3D;AACF"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Error thrown by the API layer when a request resolves with a non-2xx status.
3
+ * Carries the HTTP status so callers can branch (e.g. distinguish 401/403 from
4
+ * 5xx) instead of collapsing every failure into one generic message.
5
+ */
6
+ export declare class ApiError extends Error {
7
+ readonly status: number;
8
+ /**
9
+ * Creates an ApiError carrying the failed response status.
10
+ * @param {number} status - The HTTP status code of the failed response.
11
+ * @param {string} message - A human-readable error message.
12
+ */
13
+ constructor(status: number, message: string);
14
+ }
15
+ //# sourceMappingURL=ApiError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApiError.d.ts","sourceRoot":"","sources":["../../../src/api/ApiError.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,MAAM,EAAE,MAAM,CAAA;IAE9B;;;;OAIG;gBACgB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAKnD"}
@@ -0,0 +1,16 @@
1
+ import { FetchAllPaginatedOptions } from '../types/api/FetchAllPaginatedOptions';
2
+ /**
3
+ * Fetches every page of a Staffbase `{ data, total }` collection endpoint.
4
+ *
5
+ * Termination is driven by page contents (a short page ends the loop) rather
6
+ * than the API's reported `total`, which avoids under-fetching when `total` is
7
+ * under-reported. A `maxPages` cap bounds worst-case latency/memory; reaching it
8
+ * invokes `onError` so the truncation is never silent.
9
+ * @template TItem The mapped item type returned to the caller.
10
+ * @template TSource The raw item type returned by the API before mapping.
11
+ * @param {string} baseUrl - The base API URL (without limit/offset parameters).
12
+ * @param {FetchAllPaginatedOptions<TItem, TSource>} [options] - Mapping and pagination options.
13
+ * @returns {Promise<TItem[]>} All fetched (and optionally mapped) items.
14
+ */
15
+ export declare const fetchAllPaginated: <TItem, TSource = TItem>(baseUrl: string, options?: FetchAllPaginatedOptions<TItem, TSource>) => Promise<TItem[]>;
16
+ //# sourceMappingURL=fetchAllPaginated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchAllPaginated.d.ts","sourceRoot":"","sources":["../../../src/api/fetchAllPaginated.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAA;AAMrF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,GAAU,KAAK,EAAE,OAAO,GAAG,KAAK,EAC5D,SAAS,MAAM,EACf,UAAS,wBAAwB,CAAC,KAAK,EAAE,OAAO,CAAM,KACrD,OAAO,CAAC,KAAK,EAAE,CA2CjB,CAAA"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Fetches JSON from a URL using Staffbase session credentials.
3
+ *
4
+ * Throws {@link ApiError} (carrying the HTTP status) on a non-2xx response so
5
+ * callers can branch on the status. Network/parse errors propagate as-is. This
6
+ * helper does not log; the calling service layer owns error logging.
7
+ * @template T The expected shape of the parsed JSON body.
8
+ * @param {string} url - The API URL to fetch.
9
+ * @param {RequestInit} [init] - Optional fetch overrides (merged after credentials).
10
+ * @returns {Promise<T>} The parsed JSON body.
11
+ * @throws {ApiError} When the response status is not ok.
12
+ */
13
+ export declare const fetchJson: <T>(url: string, init?: RequestInit) => Promise<T>;
14
+ //# sourceMappingURL=fetchJson.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchJson.d.ts","sourceRoot":"","sources":["../../../src/api/fetchJson.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GAAU,CAAC,EAC/B,KAAK,MAAM,EACX,OAAO,WAAW,KACjB,OAAO,CAAC,CAAC,CAWX,CAAA"}
@@ -0,0 +1,4 @@
1
+ export { ApiError } from './ApiError';
2
+ export { fetchAllPaginated } from './fetchAllPaginated';
3
+ export { fetchJson } from './fetchJson';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Reads the active language from the Staffbase editor's selected language tab
3
+ * (`[aria-selected="true"]` with a `data-testid` suffixed by the language code).
4
+ * Returns null when no tab is active or there is no DOM, so callers can fall back.
5
+ * @returns {string | null} The detected editor language, or null.
6
+ */
7
+ export declare const detectEditorLanguage: () => string | null;
8
+ //# sourceMappingURL=detectEditorLanguage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detectEditorLanguage.d.ts","sourceRoot":"","sources":["../../../src/content/detectEditorLanguage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAAM,GAAG,IAOhD,CAAA"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Reads the `language` URL parameter the Staffbase preview shell exposes on
3
+ * `window.App._urlParameters`. Returns null when the shell or parameter is
4
+ * absent (or there is no DOM), so callers can fall back.
5
+ * @returns {string | null} The preview language, or null.
6
+ */
7
+ export declare const detectPreviewLanguage: () => string | null;
8
+ //# sourceMappingURL=detectPreviewLanguage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detectPreviewLanguage.d.ts","sourceRoot":"","sources":["../../../src/content/detectPreviewLanguage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAO,MAAM,GAAG,IAQjD,CAAA"}
@@ -0,0 +1,6 @@
1
+ export { detectEditorLanguage } from './detectEditorLanguage';
2
+ export { detectPreviewLanguage } from './detectPreviewLanguage';
3
+ export { resolveActiveLanguage } from './resolveActiveLanguage';
4
+ export { resolveLocalizedContent } from './resolveLocalizedContent';
5
+ export type { ResolveLocalizedContentOptions } from '../types/content/ResolveLocalizedContentOptions';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/content/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAA;AACnE,YAAY,EAAE,8BAA8B,EAAE,MAAM,iDAAiD,CAAA"}
@@ -0,0 +1,13 @@
1
+ import { ResolveLocalizedContentOptions } from '../types/content/ResolveLocalizedContentOptions';
2
+ /**
3
+ * Resolves the language to render content in.
4
+ *
5
+ * Editor path: the selected language tab wins. Runtime path: the explicit
6
+ * `contentLanguage` is trusted first so the article language matches the rest of
7
+ * the UI, with preview-URL sniffing only as a fallback. Both paths fall back to
8
+ * `contentLanguage ?? defaultLanguage`.
9
+ * @param {ResolveLocalizedContentOptions} options - The resolution options.
10
+ * @returns {string} The resolved active language code.
11
+ */
12
+ export declare const resolveActiveLanguage: (options: ResolveLocalizedContentOptions) => string;
13
+ //# sourceMappingURL=resolveActiveLanguage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveActiveLanguage.d.ts","sourceRoot":"","sources":["../../../src/content/resolveActiveLanguage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,iDAAiD,CAAA;AAIrG;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,GAChC,SAAS,8BAA8B,KACtC,MAOF,CAAA"}
@@ -0,0 +1,15 @@
1
+ import { LocalizedContent } from '../types/content/LocalizedContent';
2
+ import { ResolveLocalizedContentOptions } from '../types/content/ResolveLocalizedContentOptions';
3
+ /**
4
+ * Selects and field-merges the localized article content for the active language.
5
+ *
6
+ * Resolves the active language (see resolveActiveLanguage), picks that language's
7
+ * content (falling back to the default language), then fills each field from the
8
+ * default-language entry when the active one is empty. Returns null and reports
9
+ * via `onError` when contents are missing or unavailable in both languages.
10
+ * @param {Record<string, LocalizedContent> | undefined} contents - The article's per-language contents.
11
+ * @param {ResolveLocalizedContentOptions} options - Language and diagnostics options.
12
+ * @returns {LocalizedContent | null} The merged localized content, or null.
13
+ */
14
+ export declare const resolveLocalizedContent: (contents: Record<string, LocalizedContent> | undefined, options: ResolveLocalizedContentOptions) => LocalizedContent | null;
15
+ //# sourceMappingURL=resolveLocalizedContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveLocalizedContent.d.ts","sourceRoot":"","sources":["../../../src/content/resolveLocalizedContent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAA;AACzE,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,iDAAiD,CAAA;AAGrG;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAU,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,EACtD,SAAS,8BAA8B,KACtC,gBAAgB,GAAG,IA2BrB,CAAA"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Options for fetchAllPaginated.
3
+ * @template TItem The mapped item type returned to the caller.
4
+ * @template TSource The raw item type returned by the API before mapping.
5
+ */
6
+ export interface FetchAllPaginatedOptions<TItem, TSource = TItem> {
7
+ /**
8
+ * Maps/filters each raw API item. Return null to drop an item. When omitted,
9
+ * raw items are returned unchanged (TSource is assumed assignable to TItem).
10
+ * @param {TSource} item - The raw item from the API page.
11
+ * @returns {TItem | null} The mapped item, or null to skip it.
12
+ */
13
+ mapItem?: (item: TSource) => TItem | null;
14
+ /** Page size requested per call (default 50). */
15
+ limit?: number;
16
+ /** When true, appends `includeDrafts=true` to each request URL. */
17
+ includeDrafts?: boolean;
18
+ /**
19
+ * Hard cap on the number of pages fetched (default 1000), a runaway backstop.
20
+ * Lower it to bound first-paint latency on very large collections.
21
+ */
22
+ maxPages?: number;
23
+ /**
24
+ * Called with a diagnostic message when the maxPages cap is hit (so truncation
25
+ * is never silent). Injected because the library does not log directly.
26
+ * @param {string} message - The truncation diagnostic message.
27
+ * @returns {void} Nothing.
28
+ */
29
+ onError?: (message: string) => void;
30
+ }
31
+ //# sourceMappingURL=FetchAllPaginatedOptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FetchAllPaginatedOptions.d.ts","sourceRoot":"","sources":["../../../../src/types/api/FetchAllPaginatedOptions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK;IAC9D;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,KAAK,GAAG,IAAI,CAAA;IACzC,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mEAAmE;IACnE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC"}
@@ -0,0 +1,17 @@
1
+ import { ArticleImageVariant } from './ArticleImageVariant';
2
+ /**
3
+ * The set of rendition variants the Staffbase media API returns for an article
4
+ * image. Named `ArticleImage` (not `Image`) to avoid shadowing the DOM global.
5
+ */
6
+ export interface ArticleImage {
7
+ original: ArticleImageVariant & {
8
+ size: number;
9
+ };
10
+ original_scaled: ArticleImageVariant;
11
+ thumb: ArticleImageVariant;
12
+ wide: ArticleImageVariant;
13
+ compact: ArticleImageVariant;
14
+ wide_first: ArticleImageVariant;
15
+ compact_first: ArticleImageVariant;
16
+ }
17
+ //# sourceMappingURL=ArticleImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ArticleImage.d.ts","sourceRoot":"","sources":["../../../../src/types/content/ArticleImage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAEhE;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,mBAAmB,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD,eAAe,EAAE,mBAAmB,CAAA;IACpC,KAAK,EAAE,mBAAmB,CAAA;IAC1B,IAAI,EAAE,mBAAmB,CAAA;IACzB,OAAO,EAAE,mBAAmB,CAAA;IAC5B,UAAU,EAAE,mBAAmB,CAAA;IAC/B,aAAa,EAAE,mBAAmB,CAAA;CACnC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * A single rendition of an article image (a size/format variant returned by the
3
+ * Staffbase media API). `size` is only present on the original variant.
4
+ */
5
+ export interface ArticleImageVariant {
6
+ width: number;
7
+ height: number;
8
+ size?: number;
9
+ format: string | null;
10
+ mimeType: string | null;
11
+ url: string;
12
+ }
13
+ //# sourceMappingURL=ArticleImageVariant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ArticleImageVariant.d.ts","sourceRoot":"","sources":["../../../../src/types/content/ArticleImageVariant.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,GAAG,EAAE,MAAM,CAAA;CACZ"}
@@ -0,0 +1,16 @@
1
+ import { ArticleImage } from './ArticleImage';
2
+ /**
3
+ * The per-language content of an article (one entry of `ArticleData.contents`).
4
+ *
5
+ * `image` is `ArticleImage | string | null` because the API returns either the
6
+ * structured rendition set, a bare URL string, or nothing. Shared verbatim by
7
+ * the alerts and unacknowledged-bulletins widgets.
8
+ */
9
+ export interface LocalizedContent {
10
+ title: string;
11
+ teaser: string;
12
+ content: string;
13
+ image: ArticleImage | string | null;
14
+ feedImage: string | null;
15
+ }
16
+ //# sourceMappingURL=LocalizedContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalizedContent.d.ts","sourceRoot":"","sources":["../../../../src/types/content/LocalizedContent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAElD;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAAA;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Options for resolveLocalizedContent.
3
+ */
4
+ export interface ResolveLocalizedContentOptions {
5
+ /**
6
+ * Language explicitly requested by the host/widget config. On the runtime path
7
+ * this is trusted first so the article language matches the rest of the UI;
8
+ * DOM/URL sniffing is only a fallback.
9
+ */
10
+ contentLanguage?: string;
11
+ /** Fallback language. Injected because the library never reads env. */
12
+ defaultLanguage: string;
13
+ /** When true, resolves the active language from the editor's selected tab. */
14
+ isEditor?: boolean;
15
+ /**
16
+ * Called with a diagnostic message when content cannot be resolved (the
17
+ * library does not log directly).
18
+ * @param {string} message - The diagnostic message.
19
+ * @param {...unknown} args - Optional extra context.
20
+ * @returns {void} Nothing.
21
+ */
22
+ onError?: (message: string, ...args: unknown[]) => void;
23
+ }
24
+ //# sourceMappingURL=ResolveLocalizedContentOptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResolveLocalizedContentOptions.d.ts","sourceRoot":"","sources":["../../../../src/types/content/ResolveLocalizedContentOptions.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,uEAAuE;IACvE,eAAe,EAAE,MAAM,CAAA;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;CACxD"}
@@ -1,5 +1,8 @@
1
+ export type { ArticleImage } from './ArticleImage';
2
+ export type { ArticleImageVariant } from './ArticleImageVariant';
1
3
  export type { Channel } from './Channel';
2
4
  export type { ChannelLink } from './ChannelLink';
3
5
  export type { ChannelLinkParameter } from './ChannelLinkParameter';
4
6
  export type { DropdownOption } from './DropdownOption';
7
+ export type { LocalizedContent } from './LocalizedContent';
5
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/types/content/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAClE,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/types/content/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAClD,YAAY,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAChE,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAClE,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACtD,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@favish/staffbase-utils",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Shared internal/host utilities for Staffbase widgets",
5
5
  "author": "Favish <dev@favish.com>",
6
6
  "license": "UNLICENSED",
@@ -8,6 +8,16 @@
8
8
  "dist"
9
9
  ],
10
10
  "exports": {
11
+ "./api": {
12
+ "types": "./dist/src/api/index.d.ts",
13
+ "import": "./dist/api.es.mjs",
14
+ "require": "./dist/api.cjs.js"
15
+ },
16
+ "./content": {
17
+ "types": "./dist/src/content/index.d.ts",
18
+ "import": "./dist/content.es.mjs",
19
+ "require": "./dist/content.cjs.js"
20
+ },
11
21
  "./log": {
12
22
  "types": "./dist/src/log/index.d.ts",
13
23
  "import": "./dist/log.es.mjs",