@c-rex/services 0.3.0-build.39 → 0.3.0-build.40

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 (33) hide show
  1. package/dist/index.d.mts +3 -1
  2. package/dist/index.d.ts +3 -1
  3. package/dist/index.js +430 -147
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +411 -143
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/metadata-visibility-policy.d.mts +29 -0
  8. package/dist/metadata-visibility-policy.d.ts +29 -0
  9. package/dist/metadata-visibility-policy.js +129 -0
  10. package/dist/metadata-visibility-policy.js.map +1 -0
  11. package/dist/metadata-visibility-policy.mjs +95 -0
  12. package/dist/metadata-visibility-policy.mjs.map +1 -0
  13. package/dist/read-models/index.d.mts +17 -39
  14. package/dist/read-models/index.d.ts +17 -39
  15. package/dist/read-models/index.js +430 -149
  16. package/dist/read-models/index.js.map +1 -1
  17. package/dist/read-models/index.mjs +410 -144
  18. package/dist/read-models/index.mjs.map +1 -1
  19. package/dist/read-models/metadata-display-builder.d.mts +34 -0
  20. package/dist/read-models/metadata-display-builder.d.ts +34 -0
  21. package/dist/read-models/metadata-display-builder.js +521 -0
  22. package/dist/read-models/metadata-display-builder.js.map +1 -0
  23. package/dist/read-models/metadata-display-builder.mjs +489 -0
  24. package/dist/read-models/metadata-display-builder.mjs.map +1 -0
  25. package/dist/read-models/metadata-presentation-config.js +2 -2
  26. package/dist/read-models/metadata-presentation-config.js.map +1 -1
  27. package/dist/read-models/metadata-presentation-config.mjs +2 -2
  28. package/dist/read-models/metadata-presentation-config.mjs.map +1 -1
  29. package/dist/read-models/metadata-view-profile.js +63 -4
  30. package/dist/read-models/metadata-view-profile.js.map +1 -1
  31. package/dist/read-models/metadata-view-profile.mjs +63 -4
  32. package/dist/read-models/metadata-view-profile.mjs.map +1 -1
  33. package/package.json +19 -1
package/dist/index.mjs CHANGED
@@ -1420,7 +1420,109 @@ import {
1420
1420
  CREX_READMODEL_CACHE_PARTIES_TAG,
1421
1421
  CREX_READMODEL_CACHE_TAG as CREX_READMODEL_CACHE_TAG2
1422
1422
  } from "@c-rex/core/requests";
1423
+ import { CrexLogger } from "@c-rex/core/logger";
1423
1424
  import { unstable_cache as unstable_cache2 } from "next/cache";
