@digitalculture/ochre-sdk 0.4.15 → 0.5.1

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/README.md CHANGED
@@ -25,7 +25,7 @@ bun add @digitalculture/ochre-sdk
25
25
  From the root directory of the project, run the following command:
26
26
 
27
27
  ```bash
28
- pnpm run dev:ochre-sdk
28
+ pnpm run dev
29
29
  ```
30
30
 
31
31
  ## Build production server
package/dist/index.cjs CHANGED
@@ -76,7 +76,6 @@ __export(index_exports, {
76
76
  module.exports = __toCommonJS(index_exports);
77
77
 
78
78
  // src/utils/parse.ts
79
- var import_uuid = require("uuid");
80
79
  var import_zod3 = require("zod");
81
80
 
82
81
  // src/utils/fetchers/generic.ts
@@ -568,6 +567,7 @@ var componentSchema = import_zod3.z.enum(
568
567
  "button",
569
568
  "collection",
570
569
  "empty-space",
570
+ "iframe",
571
571
  "iiif-viewer",
572
572
  "image",
573
573
  "image-gallery",
@@ -726,6 +726,7 @@ function parseLink(linkRaw) {
726
726
  const returnLink = {
727
727
  category: "resource" in linkRaw ? "resource" : "concept" in linkRaw ? "concept" : "set" in linkRaw ? "set" : "person" in linkRaw ? "person" : "tree" in linkRaw ? "tree" : "bibliography" in linkRaw ? "bibliography" : "epigraphicUnit" in linkRaw ? "epigraphicUnit" : null,
728
728
  content: "content" in link ? link.content != null ? parseFakeString(link.content) : null : null,
729
+ href: "href" in link && link.href != null ? link.href : null,
729
730
  uuid: link.uuid,
730
731
  type: link.type ?? null,
731
732
  identification: link.identification ? parseIdentification(link.identification) : null,
@@ -1307,10 +1308,7 @@ var parseWebpageResources = async (webpageResources, type) => {
1307
1308
  if (!resourceProperty) continue;
1308
1309
  switch (type) {
1309
1310
  case "element": {
1310
- const element = await parseWebElement(
1311
- resource,
1312
- resourceProperty.properties
1313
- );
1311
+ const element = await parseWebElement(resource);
1314
1312
  returnElements.push(
1315
1313
  element
1316
1314
  );
@@ -1412,9 +1410,7 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1412
1410
  componentProperty.properties,
1413
1411
  "layout"
1414
1412
  );
1415
- if (layout === null) {
1416
- layout = "long";
1417
- }
1413
+ layout ??= "long";
1418
1414
  properties.bibliographies = bibliographyLink.bibliographies;
1419
1415
  properties.layout = layout;
1420
1416
  break;
@@ -1434,9 +1430,7 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1434
1430
  componentProperty.properties,
1435
1431
  "variant"
1436
1432
  );
1437
- if (variant === null) {
1438
- variant = "default";
1439
- }
1433
+ variant ??= "default";
1440
1434
  let isExternal = false;
1441
1435
  let href = getPropertyValueByLabel(
1442
1436
  componentProperty.properties,
@@ -1467,16 +1461,12 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1467
1461
  componentProperty.properties,
1468
1462
  "variant"
1469
1463
  );
1470
- if (variant === null) {
1471
- variant = "full";
1472
- }
1464
+ variant ??= "full";
1473
1465
  let layout = getPropertyValueByLabel(
1474
1466
  componentProperty.properties,
1475
1467
  "layout"
1476
1468
  );
1477
- if (layout === null) {
1478
- layout = "image-start";
1479
- }
1469
+ layout ??= "image-start";
1480
1470
  const collectionLink = links.find((link) => link.category === "set");
1481
1471
  if (!collectionLink) {
1482
1472
  throw new Error(
@@ -1501,6 +1491,16 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1501
1491
  properties.width = width;
1502
1492
  break;
1503
1493
  }
1494
+ case "iframe": {
1495
+ const url = links.find((link) => link.type === "webpage")?.href;
1496
+ if (!url) {
1497
+ throw new Error(
1498
+ `URL not found for the following component: \u201C${componentName}\u201D`
1499
+ );
1500
+ }
1501
+ properties.url = url;
1502
+ break;
1503
+ }
1504
1504
  case "iiif-viewer": {
1505
1505
  const manifestLink = links.find((link) => link.type === "IIIF");
1506
1506
  if (!manifestLink) {
@@ -1530,16 +1530,12 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1530
1530
  componentProperty.properties,
1531
1531
  "variant"
1532
1532
  );
1533
- if (variant === null) {
1534
- variant = "default";
1535
- }
1533
+ variant ??= "default";
1536
1534
  let captionLayout = getPropertyValueByLabel(
1537
1535
  componentProperty.properties,
1538
1536
  "layout-caption"
1539
1537
  );
1540
- if (captionLayout === null) {
1541
- captionLayout = "bottom";
1542
- }
1538
+ captionLayout ??= "bottom";
1543
1539
  let width = null;
1544
1540
  const widthProperty = getPropertyValueByLabel(
1545
1541
  componentProperty.properties,
@@ -1576,23 +1572,17 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1576
1572
  componentProperty.properties,
1577
1573
  "image-quality"
1578
1574
  );
1579
- if (imageQuality === null) {
1580
- imageQuality = "high";
1581
- }
1575
+ imageQuality ??= "high";
1582
1576
  let captionSource = getPropertyValueByLabel(
1583
1577
  componentProperty.properties,
1584
1578
  "caption-source"
1585
1579
  );
1586
- if (captionSource === null) {
1587
- captionSource = "name";
1588
- }
1580
+ captionSource ??= "name";
1589
1581
  let altTextSource = getPropertyValueByLabel(
1590
1582
  componentProperty.properties,
1591
1583
  "alt-text-source"
1592
1584
  );
1593
- if (altTextSource === null) {
1594
- altTextSource = "name";
1595
- }
1585
+ altTextSource ??= "name";
1596
1586
  let isTransparentBackground = false;
1597
1587
  const isTransparentBackgroundProperty = getPropertyValueByLabel(
1598
1588
  componentProperty.properties,
@@ -1713,9 +1703,7 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1713
1703
  componentProperty.properties,
1714
1704
  "variant"
1715
1705
  );
1716
- if (variant === null) {
1717
- variant = "block";
1718
- }
1706
+ variant ??= "block";
1719
1707
  const heading = getPropertyValueByLabel(
1720
1708
  componentProperty.properties,
1721
1709
  "heading"
@@ -1735,23 +1723,17 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1735
1723
  componentProperty.properties,
1736
1724
  "variant"
1737
1725
  );
1738
- if (variant === null) {
1739
- variant = "block";
1740
- }
1726
+ variant ??= "block";
1741
1727
  let layout = getPropertyValueByLabel(
1742
1728
  componentProperty.properties,
1743
1729
  "layout"
1744
1730
  );
1745
- if (layout === null) {
1746
- layout = "image-start";
1747
- }
1731
+ layout ??= "image-start";
1748
1732
  let captionLayout = getPropertyValueByLabel(
1749
1733
  componentProperty.properties,
1750
1734
  "layout-caption"
1751
1735
  );
1752
- if (captionLayout === null) {
1753
- captionLayout = "bottom";
1754
- }
1736
+ captionLayout ??= "bottom";
1755
1737
  const imageLink = links.find(
1756
1738
  (link) => link.type === "image" || link.type === "IIIF"
1757
1739
  );
@@ -1766,23 +1748,17 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1766
1748
  componentProperty.properties,
1767
1749
  "image-quality"
1768
1750
  );
1769
- if (imageQuality === null) {
1770
- imageQuality = "high";
1771
- }
1751
+ imageQuality ??= "high";
1772
1752
  let captionSource = getPropertyValueByLabel(
1773
1753
  componentProperty.properties,
1774
1754
  "caption-source"
1775
1755
  );
1776
- if (captionSource === null) {
1777
- captionSource = "name";
1778
- }
1756
+ captionSource ??= "name";
1779
1757
  let altTextSource = getPropertyValueByLabel(
1780
1758
  componentProperty.properties,
1781
1759
  "alt-text-source"
1782
1760
  );
1783
- if (altTextSource === null) {
1784
- altTextSource = "name";
1785
- }
1761
+ altTextSource ??= "name";
1786
1762
  properties.variant = variant;
1787
1763
  properties.image = {
1788
1764
  url: `https://ochre.lib.uchicago.edu/ochre?uuid=${imageLink.uuid}&preview`,
@@ -1819,9 +1795,7 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1819
1795
  componentProperty.properties,
1820
1796
  "chapters-displayed"
1821
1797
  );
1822
- if (isChaptersDislayed == null) {
1823
- isChaptersDislayed = "Yes";
1824
- }
1798
+ isChaptersDislayed ??= "Yes";
1825
1799
  properties.videoId = videoLink.uuid;
1826
1800
  properties.isChaptersDislayed = isChaptersDislayed === "Yes";
1827
1801
  break;
@@ -1837,9 +1811,20 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1837
1811
  }
1838
1812
  return properties;
1839
1813
  }
1840
- async function parseWebElement(elementResource, elementProperties) {
1814
+ async function parseWebElement(elementResource) {
1841
1815
  const identification = parseIdentification(elementResource.identification);
1842
- const componentProperty = elementProperties.find(
1816
+ const elementProperties = elementResource.properties?.property ? parseProperties(
1817
+ Array.isArray(elementResource.properties.property) ? elementResource.properties.property : [elementResource.properties.property]
1818
+ ) : [];
1819
+ const presentationProperty = elementProperties.find(
1820
+ (property) => property.label === "presentation"
1821
+ );
1822
+ if (!presentationProperty) {
1823
+ throw new Error(
1824
+ `Presentation property not found for element \u201C${identification.label}\u201D`
1825
+ );
1826
+ }
1827
+ const componentProperty = presentationProperty.properties.find(
1843
1828
  (property) => property.label === "component"
1844
1829
  );
1845
1830
  if (!componentProperty) {
@@ -1903,6 +1888,7 @@ async function parseWebElement(elementResource, elementProperties) {
1903
1888
  }
1904
1889
  return {
1905
1890
  uuid: elementResource.uuid,
1891
+ type: "element",
1906
1892
  title: {
1907
1893
  label: identification.label,
1908
1894
  variant,
@@ -1937,8 +1923,7 @@ async function parseWebpage(webpageResource) {
1937
1923
  (link) => link.type === "image" || link.type === "IIIF"
1938
1924
  );
1939
1925
  const webpageResources = webpageResource.resource ? Array.isArray(webpageResource.resource) ? webpageResource.resource : [webpageResource.resource] : [];
1940
- const blocks = [];
1941
- let elementsToHandle = [];
1926
+ const items = [];
1942
1927
  for (const resource of webpageResources) {
1943
1928
  const resourceProperties = resource.properties ? parseProperties(
1944
1929
  Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
@@ -1950,55 +1935,21 @@ async function parseWebpage(webpageResource) {
1950
1935
  if (!resourceType) {
1951
1936
  continue;
1952
1937
  }
1953
- if (resourceType === "element") {
1954
- elementsToHandle.push(resource);
1955
- } else if (resourceType === "block") {
1956
- if (elementsToHandle.length > 0) {
1957
- const elements = await parseWebpageResources(
1958
- elementsToHandle,
1959
- "element"
1960
- );
1961
- const block = {
1962
- uuid: (0, import_uuid.v4)(),
1963
- layout: "vertical",
1964
- blocks: [],
1965
- elements,
1966
- properties: {
1967
- spacing: void 0,
1968
- gap: void 0,
1969
- alignItems: "start",
1970
- justifyContent: "stretch"
1971
- },
1972
- propertiesMobile: null,
1973
- cssStyles: [],
1974
- cssStylesMobile: []
1975
- };
1976
- blocks.push(block);
1977
- elementsToHandle = [];
1938
+ switch (resourceType) {
1939
+ case "element": {
1940
+ const element = await parseWebElement(resource);
1941
+ items.push(element);
1942
+ break;
1943
+ }
1944
+ case "block": {
1945
+ const block = await parseBlock(resource);
1946
+ if (block) {
1947
+ items.push(block);
1948
+ }
1949
+ break;
1978
1950
  }
1979
- const parsedBlocks = await parseWebpageResources([resource], "block");
1980
- blocks.push(...parsedBlocks);
1981
1951
  }
1982
1952
  }
1983
- if (elementsToHandle.length > 0) {
1984
- const elements = await parseWebpageResources(elementsToHandle, "element");
1985
- const block = {
1986
- uuid: (0, import_uuid.v4)(),
1987
- layout: "vertical",
1988
- blocks: [],
1989
- elements,
1990
- properties: {
1991
- spacing: void 0,
1992
- gap: void 0,
1993
- alignItems: "start",
1994
- justifyContent: "stretch"
1995
- },
1996
- propertiesMobile: null,
1997
- cssStyles: [],
1998
- cssStylesMobile: []
1999
- };
2000
- blocks.push(block);
2001
- }
2002
1953
  const webpages = webpageResource.resource ? await parseWebpageResources(
2003
1954
  Array.isArray(webpageResource.resource) ? webpageResource.resource : [webpageResource.resource],
2004
1955
  "page"
@@ -2063,7 +2014,7 @@ async function parseWebpage(webpageResource) {
2063
2014
  return {
2064
2015
  title: identification.label,
2065
2016
  slug,
2066
- blocks,
2017
+ items,
2067
2018
  properties: {
2068
2019
  displayedInHeader,
2069
2020
  width,
@@ -2090,9 +2041,9 @@ async function parseWebpages(webpageResources) {
2090
2041
  async function parseBlock(blockResource) {
2091
2042
  const returnBlock = {
2092
2043
  uuid: blockResource.uuid,
2044
+ type: "block",
2093
2045
  layout: "vertical",
2094
- blocks: [],
2095
- elements: [],
2046
+ items: [],
2096
2047
  properties: {
2097
2048
  spacing: void 0,
2098
2049
  gap: void 0,
@@ -2152,20 +2103,35 @@ async function parseBlock(blockResource) {
2152
2103
  returnBlock.propertiesMobile = propertiesMobile;
2153
2104
  }
2154
2105
  }
2155
- const blockBlocks = blockResource.resource ? await parseWebpageResources(
2156
- Array.isArray(blockResource.resource) ? blockResource.resource : [blockResource.resource],
2157
- "block"
2158
- ) : [];
2159
- for (const block of blockBlocks) {
2160
- returnBlock.blocks.push(block);
2161
- }
2162
- const blockElements = blockResource.resource ? await parseWebpageResources(
2163
- Array.isArray(blockResource.resource) ? blockResource.resource : [blockResource.resource],
2164
- "element"
2165
- ) : [];
2166
- for (const element of blockElements) {
2167
- returnBlock.elements.push(element);
2106
+ const blockResources = blockResource.resource ? Array.isArray(blockResource.resource) ? blockResource.resource : [blockResource.resource] : [];
2107
+ const blockItems = [];
2108
+ for (const resource of blockResources) {
2109
+ const resourceProperties = resource.properties ? parseProperties(
2110
+ Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
2111
+ ) : [];
2112
+ const resourceType = getPropertyValueByLabel(
2113
+ resourceProperties,
2114
+ "presentation"
2115
+ );
2116
+ if (!resourceType) {
2117
+ continue;
2118
+ }
2119
+ switch (resourceType) {
2120
+ case "element": {
2121
+ const element = await parseWebElement(resource);
2122
+ blockItems.push(element);
2123
+ break;
2124
+ }
2125
+ case "block": {
2126
+ const block = await parseBlock(resource);
2127
+ if (block) {
2128
+ blockItems.push(block);
2129
+ }
2130
+ break;
2131
+ }
2132
+ }
2168
2133
  }
2134
+ returnBlock.items = blockItems;
2169
2135
  const blockCssStyles = blockProperties.find(
2170
2136
  (property) => property.label === "presentation" && property.values[0]?.content === "css"
2171
2137
  )?.properties;
@@ -2211,9 +2177,7 @@ function parseWebsiteProperties(properties) {
2211
2177
  let privacy = websiteProperties.find(
2212
2178
  (property) => property.label === "privacy"
2213
2179
  )?.values[0]?.content;
2214
- if (privacy == null) {
2215
- privacy = "public";
2216
- }
2180
+ privacy ??= "public";
2217
2181
  const result = websiteSchema.safeParse({
2218
2182
  type,
2219
2183
  status,
@@ -2399,15 +2363,7 @@ async function parseWebsite(websiteTree, projectName, website) {
2399
2363
  }
2400
2364
  const sidebarResources = sidebarResource.resource ? Array.isArray(sidebarResource.resource) ? sidebarResource.resource : [sidebarResource.resource] : [];
2401
2365
  for (const resource of sidebarResources) {
2402
- const sidebarResourceProperties = resource.properties ? parseProperties(
2403
- Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
2404
- ) : [];
2405
- const element = await parseWebElement(
2406
- resource,
2407
- sidebarResourceProperties.find(
2408
- (property) => property.label === "presentation" && property.values[0]?.content === "element"
2409
- )?.properties ?? []
2410
- );
2366
+ const element = await parseWebElement(resource);
2411
2367
  sidebarElements.push(element);
2412
2368
  }
2413
2369
  }
package/dist/index.d.cts CHANGED
@@ -115,6 +115,7 @@ type Link = {
115
115
  category: "resource" | "concept" | "set" | "tree" | "person" | "bibliography" | "epigraphicUnit" | null;
116
116
  identification: Identification | null;
117
117
  content: string | null;
118
+ href: string | null;
118
119
  image: {
119
120
  isInline: boolean;
120
121
  heightPreview: number;
@@ -440,7 +441,7 @@ type Webpage = {
440
441
  title: string;
441
442
  slug: string;
442
443
  properties: WebpageProperties;
443
- blocks: Array<WebBlock>;
444
+ items: Array<WebElement | WebBlock>;
444
445
  webpages: Array<Webpage>;
445
446
  };
446
447
  /**
@@ -460,6 +461,7 @@ type WebpageProperties = {
460
461
  */
461
462
  type WebElement = {
462
463
  uuid: string;
464
+ type: "element";
463
465
  title: {
464
466
  label: string;
465
467
  variant: "default" | "simple";
@@ -506,6 +508,9 @@ type WebElementComponent = {
506
508
  component: "empty-space";
507
509
  height: string | null;
508
510
  width: string | null;
511
+ } | {
512
+ component: "iframe";
513
+ url: string;
509
514
  } | {
510
515
  component: "iiif-viewer";
511
516
  IIIFId: string;
@@ -588,9 +593,9 @@ type Style = {
588
593
  */
589
594
  type WebBlock = {
590
595
  uuid: string;
596
+ type: "block";
591
597
  layout: "vertical" | "horizontal" | "grid";
592
- blocks: Array<WebBlock>;
593
- elements: Array<WebElement>;
598
+ items: Array<WebElement | WebBlock>;
594
599
  properties: {
595
600
  /**
596
601
  * valid `gridTemplateColumns` or `gridTemplateRows` CSS property value
package/dist/index.d.ts CHANGED
@@ -115,6 +115,7 @@ type Link = {
115
115
  category: "resource" | "concept" | "set" | "tree" | "person" | "bibliography" | "epigraphicUnit" | null;
116
116
  identification: Identification | null;
117
117
  content: string | null;
118
+ href: string | null;
118
119
  image: {
119
120
  isInline: boolean;
120
121
  heightPreview: number;
@@ -440,7 +441,7 @@ type Webpage = {
440
441
  title: string;
441
442
  slug: string;
442
443
  properties: WebpageProperties;
443
- blocks: Array<WebBlock>;
444
+ items: Array<WebElement | WebBlock>;
444
445
  webpages: Array<Webpage>;
445
446
  };
446
447
  /**
@@ -460,6 +461,7 @@ type WebpageProperties = {
460
461
  */
461
462
  type WebElement = {
462
463
  uuid: string;
464
+ type: "element";
463
465
  title: {
464
466
  label: string;
465
467
  variant: "default" | "simple";
@@ -506,6 +508,9 @@ type WebElementComponent = {
506
508
  component: "empty-space";
507
509
  height: string | null;
508
510
  width: string | null;
511
+ } | {
512
+ component: "iframe";
513
+ url: string;
509
514
  } | {
510
515
  component: "iiif-viewer";
511
516
  IIIFId: string;
@@ -588,9 +593,9 @@ type Style = {
588
593
  */
589
594
  type WebBlock = {
590
595
  uuid: string;
596
+ type: "block";
591
597
  layout: "vertical" | "horizontal" | "grid";
592
- blocks: Array<WebBlock>;
593
- elements: Array<WebElement>;
598
+ items: Array<WebElement | WebBlock>;
594
599
  properties: {
595
600
  /**
596
601
  * valid `gridTemplateColumns` or `gridTemplateRows` CSS property value
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  // src/utils/parse.ts
2
- import { v4 as uuidv4 } from "uuid";
3
2
  import { z as z3 } from "zod";
4
3
 
5
4
  // src/utils/fetchers/generic.ts
@@ -491,6 +490,7 @@ var componentSchema = z3.enum(
491
490
  "button",
492
491
  "collection",
493
492
  "empty-space",
493
+ "iframe",
494
494
  "iiif-viewer",
495
495
  "image",
496
496
  "image-gallery",
@@ -649,6 +649,7 @@ function parseLink(linkRaw) {
649
649
  const returnLink = {
650
650
  category: "resource" in linkRaw ? "resource" : "concept" in linkRaw ? "concept" : "set" in linkRaw ? "set" : "person" in linkRaw ? "person" : "tree" in linkRaw ? "tree" : "bibliography" in linkRaw ? "bibliography" : "epigraphicUnit" in linkRaw ? "epigraphicUnit" : null,
651
651
  content: "content" in link ? link.content != null ? parseFakeString(link.content) : null : null,
652
+ href: "href" in link && link.href != null ? link.href : null,
652
653
  uuid: link.uuid,
653
654
  type: link.type ?? null,
654
655
  identification: link.identification ? parseIdentification(link.identification) : null,
@@ -1230,10 +1231,7 @@ var parseWebpageResources = async (webpageResources, type) => {
1230
1231
  if (!resourceProperty) continue;
1231
1232
  switch (type) {
1232
1233
  case "element": {
1233
- const element = await parseWebElement(
1234
- resource,
1235
- resourceProperty.properties
1236
- );
1234
+ const element = await parseWebElement(resource);
1237
1235
  returnElements.push(
1238
1236
  element
1239
1237
  );
@@ -1335,9 +1333,7 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1335
1333
  componentProperty.properties,
1336
1334
  "layout"
1337
1335
  );
1338
- if (layout === null) {
1339
- layout = "long";
1340
- }
1336
+ layout ??= "long";
1341
1337
  properties.bibliographies = bibliographyLink.bibliographies;
1342
1338
  properties.layout = layout;
1343
1339
  break;
@@ -1357,9 +1353,7 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1357
1353
  componentProperty.properties,
1358
1354
  "variant"
1359
1355
  );
1360
- if (variant === null) {
1361
- variant = "default";
1362
- }
1356
+ variant ??= "default";
1363
1357
  let isExternal = false;
1364
1358
  let href = getPropertyValueByLabel(
1365
1359
  componentProperty.properties,
@@ -1390,16 +1384,12 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1390
1384
  componentProperty.properties,
1391
1385
  "variant"
1392
1386
  );
1393
- if (variant === null) {
1394
- variant = "full";
1395
- }
1387
+ variant ??= "full";
1396
1388
  let layout = getPropertyValueByLabel(
1397
1389
  componentProperty.properties,
1398
1390
  "layout"
1399
1391
  );
1400
- if (layout === null) {
1401
- layout = "image-start";
1402
- }
1392
+ layout ??= "image-start";
1403
1393
  const collectionLink = links.find((link) => link.category === "set");
1404
1394
  if (!collectionLink) {
1405
1395
  throw new Error(
@@ -1424,6 +1414,16 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1424
1414
  properties.width = width;
1425
1415
  break;
1426
1416
  }
1417
+ case "iframe": {
1418
+ const url = links.find((link) => link.type === "webpage")?.href;
1419
+ if (!url) {
1420
+ throw new Error(
1421
+ `URL not found for the following component: \u201C${componentName}\u201D`
1422
+ );
1423
+ }
1424
+ properties.url = url;
1425
+ break;
1426
+ }
1427
1427
  case "iiif-viewer": {
1428
1428
  const manifestLink = links.find((link) => link.type === "IIIF");
1429
1429
  if (!manifestLink) {
@@ -1453,16 +1453,12 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1453
1453
  componentProperty.properties,
1454
1454
  "variant"
1455
1455
  );
1456
- if (variant === null) {
1457
- variant = "default";
1458
- }
1456
+ variant ??= "default";
1459
1457
  let captionLayout = getPropertyValueByLabel(
1460
1458
  componentProperty.properties,
1461
1459
  "layout-caption"
1462
1460
  );
1463
- if (captionLayout === null) {
1464
- captionLayout = "bottom";
1465
- }
1461
+ captionLayout ??= "bottom";
1466
1462
  let width = null;
1467
1463
  const widthProperty = getPropertyValueByLabel(
1468
1464
  componentProperty.properties,
@@ -1499,23 +1495,17 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1499
1495
  componentProperty.properties,
1500
1496
  "image-quality"
1501
1497
  );
1502
- if (imageQuality === null) {
1503
- imageQuality = "high";
1504
- }
1498
+ imageQuality ??= "high";
1505
1499
  let captionSource = getPropertyValueByLabel(
1506
1500
  componentProperty.properties,
1507
1501
  "caption-source"
1508
1502
  );
1509
- if (captionSource === null) {
1510
- captionSource = "name";
1511
- }
1503
+ captionSource ??= "name";
1512
1504
  let altTextSource = getPropertyValueByLabel(
1513
1505
  componentProperty.properties,
1514
1506
  "alt-text-source"
1515
1507
  );
1516
- if (altTextSource === null) {
1517
- altTextSource = "name";
1518
- }
1508
+ altTextSource ??= "name";
1519
1509
  let isTransparentBackground = false;
1520
1510
  const isTransparentBackgroundProperty = getPropertyValueByLabel(
1521
1511
  componentProperty.properties,
@@ -1636,9 +1626,7 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1636
1626
  componentProperty.properties,
1637
1627
  "variant"
1638
1628
  );
1639
- if (variant === null) {
1640
- variant = "block";
1641
- }
1629
+ variant ??= "block";
1642
1630
  const heading = getPropertyValueByLabel(
1643
1631
  componentProperty.properties,
1644
1632
  "heading"
@@ -1658,23 +1646,17 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1658
1646
  componentProperty.properties,
1659
1647
  "variant"
1660
1648
  );
1661
- if (variant === null) {
1662
- variant = "block";
1663
- }
1649
+ variant ??= "block";
1664
1650
  let layout = getPropertyValueByLabel(
1665
1651
  componentProperty.properties,
1666
1652
  "layout"
1667
1653
  );
1668
- if (layout === null) {
1669
- layout = "image-start";
1670
- }
1654
+ layout ??= "image-start";
1671
1655
  let captionLayout = getPropertyValueByLabel(
1672
1656
  componentProperty.properties,
1673
1657
  "layout-caption"
1674
1658
  );
1675
- if (captionLayout === null) {
1676
- captionLayout = "bottom";
1677
- }
1659
+ captionLayout ??= "bottom";
1678
1660
  const imageLink = links.find(
1679
1661
  (link) => link.type === "image" || link.type === "IIIF"
1680
1662
  );
@@ -1689,23 +1671,17 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1689
1671
  componentProperty.properties,
1690
1672
  "image-quality"
1691
1673
  );
1692
- if (imageQuality === null) {
1693
- imageQuality = "high";
1694
- }
1674
+ imageQuality ??= "high";
1695
1675
  let captionSource = getPropertyValueByLabel(
1696
1676
  componentProperty.properties,
1697
1677
  "caption-source"
1698
1678
  );
1699
- if (captionSource === null) {
1700
- captionSource = "name";
1701
- }
1679
+ captionSource ??= "name";
1702
1680
  let altTextSource = getPropertyValueByLabel(
1703
1681
  componentProperty.properties,
1704
1682
  "alt-text-source"
1705
1683
  );
1706
- if (altTextSource === null) {
1707
- altTextSource = "name";
1708
- }
1684
+ altTextSource ??= "name";
1709
1685
  properties.variant = variant;
1710
1686
  properties.image = {
1711
1687
  url: `https://ochre.lib.uchicago.edu/ochre?uuid=${imageLink.uuid}&preview`,
@@ -1742,9 +1718,7 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1742
1718
  componentProperty.properties,
1743
1719
  "chapters-displayed"
1744
1720
  );
1745
- if (isChaptersDislayed == null) {
1746
- isChaptersDislayed = "Yes";
1747
- }
1721
+ isChaptersDislayed ??= "Yes";
1748
1722
  properties.videoId = videoLink.uuid;
1749
1723
  properties.isChaptersDislayed = isChaptersDislayed === "Yes";
1750
1724
  break;
@@ -1760,9 +1734,20 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1760
1734
  }
1761
1735
  return properties;
1762
1736
  }
1763
- async function parseWebElement(elementResource, elementProperties) {
1737
+ async function parseWebElement(elementResource) {
1764
1738
  const identification = parseIdentification(elementResource.identification);
1765
- const componentProperty = elementProperties.find(
1739
+ const elementProperties = elementResource.properties?.property ? parseProperties(
1740
+ Array.isArray(elementResource.properties.property) ? elementResource.properties.property : [elementResource.properties.property]
1741
+ ) : [];
1742
+ const presentationProperty = elementProperties.find(
1743
+ (property) => property.label === "presentation"
1744
+ );
1745
+ if (!presentationProperty) {
1746
+ throw new Error(
1747
+ `Presentation property not found for element \u201C${identification.label}\u201D`
1748
+ );
1749
+ }
1750
+ const componentProperty = presentationProperty.properties.find(
1766
1751
  (property) => property.label === "component"
1767
1752
  );
1768
1753
  if (!componentProperty) {
@@ -1826,6 +1811,7 @@ async function parseWebElement(elementResource, elementProperties) {
1826
1811
  }
1827
1812
  return {
1828
1813
  uuid: elementResource.uuid,
1814
+ type: "element",
1829
1815
  title: {
1830
1816
  label: identification.label,
1831
1817
  variant,
@@ -1860,8 +1846,7 @@ async function parseWebpage(webpageResource) {
1860
1846
  (link) => link.type === "image" || link.type === "IIIF"
1861
1847
  );
1862
1848
  const webpageResources = webpageResource.resource ? Array.isArray(webpageResource.resource) ? webpageResource.resource : [webpageResource.resource] : [];
1863
- const blocks = [];
1864
- let elementsToHandle = [];
1849
+ const items = [];
1865
1850
  for (const resource of webpageResources) {
1866
1851
  const resourceProperties = resource.properties ? parseProperties(
1867
1852
  Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
@@ -1873,55 +1858,21 @@ async function parseWebpage(webpageResource) {
1873
1858
  if (!resourceType) {
1874
1859
  continue;
1875
1860
  }
1876
- if (resourceType === "element") {
1877
- elementsToHandle.push(resource);
1878
- } else if (resourceType === "block") {
1879
- if (elementsToHandle.length > 0) {
1880
- const elements = await parseWebpageResources(
1881
- elementsToHandle,
1882
- "element"
1883
- );
1884
- const block = {
1885
- uuid: uuidv4(),
1886
- layout: "vertical",
1887
- blocks: [],
1888
- elements,
1889
- properties: {
1890
- spacing: void 0,
1891
- gap: void 0,
1892
- alignItems: "start",
1893
- justifyContent: "stretch"
1894
- },
1895
- propertiesMobile: null,
1896
- cssStyles: [],
1897
- cssStylesMobile: []
1898
- };
1899
- blocks.push(block);
1900
- elementsToHandle = [];
1861
+ switch (resourceType) {
1862
+ case "element": {
1863
+ const element = await parseWebElement(resource);
1864
+ items.push(element);
1865
+ break;
1866
+ }
1867
+ case "block": {
1868
+ const block = await parseBlock(resource);
1869
+ if (block) {
1870
+ items.push(block);
1871
+ }
1872
+ break;
1901
1873
  }
1902
- const parsedBlocks = await parseWebpageResources([resource], "block");
1903
- blocks.push(...parsedBlocks);
1904
1874
  }
1905
1875
  }
1906
- if (elementsToHandle.length > 0) {
1907
- const elements = await parseWebpageResources(elementsToHandle, "element");
1908
- const block = {
1909
- uuid: uuidv4(),
1910
- layout: "vertical",
1911
- blocks: [],
1912
- elements,
1913
- properties: {
1914
- spacing: void 0,
1915
- gap: void 0,
1916
- alignItems: "start",
1917
- justifyContent: "stretch"
1918
- },
1919
- propertiesMobile: null,
1920
- cssStyles: [],
1921
- cssStylesMobile: []
1922
- };
1923
- blocks.push(block);
1924
- }
1925
1876
  const webpages = webpageResource.resource ? await parseWebpageResources(
1926
1877
  Array.isArray(webpageResource.resource) ? webpageResource.resource : [webpageResource.resource],
1927
1878
  "page"
@@ -1986,7 +1937,7 @@ async function parseWebpage(webpageResource) {
1986
1937
  return {
1987
1938
  title: identification.label,
1988
1939
  slug,
1989
- blocks,
1940
+ items,
1990
1941
  properties: {
1991
1942
  displayedInHeader,
1992
1943
  width,
@@ -2013,9 +1964,9 @@ async function parseWebpages(webpageResources) {
2013
1964
  async function parseBlock(blockResource) {
2014
1965
  const returnBlock = {
2015
1966
  uuid: blockResource.uuid,
1967
+ type: "block",
2016
1968
  layout: "vertical",
2017
- blocks: [],
2018
- elements: [],
1969
+ items: [],
2019
1970
  properties: {
2020
1971
  spacing: void 0,
2021
1972
  gap: void 0,
@@ -2075,20 +2026,35 @@ async function parseBlock(blockResource) {
2075
2026
  returnBlock.propertiesMobile = propertiesMobile;
2076
2027
  }
2077
2028
  }
2078
- const blockBlocks = blockResource.resource ? await parseWebpageResources(
2079
- Array.isArray(blockResource.resource) ? blockResource.resource : [blockResource.resource],
2080
- "block"
2081
- ) : [];
2082
- for (const block of blockBlocks) {
2083
- returnBlock.blocks.push(block);
2084
- }
2085
- const blockElements = blockResource.resource ? await parseWebpageResources(
2086
- Array.isArray(blockResource.resource) ? blockResource.resource : [blockResource.resource],
2087
- "element"
2088
- ) : [];
2089
- for (const element of blockElements) {
2090
- returnBlock.elements.push(element);
2029
+ const blockResources = blockResource.resource ? Array.isArray(blockResource.resource) ? blockResource.resource : [blockResource.resource] : [];
2030
+ const blockItems = [];
2031
+ for (const resource of blockResources) {
2032
+ const resourceProperties = resource.properties ? parseProperties(
2033
+ Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
2034
+ ) : [];
2035
+ const resourceType = getPropertyValueByLabel(
2036
+ resourceProperties,
2037
+ "presentation"
2038
+ );
2039
+ if (!resourceType) {
2040
+ continue;
2041
+ }
2042
+ switch (resourceType) {
2043
+ case "element": {
2044
+ const element = await parseWebElement(resource);
2045
+ blockItems.push(element);
2046
+ break;
2047
+ }
2048
+ case "block": {
2049
+ const block = await parseBlock(resource);
2050
+ if (block) {
2051
+ blockItems.push(block);
2052
+ }
2053
+ break;
2054
+ }
2055
+ }
2091
2056
  }
2057
+ returnBlock.items = blockItems;
2092
2058
  const blockCssStyles = blockProperties.find(
2093
2059
  (property) => property.label === "presentation" && property.values[0]?.content === "css"
2094
2060
  )?.properties;
@@ -2134,9 +2100,7 @@ function parseWebsiteProperties(properties) {
2134
2100
  let privacy = websiteProperties.find(
2135
2101
  (property) => property.label === "privacy"
2136
2102
  )?.values[0]?.content;
2137
- if (privacy == null) {
2138
- privacy = "public";
2139
- }
2103
+ privacy ??= "public";
2140
2104
  const result = websiteSchema.safeParse({
2141
2105
  type,
2142
2106
  status,
@@ -2322,15 +2286,7 @@ async function parseWebsite(websiteTree, projectName, website) {
2322
2286
  }
2323
2287
  const sidebarResources = sidebarResource.resource ? Array.isArray(sidebarResource.resource) ? sidebarResource.resource : [sidebarResource.resource] : [];
2324
2288
  for (const resource of sidebarResources) {
2325
- const sidebarResourceProperties = resource.properties ? parseProperties(
2326
- Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
2327
- ) : [];
2328
- const element = await parseWebElement(
2329
- resource,
2330
- sidebarResourceProperties.find(
2331
- (property) => property.label === "presentation" && property.values[0]?.content === "element"
2332
- )?.properties ?? []
2333
- );
2289
+ const element = await parseWebElement(resource);
2334
2290
  sidebarElements.push(element);
2335
2291
  }
2336
2292
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitalculture/ochre-sdk",
3
- "version": "0.4.15",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Node.js library for working with OCHRE (Online Cultural and Historical Research Environment) data",
@@ -41,21 +41,20 @@
41
41
  ],
42
42
  "dependencies": {
43
43
  "iso-639-3": "^3.0.1",
44
- "uuid": "^11.1.0",
45
44
  "zod": "^3.24.2"
46
45
  },
47
46
  "devDependencies": {
48
- "@antfu/eslint-config": "^4.3.0",
47
+ "@antfu/eslint-config": "^4.11.0",
49
48
  "@arethetypeswrong/cli": "^0.17.4",
50
49
  "@changesets/cli": "^2.28.1",
51
50
  "@total-typescript/ts-reset": "^0.6.1",
52
- "@types/node": "^22.13.5",
53
- "eslint": "^9.21.0",
51
+ "@types/node": "^22.13.14",
52
+ "eslint": "^9.23.0",
54
53
  "eslint-plugin-unused-imports": "^4.1.4",
55
- "prettier": "^3.5.2",
54
+ "prettier": "^3.5.3",
56
55
  "tsup": "^8.4.0",
57
- "typescript": "^5.7.3",
58
- "vitest": "^3.0.7"
56
+ "typescript": "^5.8.2",
57
+ "vitest": "^3.0.9"
59
58
  },
60
59
  "scripts": {
61
60
  "dev": "tsup src/index.ts --watch",