ochre-sdk 1.0.0 → 1.0.2

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 (2) hide show
  1. package/dist/index.mjs +286 -138
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -54,6 +54,7 @@ const XML_ARRAY_TAGS = [
54
54
  ];
55
55
  const XML_PARSER_OPTIONS = {
56
56
  alwaysCreateTextNode: true,
57
+ captureMetaData: true,
57
58
  ignoreAttributes: false,
58
59
  removeNSPrefix: true,
59
60
  ignorePiTags: true,
@@ -814,6 +815,34 @@ const setItemsParamsSchema = v.object({
814
815
  pageSize: v.optional(positiveNumber("Page size must be positive"), 48)
815
816
  });
816
817
  //#endregion
818
+ //#region src/xml/metadata.ts
819
+ const XML_METADATA_SYMBOL = XMLParser.getMetaDataSymbol();
820
+ function isRecord$4(value) {
821
+ return typeof value === "object" && value != null;
822
+ }
823
+ function getXMLMetadata(value) {
824
+ if (!isRecord$4(value)) return null;
825
+ return value[XML_METADATA_SYMBOL] ?? null;
826
+ }
827
+ function getXMLSourceIndex(value) {
828
+ const startIndex = getXMLMetadata(value)?.startIndex;
829
+ return typeof startIndex === "number" ? startIndex : null;
830
+ }
831
+ function restoreXMLMetadata(output, input) {
832
+ if (!isRecord$4(output) || !isRecord$4(input)) return;
833
+ const metadata = getXMLMetadata(input);
834
+ if (metadata != null) Object.defineProperty(output, XML_METADATA_SYMBOL, {
835
+ value: metadata,
836
+ enumerable: false,
837
+ configurable: true
838
+ });
839
+ if (Array.isArray(output) && Array.isArray(input)) {
840
+ for (const [index, outputValue] of output.entries()) restoreXMLMetadata(outputValue, input[index]);
841
+ return;
842
+ }
843
+ for (const [key, outputValue] of Object.entries(output)) restoreXMLMetadata(outputValue, input[key]);
844
+ }
845
+ //#endregion
817
846
  //#region src/parsers/string.ts
818
847
  const TEXT_ANNOTATION_TOKEN = "text-annotation";
819
848
  const TEXT_STYLING_TOKEN = "text-styling";
@@ -979,19 +1008,20 @@ function parseXMLString(string, options) {
979
1008
  */
980
1009
  function createMDXComponent(variant, properties) {
981
1010
  const { uuid, href, height, width, content, text } = properties;
1011
+ const tooltipContent = getDistinctTooltipContent(content, text);
982
1012
  let returnString = "";
983
1013
  switch (variant) {
984
1014
  case "inlineImage":
985
1015
  returnString = `<InlineImage uuid="${uuid}"${createMDXStringAttribute("content", content)} height={${height ?? "null"}} width={${width ?? "null"}} />`;
986
1016
  break;
987
1017
  case "internalLink":
988
- returnString = `<InternalLink uuid="${uuid}"${createMDXStringAttribute("content", content)}>${text}</InternalLink>`;
1018
+ returnString = `<InternalLink uuid="${uuid}"${createMDXStringAttribute("content", tooltipContent)}>${text}</InternalLink>`;
989
1019
  break;
990
1020
  case "externalLink":
991
- returnString = `<ExternalLink href="${href == null ? "#" : transformPermanentIdentificationUrl(href)}"${createMDXStringAttribute("content", content)}>${text}</ExternalLink>`;
1021
+ returnString = `<ExternalLink href="${href == null ? "#" : transformPermanentIdentificationUrl(href)}"${createMDXStringAttribute("content", tooltipContent)}>${text}</ExternalLink>`;
992
1022
  break;
993
1023
  case "documentLink":
994
- returnString = String.raw`<ExternalLink href="https:\/\/ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=${uuid}&load"${createMDXStringAttribute("content", content)}>${text}</ExternalLink>`;
1024
+ returnString = String.raw`<ExternalLink href="https:\/\/ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=${uuid}&load"${createMDXStringAttribute("content", tooltipContent)}>${text}</ExternalLink>`;
995
1025
  break;
996
1026
  case "tooltipSpan":
997
1027
  returnString = `<TooltipSpan${createMDXStringAttribute("content", content)}>${text}</TooltipSpan>`;
@@ -999,6 +1029,9 @@ function createMDXComponent(variant, properties) {
999
1029
  }
1000
1030
  return returnString;
1001
1031
  }
1032
+ function getDistinctTooltipContent(content, textContent) {
1033
+ return content === textContent ? void 0 : content;
1034
+ }
1002
1035
  function createMDXStringAttribute(name, value) {
1003
1036
  if (value == null || value === "") return "";
1004
1037
  return ` ${name}=${MDX_QUOTED_ATTRIBUTE_ESCAPE_REGEX.test(value) ? `{${JSON.stringify(value)}}` : `"${value}"`}`;
@@ -1144,16 +1177,31 @@ function createInternalLinkComponent(properties) {
1144
1177
  case "hover-card": return `<Annotation type="hover-card" uuid="${properties.uuid}">${innerContent}</Annotation>`;
1145
1178
  case "item-page": return `<InternalLink type="item" uuid="${properties.uuid}">${innerContent}</InternalLink>`;
1146
1179
  case "entry-page": return `<InternalLink type="entry" uuid="${properties.uuid}">${innerContent}</InternalLink>`;
1147
- default: return `<InternalLink uuid="${properties.uuid}"${properties.propertyMetadata != null ? ` properties="${properties.propertyMetadata.labelUuid}"${properties.propertyMetadata.valueUuid != null ? ` value="${properties.propertyMetadata.valueUuid}"` : ""}` : ""}${createMDXStringAttribute("content", properties.content)}>${innerContent}</InternalLink>`;
1180
+ default: return `<InternalLink uuid="${properties.uuid}"${properties.propertyMetadata != null ? ` properties="${properties.propertyMetadata.labelUuid}"${properties.propertyMetadata.valueUuid != null ? ` value="${properties.propertyMetadata.valueUuid}"` : ""}` : ""}${createMDXStringAttribute("content", getDistinctTooltipContent(properties.content, properties.text))}>${innerContent}</InternalLink>`;
1148
1181
  }
1149
1182
  }
1150
1183
  function getXMLRichTextLinks(item) {
1151
1184
  const links = [];
1185
+ let fallbackIndex = 0;
1152
1186
  for (const rawLinks of Object.values(item.links ?? {})) {
1153
1187
  if (!Array.isArray(rawLinks)) continue;
1154
- for (const rawLink of rawLinks) if (isXMLRichTextLink(rawLink) && !isTextAnnotationMarkerLink(rawLink)) links.push(rawLink);
1188
+ for (const rawLink of rawLinks) if (isXMLRichTextLink(rawLink) && !isTextAnnotationMarkerLink(rawLink)) {
1189
+ links.push({
1190
+ link: rawLink,
1191
+ fallbackIndex
1192
+ });
1193
+ fallbackIndex += 1;
1194
+ }
1155
1195
  }
1156
- return links;
1196
+ links.sort((left, right) => {
1197
+ const leftIndex = getXMLSourceIndex(left.link);
1198
+ const rightIndex = getXMLSourceIndex(right.link);
1199
+ if (leftIndex != null && rightIndex != null && leftIndex !== rightIndex) return leftIndex - rightIndex;
1200
+ return left.fallbackIndex - right.fallbackIndex;
1201
+ });
1202
+ const sortedLinks = [];
1203
+ for (const { link } of links) sortedLinks.push(link);
1204
+ return sortedLinks;
1157
1205
  }
1158
1206
  function renderRichTextItem(item, linkString, contentItem, options) {
1159
1207
  const { languages, rendering } = options;
@@ -1556,29 +1604,83 @@ function normalizeCategory(category) {
1556
1604
  function isHeadingItemCategory(category) {
1557
1605
  return HEADING_ITEM_CATEGORIES.includes(category);
1558
1606
  }
1559
- function pushCategoryIfPresent(categories, category, items) {
1560
- if ((items?.length ?? 0) === 0) return;
1561
- pushCategory(categories, category);
1562
- }
1563
1607
  function pushCategory(categories, category) {
1564
1608
  if (!categories.includes(category)) categories.push(category);
1565
1609
  }
1610
+ function getHierarchyEntryCategory(key) {
1611
+ switch (key) {
1612
+ case "heading":
1613
+ case "tree":
1614
+ case "bibliography":
1615
+ case "concept":
1616
+ case "spatialUnit":
1617
+ case "period":
1618
+ case "person":
1619
+ case "resource":
1620
+ case "text":
1621
+ case "set":
1622
+ case "dictionaryUnit": return key;
1623
+ case "propertyVariable":
1624
+ case "variable": return "propertyVariable";
1625
+ case "propertyValue":
1626
+ case "value": return "propertyValue";
1627
+ default: return null;
1628
+ }
1629
+ }
1630
+ function isRecord$3(value) {
1631
+ return typeof value === "object" && value != null;
1632
+ }
1633
+ function isResourceWrapper(value) {
1634
+ return isRecord$3(value) && !("uuid" in value) && Array.isArray(value.resource);
1635
+ }
1636
+ function sourceOrderSort(left, right) {
1637
+ const leftIndex = getXMLSourceIndex(left.item);
1638
+ const rightIndex = getXMLSourceIndex(right.item);
1639
+ if (leftIndex != null && rightIndex != null && leftIndex !== rightIndex) return leftIndex - rightIndex;
1640
+ return left.fallbackIndex - right.fallbackIndex;
1641
+ }
1642
+ function collectHierarchyEntries(hierarchy, categories) {
1643
+ const entries = [];
1644
+ if (hierarchy == null) return entries;
1645
+ let fallbackIndex = 0;
1646
+ for (const key of Object.keys(hierarchy)) {
1647
+ const category = getHierarchyEntryCategory(key);
1648
+ if (category == null || !(categories == null || categories.includes(category))) continue;
1649
+ const values = hierarchy[key];
1650
+ if (!Array.isArray(values)) continue;
1651
+ for (const value of values) {
1652
+ if (category === "resource" && isResourceWrapper(value)) {
1653
+ for (const resource of value.resource) {
1654
+ entries.push({
1655
+ category,
1656
+ item: resource,
1657
+ fallbackIndex
1658
+ });
1659
+ fallbackIndex += 1;
1660
+ }
1661
+ continue;
1662
+ }
1663
+ entries.push({
1664
+ category,
1665
+ item: value,
1666
+ fallbackIndex
1667
+ });
1668
+ fallbackIndex += 1;
1669
+ }
1670
+ }
1671
+ entries.sort(sourceOrderSort);
1672
+ return entries;
1673
+ }
1566
1674
  function inferItemCategories(hierarchy) {
1567
1675
  const categories = [];
1568
1676
  if (hierarchy == null) return categories;
1569
- for (const heading of hierarchy.heading ?? []) for (const category of inferItemCategories(heading)) pushCategory(categories, category);
1570
- for (const category of SET_ITEM_CATEGORIES) {
1571
- if (category === "propertyVariable") {
1572
- pushCategoryIfPresent(categories, category, hierarchy.propertyVariable);
1573
- pushCategoryIfPresent(categories, category, hierarchy.variable);
1574
- continue;
1575
- }
1576
- if (category === "propertyValue") {
1577
- pushCategoryIfPresent(categories, category, hierarchy.propertyValue);
1578
- pushCategoryIfPresent(categories, category, hierarchy.value);
1677
+ for (const entry of collectHierarchyEntries(hierarchy)) {
1678
+ if (entry.category === "heading") {
1679
+ for (const category of inferItemCategories(entry.item)) pushCategory(categories, category);
1579
1680
  continue;
1580
1681
  }
1581
- pushCategoryIfPresent(categories, category, hierarchy[category]);
1682
+ if (entry.category === "dictionaryUnit") continue;
1683
+ pushCategory(categories, entry.category);
1582
1684
  }
1583
1685
  return categories;
1584
1686
  }
@@ -1864,62 +1966,79 @@ function withoutItems(item) {
1864
1966
  const { items: _items, ...itemWithoutItems } = item;
1865
1967
  return itemWithoutItems;
1866
1968
  }
1969
+ function parseEmbeddedItemEntry(entry, options) {
1970
+ switch (entry.category) {
1971
+ case "tree": return parseTree(entry.item, {
1972
+ ...options,
1973
+ containedItemCategory: normalizeTreeItemCategory(options.containedItemCategory)
1974
+ });
1975
+ case "bibliography": return parseBibliography(entry.item, options);
1976
+ case "concept": return parseConcept(entry.item, options);
1977
+ case "spatialUnit": return parseSpatialUnit(entry.item, options);
1978
+ case "period": return parsePeriod(entry.item, options);
1979
+ case "person": return parsePerson(entry.item, options);
1980
+ case "propertyVariable": return parsePropertyVariable(entry.item, options);
1981
+ case "propertyValue": return parsePropertyValue(entry.item, options);
1982
+ case "resource": return parseResource(entry.item, options);
1983
+ case "text": return parseText(entry.item, options);
1984
+ case "set": return parseSet(entry.item, options);
1985
+ case "dictionaryUnit":
1986
+ case "heading": return null;
1987
+ }
1988
+ }
1867
1989
  function parseItemHierarchy(hierarchy, options, categories) {
1868
1990
  const items = [];
1869
1991
  if (hierarchy == null) return items;
1870
- const shouldParse = (category) => categories == null || categories.includes(category);
1871
- if (shouldParse("tree")) for (const tree of hierarchy.tree ?? []) items.push(parseTree(tree, options));
1872
- if (shouldParse("bibliography")) for (const bibliography of hierarchy.bibliography ?? []) items.push(parseBibliography(bibliography, options));
1873
- if (shouldParse("concept")) for (const concept of hierarchy.concept ?? []) items.push(parseConcept(concept, options));
1874
- if (shouldParse("spatialUnit")) for (const spatialUnit of hierarchy.spatialUnit ?? []) items.push(parseSpatialUnit(spatialUnit, options));
1875
- if (shouldParse("period")) for (const period of hierarchy.period ?? []) items.push(parsePeriod(period, options));
1876
- if (shouldParse("person")) for (const person of hierarchy.person ?? []) items.push(parsePerson(person, options));
1877
- if (shouldParse("propertyVariable")) {
1878
- for (const propertyVariable of hierarchy.propertyVariable ?? []) items.push(parsePropertyVariable(propertyVariable, options));
1879
- for (const propertyVariable of hierarchy.variable ?? []) items.push(parsePropertyVariable(propertyVariable, options));
1880
- }
1881
- if (shouldParse("propertyValue")) {
1882
- for (const propertyValue of hierarchy.propertyValue ?? []) items.push(parsePropertyValue(propertyValue, options));
1883
- for (const propertyValue of hierarchy.value ?? []) items.push(parsePropertyValue(propertyValue, options));
1884
- }
1885
- if (shouldParse("resource")) for (const resource of hierarchy.resource ?? []) {
1886
- if (!("uuid" in resource)) {
1887
- for (const embeddedResource of resource.resource) items.push(parseResource(embeddedResource, options));
1888
- continue;
1889
- }
1890
- items.push(parseResource(resource, options));
1992
+ for (const entry of collectHierarchyEntries(hierarchy, categories)) {
1993
+ const item = parseEmbeddedItemEntry(entry, options);
1994
+ if (item != null) items.push(item);
1891
1995
  }
1892
- if (shouldParse("text")) for (const text of hierarchy.text ?? []) items.push(parseText(text, options));
1893
- if (shouldParse("set")) for (const set of hierarchy.set ?? []) items.push(parseSet(set, options));
1894
1996
  return items;
1895
1997
  }
1896
1998
  function parseSetItemHierarchy(hierarchy, options, categories) {
1897
1999
  const items = [];
1898
2000
  if (hierarchy == null) return items;
1899
- const shouldParse = (category) => categories == null || categories.includes(category);
1900
- if (shouldParse("tree")) for (const tree of hierarchy.tree ?? []) items.push(parseSetTree(tree, options));
1901
- if (shouldParse("bibliography")) for (const bibliography of hierarchy.bibliography ?? []) items.push(parseSetBibliography(bibliography, options));
1902
- if (shouldParse("concept")) for (const concept of hierarchy.concept ?? []) items.push(parseSetConcept(concept, options));
1903
- if (shouldParse("spatialUnit")) for (const spatialUnit of hierarchy.spatialUnit ?? []) items.push(parseSetSpatialUnit(spatialUnit, options));
1904
- if (shouldParse("period")) for (const period of hierarchy.period ?? []) items.push(parseSetPeriod(period, options));
1905
- if (shouldParse("person")) for (const person of hierarchy.person ?? []) items.push(withSetItemProperties(parsePerson(person, options), person.properties, options));
1906
- if (shouldParse("propertyVariable")) {
1907
- for (const propertyVariable of hierarchy.propertyVariable ?? []) items.push(parsePropertyVariable(propertyVariable, options));
1908
- for (const propertyVariable of hierarchy.variable ?? []) items.push(parsePropertyVariable(propertyVariable, options));
1909
- }
1910
- if (shouldParse("propertyValue")) {
1911
- for (const propertyValue of hierarchy.propertyValue ?? []) items.push(withSetItemProperties(parsePropertyValue(propertyValue, options), propertyValue.properties, options));
1912
- for (const propertyValue of hierarchy.value ?? []) items.push(withSetItemProperties(parsePropertyValue(propertyValue, options), propertyValue.properties, options));
1913
- }
1914
- if (shouldParse("resource")) for (const resource of hierarchy.resource ?? []) {
1915
- if (!("uuid" in resource)) {
1916
- for (const embeddedResource of resource.resource) items.push(parseSetResource(embeddedResource, options));
1917
- continue;
2001
+ for (const entry of collectHierarchyEntries(hierarchy, categories)) switch (entry.category) {
2002
+ case "tree":
2003
+ items.push(parseSetTree(entry.item, options));
2004
+ break;
2005
+ case "bibliography":
2006
+ items.push(parseSetBibliography(entry.item, options));
2007
+ break;
2008
+ case "concept":
2009
+ items.push(parseSetConcept(entry.item, options));
2010
+ break;
2011
+ case "spatialUnit":
2012
+ items.push(parseSetSpatialUnit(entry.item, options));
2013
+ break;
2014
+ case "period":
2015
+ items.push(parseSetPeriod(entry.item, options));
2016
+ break;
2017
+ case "person": {
2018
+ const person = entry.item;
2019
+ items.push(withSetItemProperties(parsePerson(person, options), person.properties, options));
2020
+ break;
2021
+ }
2022
+ case "propertyVariable":
2023
+ items.push(parsePropertyVariable(entry.item, options));
2024
+ break;
2025
+ case "propertyValue": {
2026
+ const propertyValue = entry.item;
2027
+ items.push(withSetItemProperties(parsePropertyValue(propertyValue, options), propertyValue.properties, options));
2028
+ break;
1918
2029
  }
1919
- items.push(parseSetResource(resource, options));
2030
+ case "resource":
2031
+ items.push(parseSetResource(entry.item, options));
2032
+ break;
2033
+ case "text":
2034
+ items.push(parseText(entry.item, options));
2035
+ break;
2036
+ case "set":
2037
+ items.push(parseSetSet(entry.item, options));
2038
+ break;
2039
+ case "dictionaryUnit":
2040
+ case "heading": break;
1920
2041
  }
1921
- if (shouldParse("text")) for (const text of hierarchy.text ?? []) items.push(parseText(text, options));
1922
- if (shouldParse("set")) for (const set of hierarchy.set ?? []) items.push(parseSetSet(set, options));
1923
2042
  return items;
1924
2043
  }
1925
2044
  function normalizeTreeLinkItemsCategory(type) {
@@ -2088,26 +2207,45 @@ function parseLinks(rawLinks, options) {
2088
2207
  const links = [];
2089
2208
  if (rawLinks == null) return links;
2090
2209
  const hierarchy = rawLinks;
2091
- for (const tree of hierarchy.tree ?? []) links.push(parseTreeItemLink(tree, options));
2092
- for (const bibliography of hierarchy.bibliography ?? []) links.push(parseBibliographyItemLink(bibliography, options));
2093
- for (const concept of hierarchy.concept ?? []) links.push(parseConceptItemLink(concept, options));
2094
- for (const spatialUnit of hierarchy.spatialUnit ?? []) links.push(parseSpatialUnitItemLink(spatialUnit, options));
2095
- for (const period of hierarchy.period ?? []) links.push(parsePeriodItemLink(period, options));
2096
- for (const person of hierarchy.person ?? []) links.push(parsePersonItemLink(person, options));
2097
- for (const propertyVariable of hierarchy.propertyVariable ?? []) links.push(parsePropertyVariableItemLink(propertyVariable, options));
2098
- for (const propertyVariable of hierarchy.variable ?? []) links.push(parsePropertyVariableItemLink(propertyVariable, options));
2099
- for (const propertyValue of hierarchy.propertyValue ?? []) links.push(parsePropertyValueItemLink(propertyValue, options));
2100
- for (const propertyValue of hierarchy.value ?? []) links.push(parsePropertyValueItemLink(propertyValue, options));
2101
- for (const resource of hierarchy.resource ?? []) {
2102
- if (!("uuid" in resource)) {
2103
- for (const embeddedResource of resource.resource) links.push(parseResourceItemLink(embeddedResource, options));
2104
- continue;
2105
- }
2106
- links.push(parseResourceItemLink(resource, options));
2210
+ for (const entry of collectHierarchyEntries(hierarchy)) switch (entry.category) {
2211
+ case "tree":
2212
+ links.push(parseTreeItemLink(entry.item, options));
2213
+ break;
2214
+ case "bibliography":
2215
+ links.push(parseBibliographyItemLink(entry.item, options));
2216
+ break;
2217
+ case "concept":
2218
+ links.push(parseConceptItemLink(entry.item, options));
2219
+ break;
2220
+ case "spatialUnit":
2221
+ links.push(parseSpatialUnitItemLink(entry.item, options));
2222
+ break;
2223
+ case "period":
2224
+ links.push(parsePeriodItemLink(entry.item, options));
2225
+ break;
2226
+ case "person":
2227
+ links.push(parsePersonItemLink(entry.item, options));
2228
+ break;
2229
+ case "propertyVariable":
2230
+ links.push(parsePropertyVariableItemLink(entry.item, options));
2231
+ break;
2232
+ case "propertyValue":
2233
+ links.push(parsePropertyValueItemLink(entry.item, options));
2234
+ break;
2235
+ case "resource":
2236
+ links.push(parseResourceItemLink(entry.item, options));
2237
+ break;
2238
+ case "text":
2239
+ links.push(parseTextItemLink(entry.item, options));
2240
+ break;
2241
+ case "set":
2242
+ links.push(parseSetItemLink(entry.item, options));
2243
+ break;
2244
+ case "dictionaryUnit":
2245
+ links.push(parseDictionaryUnitItemLink(entry.item, options));
2246
+ break;
2247
+ case "heading": break;
2107
2248
  }
2108
- for (const text of hierarchy.text ?? []) links.push(parseTextItemLink(text, options));
2109
- for (const set of hierarchy.set ?? []) links.push(parseSetItemLink(set, options));
2110
- for (const dictionaryUnit of hierarchy.dictionaryUnit ?? []) links.push(parseDictionaryUnitItemLink(dictionaryUnit, options));
2111
2249
  return links;
2112
2250
  }
2113
2251
  function parseReverseLinks(rawLinks, options) {
@@ -2183,8 +2321,19 @@ function parseTree(rawTree, options) {
2183
2321
  const childOptions = getParserOptions(options);
2184
2322
  const containedItemCategory = resolveTreeItemCategory(rawTree, normalizeTreeItemCategory(options.containedItemCategory));
2185
2323
  const items = [];
2186
- if (containedItemCategory != null && isHeadingItemCategory(containedItemCategory)) for (const heading of rawTree.items?.heading ?? []) items.push(parseHeading(heading, containedItemCategory, childOptions));
2187
- if (containedItemCategory != null) items.push(...parseItemHierarchy(rawTree.items, childOptions, [containedItemCategory]));
2324
+ if (containedItemCategory != null) {
2325
+ const itemCategories = [containedItemCategory];
2326
+ if (isHeadingItemCategory(containedItemCategory)) itemCategories.push("heading");
2327
+ for (const entry of collectHierarchyEntries(rawTree.items, itemCategories)) {
2328
+ if (entry.category === "heading") {
2329
+ if (!isHeadingItemCategory(containedItemCategory)) continue;
2330
+ items.push(parseHeading(entry.item, containedItemCategory, childOptions));
2331
+ continue;
2332
+ }
2333
+ const item = parseEmbeddedItemEntry(entry, childOptions);
2334
+ if (item != null) items.push(item);
2335
+ }
2336
+ }
2188
2337
  return {
2189
2338
  ...parseBaseItem("tree", rawTree, childOptions),
2190
2339
  type: rawTree.type ?? null,
@@ -2516,22 +2665,10 @@ function parseLinkedItems(rawItems, options) {
2516
2665
  containedItemCategory: options.containedItemCategory
2517
2666
  };
2518
2667
  const items = [];
2519
- for (const tree of rawItems?.tree ?? []) items.push(parseTree(tree, {
2520
- ...parserOptions,
2521
- containedItemCategory: normalizeTreeItemCategory(options.containedItemCategory)
2522
- }));
2523
- for (const bibliography of rawItems?.bibliography ?? []) items.push(parseBibliography(bibliography, parserOptions));
2524
- for (const concept of rawItems?.concept ?? []) items.push(parseConcept(concept, parserOptions));
2525
- for (const spatialUnit of rawItems?.spatialUnit ?? []) items.push(parseSpatialUnit(spatialUnit, parserOptions));
2526
- for (const period of rawItems?.period ?? []) items.push(parsePeriod(period, parserOptions));
2527
- for (const person of rawItems?.person ?? []) items.push(parsePerson(person, parserOptions));
2528
- for (const propertyVariable of rawItems?.propertyVariable ?? []) items.push(parsePropertyVariable(propertyVariable, parserOptions));
2529
- for (const propertyVariable of rawItems?.variable ?? []) items.push(parsePropertyVariable(propertyVariable, parserOptions));
2530
- for (const propertyValue of rawItems?.propertyValue ?? []) items.push(parsePropertyValue(propertyValue, parserOptions));
2531
- for (const propertyValue of rawItems?.value ?? []) items.push(parsePropertyValue(propertyValue, parserOptions));
2532
- for (const resource of rawItems?.resource ?? []) items.push(parseResource(resource, parserOptions));
2533
- for (const text of rawItems?.text ?? []) items.push(parseText(text, parserOptions));
2534
- for (const set of rawItems?.set ?? []) items.push(parseSet(set, parserOptions));
2668
+ for (const entry of collectHierarchyEntries(rawItems)) {
2669
+ const item = parseEmbeddedItemEntry(entry, parserOptions);
2670
+ if (item != null) items.push(item);
2671
+ }
2535
2672
  return items;
2536
2673
  }
2537
2674
  function parseSetItems(rawItems, options) {
@@ -3481,6 +3618,7 @@ async function fetchGallery(params, options) {
3481
3618
  logIssues(issues);
3482
3619
  throw new Error("Failed to parse gallery XML");
3483
3620
  }
3621
+ restoreXMLMetadata(output, data);
3484
3622
  return {
3485
3623
  gallery: parseGallery(output, { languages: resolveGalleryLanguages(output, requestedLanguages) }),
3486
3624
  error: null
@@ -3532,39 +3670,46 @@ function resolveItemLinksLanguages(data, requestedLanguages) {
3532
3670
  function buildXQuery$2(uuid) {
3533
3671
  return `<ochre>{${`let $item-uuid := "${uuid}"
3534
3672
 
3535
- let $uuids :=
3536
- distinct-values((
3537
-
3538
- (: Direct links on most item categories :)
3539
- fn:collection("ochre/resource")/ochre[@uuid = $item-uuid]/resource/links/*/@uuid/string(),
3540
- fn:collection("ochre/bibliography")/ochre[@uuid = $item-uuid]/bibliography/links/*/@uuid/string(),
3541
- fn:collection("ochre/period")/ochre[@uuid = $item-uuid]/period/links/*/@uuid/string(),
3542
- fn:collection("ochre/person")/ochre[@uuid = $item-uuid]/person/links/*/@uuid/string(),
3543
- fn:collection("ochre/propertyVariable")/ochre[@uuid = $item-uuid]/propertyVariable/links/*/@uuid/string(),
3544
- fn:collection("ochre/propertyValue")/ochre[@uuid = $item-uuid]/propertyValue/links/*/@uuid/string(),
3545
- fn:collection("ochre/text")/ochre[@uuid = $item-uuid]/text/links/*/@uuid/string(),
3546
- fn:collection("ochre/tree")/ochre[@uuid = $item-uuid]/tree/links/*/@uuid/string(),
3547
- fn:collection("ochre/set")/ochre[@uuid = $item-uuid]/set/links/*/@uuid/string(),
3673
+ let $source-items := (
3674
+ fn:collection("ochre/resource")/ochre[@uuid = $item-uuid]/resource,
3675
+ fn:collection("ochre/bibliography")/ochre[@uuid = $item-uuid]/bibliography,
3676
+ fn:collection("ochre/period")/ochre[@uuid = $item-uuid]/period,
3677
+ fn:collection("ochre/person")/ochre[@uuid = $item-uuid]/person,
3678
+ fn:collection("ochre/propertyVariable")/ochre[@uuid = $item-uuid]/propertyVariable,
3679
+ fn:collection("ochre/propertyValue")/ochre[@uuid = $item-uuid]/propertyValue,
3680
+ fn:collection("ochre/text")/ochre[@uuid = $item-uuid]/text,
3681
+ fn:collection("ochre/tree")/ochre[@uuid = $item-uuid]/tree,
3682
+ fn:collection("ochre/set")/ochre[@uuid = $item-uuid]/set,
3683
+ fn:collection("ochre/spatialUnit")/ochre[@uuid = $item-uuid]/spatialUnit,
3684
+ fn:collection("ochre/concept")/ochre[@uuid = $item-uuid]/concept
3685
+ )
3548
3686
 
3549
- (: Special category structures :)
3550
- fn:collection("ochre/spatialUnit")/ochre[@uuid = $item-uuid]/spatialUnit/observations/observation/links/*/@uuid/string(),
3551
- fn:collection("ochre/concept")/ochre[@uuid = $item-uuid]/concept/interpretations/interpretation/links/*/@uuid/string()
3552
- ))
3687
+ let $link-nodes := (
3688
+ $source-items/links/*,
3689
+ $source-items/observations/observation/links/*,
3690
+ $source-items/interpretations/interpretation/links/*
3691
+ )
3553
3692
 
3554
3693
  return
3555
- <items>{(
3556
- fn:collection("ochre/resource")/ochre/resource[@uuid = $uuids],
3557
- fn:collection("ochre/bibliography")/ochre/bibliography[@uuid = $uuids],
3558
- fn:collection("ochre/period")/ochre/period[@uuid = $uuids],
3559
- fn:collection("ochre/person")/ochre/person[@uuid = $uuids],
3560
- fn:collection("ochre/propertyVariable")/ochre/propertyVariable[@uuid = $uuids],
3561
- fn:collection("ochre/propertyValue")/ochre/propertyValue[@uuid = $uuids],
3562
- fn:collection("ochre/text")/ochre/text[@uuid = $uuids],
3563
- fn:collection("ochre/tree")/ochre/tree[@uuid = $uuids],
3564
- fn:collection("ochre/set")/ochre/set[@uuid = $uuids],
3565
- fn:collection("ochre/spatialUnit")/ochre/spatialUnit[@uuid = $uuids],
3566
- fn:collection("ochre/concept")/ochre/concept[@uuid = $uuids]
3567
- )}</items>`}}</ochre>`;
3694
+ <items>{
3695
+ for $link at $position in $link-nodes
3696
+ let $uuid := $link/@uuid/string()
3697
+ let $category := name($link)
3698
+ where $uuid ne "" and not($uuid = $link-nodes[position() lt $position]/@uuid/string())
3699
+ return
3700
+ if ($category = "resource") then fn:collection("ochre/resource")/ochre/resource[@uuid = $uuid]
3701
+ else if ($category = "bibliography") then fn:collection("ochre/bibliography")/ochre/bibliography[@uuid = $uuid]
3702
+ else if ($category = "period") then fn:collection("ochre/period")/ochre/period[@uuid = $uuid]
3703
+ else if ($category = "person") then fn:collection("ochre/person")/ochre/person[@uuid = $uuid]
3704
+ else if ($category = "propertyVariable" or $category = "variable") then fn:collection("ochre/propertyVariable")/ochre/propertyVariable[@uuid = $uuid]
3705
+ else if ($category = "propertyValue" or $category = "value") then fn:collection("ochre/propertyValue")/ochre/propertyValue[@uuid = $uuid]
3706
+ else if ($category = "text") then fn:collection("ochre/text")/ochre/text[@uuid = $uuid]
3707
+ else if ($category = "tree") then fn:collection("ochre/tree")/ochre/tree[@uuid = $uuid]
3708
+ else if ($category = "set") then fn:collection("ochre/set")/ochre/set[@uuid = $uuid]
3709
+ else if ($category = "spatialUnit") then fn:collection("ochre/spatialUnit")/ochre/spatialUnit[@uuid = $uuid]
3710
+ else if ($category = "concept") then fn:collection("ochre/concept")/ochre/concept[@uuid = $uuid]
3711
+ else ()
3712
+ }</items>`}}</ochre>`;
3568
3713
  }
3569
3714
  async function fetchItemLinks(uuid, options) {
3570
3715
  try {
@@ -3583,6 +3728,7 @@ async function fetchItemLinks(uuid, options) {
3583
3728
  logIssues(issues);
3584
3729
  throw new Error("Failed to parse OCHRE item links");
3585
3730
  }
3731
+ restoreXMLMetadata(output, data);
3586
3732
  const languages = resolveItemLinksLanguages(output, requestedLanguages);
3587
3733
  return {
3588
3734
  items: parseLinkedItems(output.result.ochre.items, {
@@ -3683,6 +3829,7 @@ async function fetchItem(uuid, options) {
3683
3829
  logIssues(issues);
3684
3830
  throw new Error("Failed to parse OCHRE data");
3685
3831
  }
3832
+ restoreXMLMetadata(output, data);
3686
3833
  const category = options?.category ?? inferFetchItemCategory(output.result.ochre);
3687
3834
  assertItemCategoryAllowed(category, options?.containedItemCategory);
3688
3835
  return {
@@ -4756,6 +4903,7 @@ async function fetchSetItems(params, containedItemCategories, options) {
4756
4903
  logIssues(issues);
4757
4904
  throw new Error("Failed to parse OCHRE Set items");
4758
4905
  }
4906
+ restoreXMLMetadata(output, data);
4759
4907
  if (containedItemCategories != null) {
4760
4908
  const missingCategories = containedItemCategories.filter((category) => !hasSetItemsCategory(output.result.ochre.items, category));
4761
4909
  if (missingCategories.length > 0) throw new Error(`No Set items found for item categories: ${missingCategories.join(", ")}`);
@@ -6096,15 +6244,14 @@ function parseWebElementProperties(componentProperty, elementResource, options)
6096
6244
  break;
6097
6245
  }
6098
6246
  case "image": {
6099
- const imageLinks = getWebsiteLinks(websiteLinks, "resource").filter((link) => link.type === "image" || link.type === "IIIF");
6100
- if (imageLinks.length === 0) throw new Error(formatComponentError("No links found", componentName, elementResource));
6247
+ if (websiteLinks.length === 0) throw new Error(formatComponentError("No links found", componentName, elementResource));
6101
6248
  const imageQuality = componentReader.valueOr("image-quality", "high");
6102
6249
  const images = [];
6103
- for (const link of imageLinks) images.push({
6250
+ for (const link of websiteLinks) images.push({
6104
6251
  uuid: link.uuid,
6105
6252
  label: link.identification.label,
6106
- width: link.image?.width ?? 0,
6107
- height: link.image?.height ?? 0,
6253
+ width: "image" in link ? link.image?.width ?? 0 : 0,
6254
+ height: "image" in link ? link.image?.height ?? 0 : 0,
6108
6255
  description: link.description,
6109
6256
  quality: imageQuality
6110
6257
  });
@@ -7033,6 +7180,7 @@ async function fetchWebsite(abbreviation, options) {
7033
7180
  logIssues(issues);
7034
7181
  throw new Error("Failed to parse website XML");
7035
7182
  }
7183
+ restoreXMLMetadata(output, data);
7036
7184
  return {
7037
7185
  website: parseWebsite(output, { languages: options?.languages }),
7038
7186
  error: null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ochre-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
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",
@@ -45,7 +45,7 @@
45
45
  "dependencies": {
46
46
  "date-fns": "^4.1.0",
47
47
  "fast-equals": "^6.0.0",
48
- "fast-xml-parser": "^5.7.3",
48
+ "fast-xml-parser": "^5.8.0",
49
49
  "valibot": "^1.4.0"
50
50
  },
51
51
  "devDependencies": {