@c-rex/services 0.3.0-build.51 → 0.3.0-build.53

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/html-enrichment/descriptor.ts","../../src/html-enrichment/endpoint-path.ts","../../src/base-client-request.ts","../../src/html-enrichment/loader.ts"],"sourcesContent":["import type {\r\n HtmlEnrichmentDescriptor,\r\n HtmlEnrichmentLoadMode,\r\n HtmlEnrichmentState,\r\n} from \"./types\";\r\n\r\nconst VALID_LOAD_MODES = new Set<string>([\"onload\", \"onappear\", \"onclick\"]);\r\nconst VALID_STATES = new Set<string>([\r\n \"unloaded\",\r\n \"loading\",\r\n \"loaded\",\r\n \"denied\",\r\n \"error\",\r\n]);\r\n\r\nconst toQueryParamName = (rawName: string): string => {\r\n const normalized = rawName.toLowerCase();\r\n if (normalized === \"restriction\") return \"Restrict\";\r\n\r\n return rawName\r\n .split(\"-\")\r\n .filter(Boolean)\r\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\r\n .join(\"\");\r\n};\r\n\r\nconst parseParamValue = (name: string, value: string): string | string[] => {\r\n // Restrict selectors use semicolons internally, so the complete selector is\r\n // one API value. Other marker params use semicolons as list separators.\r\n if (name === \"Restrict\") return value.trim();\r\n return value\r\n .split(\";\")\r\n .map((item) => item.trim())\r\n .filter(Boolean);\r\n};\r\n\r\nconst parseParams = (\r\n el: HTMLElement,\r\n): Record<string, string | string[]> | undefined => {\r\n const params: Record<string, string | string[]> = {};\r\n\r\n for (const attributeName of el.getAttributeNames()) {\r\n const prefix = \"data-crex-param-\";\r\n if (!attributeName.startsWith(prefix)) continue;\r\n\r\n const rawValue = el.getAttribute(attributeName);\r\n if (!rawValue) continue;\r\n const name = toQueryParamName(attributeName.slice(prefix.length));\r\n params[name] = parseParamValue(name, rawValue);\r\n }\r\n\r\n return Object.keys(params).length > 0 ? params : undefined;\r\n};\r\n\r\n/**\r\n * Convert a `data-crex-*` enriched DOM node into a typed descriptor.\r\n * Unknown `load`/`state` values are dropped (undefined) rather than trusted.\r\n */\r\nexport const parseEnrichmentDescriptor = (\r\n el: HTMLElement,\r\n): HtmlEnrichmentDescriptor => {\r\n const loadModeRaw = el.getAttribute(\"data-crex-load\") ?? undefined;\r\n const stateRaw = el.getAttribute(\"data-crex-state\") ?? undefined;\r\n\r\n return {\r\n elementId: el.id || undefined,\r\n loadMode:\r\n loadModeRaw && VALID_LOAD_MODES.has(loadModeRaw)\r\n ? (loadModeRaw as HtmlEnrichmentLoadMode)\r\n : undefined,\r\n endpoint: el.getAttribute(\"data-crex-endpoint\") ?? undefined,\r\n state:\r\n stateRaw && VALID_STATES.has(stateRaw)\r\n ? (stateRaw as HtmlEnrichmentState)\r\n : undefined,\r\n clientFilter: el.getAttribute(\"data-crex-client-filter\") ?? undefined,\r\n renderer: el.getAttribute(\"data-crex-renderer\") ?? undefined,\r\n resourceId: el.getAttribute(\"data-crex-id\") ?? undefined,\r\n params: parseParams(el),\r\n };\r\n};\r\n","/**\r\n * API version segment injected into resolved enrichment paths.\r\n * Kept as a code constant for now; promote to an env var when versioning\r\n * needs to vary per deployment (note: client-side use requires NEXT_PUBLIC_*).\r\n */\r\nexport const IDS_API_VERSION = \"v1\";\r\n\r\nconst capitalize = (s: string): string =>\r\n s ? s.charAt(0).toUpperCase() + s.slice(1) : s;\r\n\r\n/**\r\n * Convert a logical endpoint slug into a versioned API base path.\r\n *\r\n * Slug shape: `<area>-<resource>` (resource may be multi-word, dash-separated).\r\n * `ids-resources` -> `ids/v1/Resources`\r\n * `ids-info-units` -> `ids/v1/InfoUnits`\r\n *\r\n * Returns null when the slug does not match the expected shape.\r\n * The full path is later combined with the encoded resource id by the loader\r\n * and resolved against the API root by `CrexApi.buildUrl`.\r\n */\r\nexport const slugToPath = (\r\n slug: string,\r\n version: string = IDS_API_VERSION,\r\n): string | null => {\r\n const parts = slug.split(\"-\").filter(Boolean);\r\n if (parts.length < 2) return null;\r\n\r\n const [area, ...resourceParts] = parts;\r\n const resource = resourceParts.map(capitalize).join(\"\");\r\n if (!area || !resource) return null;\r\n\r\n return `${area}/${version}/${resource}`;\r\n};\r\n","export const baseClientRequest = async <T>(\r\n endpoint: string,\r\n queryParams?: Record<\r\n string,\r\n string | number | boolean | (string | number | boolean)[] | undefined\r\n >,\r\n options?: { signal?: AbortSignal },\r\n extra?: { responseType?: \"json\" | \"text\" },\r\n): Promise<T> => {\r\n const res = await fetch(`/api/rpc`, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n method: \"CrexApi.execute\",\r\n params: {\r\n url: endpoint,\r\n method: \"GET\",\r\n params: queryParams,\r\n responseType: extra?.responseType,\r\n },\r\n }),\r\n credentials: \"include\",\r\n signal: options?.signal,\r\n });\r\n\r\n const json = await res.json();\r\n\r\n if (!res.ok) throw new Error(json.error || \"Unknown error\");\r\n\r\n return json.data;\r\n};\r\n","import type { LiteralModel, RenditionModel } from \"@c-rex/interfaces\";\r\nimport { baseClientRequest } from \"../base-client-request\";\r\nimport { slugToPath } from \"./endpoint-path\";\r\nimport type {\r\n HtmlEnrichmentDescriptor,\r\n HtmlEnrichmentEndpointResult,\r\n} from \"./types\";\r\n\r\n/**\r\n * Access-denied heuristic. This is NOT an HTTP auth failure — metadata may\r\n * resolve while the content rendition is licence-restricted. HTTP 403 is the\r\n * current signal until the IDS API confirms a canonical denied contract\r\n * (see HTML_RUNTIME_ENRICHMENT_SPEC.md → Open Questions).\r\n */\r\nconst isDeniedError = (err: unknown): boolean =>\r\n err instanceof Error && err.message.includes(\"403\");\r\n\r\n/**\r\n * Generic enrichment loader: resolves `data-crex-endpoint` (slug) + the\r\n * `data-crex-id` resource IRI into a single request and returns the raw HTML.\r\n *\r\n * Request shape: `{<area>/<version>/<Resource>}/view/{encodeURIComponent(id)}`\r\n * fetched as text. The `/view/` segment returns the rendered HTML rendition.\r\n * Works for any `<area>-<resource>` slug — no per-endpoint registration needed.\r\n */\r\nexport const loadEnrichmentHtml = async (\r\n descriptor: HtmlEnrichmentDescriptor,\r\n): Promise<HtmlEnrichmentEndpointResult> => {\r\n if (!descriptor.endpoint || !descriptor.resourceId) {\r\n return { html: null, isDenied: false };\r\n }\r\n\r\n const basePath = slugToPath(descriptor.endpoint);\r\n if (!basePath) {\r\n return { html: null, isDenied: false };\r\n }\r\n\r\n const url = `${basePath}/view/${encodeURIComponent(descriptor.resourceId)}`;\r\n\r\n try {\r\n const html = await baseClientRequest<string>(\r\n url,\r\n descriptor.params,\r\n undefined,\r\n {\r\n responseType: \"text\",\r\n },\r\n );\r\n return { html, isDenied: false };\r\n } catch (err: unknown) {\r\n if (isDeniedError(err)) {\r\n return { html: null, isDenied: true };\r\n }\r\n throw err;\r\n }\r\n};\r\n\r\n/**\r\n * Loads a collection-style enrichment marker such as the GMP Startseite tile\r\n * container. Collection markers do not have a data-crex-id; their API query is\r\n * carried by data-crex-param-* attributes instead.\r\n */\r\nexport const loadEnrichmentCollection = async (\r\n descriptor: HtmlEnrichmentDescriptor,\r\n): Promise<HtmlEnrichmentEndpointResult> => {\r\n if (!descriptor.endpoint) return { html: null, isDenied: false };\r\n\r\n const basePath = slugToPath(descriptor.endpoint);\r\n if (!basePath) return { html: null, isDenied: false };\r\n\r\n const params = { ...(descriptor.params ?? {}) };\r\n const fields = params.Fields;\r\n const hasRenditions = Array.isArray(fields)\r\n ? fields.some((field) => String(field).toLowerCase() === \"renditions\")\r\n : String(fields ?? \"\")\r\n .toLowerCase()\r\n .includes(\"renditions\");\r\n\r\n // Tile renderers need the rendition objects and their view links in the\r\n // collection response. Keep the marker's explicit params authoritative,\r\n // while filling in the two optional API flags when renditions were asked for.\r\n if (hasRenditions) {\r\n if (params.Embed === undefined) params.Embed = [\"renditions\"];\r\n if (params.Links === undefined) params.Links = true;\r\n }\r\n\r\n try {\r\n const data = await baseClientRequest<unknown>(basePath, params);\r\n return { html: null, data, isDenied: false };\r\n } catch (err: unknown) {\r\n if (isDeniedError(err)) {\r\n return { html: null, isDenied: true };\r\n }\r\n throw err;\r\n }\r\n};\r\n\r\n/** Selects the individual-resource or collection loader from the marker. */\r\nexport const loadEnrichment = async (\r\n descriptor: HtmlEnrichmentDescriptor,\r\n): Promise<HtmlEnrichmentEndpointResult> =>\r\n descriptor.resourceId\r\n ? loadEnrichmentHtml(descriptor)\r\n : loadEnrichmentCollection(descriptor);\r\n\r\n/**\r\n * Minimal shape shared by the iiRDS model endpoints used for onclick enrichment\r\n * (Fragments, InformationUnits, …). Kept loose so any model-with-renditions\r\n * endpoint works without per-endpoint typing.\r\n */\r\nexport type EnrichmentResourceModel = {\r\n /**\r\n * Root short id of the resource. Used to build in-app navigation targets\r\n * (e.g. swapping the topic id segment) instead of rendering the payload.\r\n */\r\n shortId?: string | null;\r\n renditions?: RenditionModel[] | null;\r\n titles?: LiteralModel[] | null;\r\n labels?: LiteralModel[] | null;\r\n /**\r\n * Content access state. The request itself can succeed (HTTP 200, metadata\r\n * present) while the content is licence-restricted — IDS signals that here\r\n * rather than via an HTTP error.\r\n */\r\n authorization?: { state?: string | null } | null;\r\n};\r\n\r\nexport type EnrichmentResourceResult = {\r\n data: EnrichmentResourceModel | null;\r\n isDenied: boolean;\r\n};\r\n\r\n/**\r\n * Generic onclick enrichment loader: resolves `data-crex-endpoint` (slug) + the\r\n * `data-crex-id` resource IRI and fetches the JSON model (e.g. a Fragment or\r\n * InformationUnit) for rendering inside a popover/modal. Endpoint resolution is\r\n * fully dynamic via `slugToPath` — no per-endpoint hardcoding.\r\n */\r\nexport const loadEnrichmentResource = async (\r\n descriptor: HtmlEnrichmentDescriptor,\r\n queryParams?: Record<\r\n string,\r\n string | number | boolean | (string | number | boolean)[] | undefined\r\n >,\r\n): Promise<EnrichmentResourceResult> => {\r\n if (!descriptor.endpoint || !descriptor.resourceId) {\r\n return { data: null, isDenied: false };\r\n }\r\n\r\n const basePath = slugToPath(descriptor.endpoint);\r\n if (!basePath) {\r\n return { data: null, isDenied: false };\r\n }\r\n\r\n const url = `${basePath}/${encodeURIComponent(descriptor.resourceId)}`;\r\n\r\n try {\r\n const data = await baseClientRequest<EnrichmentResourceModel>(\r\n url,\r\n queryParams,\r\n );\r\n // Denied is carried in the (successful) payload, not as an HTTP error.\r\n const isDenied = data?.authorization?.state === \"denied\";\r\n return { data, isDenied };\r\n } catch (err: unknown) {\r\n if (isDeniedError(err)) {\r\n return { data: null, isDenied: true };\r\n }\r\n throw err;\r\n }\r\n};\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/html-enrichment/descriptor.ts","../../src/html-enrichment/endpoint-path.ts","../../src/base-client-request.ts","../../src/html-enrichment/loader.ts"],"sourcesContent":["import type {\r\n HtmlEnrichmentDescriptor,\r\n HtmlEnrichmentLoadMode,\r\n HtmlEnrichmentState,\r\n} from \"./types\";\r\n\r\nconst VALID_LOAD_MODES = new Set<string>([\"onload\", \"onappear\", \"onclick\"]);\r\nconst VALID_STATES = new Set<string>([\r\n \"unloaded\",\r\n \"loading\",\r\n \"loaded\",\r\n \"denied\",\r\n \"error\",\r\n]);\r\n\r\nconst toQueryParamName = (rawName: string): string => {\r\n const normalized = rawName.toLowerCase();\r\n if (normalized === \"restriction\") return \"Restrict\";\r\n\r\n return rawName\r\n .split(\"-\")\r\n .filter(Boolean)\r\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\r\n .join(\"\");\r\n};\r\n\r\nconst parseParamValue = (name: string, value: string): string | string[] => {\r\n // Restrict selectors use semicolons internally, so the complete selector is\r\n // one API value. Other marker params use semicolons as list separators.\r\n if (name === \"Restrict\") return value.trim();\r\n return value\r\n .split(\";\")\r\n .map((item) => item.trim())\r\n .filter(Boolean);\r\n};\r\n\r\nconst parseParams = (\r\n el: HTMLElement,\r\n): Record<string, string | string[]> | undefined => {\r\n const params: Record<string, string | string[]> = {};\r\n\r\n for (const attributeName of el.getAttributeNames()) {\r\n const prefix = \"data-crex-param-\";\r\n if (!attributeName.startsWith(prefix)) continue;\r\n\r\n const rawValue = el.getAttribute(attributeName);\r\n if (!rawValue) continue;\r\n const name = toQueryParamName(attributeName.slice(prefix.length));\r\n params[name] = parseParamValue(name, rawValue);\r\n }\r\n\r\n return Object.keys(params).length > 0 ? params : undefined;\r\n};\r\n\r\n/**\r\n * Convert a `data-crex-*` enriched DOM node into a typed descriptor.\r\n * Unknown `load`/`state` values are dropped (undefined) rather than trusted.\r\n */\r\nexport const parseEnrichmentDescriptor = (\r\n el: HTMLElement,\r\n): HtmlEnrichmentDescriptor => {\r\n const loadModeRaw = el.getAttribute(\"data-crex-load\") ?? undefined;\r\n const stateRaw = el.getAttribute(\"data-crex-state\") ?? undefined;\r\n\r\n return {\r\n elementId: el.id || undefined,\r\n loadMode:\r\n loadModeRaw && VALID_LOAD_MODES.has(loadModeRaw)\r\n ? (loadModeRaw as HtmlEnrichmentLoadMode)\r\n : undefined,\r\n endpoint: el.getAttribute(\"data-crex-endpoint\") ?? undefined,\r\n state:\r\n stateRaw && VALID_STATES.has(stateRaw)\r\n ? (stateRaw as HtmlEnrichmentState)\r\n : undefined,\r\n clientFilter: el.getAttribute(\"data-crex-client-filter\") ?? undefined,\r\n renderer: el.getAttribute(\"data-crex-renderer\") ?? undefined,\r\n resourceId: el.getAttribute(\"data-crex-id\") ?? undefined,\r\n params: parseParams(el),\r\n };\r\n};\r\n","/**\r\n * API version segment injected into resolved enrichment paths.\r\n * Kept as a code constant for now; promote to an env var when versioning\r\n * needs to vary per deployment (note: client-side use requires NEXT_PUBLIC_*).\r\n */\r\nexport const IDS_API_VERSION = \"v1\";\r\n\r\nconst capitalize = (s: string): string =>\r\n s ? s.charAt(0).toUpperCase() + s.slice(1) : s;\r\n\r\n/**\r\n * Convert a logical endpoint slug into a versioned API base path.\r\n *\r\n * Slug shape: `<area>-<resource>` (resource may be multi-word, dash-separated).\r\n * `ids-resources` -> `ids/v1/Resources`\r\n * `ids-info-units` -> `ids/v1/InfoUnits`\r\n *\r\n * Returns null when the slug does not match the expected shape.\r\n * The full path is later combined with the encoded resource id by the loader\r\n * and resolved against the API root by `CrexApi.buildUrl`.\r\n */\r\nexport const slugToPath = (\r\n slug: string,\r\n version: string = IDS_API_VERSION,\r\n): string | null => {\r\n const parts = slug.split(\"-\").filter(Boolean);\r\n if (parts.length < 2) return null;\r\n\r\n const [area, ...resourceParts] = parts;\r\n const resource = resourceParts.map(capitalize).join(\"\");\r\n if (!area || !resource) return null;\r\n\r\n return `${area}/${version}/${resource}`;\r\n};\r\n","export const baseClientRequest = async <T>(\r\n endpoint: string,\r\n queryParams?: Record<\r\n string,\r\n string | number | boolean | (string | number | boolean)[] | undefined\r\n >,\r\n options?: { signal?: AbortSignal },\r\n extra?: {\r\n responseType?: \"json\" | \"text\";\r\n authMode?: \"configured\" | \"anonymous\";\r\n },\r\n): Promise<T> => {\r\n const res = await fetch(`/api/rpc`, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n method: \"CrexApi.execute\",\r\n params: {\r\n url: endpoint,\r\n method: \"GET\",\r\n params: queryParams,\r\n responseType: extra?.responseType,\r\n authMode: extra?.authMode,\r\n },\r\n }),\r\n credentials: \"include\",\r\n signal: options?.signal,\r\n });\r\n\r\n const json = await res.json();\r\n\r\n if (!res.ok) throw new Error(json.error || \"Unknown error\");\r\n\r\n return json.data;\r\n};\r\n","import type { LiteralModel, RenditionModel } from \"@c-rex/interfaces\";\r\nimport { baseClientRequest } from \"../base-client-request\";\r\nimport { slugToPath } from \"./endpoint-path\";\r\nimport type {\r\n HtmlEnrichmentDescriptor,\r\n HtmlEnrichmentEndpointResult,\r\n} from \"./types\";\r\n\r\n/**\r\n * Access-denied heuristic. This is NOT an HTTP auth failure — metadata may\r\n * resolve while the content rendition is licence-restricted. HTTP 403 is the\r\n * current signal until the IDS API confirms a canonical denied contract\r\n * (see HTML_RUNTIME_ENRICHMENT_SPEC.md → Open Questions).\r\n */\r\nconst isDeniedError = (err: unknown): boolean =>\r\n err instanceof Error && err.message.includes(\"403\");\r\n\r\n/**\r\n * Generic enrichment loader: resolves `data-crex-endpoint` (slug) + the\r\n * `data-crex-id` resource IRI into a single request and returns the raw HTML.\r\n *\r\n * Request shape: `{<area>/<version>/<Resource>}/view/{encodeURIComponent(id)}`\r\n * fetched as text. The `/view/` segment returns the rendered HTML rendition.\r\n * Works for any `<area>-<resource>` slug — no per-endpoint registration needed.\r\n */\r\nexport const loadEnrichmentHtml = async (\r\n descriptor: HtmlEnrichmentDescriptor,\r\n): Promise<HtmlEnrichmentEndpointResult> => {\r\n if (!descriptor.endpoint || !descriptor.resourceId) {\r\n return { html: null, isDenied: false };\r\n }\r\n\r\n const basePath = slugToPath(descriptor.endpoint);\r\n if (!basePath) {\r\n return { html: null, isDenied: false };\r\n }\r\n\r\n const url = `${basePath}/view/${encodeURIComponent(descriptor.resourceId)}`;\r\n\r\n try {\r\n const html = await baseClientRequest<string>(\r\n url,\r\n descriptor.params,\r\n undefined,\r\n {\r\n responseType: \"text\",\r\n },\r\n );\r\n return { html, isDenied: false };\r\n } catch (err: unknown) {\r\n if (isDeniedError(err)) {\r\n return { html: null, isDenied: true };\r\n }\r\n throw err;\r\n }\r\n};\r\n\r\n/**\r\n * Loads a collection-style enrichment marker such as the GMP Startseite tile\r\n * container. Collection markers do not have a data-crex-id; their API query is\r\n * carried by data-crex-param-* attributes instead.\r\n */\r\nexport const loadEnrichmentCollection = async (\r\n descriptor: HtmlEnrichmentDescriptor,\r\n): Promise<HtmlEnrichmentEndpointResult> => {\r\n if (!descriptor.endpoint) return { html: null, isDenied: false };\r\n\r\n const basePath = slugToPath(descriptor.endpoint);\r\n if (!basePath) return { html: null, isDenied: false };\r\n\r\n const params = { ...(descriptor.params ?? {}) };\r\n const fields = params.Fields;\r\n const hasRenditions = Array.isArray(fields)\r\n ? fields.some((field) => String(field).toLowerCase() === \"renditions\")\r\n : String(fields ?? \"\")\r\n .toLowerCase()\r\n .includes(\"renditions\");\r\n\r\n // Tile renderers need the rendition objects and their view links in the\r\n // collection response. Keep the marker's explicit params authoritative,\r\n // while filling in the two optional API flags when renditions were asked for.\r\n if (hasRenditions) {\r\n if (params.Embed === undefined) params.Embed = [\"renditions\"];\r\n if (params.Links === undefined) params.Links = true;\r\n }\r\n\r\n try {\r\n const isPublicGmpStartTiles =\r\n descriptor.renderer === \"GMPTilesRenderer\" &&\r\n basePath === \"iirds/v1/Fragments\";\r\n const data = isPublicGmpStartTiles\r\n ? await baseClientRequest<unknown>(basePath, params, undefined, {\r\n authMode: \"anonymous\",\r\n })\r\n : await baseClientRequest<unknown>(basePath, params);\r\n return { html: null, data, isDenied: false };\r\n } catch (err: unknown) {\r\n if (isDeniedError(err)) {\r\n return { html: null, isDenied: true };\r\n }\r\n throw err;\r\n }\r\n};\r\n\r\n/** Selects the individual-resource or collection loader from the marker. */\r\nexport const loadEnrichment = async (\r\n descriptor: HtmlEnrichmentDescriptor,\r\n): Promise<HtmlEnrichmentEndpointResult> =>\r\n descriptor.resourceId\r\n ? loadEnrichmentHtml(descriptor)\r\n : loadEnrichmentCollection(descriptor);\r\n\r\n/**\r\n * Minimal shape shared by the iiRDS model endpoints used for onclick enrichment\r\n * (Fragments, InformationUnits, …). Kept loose so any model-with-renditions\r\n * endpoint works without per-endpoint typing.\r\n */\r\nexport type EnrichmentResourceModel = {\r\n /**\r\n * Root short id of the resource. Used to build in-app navigation targets\r\n * (e.g. swapping the topic id segment) instead of rendering the payload.\r\n */\r\n shortId?: string | null;\r\n renditions?: RenditionModel[] | null;\r\n titles?: LiteralModel[] | null;\r\n labels?: LiteralModel[] | null;\r\n /**\r\n * Content access state. The request itself can succeed (HTTP 200, metadata\r\n * present) while the content is licence-restricted — IDS signals that here\r\n * rather than via an HTTP error.\r\n */\r\n authorization?: { state?: string | null } | null;\r\n};\r\n\r\nexport type EnrichmentResourceResult = {\r\n data: EnrichmentResourceModel | null;\r\n isDenied: boolean;\r\n};\r\n\r\n/**\r\n * Generic onclick enrichment loader: resolves `data-crex-endpoint` (slug) + the\r\n * `data-crex-id` resource IRI and fetches the JSON model (e.g. a Fragment or\r\n * InformationUnit) for rendering inside a popover/modal. Endpoint resolution is\r\n * fully dynamic via `slugToPath` — no per-endpoint hardcoding.\r\n */\r\nexport const loadEnrichmentResource = async (\r\n descriptor: HtmlEnrichmentDescriptor,\r\n queryParams?: Record<\r\n string,\r\n string | number | boolean | (string | number | boolean)[] | undefined\r\n >,\r\n): Promise<EnrichmentResourceResult> => {\r\n if (!descriptor.endpoint || !descriptor.resourceId) {\r\n return { data: null, isDenied: false };\r\n }\r\n\r\n const basePath = slugToPath(descriptor.endpoint);\r\n if (!basePath) {\r\n return { data: null, isDenied: false };\r\n }\r\n\r\n const url = `${basePath}/${encodeURIComponent(descriptor.resourceId)}`;\r\n\r\n try {\r\n const data = await baseClientRequest<EnrichmentResourceModel>(\r\n url,\r\n queryParams,\r\n );\r\n // Denied is carried in the (successful) payload, not as an HTTP error.\r\n const isDenied = data?.authorization?.state === \"denied\";\r\n return { data, isDenied };\r\n } catch (err: unknown) {\r\n if (isDeniedError(err)) {\r\n return { data: null, isDenied: true };\r\n }\r\n throw err;\r\n }\r\n};\r\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -890,7 +890,8 @@ var baseClientRequest = async (endpoint, queryParams, options, extra) => {
890
890
  url: endpoint,
891
891
  method: "GET",
892
892
  params: queryParams,
893
- responseType: extra?.responseType
893
+ responseType: extra?.responseType,
894
+ authMode: extra?.authMode
894
895
  }