1425
+
1426
+ // src/read-models/metadata-visibility-policy.ts
1427
+ var CONTENT_LICENSE_CLASS_ID = "https://ids.c-crex.net/ns/iirds/ext#ContentLicense";
1428
+ var METADATA_VISIBILITY_POLICY = {
1429
+ parties: {
1430
+ cacheFamily: "parties"
1431
+ },
1432
+ components: {
1433
+ cacheFamily: "components"
1434
+ },
1435
+ informationSubjects: {
1436
+ cacheFamily: "informationSubjects"
1437
+ },
1438
+ topicTypes: {
1439
+ cacheFamily: "topicTypes"
1440
+ },
1441
+ documentTypes: {
1442
+ cacheFamily: "documentTypes"
1443
+ },
1444
+ contentLifeCycleStatus: {
1445
+ cacheFamily: "contentLifeCycleStatus"
1446
+ },
1447
+ applicableForTypes: {
1448
+ cacheFamily: "documentTypes"
1449
+ },
1450
+ planningTimes: {
1451
+ cacheFamily: "planningTimes"
1452
+ },
1453
+ functionalMetadata: {
1454
+ cacheFamily: "functionalMetadata"
1455
+ },
1456
+ productFeatures: {
1457
+ cacheFamily: "productFeatures"
1458
+ },
1459
+ productLifeCyclePhases: {
1460
+ cacheFamily: "productLifeCyclePhases"
1461
+ },
1462
+ productMetadata: {
1463
+ cacheFamily: "productMetadata"
1464
+ },
1465
+ productVariants: {
1466
+ cacheFamily: "productVariants"
1467
+ },
1468
+ qualifications: {
1469
+ cacheFamily: "qualifications",
1470
+ deniedClassIds: [CONTENT_LICENSE_CLASS_ID]
1471
+ },
1472
+ supplies: {
1473
+ cacheFamily: "supplies"
1474
+ }
1475
+ };
1476
+ var runtimeSuppressedFamilies = /* @__PURE__ */ new Map();
1477
+ var getMetadataCacheFamily = (property) => METADATA_VISIBILITY_POLICY[property]?.cacheFamily;
1478
+ var isMetadataGroupDenied = (property, options) => {
1479
+ const policy = METADATA_VISIBILITY_POLICY[property];
1480
+ const classId = options.classId?.trim();
1481
+ const partyRoleId = options.partyRoleId?.trim();
1482
+ if (classId && policy?.deniedClassIds?.includes(classId)) {
1483
+ return true;
1484
+ }
1485
+ if (partyRoleId && policy?.deniedPartyRoleIds?.includes(partyRoleId)) {
1486
+ return true;
1487
+ }
1488
+ return false;
1489
+ };
1490
+ var suppressMetadataCacheFamilyRuntime = (family, entry) => {
1491
+ runtimeSuppressedFamilies.set(family, {
1492
+ family,
1493
+ reason: "cache-overflow",
1494
+ bytes: entry.bytes,
1495
+ limitBytes: entry.limitBytes,
1496
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1497
+ });
1498
+ };
1499
+ var clearMetadataCacheFamilyRuntimeSuppression = (family) => {
1500
+ runtimeSuppressedFamilies.delete(family);
1501
+ };
1502
+ var isMetadataPropertyRuntimeSuppressed = (property) => {
1503
+ const family = getMetadataCacheFamily(property);
1504
+ return family ? runtimeSuppressedFamilies.has(family) : false;
1505
+ };
1506
+ var getRuntimeSuppressedMetadataFamilies = () => Array.from(runtimeSuppressedFamilies.values());
1507
+ var resetRuntimeSuppressedMetadataFamiliesForTests = () => {
1508
+ runtimeSuppressedFamilies.clear();
1509
+ };
1510
+
1511
+ // src/read-models/metadata-read-models.ts
1512
+ var DEFAULT_METADATA_CACHE_MAX_BYTES = 15e5;
1513
+ var resolveMetadataCacheMaxBytes = () => {
1514
+ const value = Number(process.env.CREX_CACHE_METADATA_MAX_BYTES);
1515
+ return Number.isFinite(value) && value > 0 ? value : DEFAULT_METADATA_CACHE_MAX_BYTES;
1516
+ };
1517
+ var measureSerializedBytes = (value) => Buffer.byteLength(JSON.stringify(value), "utf8");
1518
+ var logCacheOverflow = async (family, bytes, limitBytes, cacheKey) => {
1519
+ const logger = new CrexLogger();
1520
+ await logger.log({
1521
+ level: "warning",
1522
+ category: "Scenario",
1523
+ message: `[MetadataCacheOverflow] family=${family} bytes=${bytes} limitBytes=${limitBytes} cacheKey=${cacheKey}`
1524
+ });
1525
+ };
1424
1526
  var resolveLocalizedLabel = (labels, uiLanguage) => {
1425
1527
  if (!labels || labels.length === 0) return void 0;
1426
1528
  const normalizedUiLanguage = uiLanguage.trim().toLowerCase();
@@ -1479,12 +1581,39 @@ var toEntityRef = (entity, uiLanguage) => {
1479
1581
  const label = resolveLocalizedLabel(entity.labels || [], uiLanguage) || entity.shortId?.trim() || id;
1480
1582
  return { id, shortId: entity.shortId?.trim() || void 0, label };
1481
1583
  };
1482
- var toClassRef = (classRef, uiLanguage) => {
1584
+ var toClassRef = (classRef, uiLanguage, classLabelsById) => {
1483
1585
  const id = classRef?.id?.trim();
1484
1586
  if (!id) return void 0;
1485
- const label = resolveLocalizedLabel(classRef?.labels || [], uiLanguage) || classRef?.shortId?.trim() || id;
1587
+ const label = resolveLocalizedLabel(classRef?.labels || [], uiLanguage) || classLabelsById?.[id] || classRef?.shortId?.trim() || id;
1486
1588
  return { id, shortId: classRef?.shortId?.trim() || void 0, label };
1487
1589
  };
1590
+ var isNotFoundError = (error) => {
1591
+ if (!(error instanceof Error)) {
1592
+ return false;
1593
+ }
1594
+ return error.message.includes("HTTP error! status: 404");
1595
+ };
1596
+ var resolveMissingClassLabels = async (items, uiLanguage) => {
1597
+ const classIds = Array.from(new Set(
1598
+ items.map((item) => item.class?.id?.trim()).filter((id) => Boolean(id)).filter((id) => !resolveLocalizedLabel(items.find((item) => item.class?.id?.trim() === id)?.class?.labels || [], uiLanguage))
1599
+ ));
1600
+ const result = {};
1601
+ await Promise.all(classIds.map(async (classId) => {
1602
+ try {
1603
+ const entity = await withServerRequestContext(
1604
+ { skipCookieTokenLookup: true },
1605
+ () => domainEntitiesGetByIdServer({ id: classId }, { Fields: ["labels"] })
1606
+ );
1607
+ result[classId] = resolveLocalizedLabel(entity.labels || [], uiLanguage);
1608
+ } catch (error) {
1609
+ if (!isNotFoundError(error)) {
1610
+ throw error;
1611
+ }
1612
+ result[classId] = void 0;
1613
+ }
1614
+ }));
1615
+ return result;
1616
+ };
1488
1617
  var deriveParentIds = (entity) => {
1489
1618
  const directParents = (entity.parents || []).map((parent) => getRefId(parent)).filter((value) => Boolean(value));
1490
1619
  if (directParents.length > 0) {
@@ -1493,10 +1622,6 @@ var deriveParentIds = (entity) => {
1493
1622
  const ancestorParents = (entity.ancestors || []).map((path) => path[path.length - 1]).map((parent) => getRefId(parent)).filter((value) => Boolean(value));
1494
1623
  return Array.from(new Set(ancestorParents));
1495
1624
  };
1496
- var deriveAncestorIds = (entity) => {
1497
- const ids = (entity.ancestors || []).flatMap((path) => path).map((item) => getRefId(item)).filter((value) => Boolean(value));
1498
- return Array.from(new Set(ids));
1499
- };
1500
1625
  var buildTaxonomyTree = (items, options) => {
1501
1626
  const nodesById = {};
1502
1627
  const parentLinks = /* @__PURE__ */ new Map();
@@ -1505,48 +1630,13 @@ var buildTaxonomyTree = (items, options) => {
1505
1630
  if (!entityRef) continue;
1506
1631
  nodesById[entityRef.id] = {
1507
1632
  ...entityRef,
1508
- classRef: toClassRef(item.class, options.uiLanguage),
1509
- hasInformationUnits: item.hasInformationUnits === true || item.hasInformationUnitReferences === true,
1510
- parentIds: deriveParentIds(item),
1511
- ancestorIds: deriveAncestorIds(item),
1512
- children: []
1633
+ classRef: toClassRef(item.class, options.uiLanguage, options.classLabelsById),
1634
+ parentIds: deriveParentIds(item)
1513
1635
  };
1514
1636
  }
1515
1637
  Object.values(nodesById).forEach((node) => {
1516
1638
  parentLinks.set(node.id, node.parentIds.filter((id) => Boolean(nodesById[id])));
1517
1639
  });
1518
- if (options.includeOnlyUsed) {
1519
- const keepIds = new Set(
1520
- Object.values(nodesById).filter((node) => node.hasInformationUnits === true).map((node) => node.id)
1521
- );
1522
- let changed = true;
1523
- while (changed) {
1524
- changed = false;
1525
- Array.from(keepIds).forEach((nodeId) => {
1526
- const parentIds = parentLinks.get(nodeId) || [];
1527
- parentIds.forEach((parentId) => {
1528
- if (!keepIds.has(parentId)) {
1529
- keepIds.add(parentId);
1530
- changed = true;
1531
- }
1532
- });
1533
- });
1534
- }
1535
- Object.keys(nodesById).forEach((nodeId) => {
1536
- if (!keepIds.has(nodeId)) {
1537
- delete nodesById[nodeId];
1538
- parentLinks.delete(nodeId);
1539
- }
1540
- });
1541
- }
1542
- Object.values(nodesById).forEach((node) => {
1543
- const parents = parentLinks.get(node.id) || [];
1544
- parents.forEach((parentId) => {
1545
- if (nodesById[parentId]) {
1546
- nodesById[parentId].children.push(node);
1547
- }
1548
- });
1549
- });
1550
1640
  const roots = Object.values(nodesById).filter((node) => (parentLinks.get(node.id) || []).length === 0);
1551
1641
  if (!options.groupRootsByClass) {
1552
1642
  return {
@@ -1556,13 +1646,12 @@ var buildTaxonomyTree = (items, options) => {
1556
1646
  };
1557
1647
  }
1558
1648
  const groupedRoots = /* @__PURE__ */ new Map();
1649
+ const groupedRootCounts = /* @__PURE__ */ new Map();
1559
1650
  const ungroupedRootId = "class:ungrouped";
1560
1651
  groupedRoots.set(ungroupedRootId, {
1561
1652
  id: ungroupedRootId,
1562
1653
  label: "Ungrouped",
1563
1654
  parentIds: [],
1564
- ancestorIds: [],
1565
- children: [],
1566
1655
  isVirtualGroup: true
1567
1656
  });
1568
1657
  roots.forEach((root) => {
@@ -1573,17 +1662,12 @@ var buildTaxonomyTree = (items, options) => {
1573
1662
  shortId: root.classRef?.shortId,
1574
1663
  label: root.classRef?.label || "Ungrouped",
1575
1664
  parentIds: [],
1576
- ancestorIds: [],
1577
- children: [],
1578
1665
  isVirtualGroup: true
1579
1666
  });
1580
1667
  }
1581
- groupedRoots.get(classId)?.children.push(root);
1668
+ groupedRootCounts.set(classId, (groupedRootCounts.get(classId) || 0) + 1);
1582
1669
  });
1583
- const groupedRootNodes = Array.from(groupedRoots.values()).filter((group) => group.children.length > 0).map((group) => ({
1584
- ...group,
1585
- children: [...group.children].sort((a, b) => a.label.localeCompare(b.label))
1586
- })).sort((a, b) => a.label.localeCompare(b.label));
1670
+ const groupedRootNodes = Array.from(groupedRoots.values()).filter((group) => (groupedRootCounts.get(group.id) || 0) > 0).sort((a, b) => a.label.localeCompare(b.label));
1587
1671
  return {
1588
1672
  roots: groupedRootNodes,
1589
1673
  nodesById,
@@ -1596,15 +1680,29 @@ var defaultTreeOptions = (uiLanguage) => ({
1596
1680
  });
1597
1681
  var createTaxonomyResolver = (options) => {
1598
1682
  const cached = unstable_cache2(
1599
- async (_queryKey, query, uiLanguage, readOptions) => {
1683
+ async (_queryKey, query, uiLanguage) => {
1600
1684
  const items = await withServerRequestContext(
1601
1685
  { skipCookieTokenLookup: true },
1602
1686
  () => fetchAllPages(options.fetch, query)
1603
1687
  );
1604
- return buildTaxonomyTree(items, {
1688
+ const classLabelsById = await resolveMissingClassLabels(items, uiLanguage);
1689
+ const result = buildTaxonomyTree(items, {
1605
1690
  ...defaultTreeOptions(uiLanguage),
1606
- includeOnlyUsed: readOptions.includeOnlyUsed
1691
+ classLabelsById
1607
1692
  });
1693
+ const bytes = measureSerializedBytes(result);
1694
+ const limitBytes = resolveMetadataCacheMaxBytes();
1695
+ if (bytes > limitBytes) {
1696
+ suppressMetadataCacheFamilyRuntime(options.cacheFamily, { bytes, limitBytes });
1697
+ await logCacheOverflow(options.cacheFamily, bytes, limitBytes, options.cacheKey);
1698
+ return {
1699
+ roots: [],
1700
+ nodesById: {},
1701
+ generatedAt: result.generatedAt
1702
+ };
1703
+ }
1704
+ clearMetadataCacheFamilyRuntimeSuppression(options.cacheFamily);
1705
+ return result;
1608
1706
  },
1609
1707
  ["read-model", options.cacheKey],
1610
1708
  {
@@ -1617,101 +1715,116 @@ var createTaxonomyResolver = (options) => {
1617
1715
  ]
1618
1716
  }
1619
1717
  );
1620
- return async (query, readOptions) => {
1718
+ return async (query) => {
1621
1719
  const uiLanguage = resolveUiLanguage();
1622
1720
  const normalizedQuery = { ...options.defaultQuery, ...query || {} };
1623
- const normalizedReadOptions = {
1624
- includeOnlyUsed: readOptions?.includeOnlyUsed === true
1625
- };
1626
- const cacheKey = stableSerialize({ normalizedQuery, uiLanguage, normalizedReadOptions });
1627
- return cached(cacheKey, normalizedQuery, uiLanguage, normalizedReadOptions);
1721
+ const cacheKey = stableSerialize({ normalizedQuery, uiLanguage });
1722
+ return cached(cacheKey, normalizedQuery, uiLanguage);
1628
1723
  };
1629
1724
  };
1630
1725
  var defaultMetadataQuery = {
1631
1726
  PageNumber: 1,
1632
1727
  PageSize: READMODEL_PAGE_SIZE,
1633
- Fields: ["labels", "class", "hasInformationUnits"],
1728
+ Fields: ["labels", "class"],
1634
1729
  Sort: ["shortId"]
1635
1730
  };
1636
1731
  var getCategoriesCached = createTaxonomyResolver({
1637
1732
  cacheKey: "categories",
1733
+ cacheFamily: "informationSubjects",
1638
1734
  tags: [CREX_READMODEL_CACHE_INFORMATION_SUBJECTS_TAG],
1639
- defaultQuery: defaultMetadataQuery,
1735
+ defaultQuery: {
1736
+ ...defaultMetadataQuery,
1737
+ Fields: ["labels", "class", "parents", "ancestors", "ancestorsOrSelf"]
1738
+ },
1640
1739
  fetch: categoriesGetAllServer
1641
1740
  });
1642
1741
  var getInformationSubjectsLegacyCached = createTaxonomyResolver({
1643
1742
  cacheKey: "information-subjects-legacy",
1743
+ cacheFamily: "informationSubjects",
1644
1744
  tags: [CREX_READMODEL_CACHE_INFORMATION_SUBJECTS_TAG],
1645
1745
  defaultQuery: defaultMetadataQuery,
1646
1746
  fetch: informationSubjectsGetAllServer
1647
1747
  });
1648
1748
  var getDocumentTypesCached = createTaxonomyResolver({
1649
1749
  cacheKey: "document-types",
1750
+ cacheFamily: "documentTypes",
1650
1751
  tags: [CREX_READMODEL_CACHE_DOCUMENT_TYPES_TAG],
1651
1752
  defaultQuery: defaultMetadataQuery,
1652
1753
  fetch: documentTypesGetAllServer
1653
1754
  });
1654
1755
  var getComponentsCached = createTaxonomyResolver({
1655
1756
  cacheKey: "components",
1757
+ cacheFamily: "components",
1656
1758
  tags: [CREX_READMODEL_CACHE_COMPONENTS_TAG],
1657
1759
  defaultQuery: {
1658
1760
  ...defaultMetadataQuery,
1659
- Fields: ["labels", "class", "parents", "ancestors", "ancestorsOrSelf", "hasInformationUnits"]
1761
+ Fields: ["labels", "class", "parents", "ancestors", "ancestorsOrSelf"]
1660
1762
  },
1661
1763
  fetch: componentsGetAllServer
1662
1764
  });
1663
1765
  var getTopicTypesCached = createTaxonomyResolver({
1664
1766
  cacheKey: "topic-types",
1767
+ cacheFamily: "topicTypes",
1665
1768
  defaultQuery: defaultMetadataQuery,
1666
1769
  fetch: topicTypesGetAllServer
1667
1770
  });
1668
1771
  var getContentLifeCycleStatusCached = createTaxonomyResolver({
1669
1772
  cacheKey: "content-life-cycle-status",
1773
+ cacheFamily: "contentLifeCycleStatus",
1670
1774
  defaultQuery: defaultMetadataQuery,
1671
1775
  fetch: contentLifeCycleStatusGetAllServer
1672
1776
  });
1673
1777
  var getApplicableForTypesCached = createTaxonomyResolver({
1674
1778
  cacheKey: "applicable-for-types",
1779
+ cacheFamily: "documentTypes",
1675
1780
  defaultQuery: defaultMetadataQuery,
1676
1781
  fetch: informationTypesGetAllServer
1677
1782
  });
1678
1783
  var getPlanningTimesCached = createTaxonomyResolver({
1679
1784
  cacheKey: "planning-times",
1785
+ cacheFamily: "planningTimes",
1680
1786
  defaultQuery: defaultMetadataQuery,
1681
1787
  fetch: planningTimesGetAllServer
1682
1788
  });
1683
1789
  var getFunctionalMetadataCached = createTaxonomyResolver({
1684
1790
  cacheKey: "functional-metadata",
1791
+ cacheFamily: "functionalMetadata",
1685
1792
  defaultQuery: defaultMetadataQuery,
1686
1793
  fetch: functionalMetadatasGetAllServer
1687
1794
  });
1688
1795
  var getProductFeaturesCached = createTaxonomyResolver({
1689
1796
  cacheKey: "product-features",
1797
+ cacheFamily: "productFeatures",
1690
1798
  defaultQuery: defaultMetadataQuery,
1691
1799
  fetch: productFeaturesGetAllServer
1692
1800
  });
1693
1801
  var getProductLifeCyclePhasesCached = createTaxonomyResolver({
1694
1802
  cacheKey: "product-life-cycle-phases",
1803
+ cacheFamily: "productLifeCyclePhases",
1695
1804
  defaultQuery: defaultMetadataQuery,
1696
1805
  fetch: productLifeCyclePhasesGetAllServer
1697
1806
  });
1698
1807
  var getProductMetadataCached = createTaxonomyResolver({
1699
1808
  cacheKey: "product-metadata",
1809
+ cacheFamily: "productMetadata",
1700
1810
  defaultQuery: defaultMetadataQuery,
1701
1811
  fetch: productMetadataGetAllServer
1702
1812
  });
1703
1813
  var getProductVariantsCached = createTaxonomyResolver({
1704
1814
  cacheKey: "product-variants",
1815
+ cacheFamily: "productVariants",
1705
1816
  defaultQuery: defaultMetadataQuery,
1706
1817
  fetch: productVariantsGetAllServer
1707
1818
  });
1708
1819
  var getQualificationsCached = createTaxonomyResolver({
1709
1820
  cacheKey: "qualifications",
1821
+ cacheFamily: "qualifications",
1710
1822
  defaultQuery: defaultMetadataQuery,
1711
1823
  fetch: qualificationsGetAllServer
1712
1824
  });
1713
1825
  var getSuppliesCached = createTaxonomyResolver({
1714
1826
  cacheKey: "supplies",
1827
+ cacheFamily: "supplies",
1715
1828
  defaultQuery: defaultMetadataQuery,
1716
1829
  fetch: suppliesGetAllServer
1717
1830
  });
@@ -1724,6 +1837,18 @@ var toRoleRef = (party, uiLanguage) => {
1724
1837
  label: resolveLocalizedLabel(role?.labels || [], uiLanguage) || role?.shortId?.trim() || "Unknown role"
1725
1838
  };
1726
1839
  };
1840
+ var mergeRoleRef = (current, candidate) => {
1841
+ if (current.id !== candidate.id) {
1842
+ return current;
1843
+ }
1844
+ const preferCurrentLabel = current.label.trim().length > 0 && current.label !== "Unknown role";
1845
+ const preferCurrentShortId = Boolean(current.shortId?.trim());
1846
+ return {
1847
+ id: current.id,
1848
+ shortId: preferCurrentShortId ? current.shortId : candidate.shortId,
1849
+ label: preferCurrentLabel ? current.label : candidate.label
1850
+ };
1851
+ };
1727
1852
  var getPartiesCached = unstable_cache2(
1728
1853
  async (_queryKey, query, uiLanguage) => {
1729
1854
  const items = await withServerRequestContext(
@@ -1735,15 +1860,23 @@ var getPartiesCached = unstable_cache2(
1735
1860
  const id = party.id?.trim();
1736
1861
  if (!id) return;
1737
1862
  const role = toRoleRef(party, uiLanguage);
1738
- if (!groups.has(role.id)) {
1863
+ const existingGroup = groups.get(role.id);
1864
+ if (!existingGroup) {
1739
1865
  groups.set(role.id, { role, parties: [] });
1866
+ } else {
1867
+ const mergedRole = mergeRoleRef(existingGroup.role, role);
1868
+ existingGroup.role = mergedRole;
1869
+ existingGroup.parties.forEach((groupedParty) => {
1870
+ groupedParty.role = mergedRole;
1871
+ });
1740
1872
  }
1741
1873
  const label = resolveLocalizedLabel(party.labels || [], uiLanguage) || party.shortId?.trim() || id;
1874
+ const resolvedRole = groups.get(role.id)?.role || role;
1742
1875
  groups.get(role.id)?.parties.push({
1743
1876
  id,
1744
1877
  shortId: party.shortId?.trim() || void 0,
1745
1878
  label,
1746
- role,
1879
+ role: resolvedRole,
1747
1880
  vcardId: party.vcard?.id?.trim() || void 0
1748
1881
  });
1749
1882
  });
@@ -1761,17 +1894,19 @@ var getPartiesCached = unstable_cache2(
1761
1894
  tags: [CREX_API_CACHE_TAG2, CREX_READMODEL_CACHE_TAG2, CREX_READMODEL_CACHE_METADATA_TAG, CREX_READMODEL_CACHE_PARTIES_TAG]
1762
1895
  }
1763
1896
  );
1764
- var getInformationSubjects = async (options) => {
1765
- const readOptions = { includeOnlyUsed: options?.includeOnlyUsed === true };
1897
+ var getInformationSubjects = async () => {
1766
1898
  try {
1767
- const categories = await getCategoriesCached(void 0, readOptions);
1899
+ const categories = await getCategoriesCached();
1900
+ if (isMetadataPropertyRuntimeSuppressed("informationSubjects")) {
1901
+ return categories;
1902
+ }
1768
1903
  if (categories.roots.length > 0) {
1769
1904
  return categories;
1770
1905
  }
1771
1906
  } catch (error) {
1772
1907
  console.warn("[MetadataReadModels] Categories endpoint unavailable, falling back to InformationSubjects.", error);
1773
1908
  }
1774
- return getInformationSubjectsLegacyCached(void 0, readOptions);
1909
+ return getInformationSubjectsLegacyCached();
1775
1910
  };
1776
1911
  var getDocumentTypes = async () => {
1777
1912
  return getDocumentTypesCached();
@@ -1785,8 +1920,7 @@ var getParties = async () => {
1785
1920
  PageNumber: 1,
1786
1921
  PageSize: READMODEL_PAGE_SIZE,
1787
1922
  Fields: ["labels", "partyRole", "vcard", "class"],
1788
- Sort: ["shortId"],
1789
- Embed: ["vcard"]
1923
+ Sort: ["shortId"]
1790
1924
  };
1791
1925
  return getPartiesCached(stableSerialize({ query, uiLanguage }), query, uiLanguage);
1792
1926
  };
@@ -1985,8 +2119,8 @@ var INFORMATION_UNIT_PROPERTY_PRESENTATION = {
1985
2119
  },
1986
2120
  applicableForTypes: {
1987
2121
  valueKind: "objectRefArray",
1988
- facet: { supported: true, sectionStrategy: "class" },
1989
- metadataDisplay: { supported: true, sectionStrategy: "class" }
2122
+ facet: { supported: true, sectionStrategy: "none" },
2123
+ metadataDisplay: { supported: true, sectionStrategy: "none" }
1990
2124
  },
1991
2125
  planningTimes: {
1992
2126
  valueKind: "objectRefArray",
@@ -2043,23 +2177,70 @@ var INFORMATION_UNIT_PROPERTY_PRESENTATION = {
2043
2177
  // src/read-models/metadata-presentation.ts
2044
2178
  import { unstable_cache as unstable_cache3 } from "next/cache";
2045
2179
  import { CREX_API_CACHE_TAG as CREX_API_CACHE_TAG3, CREX_READMODEL_CACHE_METADATA_TAG as CREX_READMODEL_CACHE_METADATA_TAG2, CREX_READMODEL_CACHE_TAG as CREX_READMODEL_CACHE_TAG3 } from "@c-rex/core/requests";
2046
- var addTaxonomyNodeOverride = (node, map, sectionLabel) => {
2047
- if (!node.shortId) return;
2048
- map[node.shortId] = {
2180
+ var UNKNOWN_SECTION_LABELS = {
2181
+ en: {
2182
+ components: "Unknown component",
2183
+ informationSubjects: "Unknown information subject",
2184
+ topicTypes: "Unknown topic type",
2185
+ documentTypes: "Unknown document type",
2186
+ contentLifeCycleStatus: "Unknown content life cycle status",
2187
+ applicableForTypes: "Unknown document type",
2188
+ planningTimes: "Unknown planning time",
2189
+ functionalMetadata: "Unknown functional metadata",
2190
+ productFeatures: "Unknown product feature",
2191
+ productLifeCyclePhases: "Unknown product life cycle phase",
2192
+ productMetadata: "Unknown product metadata",
2193
+ productVariants: "Unknown product variant",
2194
+ qualifications: "Unknown qualification",
2195
+ supplies: "Unknown supply"
2196
+ },
2197
+ de: {
2198
+ components: "Unbekannte Komponente",
2199
+ informationSubjects: "Unbekanntes Informationsthema",
2200
+ topicTypes: "Unbekannter Topictyp",
2201
+ documentTypes: "Unbekannte Dokumentart",
2202
+ contentLifeCycleStatus: "Unbekannter Inhaltslebenszyklusstatus",
2203
+ applicableForTypes: "Unbekannte Dokumentart",
2204
+ planningTimes: "Unbekannte Planungszeit",
2205
+ functionalMetadata: "Unbekannte funktionale Metadaten",
2206
+ productFeatures: "Unbekanntes Produktmerkmal",
2207
+ productLifeCyclePhases: "Unbekannte Produktlebenszyklusphase",
2208
+ productMetadata: "Unbekannte Produktmetadaten",
2209
+ productVariants: "Unbekannte Produktvariante",
2210
+ qualifications: "Unbekannte Qualifikation",
2211
+ supplies: "Unbekannter Lieferumfang"
2212
+ }
2213
+ };
2214
+ var resolveUnknownSectionLabel = (property, uiLanguage) => {
2215
+ const normalizedLanguage = uiLanguage.toLowerCase();
2216
+ const baseLanguage = normalizedLanguage.split("-")[0];
2217
+ return UNKNOWN_SECTION_LABELS[baseLanguage]?.[property] || UNKNOWN_SECTION_LABELS.en[property] || "Unknown metadata";
2218
+ };
2219
+ var addTaxonomyNodePresentation = (node, map, sectionLabel, options) => {
2220
+ const presentation = {
2049
2221
  label: node.label,
2050
- sectionLabel
2222
+ sectionLabel,
2223
+ hidden: options?.hidden
2051
2224
  };
2225
+ if (node.shortId) {
2226
+ map[node.shortId] = presentation;
2227
+ }
2228
+ if (node.id) {
2229
+ map[node.id] = presentation;
2230
+ }
2052
2231
  };
2053
- var createClassSectionOverrides = (nodes, options) => {
2054
- const overrides = {};
2232
+ var createClassSectionPresentation = (property, nodes, uiLanguage, options) => {
2233
+ const presentation = {};
2234
+ const unknownSectionLabel = resolveUnknownSectionLabel(property, uiLanguage);
2055
2235
  Object.values(nodes).forEach((node) => {
2056
2236
  const classId = node.classRef?.id?.trim();
2057
- const sectionLabel = node.classRef?.label || (classId ? options?.classLabelsById?.[classId] : void 0);
2058
- addTaxonomyNodeOverride(node, overrides, sectionLabel);
2237
+ const hidden = isMetadataGroupDenied(property, { classId });
2238
+ const sectionLabel = node.classRef?.label || (classId ? options?.classLabelsById?.[classId] : void 0) || unknownSectionLabel;
2239
+ addTaxonomyNodePresentation(node, presentation, sectionLabel, { hidden });
2059
2240
  });
2060
- return overrides;
2241
+ return presentation;
2061
2242
  };
2062
- var resolveMissingClassLabels = async (nodes, uiLanguage) => {
2243
+ var resolveMissingClassLabels2 = async (nodes, uiLanguage) => {
2063
2244
  const classIds = Array.from(
2064
2245
  new Set(
2065
2246
  Object.values(nodes).map((node) => node.classRef?.id?.trim()).filter((value) => Boolean(value))
@@ -2097,7 +2278,7 @@ var resolveLocalizedLabel2 = (labels, uiLanguage) => {
2097
2278
  if (english) return english;
2098
2279
  return normalized[0]?.value;
2099
2280
  };
2100
- var isNotFoundError = (error) => {
2281
+ var isNotFoundError2 = (error) => {
2101
2282
  if (!(error instanceof Error)) {
2102
2283
  return false;
2103
2284
  }
@@ -2114,7 +2295,7 @@ var getDomainEntitySectionLabelUncached = async (id, uiLanguage) => {
2114
2295
  );
2115
2296
  return resolveLocalizedLabel2(entity.labels || [], uiLanguage);
2116
2297
  } catch (error) {
2117
- if (isNotFoundError(error)) {
2298
+ if (isNotFoundError2(error)) {
2118
2299
  return void 0;
2119
2300
  }
2120
2301
  throw error;
@@ -2139,7 +2320,7 @@ var getDomainEntitySectionLabel = async (id, uiLanguage) => {
2139
2320
  inFlightDomainEntityLabelRequests.set(inFlightKey, promise);
2140
2321
  return promise;
2141
2322
  };
2142
- var getMetadataFacetLabelOverrides = async (properties, uiLanguage = "en-US", options) => {
2323
+ var getMetadataFacetPresentation = async (properties, uiLanguage = "en-US", options) => {
2143
2324
  const defaultProperties = Object.keys(INFORMATION_UNIT_PROPERTY_PRESENTATION).filter((property) => INFORMATION_UNIT_PROPERTY_PRESENTATION[property].facet.supported);
2144
2325
  const propertySet = new Set(properties && properties.length > 0 ? properties : defaultProperties);
2145
2326
  const result = {};
@@ -2147,7 +2328,10 @@ var getMetadataFacetLabelOverrides = async (properties, uiLanguage = "en-US", op
2147
2328
  components: getComponents,
2148
2329
  informationSubjects: getInformationSubjects,
2149
2330
  documentTypes: getDocumentTypes,
2150
- applicableForTypes: getApplicableForTypes,
2331
+ // API facets for `applicableForTypes` contain document type refs.
2332
+ // Resolve them through DocumentTypes so section labels come from the
2333
+ // referenced object class instead of the predicate label.
2334
+ applicableForTypes: getDocumentTypes,
2151
2335
  planningTimes: getPlanningTimes,
2152
2336
  functionalMetadata: getFunctionalMetadata,
2153
2337
  contentLifeCycleStatus: getContentLifeCycleStatus,
@@ -2165,43 +2349,56 @@ var getMetadataFacetLabelOverrides = async (properties, uiLanguage = "en-US", op
2165
2349
  const config = INFORMATION_UNIT_PROPERTY_PRESENTATION[property];
2166
2350
  if (!config?.facet.supported) continue;
2167
2351
  if (config.facet.sectionStrategy === "partyRole" && property === "parties") {
2352
+ if (isMetadataPropertyRuntimeSuppressed("parties")) {
2353
+ continue;
2354
+ }
2168
2355
  partiesTask = (async () => {
2169
2356
  const parties = await getParties();
2170
- const partyOverrides = {};
2357
+ const partyPresentation = {};
2171
2358
  parties.groups.forEach((group) => {
2359
+ const hidden = isMetadataGroupDenied("parties", { partyRoleId: group.role.id });
2172
2360
  const sectionLabel = group.role.label;
2173
2361
  group.parties.forEach((party) => {
2174
2362
  if (!party.shortId) return;
2175
- partyOverrides[party.shortId] = {
2363
+ partyPresentation[party.shortId] = {
2176
2364
  label: party.label,
2177
- sectionLabel
2365
+ sectionLabel,
2366
+ hidden
2178
2367
  };
2368
+ if (party.id) {
2369
+ partyPresentation[party.id] = {
2370
+ label: party.label,
2371
+ sectionLabel,
2372
+ hidden
2373
+ };
2374
+ }
2179
2375
  });
2180
2376
  });
2181
- result.parties = partyOverrides;
2377
+ result.parties = partyPresentation;
2182
2378
  })();
2183
2379
  continue;
2184
2380
  }
2185
2381
  if (config.facet.sectionStrategy === "class") {
2382
+ if (isMetadataPropertyRuntimeSuppressed(property)) {
2383
+ continue;
2384
+ }
2186
2385
  const resolver = classResolvers[property];
2187
2386
  if (!resolver) continue;
2188
2387
  classTasks.push(
2189
2388
  (async () => {
2190
- const taxonomy = property === "informationSubjects" && options?.includeOnlyUsedByProperty?.informationSubjects === true ? await getInformationSubjects({ includeOnlyUsed: true }) : await resolver();
2389
+ const taxonomy = await resolver();
2191
2390
  if (property === "topicTypes" || property === "contentLifeCycleStatus") {
2192
2391
  const domainEntityId = property === "topicTypes" ? TOPIC_TYPE_DOMAIN_ENTITY_ID : CONTENT_LIFECYCLE_STATUS_DOMAIN_ENTITY_ID;
2193
- const sectionLabel = await getDomainEntitySectionLabel(domainEntityId, uiLanguage);
2194
- const overrides = createClassSectionOverrides(taxonomy.nodesById);
2195
- if (sectionLabel) {
2196
- Object.values(overrides).forEach((entry) => {
2197
- entry.sectionLabel = sectionLabel;
2198
- });
2199
- }
2200
- result[property] = overrides;
2392
+ const sectionLabel = await getDomainEntitySectionLabel(domainEntityId, uiLanguage) || resolveUnknownSectionLabel(property, uiLanguage);
2393
+ const presentation = createClassSectionPresentation(property, taxonomy.nodesById, uiLanguage);
2394
+ Object.values(presentation).forEach((entry) => {
2395
+ entry.sectionLabel = sectionLabel;
2396
+ });
2397
+ result[property] = presentation;
2201
2398
  return;
2202
2399
  }
2203
- const classLabelsById = await resolveMissingClassLabels(taxonomy.nodesById, uiLanguage);
2204
- result[property] = createClassSectionOverrides(taxonomy.nodesById, { classLabelsById });
2400
+ const classLabelsById = await resolveMissingClassLabels2(taxonomy.nodesById, uiLanguage);
2401
+ result[property] = createClassSectionPresentation(property, taxonomy.nodesById, uiLanguage, { classLabelsById });
2205
2402
  })()
2206
2403
  );
2207
2404
  }
@@ -2209,9 +2406,50 @@ var getMetadataFacetLabelOverrides = async (properties, uiLanguage = "en-US", op
2209
2406
  await Promise.all([...classTasks, ...partiesTask ? [partiesTask] : []]);
2210
2407
  return result;
2211
2408
  };
2409
+ var getMetadataFacetLabelOverrides = getMetadataFacetPresentation;
2212
2410
 
2213
- // src/read-models/metadata-display.ts
2214
- import { getFileRenditionGroups, resolvePreferredLanguage, sortAndDeduplicateLanguages } from "@c-rex/utils";
2411
+ // src/read-models/metadata-display-builder.ts
2412
+ var DEFAULT_IGNORED_FORMATS = [
2413
+ "application/xhtml+xml",
2414
+ "application/json",
2415
+ "application/llm+xml",
2416
+ "text/html"
2417
+ ];
2418
+ var normalizeLanguageCode = (value) => {
2419
+ const trimmed = value.trim();
2420
+ if (!trimmed) return trimmed;
2421
+ const [rawLang, rawCountry, ...rest] = trimmed.split("-");
2422
+ const lang = (rawLang || "").toLowerCase();
2423
+ if (!rawCountry) return lang;
2424
+ const country = rawCountry.toUpperCase();
2425
+ const suffix = rest.length > 0 ? `-${rest.join("-")}` : "";
2426
+ return `${lang}-${country}${suffix}`;
2427
+ };
2428
+ var sortAndDeduplicateLanguages = (languages) => {
2429
+ const normalized = languages.map((item) => (item || "").trim()).filter((item) => item.length > 0).map((item) => normalizeLanguageCode(item));
2430
+ return Array.from(new Set(normalized)).sort((a, b) => a.localeCompare(b));
2431
+ };
2432
+ var resolvePreferredLanguage = (languages, uiLanguage) => {
2433
+ if (!languages || languages.length === 0) return void 0;
2434
+ const normalizedUiLanguage = normalizeLanguageCode(uiLanguage);
2435
+ const baseLanguage = normalizedUiLanguage.split("-")[0];
2436
+ const normalized = sortAndDeduplicateLanguages(languages);
2437
+ const exact = normalized.find((item) => item === normalizedUiLanguage);
2438
+ if (exact) return exact;
2439
+ const base = normalized.find((item) => item === baseLanguage || item.startsWith(`${baseLanguage}-`));
2440
+ if (base) return base;
2441
+ const english = normalized.find((item) => item === "en" || item === "en-US" || item.startsWith("en-"));
2442
+ if (english) return english;
2443
+ return normalized[0];
2444
+ };
2445
+ var getFirstLinkByRel = (item, rel) => item.links?.find((link) => link.rel === rel)?.href;
2446
+ var hasRenderableFileRenditions = (renditions) => {
2447
+ if (!renditions || renditions.length === 0) return false;
2448
+ return renditions.some((item) => {
2449
+ if (!item.format || DEFAULT_IGNORED_FORMATS.includes(item.format)) return false;
2450
+ return Boolean(getFirstLinkByRel(item, "download") || getFirstLinkByRel(item, "view"));
2451
+ });
2452
+ };
2215
2453
  var resolveLiteralLabel = (labels, uiLanguage) => {
2216
2454
  if (!labels || labels.length === 0) return void 0;
2217
2455
  const language = uiLanguage || "en";
@@ -2228,6 +2466,9 @@ var resolveLiteralLabel = (labels, uiLanguage) => {
2228
2466
  var resolveObjectRefLabel = (item, uiLanguage) => {
2229
2467
  return resolveLiteralLabel(item.labels || [], uiLanguage) || item.shortId || item.id || void 0;
2230
2468
  };
2469
+ var resolveObjectRefClassLabel = (item, uiLanguage) => {
2470
+ return resolveLiteralLabel(item.class?.labels || [], uiLanguage) || item.class?.shortId || item.class?.id || void 0;
2471
+ };
2231
2472
  var asObjectRefArray = (value) => {
2232
2473
  if (!Array.isArray(value)) return [];
2233
2474
  return value.filter((item) => Boolean(item && typeof item === "object"));
@@ -2236,29 +2477,27 @@ var asLiteralArray = (value) => {
2236
2477
  if (!Array.isArray(value)) return [];
2237
2478
  return value.filter((item) => Boolean(item && typeof item === "object"));
2238
2479
  };
2239
- var shouldUseOverrides = (strategy) => {
2480
+ var shouldUseFacetPresentation = (strategy) => {
2240
2481
  return strategy === "class" || strategy === "partyRole";
2241
2482
  };
2242
- var getMetadataDisplayRows = async (data, options) => {
2243
- const includeProperties = options.includeProperties || Object.keys(INFORMATION_UNIT_PROPERTY_PRESENTATION);
2244
- const uiLanguage = options.uiLanguage;
2245
- const preferredTitle = resolveLiteralLabel(data.titles || [], uiLanguage) || resolveLiteralLabel(data.labels || [], uiLanguage);
2246
- const objectRefProperties = includeProperties.filter((key) => {
2483
+ var resolveFacetPresentationProperties = (includeProperties) => {
2484
+ return includeProperties.filter((key) => {
2247
2485
  const config = INFORMATION_UNIT_PROPERTY_PRESENTATION[key];
2248
2486
  if (!config?.metadataDisplay?.supported) return false;
2249
- return config.valueKind === "objectRefArray" || config.valueKind === "objectRef";
2487
+ if (config.valueKind !== "objectRefArray" && config.valueKind !== "objectRef") return false;
2488
+ return shouldUseFacetPresentation(config.metadataDisplay.sectionStrategy);
2250
2489
  });
2251
- const overrideProperties = objectRefProperties.filter(
2252
- (key) => shouldUseOverrides(INFORMATION_UNIT_PROPERTY_PRESENTATION[key].metadataDisplay.sectionStrategy)
2253
- );
2254
- const labelOverrides = options.overrides || (overrideProperties.length > 0 ? await getMetadataFacetLabelOverrides(overrideProperties, uiLanguage) : {});
2490
+ };
2491
+ var resolveOverrideProperties = resolveFacetPresentationProperties;
2492
+ var buildMetadataDisplayRows = (data, options) => {
2493
+ const uiLanguage = options.uiLanguage;
2494
+ const preferredTitle = resolveLiteralLabel(data.titles || [], uiLanguage) || resolveLiteralLabel(data.labels || [], uiLanguage);
2255
2495
  const rows = [];
2256
- for (const key of includeProperties) {
2496
+ for (const key of options.includeProperties) {
2257
2497
  const config = INFORMATION_UNIT_PROPERTY_PRESENTATION[key];
2258
2498
  if (!config?.metadataDisplay?.supported) continue;
2259
- if (key === "labels") {
2260
- continue;
2261
- }
2499
+ if (isMetadataPropertyRuntimeSuppressed(key)) continue;
2500
+ if (key === "labels") continue;
2262
2501
  if (key === "titles") {
2263
2502
  if (preferredTitle) {
2264
2503
  rows.push({
@@ -2297,7 +2536,7 @@ var getMetadataDisplayRows = async (data, options) => {
2297
2536
  continue;
2298
2537
  }
2299
2538
  if (config.valueKind === "stringArray") {
2300
- const values = Array.from(new Set(value.map((item) => String(item)).filter((item) => item.length > 0)));
2539
+ const values = Array.from(new Set(value.map((item) => String(item)).filter(Boolean)));
2301
2540
  if (values.length > 0) {
2302
2541
  rows.push({
2303
2542
  key,
@@ -2310,8 +2549,7 @@ var getMetadataDisplayRows = async (data, options) => {
2310
2549
  continue;
2311
2550
  }
2312
2551
  if (config.valueKind === "literalArray") {
2313
- const literals = asLiteralArray(value);
2314
- const preferred = resolveLiteralLabel(literals, uiLanguage);
2552
+ const preferred = resolveLiteralLabel(asLiteralArray(value), uiLanguage);
2315
2553
  if (preferred) {
2316
2554
  rows.push({
2317
2555
  key,
@@ -2324,8 +2562,7 @@ var getMetadataDisplayRows = async (data, options) => {
2324
2562
  continue;
2325
2563
  }
2326
2564
  if (config.valueKind === "objectRef") {
2327
- const ref = value;
2328
- const label = resolveObjectRefLabel(ref, uiLanguage);
2565
+ const label = resolveObjectRefLabel(value, uiLanguage);
2329
2566
  if (label) {
2330
2567
  rows.push({
2331
2568
  key,
@@ -2340,26 +2577,28 @@ var getMetadataDisplayRows = async (data, options) => {
2340
2577
  if (config.valueKind === "objectRefArray") {
2341
2578
  const refs = asObjectRefArray(value);
2342
2579
  if (refs.length === 0) continue;
2343
- const grouped = /* @__PURE__ */ new Map();
2344
- const strategy = config.metadataDisplay.sectionStrategy;
2580
+ const groupedValues = /* @__PURE__ */ new Map();
2345
2581
  refs.forEach((ref) => {
2346
2582
  const shortId = ref.shortId || "";
2347
- const override = shortId ? labelOverrides[key]?.[shortId] : void 0;
2348
- const label = override?.label || resolveObjectRefLabel(ref, uiLanguage);
2349
- if (!label) return;
2350
- const section = strategy === "none" ? key : override?.sectionLabel || key;
2351
- if (!grouped.has(section)) grouped.set(section, []);
2352
- grouped.get(section)?.push(label);
2583
+ const presentationMap = (options.presentation || options.overrides)?.[key];
2584
+ const presentationItem = (shortId ? presentationMap?.[shortId] : void 0) || (ref.id ? presentationMap?.[ref.id] : void 0);
2585
+ if (presentationItem?.hidden) return;
2586
+ if (isMetadataGroupDenied(key, { classId: ref.class?.id })) return;
2587
+ const valueLabel = presentationItem?.label || resolveObjectRefLabel(ref, uiLanguage);
2588
+ if (!valueLabel) return;
2589
+ const sectionLabel = config.metadataDisplay.sectionStrategy === "none" ? key : presentationItem?.sectionLabel || resolveObjectRefClassLabel(ref, uiLanguage) || key;
2590
+ const existing = groupedValues.get(sectionLabel) || /* @__PURE__ */ new Set();
2591
+ existing.add(valueLabel);
2592
+ groupedValues.set(sectionLabel, existing);
2353
2593
  });
2354
- Array.from(grouped.entries()).sort((a, b) => a[0].localeCompare(b[0])).forEach(([section, values]) => {
2355
- const uniqueValues = Array.from(new Set(values)).sort((a, b) => a.localeCompare(b));
2356
- if (uniqueValues.length === 0) return;
2357
- const isDirect = section !== key;
2594
+ Array.from(groupedValues.entries()).sort(([left], [right]) => left.localeCompare(right)).forEach(([sectionLabel, valueSet]) => {
2595
+ const values = Array.from(valueSet).sort((a, b) => a.localeCompare(b));
2596
+ if (values.length === 0) return;
2358
2597
  rows.push({
2359
2598
  key,
2360
- label: section,
2361
- labelSource: isDirect ? "direct" : "translationKey",
2362
- values: uniqueValues,
2599
+ label: sectionLabel,
2600
+ labelSource: sectionLabel === key ? "translationKey" : "direct",
2601
+ values,
2363
2602
  valueType: "text"
2364
2603
  });
2365
2604
  });
@@ -2368,7 +2607,7 @@ var getMetadataDisplayRows = async (data, options) => {
2368
2607
  if (config.valueKind === "renditionArray") {
2369
2608
  const renditions = Array.isArray(value) ? value.filter((item) => Boolean(item && typeof item === "object")) : [];
2370
2609
  if (renditions.length === 0) continue;
2371
- if (getFileRenditionGroups({ renditions }).length === 0) continue;
2610
+ if (!hasRenderableFileRenditions(renditions)) continue;
2372
2611
  rows.push({
2373
2612
  key,
2374
2613
  label: "files",
@@ -2382,6 +2621,18 @@ var getMetadataDisplayRows = async (data, options) => {
2382
2621
  return rows;
2383
2622
  };
2384
2623
 
2624
+ // src/read-models/metadata-display.ts
2625
+ var getMetadataDisplayRows = async (data, options) => {
2626
+ const includeProperties = options.includeProperties || Object.keys(INFORMATION_UNIT_PROPERTY_PRESENTATION);
2627
+ const presentationProperties = resolveFacetPresentationProperties(includeProperties);
2628
+ const facetPresentation = options.presentation || options.overrides || (presentationProperties.length > 0 ? await getMetadataFacetPresentation(presentationProperties, options.uiLanguage) : {});
2629
+ return buildMetadataDisplayRows(data, {
2630
+ uiLanguage: options.uiLanguage,
2631
+ includeProperties,
2632
+ presentation: facetPresentation
2633
+ });
2634
+ };
2635
+
2385
2636
  // src/read-models/version-availability.ts
2386
2637
  import { CREX_API_CACHE_TAG as CREX_API_CACHE_TAG4, CREX_READMODEL_CACHE_METADATA_TAG as CREX_READMODEL_CACHE_METADATA_TAG3, CREX_READMODEL_CACHE_TAG as CREX_READMODEL_CACHE_TAG4 } from "@c-rex/core/requests";
2387
2638
  import { resolvePreferredLanguage as resolvePreferredLanguage2 } from "@c-rex/utils";
@@ -2515,7 +2766,9 @@ var resolveFacetTags = (options) => {
2515
2766
  toInformationUnitPropertyKeys(options?.includeProperties),
2516
2767
  toInformationUnitPropertyKeys(options?.excludeProperties)
2517
2768
  );
2518
- const tagSet = new Set(runtimeScoped);
2769
+ const tagSet = new Set(
2770
+ runtimeScoped.filter((property) => !isMetadataPropertyRuntimeSuppressed(property))
2771
+ );
2519
2772
  (facetConfig?.extraTags || []).forEach((tag) => tagSet.add(tag));
2520
2773
  (options?.extraTags || []).forEach((tag) => tagSet.add(tag));
2521
2774
  return Array.from(tagSet);
@@ -2533,7 +2786,7 @@ var resolveMetadataDisplayProperties = (options) => {
2533
2786
  profileScoped,
2534
2787
  toInformationUnitPropertyKeys(options?.includeProperties),
2535
2788
  toInformationUnitPropertyKeys(options?.excludeProperties)
2536
- );
2789
+ ).filter((property) => !isMetadataPropertyRuntimeSuppressed(property));
2537
2790
  };
2538
2791
 
2539
2792
  // src/index.ts
@@ -2607,6 +2860,7 @@ export {
2607
2860
  InformationUnitsService,
2608
2861
  LanguageService,
2609
2862
  METADATA_VIEW_PROFILES,
2863
+ METADATA_VISIBILITY_POLICY,
2610
2864
  READMODEL_CACHE_POLICY,
2611
2865
  RenditionsService,
2612
2866
  TopicsService,
@@ -2618,11 +2872,13 @@ export {
2618
2872
  afterUsesGetAllServer,
2619
2873
  afterUsesGetByIdClientService,
2620
2874
  afterUsesGetByIdServer,
2875
+ buildMetadataDisplayRows,
2621
2876
  buildTelephoneEntries,
2622
2877
  categoriesGetAllClientService,
2623
2878
  categoriesGetAllServer,
2624
2879
  categoriesGetByIdClientService,
2625
2880
  categoriesGetByIdServer,
2881
+ clearMetadataCacheFamilyRuntimeSuppression,
2626
2882
  collectionsGetAllClientService,
2627
2883
  collectionsGetAllServer,
2628
2884
  collectionsGetByIdClientService,
@@ -2717,8 +2973,10 @@ export {
2717
2973
  getIndividualProfileById,
2718
2974
  getIndividualsProfiles,
2719
2975
  getInformationSubjects,
2976
+ getMetadataCacheFamily,
2720
2977
  getMetadataDisplayRows,
2721
2978
  getMetadataFacetLabelOverrides,
2979
+ getMetadataFacetPresentation,
2722
2980
  getMetadataPropertyResolvers,
2723
2981
  getOrganizationBranding,
2724
2982
  getOrganizationProfile,
@@ -2731,6 +2989,7 @@ export {
2731
2989
  getProductMetadata,
2732
2990
  getProductVariants,
2733
2991
  getQualifications,
2992
+ getRuntimeSuppressedMetadataFamilies,
2734
2993
  getServerConfig,
2735
2994
  getSupplies,
2736
2995
  getTopicTypes,
@@ -2774,6 +3033,8 @@ export {
2774
3033
  informationUnitsGetByIdServer,
2775
3034
  informationUnitsLanguagesClientService,
2776
3035
  informationUnitsLanguagesServer,
3036
+ isMetadataGroupDenied,
3037
+ isMetadataPropertyRuntimeSuppressed,
2777
3038
  learningsGetAllClientService,
2778
3039
  learningsGetAllServer,
2779
3040
  learningsGetByIdClientService,
@@ -2863,9 +3124,15 @@ export {
2863
3124
  renditionsGetByIdServer,
2864
3125
  renditionsGetWithBinaryWithBinaryPathClientService,
2865
3126
  renditionsGetWithBinaryWithBinaryPathServer,
3127
+ resetRuntimeSuppressedMetadataFamiliesForTests,
3128
+ resolveFacetPresentationProperties,
2866
3129
  resolveFacetTags,
3130
+ resolveLiteralLabel,
2867
3131
  resolveLogoSource,
2868
3132
  resolveMetadataDisplayProperties,
3133
+ resolveObjectRefClassLabel,
3134
+ resolveObjectRefLabel,
3135
+ resolveOverrideProperties,
2869
3136
  resolveSocialProviderLabel,
2870
3137
  resolveTypedSocialLinks,
2871
3138
  resolveUiLanguage,
@@ -2909,6 +3176,7 @@ export {
2909
3176
  suppliesGetAllServer,
2910
3177
  suppliesGetByIdClientService,
2911
3178
  suppliesGetByIdServer,
3179
+ suppressMetadataCacheFamilyRuntime,
2912
3180
  tasksGetAllClientService,
2913
3181
  tasksGetAllServer,
2914
3182
  tasksGetByIdClientService,