ochre-sdk 1.0.5 → 1.0.7
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 +1 -1
- package/dist/index.mjs +144 -88
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ const result = await fetchItem("<item-uuid>", {
|
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
if (result.error != null) {
|
|
36
|
-
throw new Error(result.error);
|
|
36
|
+
throw new Error("Failed to fetch item", { cause: result.error });
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
console.log(result.item.identification.label.getText("eng"));
|
package/dist/index.mjs
CHANGED
|
@@ -487,7 +487,45 @@ var MultilingualString = class MultilingualString {
|
|
|
487
487
|
};
|
|
488
488
|
//#endregion
|
|
489
489
|
//#region src/utils.ts
|
|
490
|
+
const MAX_SCHEMA_VALIDATION_ISSUES = 3;
|
|
490
491
|
const PSEUDO_UUID_REGEX = /^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/i;
|
|
492
|
+
function getSchemaValidationLeafIssues(issues, leaves = []) {
|
|
493
|
+
for (const issue of issues) {
|
|
494
|
+
if (issue.issues != null && issue.issues.length > 0) {
|
|
495
|
+
getSchemaValidationLeafIssues(issue.issues, leaves);
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
leaves.push(issue);
|
|
499
|
+
}
|
|
500
|
+
return leaves;
|
|
501
|
+
}
|
|
502
|
+
function formatSchemaValidationIssue(issue) {
|
|
503
|
+
const path = v.getDotPath(issue);
|
|
504
|
+
return `${path != null && path.length > 0 ? path : "(root)"}: ${issue.message}`;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Formats Valibot validation issues for compact error messages.
|
|
508
|
+
* @param issues - The validation issues to format
|
|
509
|
+
* @internal
|
|
510
|
+
*/
|
|
511
|
+
function formatSchemaValidationIssues(issues) {
|
|
512
|
+
const leafIssues = getSchemaValidationLeafIssues(issues);
|
|
513
|
+
const issuesToFormat = leafIssues.length > 0 ? leafIssues : issues;
|
|
514
|
+
const formattedIssues = [];
|
|
515
|
+
for (const issue of issuesToFormat.slice(0, MAX_SCHEMA_VALIDATION_ISSUES)) formattedIssues.push(formatSchemaValidationIssue(issue));
|
|
516
|
+
const hiddenIssueCount = issuesToFormat.length - formattedIssues.length;
|
|
517
|
+
if (hiddenIssueCount > 0) formattedIssues.push(`+${hiddenIssueCount.toLocaleString("en-US")} more`);
|
|
518
|
+
return `Schema validation failed: ${formattedIssues.join("; ")}`;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Creates an Error whose message includes compact schema-validation details.
|
|
522
|
+
* @param message - The base error message
|
|
523
|
+
* @param issues - The validation issues to include
|
|
524
|
+
* @internal
|
|
525
|
+
*/
|
|
526
|
+
function createSchemaValidationError(message, issues) {
|
|
527
|
+
return new Error(`${message}. ${formatSchemaValidationIssues(issues)}`, { cause: issues });
|
|
528
|
+
}
|
|
491
529
|
/**
|
|
492
530
|
* Logs Valibot validation issues to the console with detailed formatting
|
|
493
531
|
* @param issues - The validation issues to log
|
|
@@ -1409,6 +1447,12 @@ function parseStringContent(value, options = FALLBACK_PARSER_OPTIONS) {
|
|
|
1409
1447
|
}
|
|
1410
1448
|
//#endregion
|
|
1411
1449
|
//#region src/parsers/index.ts
|
|
1450
|
+
function isXMLContextGroup(context) {
|
|
1451
|
+
return "context" in context;
|
|
1452
|
+
}
|
|
1453
|
+
function isXMLContextItem(context) {
|
|
1454
|
+
return "project" in context;
|
|
1455
|
+
}
|
|
1412
1456
|
const SET_ITEM_CATEGORIES = ["tree", ...[
|
|
1413
1457
|
"bibliography",
|
|
1414
1458
|
"concept",
|
|
@@ -1521,27 +1565,33 @@ function emptyContextItem() {
|
|
|
1521
1565
|
}
|
|
1522
1566
|
function parseContext(rawContext) {
|
|
1523
1567
|
const nodes = [];
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1568
|
+
let displayPath = "";
|
|
1569
|
+
for (const rawContextOuterItem of rawContext) {
|
|
1570
|
+
if (!isXMLContextGroup(rawContextOuterItem)) continue;
|
|
1571
|
+
displayPath = displayPath || rawContextOuterItem.displayPath;
|
|
1572
|
+
for (const rawContextItem of rawContextOuterItem.context) {
|
|
1573
|
+
if (!isXMLContextItem(rawContextItem)) continue;
|
|
1574
|
+
const node = {
|
|
1575
|
+
tree: rawContextItem.tree[0] == null ? emptyContextItem() : parseContextItem$1(rawContextItem.tree[0]),
|
|
1576
|
+
project: parseContextItem$1(rawContextItem.project),
|
|
1577
|
+
heading: []
|
|
1578
|
+
};
|
|
1579
|
+
const rawContextValues = rawContextItem;
|
|
1580
|
+
for (const heading of rawContextValues.heading ?? []) node.heading.push(parseContextItem$1(heading));
|
|
1581
|
+
for (const { raw, parsed } of CONTEXT_CATEGORY_MAPPINGS) {
|
|
1582
|
+
const contextValues = rawContextValues[raw] ?? [];
|
|
1583
|
+
for (const contextValue of contextValues) {
|
|
1584
|
+
const parsedItems = node[parsed] ?? [];
|
|
1585
|
+
parsedItems.push(parseContextItem$1(contextValue));
|
|
1586
|
+
node[parsed] = parsedItems;
|
|
1587
|
+
}
|
|
1538
1588
|
}
|
|
1589
|
+
nodes.push(node);
|
|
1539
1590
|
}
|
|
1540
|
-
nodes.push(node);
|
|
1541
1591
|
}
|
|
1542
1592
|
return {
|
|
1543
1593
|
nodes,
|
|
1544
|
-
displayPath
|
|
1594
|
+
displayPath
|
|
1545
1595
|
};
|
|
1546
1596
|
}
|
|
1547
1597
|
function parseEventReference(rawReference, options) {
|
|
@@ -1692,16 +1742,19 @@ function normalizeSetItemCategories(containedItemCategory) {
|
|
|
1692
1742
|
return uniqueCategories;
|
|
1693
1743
|
}
|
|
1694
1744
|
function normalizeTreeItemCategory(containedItemCategory) {
|
|
1695
|
-
if (containedItemCategory != null && typeof containedItemCategory !== "string") throw new Error("Tree containedItemCategory must be a single category");
|
|
1696
|
-
if (containedItemCategory === "tree") throw new Error("Tree containedItemCategory cannot be \"tree\"");
|
|
1745
|
+
if (containedItemCategory != null && typeof containedItemCategory !== "string") throw new Error("Tree containedItemCategory must be a single category", { cause: containedItemCategory });
|
|
1746
|
+
if (containedItemCategory === "tree") throw new Error("Tree containedItemCategory cannot be \"tree\"", { cause: containedItemCategory });
|
|
1697
1747
|
return containedItemCategory;
|
|
1698
1748
|
}
|
|
1699
1749
|
function resolveTreeItemCategory(rawTree, containedItemCategory) {
|
|
1700
1750
|
const inferredCategories = inferItemCategories(rawTree.items);
|
|
1701
|
-
if (inferredCategories.length > 1) throw new Error(`Expected Tree items to contain one category, received ${inferredCategories.join(", ")}
|
|
1751
|
+
if (inferredCategories.length > 1) throw new Error(`Expected Tree items to contain one category, received ${inferredCategories.join(", ")}`, { cause: inferredCategories });
|
|
1702
1752
|
const inferredCategory = inferredCategories[0] ?? null;
|
|
1703
|
-
if (inferredCategory === "tree") throw new Error("Tree items cannot contain category \"tree\"");
|
|
1704
|
-
if (containedItemCategory != null && inferredCategory != null && containedItemCategory !== inferredCategory) throw new Error(`Tree containedItemCategory "${containedItemCategory}" does not match XML items category "${inferredCategory}"
|
|
1753
|
+
if (inferredCategory === "tree") throw new Error("Tree items cannot contain category \"tree\"", { cause: inferredCategory });
|
|
1754
|
+
if (containedItemCategory != null && inferredCategory != null && containedItemCategory !== inferredCategory) throw new Error(`Tree containedItemCategory "${containedItemCategory}" does not match XML items category "${inferredCategory}"`, { cause: {
|
|
1755
|
+
containedItemCategory,
|
|
1756
|
+
inferredCategory
|
|
1757
|
+
} });
|
|
1705
1758
|
return containedItemCategory ?? inferredCategory;
|
|
1706
1759
|
}
|
|
1707
1760
|
function parseImage(rawImage, options) {
|
|
@@ -1849,12 +1902,13 @@ function parseNotes(rawNotes, options) {
|
|
|
1849
1902
|
}
|
|
1850
1903
|
function parsePropertyDataType(dataType) {
|
|
1851
1904
|
if (dataType == null || dataType === "") return "string";
|
|
1852
|
-
|
|
1853
|
-
|
|
1905
|
+
const normalizedDataType = dataType.startsWith("xs:") ? dataType.slice(3) : dataType;
|
|
1906
|
+
for (const propertyDataType of PROPERTY_DATA_TYPES) if (normalizedDataType === propertyDataType) return propertyDataType;
|
|
1907
|
+
throw new Error(`Invalid property value data type: ${dataType}`, { cause: dataType });
|
|
1854
1908
|
}
|
|
1855
1909
|
function parsePropertyValueContent(value, options) {
|
|
1856
1910
|
const dataType = parsePropertyDataType(value.dataType);
|
|
1857
|
-
const rawLabel = value.content
|
|
1911
|
+
const rawLabel = value.content != null ? parseRequiredContentLike(value, options) : value.payload != null && value.payload !== "" ? multilingualFromText(value.payload, options) : null;
|
|
1858
1912
|
const displayText = rawLabel?.getText() ?? value.payload ?? value.slug ?? "";
|
|
1859
1913
|
const contentText = value.rawValue ?? value.payload ?? displayText;
|
|
1860
1914
|
const common = {
|
|
@@ -2580,7 +2634,7 @@ function resolveLanguages(requestedLanguages, metadataLanguages) {
|
|
|
2580
2634
|
if (requestedLanguages.length === 0) return metadataLanguages;
|
|
2581
2635
|
const unsupportedLanguages = [];
|
|
2582
2636
|
for (const requestedLanguage of requestedLanguages) if (!metadataLanguages.some((metadataLanguage) => metadataLanguage.toLocaleLowerCase("en-US") === requestedLanguage.toLocaleLowerCase("en-US"))) unsupportedLanguages.push(requestedLanguage);
|
|
2583
|
-
if (unsupportedLanguages.length > 0) throw new Error(`The following language(s) are not supported by the dataset: ${unsupportedLanguages.toSorted((a, b) => a.localeCompare(b, "en-US")).join(", ")}. Available languages: ${metadataLanguages.toSorted((a, b) => a.localeCompare(b, "en-US")).join(", ")}
|
|
2637
|
+
if (unsupportedLanguages.length > 0) throw new Error(`The following language(s) are not supported by the dataset: ${unsupportedLanguages.toSorted((a, b) => a.localeCompare(b, "en-US")).join(", ")}. Available languages: ${metadataLanguages.toSorted((a, b) => a.localeCompare(b, "en-US")).join(", ")}`, { cause: unsupportedLanguages });
|
|
2584
2638
|
return requestedLanguages;
|
|
2585
2639
|
}
|
|
2586
2640
|
function resolveDefaultLanguage(rawOchre, languages) {
|
|
@@ -2590,7 +2644,7 @@ function resolveDefaultLanguage(rawOchre, languages) {
|
|
|
2590
2644
|
}
|
|
2591
2645
|
for (const language of languages) if (language === DEFAULT_LANGUAGES[0]) return language;
|
|
2592
2646
|
const firstLanguage = languages[0];
|
|
2593
|
-
if (firstLanguage == null) throw new Error("Default language not found");
|
|
2647
|
+
if (firstLanguage == null) throw new Error("Default language not found", { cause: languages });
|
|
2594
2648
|
return firstLanguage;
|
|
2595
2649
|
}
|
|
2596
2650
|
function parseMetadataPublisher(rawPublisher) {
|
|
@@ -2635,11 +2689,11 @@ function inferTopLevelCategory(rawOchre) {
|
|
|
2635
2689
|
for (const category of SET_ITEM_CATEGORIES) if (category in rawOchre) return category;
|
|
2636
2690
|
if ("variable" in rawOchre) return "propertyVariable";
|
|
2637
2691
|
if ("value" in rawOchre) return "propertyValue";
|
|
2638
|
-
throw new Error("Could not infer OCHRE item category");
|
|
2692
|
+
throw new Error("Could not infer OCHRE item category", { cause: rawOchre });
|
|
2639
2693
|
}
|
|
2640
2694
|
function getSingleTopLevelRawItem(items, category) {
|
|
2641
|
-
if (items == null || items.length === 0) throw new Error(`${category} not found
|
|
2642
|
-
if (items.length > 1) throw new Error(`Expected one ${category}, received ${items.length}
|
|
2695
|
+
if (items == null || items.length === 0) throw new Error(`${category} not found`, { cause: items });
|
|
2696
|
+
if (items.length > 1) throw new Error(`Expected one ${category}, received ${items.length}`, { cause: items });
|
|
2643
2697
|
return items[0];
|
|
2644
2698
|
}
|
|
2645
2699
|
function parseTopLevelItem(rawOchre, category, options) {
|
|
@@ -2826,10 +2880,12 @@ const XMLContextItem = v.objectWithRest({
|
|
|
2826
2880
|
tree: v.array(XMLContextValue),
|
|
2827
2881
|
displayPath: v.string("XMLContextItem: displayPath is string and required")
|
|
2828
2882
|
}, v.array(XMLContextValue), "XMLContextItem: Shape error");
|
|
2829
|
-
const
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2883
|
+
const XMLEmptyContext = v.object({ payload: v.string("XMLEmptyContext: payload is string and required") }, "XMLEmptyContext: Shape error");
|
|
2884
|
+
const XMLContextGroup = v.object({
|
|
2885
|
+
context: v.array(v.union([XMLContextItem, XMLEmptyContext]), "XMLContextGroup: context is array of XMLContextItem or XMLEmptyContext"),
|
|
2886
|
+
displayPath: v.string("XMLContextGroup: displayPath is string and required")
|
|
2887
|
+
}, "XMLContextGroup: Shape error");
|
|
2888
|
+
const XMLContext = v.array(v.union([XMLContextGroup, XMLEmptyContext], "XMLContext: item is XMLContextGroup or XMLEmptyContext"));
|
|
2833
2889
|
const XMLEvent = v.object({
|
|
2834
2890
|
dateTime: v.optional(customDateTime("XMLEvent: dateTime is not a valid datetime")),
|
|
2835
2891
|
endDateTime: v.optional(customDateTime("XMLEvent: endDateTime is not a valid datetime")),
|
|
@@ -3612,13 +3668,13 @@ async function fetchGallery(params, options) {
|
|
|
3612
3668
|
}),
|
|
3613
3669
|
headers: { "Content-Type": "application/xquery" }
|
|
3614
3670
|
});
|
|
3615
|
-
if (!response.ok) throw new Error("Error fetching gallery items, please try again later.");
|
|
3671
|
+
if (!response.ok) throw new Error("Error fetching gallery items, please try again later.", { cause: response.statusText });
|
|
3616
3672
|
const dataRaw = await response.text();
|
|
3617
3673
|
const data = new XMLParser(XML_PARSER_OPTIONS).parse(dataRaw);
|
|
3618
3674
|
const { success, issues, output } = v.safeParse(XMLGalleryData, data);
|
|
3619
3675
|
if (!success) {
|
|
3620
3676
|
logIssues(issues);
|
|
3621
|
-
throw
|
|
3677
|
+
throw createSchemaValidationError("Failed to parse gallery XML", issues);
|
|
3622
3678
|
}
|
|
3623
3679
|
restoreXMLMetadata(output, data);
|
|
3624
3680
|
return {
|
|
@@ -3722,13 +3778,13 @@ async function fetchItemLinks(uuid, options) {
|
|
|
3722
3778
|
body: buildXQuery$2(parsedUuid),
|
|
3723
3779
|
headers: { "Content-Type": "application/xquery" }
|
|
3724
3780
|
});
|
|
3725
|
-
if (!response.ok) throw new Error("Failed to fetch OCHRE item links");
|
|
3781
|
+
if (!response.ok) throw new Error("Failed to fetch OCHRE item links", { cause: response.statusText });
|
|
3726
3782
|
const dataRaw = await response.text();
|
|
3727
3783
|
const data = new XMLParser(XML_PARSER_OPTIONS).parse(dataRaw);
|
|
3728
3784
|
const { success, issues, output } = v.safeParse(XMLItemLinksData, data);
|
|
3729
3785
|
if (!success) {
|
|
3730
3786
|
logIssues(issues);
|
|
3731
|
-
throw
|
|
3787
|
+
throw createSchemaValidationError("Failed to parse OCHRE item links", issues);
|
|
3732
3788
|
}
|
|
3733
3789
|
restoreXMLMetadata(output, data);
|
|
3734
3790
|
const languages = resolveItemLinksLanguages(output, requestedLanguages);
|
|
@@ -3788,7 +3844,7 @@ function inferFetchItemCategory(rawOchre) {
|
|
|
3788
3844
|
if ("resource" in rawOchre) return "resource";
|
|
3789
3845
|
if ("text" in rawOchre) return "text";
|
|
3790
3846
|
if ("set" in rawOchre) return "set";
|
|
3791
|
-
throw new Error("Could not infer OCHRE item category");
|
|
3847
|
+
throw new Error("Could not infer OCHRE item category", { cause: rawOchre });
|
|
3792
3848
|
}
|
|
3793
3849
|
/**
|
|
3794
3850
|
* Validate language codes while preserving literal tuple inference.
|
|
@@ -3823,13 +3879,13 @@ async function fetchItem(uuid, options) {
|
|
|
3823
3879
|
assertItemCategoryAllowed(options?.category, options?.containedItemCategory);
|
|
3824
3880
|
const languages = options?.languages == null ? [] : parseLanguages$1(options.languages);
|
|
3825
3881
|
const response = await (options?.fetch ?? fetch)(`https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=${parsedUuid}&xsl=none&lang="*"`);
|
|
3826
|
-
if (!response.ok) throw new Error("Failed to fetch OCHRE data");
|
|
3882
|
+
if (!response.ok) throw new Error("Failed to fetch OCHRE data", { cause: response.statusText });
|
|
3827
3883
|
const dataRaw = await response.text();
|
|
3828
3884
|
const data = new XMLParser(XML_PARSER_OPTIONS).parse(dataRaw);
|
|
3829
3885
|
const { success, issues, output } = v.safeParse(XMLData, data);
|
|
3830
3886
|
if (!success) {
|
|
3831
3887
|
logIssues(issues);
|
|
3832
|
-
throw
|
|
3888
|
+
throw createSchemaValidationError("Failed to parse OCHRE data", issues);
|
|
3833
3889
|
}
|
|
3834
3890
|
restoreXMLMetadata(output, data);
|
|
3835
3891
|
const category = options?.category ?? inferFetchItemCategory(output.result.ochre);
|
|
@@ -4514,7 +4570,7 @@ function buildLeafQueryExpression(context, query) {
|
|
|
4514
4570
|
if (query.target === "property" && query.dataType !== "date" && query.dataType !== "dateTime" && !("value" in query) && query.propertyVariable != null) return buildPropertyPresenceQueryExpression({ propertyVariable: query.propertyVariable });
|
|
4515
4571
|
if (query.target === "property" && (query.dataType === "date" || query.dataType === "dateTime") && query.value == null) return buildPropertyDateRangeQueryExpression(query);
|
|
4516
4572
|
const searchValue = getLeafSearchValue(query);
|
|
4517
|
-
if (searchValue == null) throw new Error("Missing searchable value for query leaf");
|
|
4573
|
+
if (searchValue == null) throw new Error("Missing searchable value for query leaf", { cause: query });
|
|
4518
4574
|
const exactHelper = registerLeafHelper({
|
|
4519
4575
|
context,
|
|
4520
4576
|
query,
|
|
@@ -4589,9 +4645,9 @@ function getCompatibleIncludesGroupLeaves(query) {
|
|
|
4589
4645
|
}
|
|
4590
4646
|
function buildIncludesGroupQueryExpression(context, queries) {
|
|
4591
4647
|
const firstQuery = queries[0];
|
|
4592
|
-
if (firstQuery == null) throw new Error("Cannot build an includes group without queries");
|
|
4648
|
+
if (firstQuery == null) throw new Error("Cannot build an includes group without queries", { cause: queries });
|
|
4593
4649
|
const groupValue = getGroupableIncludesValue(firstQuery);
|
|
4594
|
-
if (groupValue == null) throw new Error("Cannot build an includes group without a search value");
|
|
4650
|
+
if (groupValue == null) throw new Error("Cannot build an includes group without a search value", { cause: firstQuery });
|
|
4595
4651
|
const terms = tokenizeIncludesSearchValue({
|
|
4596
4652
|
value: groupValue,
|
|
4597
4653
|
isCaseSensitive: firstQuery.isCaseSensitive
|
|
@@ -4897,18 +4953,18 @@ async function fetchSetItems(params, containedItemCategories, options) {
|
|
|
4897
4953
|
body: xquery,
|
|
4898
4954
|
headers: { "Content-Type": "application/xquery" }
|
|
4899
4955
|
});
|
|
4900
|
-
if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}
|
|
4956
|
+
if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`, { cause: response.statusText });
|
|
4901
4957
|
const dataRaw = await response.text();
|
|
4902
4958
|
const data = new XMLParser(XML_PARSER_OPTIONS).parse(dataRaw);
|
|
4903
4959
|
const { success, issues, output } = v.safeParse(XMLSetItemsData, data);
|
|
4904
4960
|
if (!success) {
|
|
4905
4961
|
logIssues(issues);
|
|
4906
|
-
throw
|
|
4962
|
+
throw createSchemaValidationError("Failed to parse OCHRE Set items", issues);
|
|
4907
4963
|
}
|
|
4908
4964
|
restoreXMLMetadata(output, data);
|
|
4909
4965
|
if (containedItemCategories != null) {
|
|
4910
4966
|
const missingCategories = containedItemCategories.filter((category) => !hasSetItemsCategory(output.result.ochre.items, category));
|
|
4911
|
-
if (missingCategories.length > 0) throw new Error(`No Set items found for item categories: ${missingCategories.join(", ")}
|
|
4967
|
+
if (missingCategories.length > 0) throw new Error(`No Set items found for item categories: ${missingCategories.join(", ")}`, { cause: missingCategories });
|
|
4912
4968
|
}
|
|
4913
4969
|
const languages = resolveSetItemsLanguages(output, requestedLanguages);
|
|
4914
4970
|
const items = parseSetItems(output.result.ochre.items, {
|
|
@@ -5374,13 +5430,13 @@ async function fetchSetPropertyValues(params, options) {
|
|
|
5374
5430
|
body: xquery,
|
|
5375
5431
|
headers: { "Content-Type": "application/xquery" }
|
|
5376
5432
|
});
|
|
5377
|
-
if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}
|
|
5433
|
+
if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`, { cause: response.statusText });
|
|
5378
5434
|
const dataRaw = await response.text();
|
|
5379
5435
|
const data = new XMLParser(XML_PARSER_OPTIONS).parse(dataRaw);
|
|
5380
5436
|
const { success, issues, output } = v.safeParse(responseSchema, data);
|
|
5381
5437
|
if (!success) {
|
|
5382
5438
|
logIssues(issues);
|
|
5383
|
-
throw
|
|
5439
|
+
throw createSchemaValidationError("Failed to parse OCHRE Set property values", issues);
|
|
5384
5440
|
}
|
|
5385
5441
|
const parsedPropertyValues = [];
|
|
5386
5442
|
const parsedAttributeValues = [];
|
|
@@ -5794,7 +5850,7 @@ var WebsitePresentationReader = class WebsitePresentationReader {
|
|
|
5794
5850
|
}
|
|
5795
5851
|
requiredProperty(label, message) {
|
|
5796
5852
|
const property = this.property(label);
|
|
5797
|
-
if (property === null) throw new Error(message);
|
|
5853
|
+
if (property === null) throw new Error(message, { cause: this.sourceProperties });
|
|
5798
5854
|
return property;
|
|
5799
5855
|
}
|
|
5800
5856
|
propertyByValue(label, value) {
|
|
@@ -5942,7 +5998,7 @@ function parseResponsiveCssStyles(properties) {
|
|
|
5942
5998
|
*/
|
|
5943
5999
|
function parseBounds(bounds) {
|
|
5944
6000
|
const [southWest, northEast] = bounds.trim().startsWith("[") ? parseJsonBounds(bounds) : bounds.split(";").map((pair) => pair.split(",").map((coordinate) => Number.parseFloat(coordinate.trim())));
|
|
5945
|
-
if (southWest?.length !== 2 || northEast?.length !== 2 || southWest.some((coordinate) => Number.isNaN(coordinate)) || northEast.some((coordinate) => Number.isNaN(coordinate))) throw new Error(`Invalid bounds: ${bounds}
|
|
6001
|
+
if (southWest?.length !== 2 || northEast?.length !== 2 || southWest.some((coordinate) => Number.isNaN(coordinate)) || northEast.some((coordinate) => Number.isNaN(coordinate))) throw new Error(`Invalid bounds: ${bounds}`, { cause: bounds });
|
|
5946
6002
|
return [[southWest[0], southWest[1]], [northEast[0], northEast[1]]];
|
|
5947
6003
|
}
|
|
5948
6004
|
function parseJsonBounds(bounds) {
|
|
@@ -5950,9 +6006,9 @@ function parseJsonBounds(bounds) {
|
|
|
5950
6006
|
try {
|
|
5951
6007
|
parsed = JSON.parse(bounds);
|
|
5952
6008
|
} catch {
|
|
5953
|
-
throw new Error(`Invalid bounds: ${bounds}
|
|
6009
|
+
throw new Error(`Invalid bounds: ${bounds}`, { cause: bounds });
|
|
5954
6010
|
}
|
|
5955
|
-
if (!isNumberPairArray(parsed)) throw new Error(`Invalid bounds: ${bounds}
|
|
6011
|
+
if (!isNumberPairArray(parsed)) throw new Error(`Invalid bounds: ${bounds}`, { cause: bounds });
|
|
5956
6012
|
return parsed;
|
|
5957
6013
|
}
|
|
5958
6014
|
function isNumberPairArray(value) {
|
|
@@ -6030,7 +6086,7 @@ function parseStylesheets(styles) {
|
|
|
6030
6086
|
mobile: []
|
|
6031
6087
|
};
|
|
6032
6088
|
if (style.category === "propertyValue" || style.valueUuid != null) {
|
|
6033
|
-
if (style.valueUuid == null) throw new Error(`Stylesheet property value "${style.variableUuid}" is missing a value UUID
|
|
6089
|
+
if (style.valueUuid == null) throw new Error(`Stylesheet property value "${style.variableUuid}" is missing a value UUID`, { cause: style });
|
|
6034
6090
|
parsedStyles.push({
|
|
6035
6091
|
uuid: style.valueUuid,
|
|
6036
6092
|
category: "propertyValue",
|
|
@@ -6066,7 +6122,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6066
6122
|
switch (componentName) {
|
|
6067
6123
|
case "3d-viewer": {
|
|
6068
6124
|
const resourceLink = findWebsiteLink(websiteLinks, "resource", (link) => link.fileFormat === "model/obj");
|
|
6069
|
-
if (resourceLink == null) throw new Error(formatComponentError("Resource link not found", componentName, elementResource));
|
|
6125
|
+
if (resourceLink == null) throw new Error(formatComponentError("Resource link not found", componentName, elementResource), { cause: componentProperty });
|
|
6070
6126
|
const isInteractive = componentReader.valueOr("is-interactive", true);
|
|
6071
6127
|
const isControlsDisplayed = componentReader.valueOr("controls-displayed", true);
|
|
6072
6128
|
properties = {
|
|
@@ -6081,7 +6137,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6081
6137
|
case "advanced-search": {
|
|
6082
6138
|
const boundElementPropertyUuid = componentReader.uuid("bound-element");
|
|
6083
6139
|
const href = componentReader.linkTarget("link-to", transformPermanentIdentificationUrlToItemLink);
|
|
6084
|
-
if (boundElementPropertyUuid == null && href == null) throw new Error(formatComponentError("Bound element or href not found", componentName, elementResource));
|
|
6140
|
+
if (boundElementPropertyUuid == null && href == null) throw new Error(formatComponentError("Bound element or href not found", componentName, elementResource), { cause: componentProperty });
|
|
6085
6141
|
properties = {
|
|
6086
6142
|
component: "advanced-search",
|
|
6087
6143
|
boundElementUuid: boundElementPropertyUuid,
|
|
@@ -6091,7 +6147,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6091
6147
|
}
|
|
6092
6148
|
case "annotated-document": {
|
|
6093
6149
|
const documentLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "internalDocument");
|
|
6094
|
-
if (documentLink == null) throw new Error(formatComponentError("Document link not found", componentName, elementResource));
|
|
6150
|
+
if (documentLink == null) throw new Error(formatComponentError("Document link not found", componentName, elementResource), { cause: componentProperty });
|
|
6095
6151
|
properties = {
|
|
6096
6152
|
component: "annotated-document",
|
|
6097
6153
|
linkUuid: documentLink.uuid
|
|
@@ -6100,7 +6156,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6100
6156
|
}
|
|
6101
6157
|
case "annotated-image": {
|
|
6102
6158
|
const imageLinks = getWebsiteLinks(websiteLinks, "resource").filter((link) => link.type === "image" || link.type === "IIIF");
|
|
6103
|
-
if (imageLinks.length === 0) throw new Error(formatComponentError("Image link not found", componentName, elementResource));
|
|
6159
|
+
if (imageLinks.length === 0) throw new Error(formatComponentError("Image link not found", componentName, elementResource), { cause: componentProperty });
|
|
6104
6160
|
const isFilterInputDisplayed = componentReader.valueOr("filter-input-displayed", true);
|
|
6105
6161
|
const isOptionsDisplayed = componentReader.valueOr("options-displayed", true);
|
|
6106
6162
|
const isAnnotationHighlightsDisplayed = componentReader.valueOr("annotation-highlights-displayed", true);
|
|
@@ -6117,7 +6173,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6117
6173
|
}
|
|
6118
6174
|
case "audio-player": {
|
|
6119
6175
|
const audioLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "audio");
|
|
6120
|
-
if (audioLink == null) throw new Error(formatComponentError("Audio link not found", componentName, elementResource));
|
|
6176
|
+
if (audioLink == null) throw new Error(formatComponentError("Audio link not found", componentName, elementResource), { cause: componentProperty });
|
|
6121
6177
|
const isSpeedControlsDisplayed = componentReader.valueOr("speed-controls-displayed", true);
|
|
6122
6178
|
const isVolumeControlsDisplayed = componentReader.valueOr("volume-controls-displayed", true);
|
|
6123
6179
|
const isSeekBarDisplayed = componentReader.valueOr("seek-bar-displayed", true);
|
|
@@ -6133,7 +6189,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6133
6189
|
case "bibliography": {
|
|
6134
6190
|
const itemLinks = websiteLinks.filter((link) => link.category !== "bibliography");
|
|
6135
6191
|
const bibliographies = parseBibliographyList(elementResource.bibliographies, options);
|
|
6136
|
-
if (itemLinks.length === 0 && bibliographies.length === 0) throw new Error(formatComponentError("No links found", componentName, elementResource));
|
|
6192
|
+
if (itemLinks.length === 0 && bibliographies.length === 0) throw new Error(formatComponentError("No links found", componentName, elementResource), { cause: componentProperty });
|
|
6137
6193
|
const layout = componentReader.valueOr("layout", "long");
|
|
6138
6194
|
const isSourceDocumentDisplayed = componentReader.valueOr("source-document-displayed", true);
|
|
6139
6195
|
properties = {
|
|
@@ -6151,7 +6207,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6151
6207
|
let href = componentReader.linkTarget("navigate-to", transformPermanentIdentificationUrlToItemLink);
|
|
6152
6208
|
if (href === null) {
|
|
6153
6209
|
href = componentReader.linkTarget("link-to", transformPermanentIdentificationUrlToItemLink);
|
|
6154
|
-
if (href === null) throw new Error(formatComponentError("Properties “navigate-to” or “link-to” not found", componentName, elementResource));
|
|
6210
|
+
if (href === null) throw new Error(formatComponentError("Properties “navigate-to” or “link-to” not found", componentName, elementResource), { cause: componentProperty });
|
|
6155
6211
|
else isExternal = true;
|
|
6156
6212
|
}
|
|
6157
6213
|
const startIcon = componentReader.value("start-icon");
|
|
@@ -6180,7 +6236,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6180
6236
|
}
|
|
6181
6237
|
case "collection": {
|
|
6182
6238
|
const setLinks = getWebsiteLinks(websiteLinks, "set");
|
|
6183
|
-
if (setLinks.length === 0) throw new Error(formatComponentError("Set links not found", componentName, elementResource));
|
|
6239
|
+
if (setLinks.length === 0) throw new Error(formatComponentError("Set links not found", componentName, elementResource), { cause: componentProperty });
|
|
6184
6240
|
const displayedProperties = componentReader.property("use-property");
|
|
6185
6241
|
const variant = componentReader.valueOr("variant", "slide");
|
|
6186
6242
|
const paginationVariant = componentReader.valueOr("pagination-variant", "default");
|
|
@@ -6229,7 +6285,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6229
6285
|
break;
|
|
6230
6286
|
case "entries": {
|
|
6231
6287
|
const entriesLink = findWebsiteLinkByCategories(websiteLinks, ["set", "tree"]);
|
|
6232
|
-
if (entriesLink == null) throw new Error(formatComponentError("Entries link not found", componentName, elementResource));
|
|
6288
|
+
if (entriesLink == null) throw new Error(formatComponentError("Entries link not found", componentName, elementResource), { cause: componentProperty });
|
|
6233
6289
|
const variant = componentReader.valueOr("variant", "entry");
|
|
6234
6290
|
const isFilterInputDisplayed = componentReader.valueOr("filter-input-displayed", false);
|
|
6235
6291
|
properties = {
|
|
@@ -6242,7 +6298,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6242
6298
|
}
|
|
6243
6299
|
case "iframe": {
|
|
6244
6300
|
const webpageLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "webpage");
|
|
6245
|
-
if (webpageLink?.href == null) throw new Error(formatComponentError("URL not found", componentName, elementResource));
|
|
6301
|
+
if (webpageLink?.href == null) throw new Error(formatComponentError("URL not found", componentName, elementResource), { cause: componentProperty });
|
|
6246
6302
|
properties = {
|
|
6247
6303
|
component: "iframe",
|
|
6248
6304
|
href: transformPermanentIdentificationUrlToItemLink(webpageLink.href),
|
|
@@ -6253,7 +6309,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6253
6309
|
}
|
|
6254
6310
|
case "iiif-viewer": {
|
|
6255
6311
|
const manifestLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "IIIF");
|
|
6256
|
-
if (manifestLink == null) throw new Error(formatComponentError("Manifest link not found", componentName, elementResource));
|
|
6312
|
+
if (manifestLink == null) throw new Error(formatComponentError("Manifest link not found", componentName, elementResource), { cause: componentProperty });
|
|
6257
6313
|
const variant = componentReader.valueOr("variant", "universal-viewer");
|
|
6258
6314
|
properties = {
|
|
6259
6315
|
component: "iiif-viewer",
|
|
@@ -6263,7 +6319,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6263
6319
|
break;
|
|
6264
6320
|
}
|
|
6265
6321
|
case "image": {
|
|
6266
|
-
if (websiteLinks.length === 0) throw new Error(formatComponentError("No links found", componentName, elementResource));
|
|
6322
|
+
if (websiteLinks.length === 0) throw new Error(formatComponentError("No links found", componentName, elementResource), { cause: componentProperty });
|
|
6267
6323
|
const imageQuality = componentReader.valueOr("image-quality", "high");
|
|
6268
6324
|
const images = [];
|
|
6269
6325
|
for (const link of websiteLinks) images.push({
|
|
@@ -6333,7 +6389,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6333
6389
|
}
|
|
6334
6390
|
case "image-gallery": {
|
|
6335
6391
|
const galleryLink = findWebsiteLinkByCategories(websiteLinks, ["set", "tree"]);
|
|
6336
|
-
if (galleryLink == null) throw new Error(formatComponentError("Image gallery link not found", componentName, elementResource));
|
|
6392
|
+
if (galleryLink == null) throw new Error(formatComponentError("Image gallery link not found", componentName, elementResource), { cause: componentProperty });
|
|
6337
6393
|
const isFilterInputDisplayed = componentReader.valueOr("filter-input-displayed", true);
|
|
6338
6394
|
properties = {
|
|
6339
6395
|
component: "image-gallery",
|
|
@@ -6344,7 +6400,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6344
6400
|
}
|
|
6345
6401
|
case "map": {
|
|
6346
6402
|
const mapLink = findWebsiteLinkByCategories(websiteLinks, ["set", "tree"]);
|
|
6347
|
-
if (mapLink == null) throw new Error(formatComponentError("Map link not found", componentName, elementResource));
|
|
6403
|
+
if (mapLink == null) throw new Error(formatComponentError("Map link not found", componentName, elementResource), { cause: componentProperty });
|
|
6348
6404
|
const isInteractive = componentReader.valueOr("is-interactive", true);
|
|
6349
6405
|
const isClustered = componentReader.valueOr("is-clustered", false);
|
|
6350
6406
|
const isUsingPins = componentReader.valueOr("is-using-pins", false);
|
|
@@ -6373,9 +6429,9 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6373
6429
|
}
|
|
6374
6430
|
case "query": {
|
|
6375
6431
|
const setLinks = getWebsiteLinks(websiteLinks, "set");
|
|
6376
|
-
if (setLinks.length === 0) throw new Error(formatComponentError("Set links not found", componentName, elementResource));
|
|
6432
|
+
if (setLinks.length === 0) throw new Error(formatComponentError("Set links not found", componentName, elementResource), { cause: componentProperty });
|
|
6377
6433
|
const items = [];
|
|
6378
|
-
if (componentProperty.properties.length === 0) throw new Error(formatComponentError("Query properties not found", componentName, elementResource));
|
|
6434
|
+
if (componentProperty.properties.length === 0) throw new Error(formatComponentError("Query properties not found", componentName, elementResource), { cause: componentProperty });
|
|
6379
6435
|
for (const queryItem of componentProperty.properties) {
|
|
6380
6436
|
const queryReader = websitePresentationReader(queryItem.properties);
|
|
6381
6437
|
const label = queryReader.multilingualValue("query-prompt", options);
|
|
@@ -6385,9 +6441,9 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6385
6441
|
if (queryLanguage == null) throw new Error(formatComponentError("Query language not found", componentName, elementResource));
|
|
6386
6442
|
const queries = [];
|
|
6387
6443
|
for (const propertyVariable of propertyVariables) {
|
|
6388
|
-
if (propertyVariable.uuid === null) throw new Error(formatComponentError("Property variable UUID not found", componentName, elementResource));
|
|
6444
|
+
if (propertyVariable.uuid === null) throw new Error(formatComponentError("Property variable UUID not found", componentName, elementResource), { cause: propertyVariable });
|
|
6389
6445
|
const dataType = propertyVariable.dataType;
|
|
6390
|
-
if (dataType === "coordinate") throw new Error(formatComponentError("Query prompts with data type \"coordinate\" are not supported", componentName, elementResource));
|
|
6446
|
+
if (dataType === "coordinate") throw new Error(formatComponentError("Query prompts with data type \"coordinate\" are not supported", componentName, elementResource), { cause: propertyVariable });
|
|
6391
6447
|
queries.push({
|
|
6392
6448
|
target: "property",
|
|
6393
6449
|
propertyVariable: propertyVariable.uuid,
|
|
@@ -6406,7 +6462,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6406
6462
|
endIcon
|
|
6407
6463
|
});
|
|
6408
6464
|
}
|
|
6409
|
-
if (items.length === 0) throw new Error(formatComponentError("No queries found", componentName, elementResource));
|
|
6465
|
+
if (items.length === 0) throw new Error(formatComponentError("No queries found", componentName, elementResource), { cause: componentProperty });
|
|
6410
6466
|
const componentOptions = parseWebsiteOptions(elementResource.options, options);
|
|
6411
6467
|
const displayedProperties = componentReader.property("use-property");
|
|
6412
6468
|
const variant = componentReader.valueOr("variant", "slide");
|
|
@@ -6433,7 +6489,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6433
6489
|
}
|
|
6434
6490
|
case "table": {
|
|
6435
6491
|
const tableLink = findWebsiteLink(websiteLinks, "set");
|
|
6436
|
-
if (tableLink == null) throw new Error(formatComponentError("Table link not found", componentName, elementResource));
|
|
6492
|
+
if (tableLink == null) throw new Error(formatComponentError("Table link not found", componentName, elementResource), { cause: componentProperty });
|
|
6437
6493
|
properties = {
|
|
6438
6494
|
component: "table",
|
|
6439
6495
|
linkUuid: tableLink.uuid
|
|
@@ -6444,7 +6500,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6444
6500
|
const queryVariant = componentReader.valueOr("query-variant", "submit");
|
|
6445
6501
|
const boundElementUuid = componentReader.uuid("bound-element");
|
|
6446
6502
|
const href = componentReader.linkTarget("link-to", transformPermanentIdentificationUrlToItemLink);
|
|
6447
|
-
if (!boundElementUuid && !href) throw new Error(formatComponentError("Bound element or href not found", componentName, elementResource));
|
|
6503
|
+
if (!boundElementUuid && !href) throw new Error(formatComponentError("Bound element or href not found", componentName, elementResource), { cause: componentProperty });
|
|
6448
6504
|
properties = {
|
|
6449
6505
|
component: "search-bar",
|
|
6450
6506
|
queryVariant,
|
|
@@ -6457,7 +6513,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6457
6513
|
}
|
|
6458
6514
|
case "text": {
|
|
6459
6515
|
const content = elementResource.document && "content" in elementResource.document ? parseXMLContent(elementResource.document, options) : null;
|
|
6460
|
-
if (content == null) throw new Error(formatComponentError("Content not found", componentName, elementResource));
|
|
6516
|
+
if (content == null) throw new Error(formatComponentError("Content not found", componentName, elementResource), { cause: componentProperty });
|
|
6461
6517
|
let variantName = "block";
|
|
6462
6518
|
let variant;
|
|
6463
6519
|
const variantProperty = componentReader.property("variant");
|
|
@@ -6503,7 +6559,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6503
6559
|
}
|
|
6504
6560
|
case "timeline": {
|
|
6505
6561
|
const timelineLink = findWebsiteLink(websiteLinks, "tree");
|
|
6506
|
-
if (timelineLink == null) throw new Error(formatComponentError("Timeline link not found", componentName, elementResource));
|
|
6562
|
+
if (timelineLink == null) throw new Error(formatComponentError("Timeline link not found", componentName, elementResource), { cause: componentProperty });
|
|
6507
6563
|
properties = {
|
|
6508
6564
|
component: "timeline",
|
|
6509
6565
|
linkUuid: timelineLink.uuid
|
|
@@ -6512,7 +6568,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6512
6568
|
}
|
|
6513
6569
|
case "video": {
|
|
6514
6570
|
const videoLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "video");
|
|
6515
|
-
if (videoLink == null) throw new Error(formatComponentError("Video link not found", componentName, elementResource));
|
|
6571
|
+
if (videoLink == null) throw new Error(formatComponentError("Video link not found", componentName, elementResource), { cause: componentProperty });
|
|
6516
6572
|
const isChaptersDisplayed = componentReader.valueOr("chapters-displayed", true);
|
|
6517
6573
|
properties = {
|
|
6518
6574
|
component: "video",
|
|
@@ -6525,7 +6581,7 @@ function parseWebElementProperties(componentProperty, elementResource, options)
|
|
|
6525
6581
|
console.warn(`Invalid or non-implemented component name “${unparsedComponentName.toString()}” for the following element: “${parseStringContent(elementResource.identification.label, options)}”`);
|
|
6526
6582
|
break;
|
|
6527
6583
|
}
|
|
6528
|
-
if (properties === null) throw new Error(formatComponentError("Properties not found", componentName, elementResource));
|
|
6584
|
+
if (properties === null) throw new Error(formatComponentError("Properties not found", componentName, elementResource), { cause: componentProperty });
|
|
6529
6585
|
return properties;
|
|
6530
6586
|
}
|
|
6531
6587
|
function parseWebTitle(properties, identification, overrides) {
|
|
@@ -6586,7 +6642,7 @@ function parseWebpage(webpageResource, options, context, slugPrefix) {
|
|
|
6586
6642
|
if (webpageReader.value("presentation") !== "page") return null;
|
|
6587
6643
|
const identification = parseIdentification(webpageResource.identification, options);
|
|
6588
6644
|
const slug = webpageResource.slug?.replace(SEGMENT_UNIQUE_SLUG_PREFIX_REGEX, "") ?? null;
|
|
6589
|
-
if (slug == null) throw new Error(`Slug not found for page (${formatXMLWebsiteResourceMetadata(webpageResource)})
|
|
6645
|
+
if (slug == null) throw new Error(`Slug not found for page (${formatXMLWebsiteResourceMetadata(webpageResource)})`, { cause: webpageResource });
|
|
6590
6646
|
const returnWebpage = {
|
|
6591
6647
|
uuid: webpageResource.uuid,
|
|
6592
6648
|
type: "page",
|
|
@@ -6673,7 +6729,7 @@ function parseWebsiteSegments(resources, context, options, slugPrefix) {
|
|
|
6673
6729
|
if (!("segments" in resource)) continue;
|
|
6674
6730
|
for (const tree of resource.segments.tree) {
|
|
6675
6731
|
const segmentSlug = tree.identification.abbreviation == null ? null : parseStringContent(tree.identification.abbreviation, options);
|
|
6676
|
-
if (segmentSlug == null) throw new Error(`Slug not found for segment website (website uuid “${tree.uuid}”)
|
|
6732
|
+
if (segmentSlug == null) throw new Error(`Slug not found for segment website (website uuid “${tree.uuid}”)`, { cause: tree });
|
|
6677
6733
|
segments.push(parseWebsiteTree(tree, context, "segment", options, prefixSlug(segmentSlug, slugPrefix)));
|
|
6678
6734
|
}
|
|
6679
6735
|
}
|
|
@@ -6854,9 +6910,9 @@ function parseWebBlock(blockResource, options) {
|
|
|
6854
6910
|
for (const resource of blockResources) {
|
|
6855
6911
|
const resourceReader = websitePresentationReader(resource.properties ? parseSimplifiedProperties(resource.properties, options) : []);
|
|
6856
6912
|
const resourceType = resourceReader.value("presentation");
|
|
6857
|
-
if (resourceType !== "element") throw new Error(`Accordion only accepts elements, but got “${resourceType}” (${formatXMLWebsiteResourceMetadata(resource)})
|
|
6913
|
+
if (resourceType !== "element") throw new Error(`Accordion only accepts elements, but got “${resourceType}” (${formatXMLWebsiteResourceMetadata(resource)})`, { cause: resource });
|
|
6858
6914
|
const componentType = resourceReader.nestedByValue("presentation", "element").value("component");
|
|
6859
|
-
if (componentType !== "text") throw new Error(`Accordion only accepts text components, but got “${componentType}” (${formatXMLWebsiteResourceMetadata(resource)})
|
|
6915
|
+
if (componentType !== "text") throw new Error(`Accordion only accepts text components, but got “${componentType}” (${formatXMLWebsiteResourceMetadata(resource)})`, { cause: resource });
|
|
6860
6916
|
const element = parseWebElementForAccordion(resource, options);
|
|
6861
6917
|
accordionItems.push(element);
|
|
6862
6918
|
}
|
|
@@ -6950,7 +7006,7 @@ function parseWebsiteProperties(properties, websiteTree, sidebar, options) {
|
|
|
6950
7006
|
name: contactContent[0],
|
|
6951
7007
|
email: contactContent[1] ?? null
|
|
6952
7008
|
};
|
|
6953
|
-
else throw new Error(`Contact property must use “name;email”, got “${contactProperty.values[0]?.content}” (website uuid “${websiteTree.uuid}”)
|
|
7009
|
+
else throw new Error(`Contact property must use “name;email”, got “${contactProperty.values[0]?.content}” (website uuid “${websiteTree.uuid}”)`, { cause: websiteTree });
|
|
6954
7010
|
}
|
|
6955
7011
|
returnProperties.loadingVariant = websiteReader.valueOr("loading-variant", "spinner");
|
|
6956
7012
|
returnProperties.theme.isThemeToggleDisplayed = websiteReader.valueOr("supports-theme-toggle", true);
|
|
@@ -7056,8 +7112,8 @@ function parseFilterContexts(filterContextLevels, options) {
|
|
|
7056
7112
|
return filterContextTreeLevels;
|
|
7057
7113
|
}
|
|
7058
7114
|
function parseWebsiteTree(websiteTree, context, type, options, slugPrefix) {
|
|
7059
|
-
if (!websiteTree.properties) throw new Error(`Website properties not found (website uuid “${websiteTree.uuid}”)
|
|
7060
|
-
if (type === "website" && websiteTree.items?.resource == null) throw new Error(`Website pages not found (website uuid “${websiteTree.uuid}”)
|
|
7115
|
+
if (!websiteTree.properties) throw new Error(`Website properties not found (website uuid “${websiteTree.uuid}”)`, { cause: websiteTree });
|
|
7116
|
+
if (type === "website" && websiteTree.items?.resource == null) throw new Error(`Website pages not found (website uuid “${websiteTree.uuid}”)`, { cause: websiteTree });
|
|
7061
7117
|
const resources = normalizeWebsiteResources(websiteTree.items?.resource);
|
|
7062
7118
|
const sidebar = parseSidebar(resources, options);
|
|
7063
7119
|
const properties = parseWebsiteProperties(websiteTree.properties.property, websiteTree, sidebar, options);
|
|
@@ -7081,7 +7137,7 @@ function parseWebsite(data, options) {
|
|
|
7081
7137
|
const parserOptions = { languages };
|
|
7082
7138
|
const defaultLanguage = resolveDefaultLanguage(rawOchre, languages);
|
|
7083
7139
|
const websiteTree = rawOchre.tree[0];
|
|
7084
|
-
if (websiteTree == null) throw new Error("Website tree not found");
|
|
7140
|
+
if (websiteTree == null) throw new Error("Website tree not found", { cause: data });
|
|
7085
7141
|
return parseWebsiteTree(websiteTree, {
|
|
7086
7142
|
belongsTo: {
|
|
7087
7143
|
uuid: rawOchre.uuidBelongsTo,
|
|
@@ -7102,13 +7158,13 @@ async function fetchWebsite(abbreviation, options) {
|
|
|
7102
7158
|
try {
|
|
7103
7159
|
const cleanAbbreviation = abbreviation.trim().toLocaleLowerCase("en-US");
|
|
7104
7160
|
const response = await (options?.fetch ?? fetch)(`https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery=${encodeURIComponent(`collection('ochre/tree')/ochre[tree/identification/abbreviation/content/string='${cleanAbbreviation}']`)}&xsl=none&lang="*"`);
|
|
7105
|
-
if (!response.ok) throw new Error("Failed to fetch website");
|
|
7161
|
+
if (!response.ok) throw new Error("Failed to fetch website", { cause: response.statusText });
|
|
7106
7162
|
const dataRaw = await response.text();
|
|
7107
7163
|
const data = new XMLParser(XML_PARSER_OPTIONS).parse(dataRaw);
|
|
7108
7164
|
const { success, issues, output } = v.safeParse(XMLWebsiteData, data);
|
|
7109
7165
|
if (!success) {
|
|
7110
7166
|
logIssues(issues);
|
|
7111
|
-
throw
|
|
7167
|
+
throw createSchemaValidationError("Failed to parse website XML", issues);
|
|
7112
7168
|
}
|
|
7113
7169
|
restoreXMLMetadata(output, data);
|
|
7114
7170
|
return {
|