@c-rex/utils 0.3.0-build.24 → 0.3.0-build.26

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.
package/dist/index.mjs CHANGED
@@ -1497,44 +1497,127 @@ var generateQueryParams = (params) => {
1497
1497
  };
1498
1498
 
1499
1499
  // src/renditions.ts
1500
- var getFileRenditions = ({ renditions }) => {
1501
- if (renditions == void 0 || renditions.length == 0) {
1502
- return {};
1503
- }
1504
- const filteredRenditions = renditions.filter(
1505
- (item) => item.format != "application/xhtml+xml" && item.format != "application/json" && item.format != "application/llm+xml"
1506
- );
1507
- if (filteredRenditions.length == 0 || filteredRenditions[0] == void 0) {
1508
- return {};
1500
+ var DEFAULT_IGNORED_FORMATS = [
1501
+ "application/xhtml+xml",
1502
+ "application/json",
1503
+ "application/llm+xml",
1504
+ "text/html"
1505
+ ];
1506
+ var isZipLikeFormat = (format) => {
1507
+ const normalized = format.toLowerCase();
1508
+ return normalized === "application/zip" || normalized.startsWith("application/") && normalized.endsWith("+zip");
1509
+ };
1510
+ var getArchiveDisplayCode = (format) => {
1511
+ const normalized = format.toLowerCase();
1512
+ if (normalized === "application/zip") {
1513
+ return "ZIP";
1509
1514
  }
1510
- const result = {};
1511
- filteredRenditions.forEach((item) => {
1512
- const key = item.format;
1513
- if (result[key] == void 0) {
1514
- result[key] = {
1515
- view: "",
1516
- download: ""
1517
- };
1515
+ if (normalized.startsWith("application/") && normalized.endsWith("+zip")) {
1516
+ const subtype = normalized.slice("application/".length, -"+zip".length);
1517
+ const parts = subtype.split(".");
1518
+ const lastPart = parts[parts.length - 1]?.trim();
1519
+ const token = lastPart && lastPart.length > 0 ? lastPart : subtype.trim();
1520
+ if (token && token.length > 0 && token !== "vnd") {
1521
+ return token.replace(/[^a-z0-9]/gi, "").slice(0, 4).toUpperCase() || "ZIP";
1518
1522
  }
1519
- const download = item.links?.filter((link) => link.rel == "download")[0]?.href;
1520
- const view = item.links?.filter((link) => link.rel == "view")[0]?.href;
1521
- if (download != null && download != void 0) {
1522
- result[key].download = download;
1523
- }
1524
- if (view != null && view != void 0) {
1525
- result[key].view = view;
1523
+ }
1524
+ return null;
1525
+ };
1526
+ var getDisplayLabel = (format) => {
1527
+ if (format === "application/pdf") return "PDF";
1528
+ if (isZipLikeFormat(format)) return "ZIP";
1529
+ const subtype = format.split("/")[1] || format;
1530
+ return subtype.replace(/\+.*/, "").toUpperCase();
1531
+ };
1532
+ var getIconFormat = (format) => {
1533
+ if (format === "application/pdf") return "application/pdf";
1534
+ if (format === "application/zip") return "application/zip";
1535
+ if (isZipLikeFormat(format)) return format;
1536
+ return format;
1537
+ };
1538
+ var getIconLabel = (format) => {
1539
+ if (format === "application/pdf") return "PDF";
1540
+ const archiveCode = getArchiveDisplayCode(format);
1541
+ if (archiveCode) return archiveCode;
1542
+ return getDisplayLabel(format);
1543
+ };
1544
+ var getFirstLinkByRel = (item, rel) => item.links?.find((link) => link.rel === rel)?.href;
1545
+ var getRenditionFileName = (item, fallbackFormat) => {
1546
+ const rawSource = item.source?.split("/").pop();
1547
+ if (rawSource && rawSource.length > 0) {
1548
+ return decodeURIComponent(rawSource);
1549
+ }
1550
+ const urlCandidate = getFirstLinkByRel(item, "download") || getFirstLinkByRel(item, "view");
1551
+ if (urlCandidate) {
1552
+ try {
1553
+ const url = new URL(urlCandidate);
1554
+ const lastSegment = url.pathname.split("/").pop();
1555
+ if (lastSegment && lastSegment.length > 0) {
1556
+ return decodeURIComponent(lastSegment);
1557
+ }
1558
+ } catch {
1559
+ const lastSegment = urlCandidate.split("/").pop();
1560
+ if (lastSegment && lastSegment.length > 0) {
1561
+ return decodeURIComponent(lastSegment);
1562
+ }
1526
1563
  }
1564
+ }
1565
+ return getDisplayLabel(fallbackFormat);
1566
+ };
1567
+ var getFileRenditionGroups = ({
1568
+ renditions,
1569
+ ignoreFormats = DEFAULT_IGNORED_FORMATS
1570
+ }) => {
1571
+ if (!renditions || renditions.length === 0) return [];
1572
+ const grouped = /* @__PURE__ */ new Map();
1573
+ renditions.filter((item) => item.format && !ignoreFormats.includes(item.format)).forEach((item) => {
1574
+ const format = item.format;
1575
+ const downloadUrl = getFirstLinkByRel(item, "download");
1576
+ const viewUrl = getFirstLinkByRel(item, "view");
1577
+ if (!downloadUrl && !viewUrl) return;
1578
+ const current = grouped.get(format) || [];
1579
+ current.push({
1580
+ label: getRenditionFileName(item, format),
1581
+ downloadUrl: downloadUrl || viewUrl || "",
1582
+ viewUrl: viewUrl || void 0
1583
+ });
1584
+ grouped.set(format, current);
1527
1585
  });
1528
- return result;
1586
+ return Array.from(grouped.entries()).map(([format, items]) => {
1587
+ const deduplicatedItems = items.filter((item) => item.downloadUrl);
1588
+ const hasMultiple = deduplicatedItems.length > 1;
1589
+ const isPdf = format === "application/pdf";
1590
+ return {
1591
+ format,
1592
+ displayLabel: getDisplayLabel(format),
1593
+ iconFormat: getIconFormat(format),
1594
+ iconLabel: getIconLabel(format),
1595
+ items: deduplicatedItems,
1596
+ hasMultiple,
1597
+ supportsOpen: isPdf && !hasMultiple && !!deduplicatedItems[0]?.viewUrl
1598
+ };
1599
+ }).filter((group) => group.items.length > 0);
1600
+ };
1601
+ var getFileRenditions = ({ renditions }) => {
1602
+ return getFileRenditionGroups({ renditions }).reduce((acc, group) => {
1603
+ const first = group.items[0];
1604
+ if (!first) return acc;
1605
+ acc[group.format] = {
1606
+ view: first.viewUrl || "",
1607
+ download: first.downloadUrl || ""
1608
+ };
1609
+ return acc;
1610
+ }, {});
1529
1611
  };
1530
1612
 
1531
1613
  // src/call.ts
1532
- var call = async (method, params) => {
1614
+ var call = async (method, params, options) => {
1533
1615
  const res = await fetch(`/api/rpc`, {
1534
1616
  method: "POST",
1535
1617
  headers: { "Content-Type": "application/json" },
1536
1618
  body: JSON.stringify({ method, params }),
1537
- credentials: "include"
1619
+ credentials: "include",
1620
+ signal: options?.signal
1538
1621
  });
1539
1622
  const json = await res.json();
1540
1623
  if (!res.ok) throw new Error(json.error || "Unknown error");
@@ -1603,8 +1686,31 @@ var filteredItems = (items) => {
1603
1686
  const hasTitle = items.some((item) => item.label === "titles");
1604
1687
  return items.filter((item) => hasTitle ? item.label !== "labels" : true);
1605
1688
  };
1606
- var getType = (classObj) => {
1607
- const type = classObj?.labels?.find((item) => item.language === EN_LANG)?.value?.toUpperCase();
1689
+ var resolveLabelByLanguage = (labels, lang = EN_LANG) => {
1690
+ if (!labels || labels.length === 0) {
1691
+ return void 0;
1692
+ }
1693
+ const normalizedLanguage = normalizeLanguageCode(lang || EN_LANG).toLowerCase();
1694
+ const baseLanguage = normalizedLanguage.split("-")[0];
1695
+ const normalizedLabels = labels.map((item) => ({
1696
+ language: normalizeLanguageCode(item.language || "").toLowerCase(),
1697
+ value: item.value?.trim() || ""
1698
+ })).filter((item) => item.value.length > 0);
1699
+ if (normalizedLabels.length === 0) {
1700
+ return void 0;
1701
+ }
1702
+ const exact = normalizedLabels.find((item) => item.language === normalizedLanguage)?.value;
1703
+ if (exact) return exact;
1704
+ const base = normalizedLabels.find((item) => item.language === baseLanguage)?.value || normalizedLabels.find((item) => item.language.startsWith(`${baseLanguage}-`))?.value;
1705
+ if (base) return base;
1706
+ const english = normalizedLabels.find((item) => item.language === "en-us")?.value || normalizedLabels.find((item) => item.language === "en")?.value || normalizedLabels.find((item) => item.language.startsWith("en-"))?.value;
1707
+ if (english) return english;
1708
+ const noLanguage = normalizedLabels.find((item) => item.language.length === 0)?.value;
1709
+ if (noLanguage) return noLanguage;
1710
+ return normalizedLabels[0]?.value;
1711
+ };
1712
+ var getType = (classObj, lang = EN_LANG) => {
1713
+ const type = resolveLabelByLanguage(classObj?.labels, lang)?.toUpperCase();
1608
1714
  return type;
1609
1715
  };
1610
1716
  var getTitle = (titles, labels) => {
@@ -1617,18 +1723,7 @@ var getTitle = (titles, labels) => {
1617
1723
  return "NO TITLE";
1618
1724
  };
1619
1725
  var getLabelByLang = (labels, lang = EN_LANG) => {
1620
- if (!labels) {
1621
- return "NO TITLE";
1622
- }
1623
- const preferred = labels.find((item) => item.language === lang);
1624
- if (preferred) {
1625
- return preferred.value;
1626
- }
1627
- const fallback = labels.find((item) => item.language === EN_LANG);
1628
- if (fallback) {
1629
- return fallback.value;
1630
- }
1631
- return "NO TITLE";
1726
+ return resolveLabelByLanguage(labels, lang) || "NO TITLE";
1632
1727
  };
1633
1728
  var getLanguage = (languages) => {
1634
1729
  if (languages && languages.length > 0) {
@@ -1679,6 +1774,29 @@ var metaTagsToMetadata = (metaTags) => {
1679
1774
  other: Object.keys(other).length > 0 ? other : void 0
1680
1775
  };
1681
1776
  };
1777
+
1778
+ // src/related-fragments.ts
1779
+ var matchesSubject = (fragment, informationSubjectIds) => {
1780
+ if (informationSubjectIds.length === 0) return true;
1781
+ const fragmentSubjects = fragment.informationSubjects || [];
1782
+ return fragmentSubjects.some((subject) => {
1783
+ const id = subject.id || subject.shortId;
1784
+ return !!id && informationSubjectIds.includes(id);
1785
+ });
1786
+ };
1787
+ var findRelatedFragment = ({
1788
+ item,
1789
+ informationSubjectIds = []
1790
+ }) => {
1791
+ const relatedItems = item?.informationUnits || [];
1792
+ return relatedItems.find((relatedItem) => {
1793
+ if (!relatedItem?.shortId) return false;
1794
+ return matchesSubject(relatedItem, informationSubjectIds);
1795
+ });
1796
+ };
1797
+ var findRelatedFragmentShortId = (options) => {
1798
+ return findRelatedFragment(options)?.shortId || void 0;
1799
+ };
1682
1800
  export {
1683
1801
  call,
1684
1802
  cn,
@@ -1686,10 +1804,13 @@ export {
1686
1804
  createParams,
1687
1805
  extractCountryCodeFromLanguage,
1688
1806
  filteredItems,
1807
+ findRelatedFragment,
1808
+ findRelatedFragmentShortId,
1689
1809
  formatDateToLocale,
1690
1810
  generateBreadcrumbItems,
1691
1811
  generateQueryParams,
1692
1812
  getCountryCodeByLang,
1813
+ getFileRenditionGroups,
1693
1814
  getFileRenditions,
1694
1815
  getHtmlRenditionViewUrl,
1695
1816
  getLabelByLang,
@@ -1704,6 +1825,7 @@ export {
1704
1825
  parseHtmlContent,
1705
1826
  processDataToLabelValuePairs,
1706
1827
  replacePathParams,
1828
+ resolveLabelByLanguage,
1707
1829
  resolvePreferredLanguage,
1708
1830
  sortAndDeduplicateLanguages,
1709
1831
  transformLanguageData