895
896
  }),
896
897
  credentials: "include",
@@ -1637,7 +1638,6 @@ var fetchOrganizationProfileById = async (id, query, context) => {
1637
1638
  { id },
1638
1639
  resolveOrganizationProfileQuery(query)
1639
1640
  );
1640
- console.log("[OrganizationProfile] Raw VCARD response:", organization);
1641
1641
  return toOrganizationProfile(organization, {
1642
1642
  uiLanguage: context.uiLanguage,
1643
1643
  displayNameFallback: context.fallbackName,
@@ -3224,6 +3224,8 @@ var import_cache5 = require("next/cache");
3224
3224
  var import_requests6 = require("@c-rex/core/requests");
3225
3225
  var import_logger2 = require("@c-rex/core/logger");
3226
3226
  var import_constants2 = require("@c-rex/constants");
3227
+ var import_headers2 = require("next/headers");
3228
+ var import_sdk2 = require("@c-rex/core/sdk");
3227
3229
  var DEFAULT_SORT = ["-informationSubjects.id"];
3228
3230
  var DEFAULT_PAGE_SIZE = 100;
3229
3231
  var RENDITION_FETCH_TIMEOUT_MS = 5e3;
@@ -3361,9 +3363,23 @@ var getStaticPagesCached = (0, import_cache5.unstable_cache)(
3361
3363
  ]
3362
3364
  }
3363
3365
  );
3366
+ var getCurrentUserAuthToken = async () => {
3367
+ try {
3368
+ const config = new import_sdk2.CrexSDK().getServerConfig();
3369
+ if (config.OIDC.tokenSource !== import_constants2.TOKEN_SOURCES.USER_LOGIN) return void 0;
3370
+ const token = (await (0, import_headers2.cookies)()).get(import_constants2.CREX_TOKEN_HEADER_KEY)?.value.trim();
3371
+ return token || void 0;
3372
+ } catch {
3373
+ return void 0;
3374
+ }
3375
+ };
3364
3376
  var getStaticPages = async (query) => {
3365
3377
  try {
3366
- const pages = await getStaticPagesCached(query);
3378
+ const authToken = await getCurrentUserAuthToken();
3379
+ const pages = authToken ? await withServerRequestContext(
3380
+ { skipCookieTokenLookup: true, authToken },
3381
+ () => fetchStaticPages(query)
3382
+ ) : await getStaticPagesCached(query);
3367
3383
  return { status: "ok", pages };
3368
3384
  } catch (error) {
3369
3385
  const logger = new import_logger2.CrexLogger();