ochre-sdk 1.0.78 → 1.1.0

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 (84) hide show
  1. package/README.md +31 -3
  2. package/dist/_virtual/_rolldown/runtime.mjs +13 -0
  3. package/dist/categories.d.mts +249 -0
  4. package/dist/categories.mjs +259 -0
  5. package/dist/constants.d.mts +14 -0
  6. package/dist/constants.mjs +18 -1
  7. package/dist/errors.d.mts +23 -0
  8. package/dist/{utilities.mjs → errors.mjs} +33 -84
  9. package/dist/fetchers/gallery.mjs +15 -51
  10. package/dist/fetchers/item-children.mjs +20 -66
  11. package/dist/fetchers/item-links.mjs +26 -79
  12. package/dist/fetchers/item-ocr-data.d.mts +2 -2
  13. package/dist/fetchers/item-ocr-data.mjs +11 -15
  14. package/dist/fetchers/item.d.mts +0 -15
  15. package/dist/fetchers/item.mjs +20 -72
  16. package/dist/fetchers/request.d.mts +70 -0
  17. package/dist/fetchers/request.mjs +100 -0
  18. package/dist/fetchers/set/items.mjs +27 -73
  19. package/dist/fetchers/set/property-values.d.mts +2 -3
  20. package/dist/fetchers/set/property-values.mjs +96 -130
  21. package/dist/fetchers/website-metadata.mjs +35 -57
  22. package/dist/fetchers/website.d.mts +2 -3
  23. package/dist/fetchers/website.mjs +22 -31
  24. package/dist/getters.d.mts +78 -148
  25. package/dist/getters.mjs +127 -208
  26. package/dist/helpers.d.mts +0 -4
  27. package/dist/helpers.mjs +19 -6
  28. package/dist/index.d.mts +8 -6
  29. package/dist/index.mjs +6 -4
  30. package/dist/ocr.d.mts +37 -0
  31. package/dist/ocr.mjs +52 -0
  32. package/dist/parsers/helpers.d.mts +21 -1
  33. package/dist/parsers/helpers.mjs +26 -6
  34. package/dist/parsers/index.d.mts +0 -8
  35. package/dist/parsers/index.mjs +118 -259
  36. package/dist/parsers/languages.d.mts +72 -0
  37. package/dist/parsers/languages.mjs +132 -0
  38. package/dist/parsers/multilingual.d.mts +49 -74
  39. package/dist/parsers/multilingual.mjs +88 -189
  40. package/dist/parsers/property-token.d.mts +34 -0
  41. package/dist/parsers/property-token.mjs +29 -0
  42. package/dist/parsers/string.d.mts +19 -0
  43. package/dist/parsers/string.mjs +45 -25
  44. package/dist/parsers/website/bounds.d.mts +10 -0
  45. package/dist/parsers/website/bounds.mjs +28 -0
  46. package/dist/parsers/website/components.d.mts +91 -0
  47. package/dist/parsers/website/components.mjs +681 -0
  48. package/dist/parsers/website/index.d.mts +0 -7
  49. package/dist/parsers/website/index.mjs +92 -1153
  50. package/dist/parsers/website/links.d.mts +36 -0
  51. package/dist/parsers/website/links.mjs +58 -0
  52. package/dist/parsers/website/messages.d.mts +24 -0
  53. package/dist/parsers/website/messages.mjs +31 -0
  54. package/dist/parsers/website/options.d.mts +6 -0
  55. package/dist/parsers/website/options.mjs +114 -0
  56. package/dist/parsers/website/properties.d.mts +12 -0
  57. package/dist/parsers/website/properties.mjs +158 -0
  58. package/dist/parsers/website/reader.d.mts +54 -4
  59. package/dist/parsers/website/reader.mjs +65 -20
  60. package/dist/parsers/website/slug.d.mts +64 -0
  61. package/dist/parsers/website/slug.mjs +82 -0
  62. package/dist/parsers/website/styles.d.mts +28 -0
  63. package/dist/parsers/website/styles.mjs +103 -0
  64. package/dist/parsers/website/walk.d.mts +68 -0
  65. package/dist/parsers/website/walk.mjs +116 -0
  66. package/dist/query.d.mts +66 -18
  67. package/dist/query.mjs +202 -48
  68. package/dist/reflection.d.mts +64 -0
  69. package/dist/reflection.mjs +79 -0
  70. package/dist/schemas.d.mts +7 -0
  71. package/dist/schemas.mjs +12 -3
  72. package/dist/types/index.d.mts +1 -31
  73. package/dist/types/utilities.d.mts +9 -0
  74. package/dist/types/utilities.mjs +1 -0
  75. package/dist/types/website.d.mts +49 -58
  76. package/dist/xml/metadata.d.mts +16 -0
  77. package/dist/xml/metadata.mjs +32 -11
  78. package/dist/xml/schemas.d.mts +5970 -3
  79. package/dist/xml/schemas.mjs +43 -45
  80. package/dist/xml/types.d.mts +13 -30
  81. package/dist/xquery.d.mts +46 -0
  82. package/dist/xquery.mjs +66 -0
  83. package/package.json +3 -3
  84. package/dist/utilities.d.mts +0 -54
@@ -0,0 +1,36 @@
1
+ import { WebImage } from "../../types/website.mjs";
2
+ import { ItemLink, ItemLinkCategory, ItemLinks, PropertyValueContent } from "../../types/index.mjs";
3
+ import { WebsitePageSlugs } from "./slug.mjs";
4
+ //#region src/parsers/website/links.d.ts
5
+ /**
6
+ * The link categories a website can point at
7
+ */
8
+ export type WebsiteLinkCategory = Extract<ItemLinkCategory, "resource" | "set" | "tree">;
9
+ export declare function findWebsiteLink<U extends WebsiteLinkCategory, T extends ReadonlyArray<string>>(links: ItemLinks<T>, category: U, isMatch?: (link: ItemLink<U, T>) => boolean): ItemLink<U, T> | null;
10
+ export declare function findWebsiteLinkByCategories<U extends WebsiteLinkCategory, T extends ReadonlyArray<string>>(links: ItemLinks<T>, categories: ReadonlyArray<U>): ItemLink<U, T> | null;
11
+ export declare function getWebsiteLinks<U extends WebsiteLinkCategory, T extends ReadonlyArray<string>>(links: ItemLinks<T>, category: U): Array<ItemLink<U, T>>;
12
+ /**
13
+ * Where a "link-to", "navigate-to" or "redirect-to" property points
14
+ *
15
+ * An external href is rewritten to an item route; anything else resolves
16
+ * through the page slug map, so a link to a page inside a segment gets that
17
+ * segment's full slug rather than the bare one OCHRE stores on the page.
18
+ * @param value - The raw property value holding the target
19
+ * @param pageSlugs - The website's resolved page slugs
20
+ * @returns The target, or null when the property carries none
21
+ * @internal
22
+ */
23
+ export declare function parseWebsiteLinkTarget<T extends ReadonlyArray<string>>(value: PropertyValueContent<T> | null, pageSlugs: WebsitePageSlugs | undefined): string | null;
24
+ /**
25
+ * The image a link stands for
26
+ *
27
+ * Built the same way wherever a website shows a linked image, so the six
28
+ * fields and the fallbacks for a link OCHRE has no dimensions for are stated
29
+ * once.
30
+ * @param link - The resource link pointing at the image
31
+ * @param quality - The quality the consumer should request
32
+ * @returns The image
33
+ * @internal
34
+ */
35
+ export declare function webImageFromLink<T extends ReadonlyArray<string>>(link: ItemLinks<T>[number], quality: WebImage<T>["quality"]): WebImage<T>;
36
+ //#endregion
@@ -0,0 +1,58 @@
1
+ import { transformPermanentIdentificationUrlToItemLink } from "../string.mjs";
2
+ //#region src/parsers/website/links.ts
3
+ function isWebsiteLink(link, category) {
4
+ return link.category === category;
5
+ }
6
+ function findWebsiteLink(links, category, isMatch) {
7
+ for (const link of links) if (isWebsiteLink(link, category) && (isMatch == null || isMatch(link))) return link;
8
+ return null;
9
+ }
10
+ function findWebsiteLinkByCategories(links, categories) {
11
+ for (const link of links) for (const category of categories) if (isWebsiteLink(link, category)) return link;
12
+ return null;
13
+ }
14
+ function getWebsiteLinks(links, category) {
15
+ const matchedLinks = [];
16
+ for (const link of links) if (isWebsiteLink(link, category)) matchedLinks.push(link);
17
+ return matchedLinks;
18
+ }
19
+ /**
20
+ * Where a "link-to", "navigate-to" or "redirect-to" property points
21
+ *
22
+ * An external href is rewritten to an item route; anything else resolves
23
+ * through the page slug map, so a link to a page inside a segment gets that
24
+ * segment's full slug rather than the bare one OCHRE stores on the page.
25
+ * @param value - The raw property value holding the target
26
+ * @param pageSlugs - The website's resolved page slugs
27
+ * @returns The target, or null when the property carries none
28
+ * @internal
29
+ */
30
+ function parseWebsiteLinkTarget(value, pageSlugs) {
31
+ if (value == null) return null;
32
+ if (value.href != null) return transformPermanentIdentificationUrlToItemLink(value.href);
33
+ return (value.uuid == null ? void 0 : pageSlugs?.get(value.uuid)) ?? value.slug;
34
+ }
35
+ /**
36
+ * The image a link stands for
37
+ *
38
+ * Built the same way wherever a website shows a linked image, so the six
39
+ * fields and the fallbacks for a link OCHRE has no dimensions for are stated
40
+ * once.
41
+ * @param link - The resource link pointing at the image
42
+ * @param quality - The quality the consumer should request
43
+ * @returns The image
44
+ * @internal
45
+ */
46
+ function webImageFromLink(link, quality) {
47
+ const image = "image" in link ? link.image : null;
48
+ return {
49
+ uuid: link.uuid,
50
+ label: link.identification.label,
51
+ description: link.description,
52
+ width: image?.width ?? 0,
53
+ height: image?.height ?? 0,
54
+ quality
55
+ };
56
+ }
57
+ //#endregion
58
+ export { findWebsiteLink, findWebsiteLinkByCategories, getWebsiteLinks, parseWebsiteLinkTarget, webImageFromLink };
@@ -0,0 +1,24 @@
1
+ import { WebElementComponent } from "../../types/website.mjs";
2
+ import { XMLWebsiteResource } from "../../xml/types.mjs";
3
+ //#region src/parsers/website/messages.d.ts
4
+ /**
5
+ * Name a resource well enough to find it in OCHRE from an error message
6
+ *
7
+ * Every throw in the website parser goes through here, because a UUID on its
8
+ * own is not something anyone can look up quickly and a label on its own is
9
+ * not unique.
10
+ * @param resource - The resource the error is about
11
+ * @returns A comma-separated description of the resource
12
+ * @internal
13
+ */
14
+ export declare function formatXMLWebsiteResourceMetadata(resource: XMLWebsiteResource): string;
15
+ /**
16
+ * Describe a component parser failure
17
+ * @param message - What went wrong
18
+ * @param componentName - The component being parsed
19
+ * @param elementResource - The element the component belongs to
20
+ * @returns The error message
21
+ * @internal
22
+ */
23
+ export declare function formatComponentError(message: string, componentName: WebElementComponent["component"] | undefined, elementResource: XMLWebsiteResource): string;
24
+ //#endregion
@@ -0,0 +1,31 @@
1
+ import { parseStringContent } from "../helpers.mjs";
2
+ //#region src/parsers/website/messages.ts
3
+ /**
4
+ * Name a resource well enough to find it in OCHRE from an error message
5
+ *
6
+ * Every throw in the website parser goes through here, because a UUID on its
7
+ * own is not something anyone can look up quickly and a label on its own is
8
+ * not unique.
9
+ * @param resource - The resource the error is about
10
+ * @returns A comma-separated description of the resource
11
+ * @internal
12
+ */
13
+ function formatXMLWebsiteResourceMetadata(resource) {
14
+ const metadata = [`label “${parseStringContent(resource.identification.label)}”`, `uuid “${resource.uuid}”`];
15
+ if (resource.slug != null) metadata.push(`slug “${resource.slug}”`);
16
+ if (resource.identification.abbreviation != null) metadata.push(`abbreviation “${parseStringContent(resource.identification.abbreviation)}”`);
17
+ return metadata.join(", ");
18
+ }
19
+ /**
20
+ * Describe a component parser failure
21
+ * @param message - What went wrong
22
+ * @param componentName - The component being parsed
23
+ * @param elementResource - The element the component belongs to
24
+ * @returns The error message
25
+ * @internal
26
+ */
27
+ function formatComponentError(message, componentName, elementResource) {
28
+ return `${message} for component “${componentName ?? "(unknown)"}” (${formatXMLWebsiteResourceMetadata(elementResource)})`;
29
+ }
30
+ //#endregion
31
+ export { formatComponentError, formatXMLWebsiteResourceMetadata };
@@ -0,0 +1,6 @@
1
+ import { WebOptions } from "../../types/website.mjs";
2
+ import { XMLWebsiteOptions } from "../../xml/types.mjs";
3
+ import { ParserOptions } from "../helpers.mjs";
4
+ //#region src/parsers/website/options.d.ts
5
+ export declare function parseWebsiteOptions<T extends ReadonlyArray<string>>(rawOptions: XMLWebsiteOptions | undefined, options: ParserOptions<T>): WebOptions<T>;
6
+ //#endregion
@@ -0,0 +1,114 @@
1
+ import { parseContentLike } from "../helpers.mjs";
2
+ import { parseIdentification, parseNotes } from "../index.mjs";
3
+ //#region src/parsers/website/options.ts
4
+ function parseContextTreeLevel(contextItemToParse, options) {
5
+ let type = "";
6
+ const levels = [];
7
+ const levelsToParse = contextItemToParse.levels?.level ?? [];
8
+ for (const level of levelsToParse) {
9
+ const [rawVariableUuid = "", rawValueUuid] = level.payload.split(",", 2);
10
+ const valueUuid = rawValueUuid == null || rawValueUuid.trim() === "null" ? null : rawValueUuid.trim();
11
+ type = level.dataType ?? type;
12
+ levels.push({
13
+ variableUuid: rawVariableUuid.trim(),
14
+ valueUuid
15
+ });
16
+ }
17
+ return {
18
+ context: levels,
19
+ type,
20
+ identification: parseIdentification(contextItemToParse.identification, options),
21
+ description: parseContentLike(contextItemToParse.description, options)
22
+ };
23
+ }
24
+ function parseFilterContextDisplay(filterOption) {
25
+ switch (filterOption) {
26
+ case "inline-displayed": return {
27
+ isInlineDisplayed: true,
28
+ isSidebarDisplayed: false,
29
+ isSidebarOpen: false
30
+ };
31
+ case "inline-sidebar-displayed-closed": return {
32
+ isInlineDisplayed: true,
33
+ isSidebarDisplayed: true,
34
+ isSidebarOpen: false
35
+ };
36
+ case "inline-sidebar-displayed-open": return {
37
+ isInlineDisplayed: true,
38
+ isSidebarDisplayed: true,
39
+ isSidebarOpen: true
40
+ };
41
+ case "sidebar-displayed-closed": return {
42
+ isInlineDisplayed: false,
43
+ isSidebarDisplayed: true,
44
+ isSidebarOpen: false
45
+ };
46
+ case "sidebar-displayed-open": return {
47
+ isInlineDisplayed: false,
48
+ isSidebarDisplayed: true,
49
+ isSidebarOpen: true
50
+ };
51
+ default: return {
52
+ isInlineDisplayed: false,
53
+ isSidebarDisplayed: false,
54
+ isSidebarOpen: false
55
+ };
56
+ }
57
+ }
58
+ function parseContexts(contextLevels, options) {
59
+ const contextTreeLevels = [];
60
+ for (const contextLevel of contextLevels) for (const contextItemToParse of contextLevel.context) contextTreeLevels.push(parseContextTreeLevel(contextItemToParse, options));
61
+ return contextTreeLevels;
62
+ }
63
+ function parseFilterContexts(filterContextLevels, options) {
64
+ const filterContextTreeLevels = [];
65
+ for (const filterContextLevel of filterContextLevels) for (const contextItemToParse of filterContextLevel.context) filterContextTreeLevels.push({
66
+ ...parseContextTreeLevel(contextItemToParse, options),
67
+ filterType: contextItemToParse.filterType ?? "property",
68
+ filterVariant: contextItemToParse.filterVariant ?? null,
69
+ ...parseFilterContextDisplay(contextItemToParse.filterOption)
70
+ });
71
+ return filterContextTreeLevels;
72
+ }
73
+ function parseAllOptionContexts(options, parserOptions) {
74
+ function handleContexts(contexts) {
75
+ return parseContexts(contexts ?? [], parserOptions);
76
+ }
77
+ function handleFilterContexts(contexts) {
78
+ return parseFilterContexts(contexts ?? [], parserOptions);
79
+ }
80
+ return {
81
+ flatten: handleContexts(options.flattenContexts),
82
+ suppress: handleContexts(options.suppressContexts),
83
+ filter: handleFilterContexts(options.filterContexts),
84
+ sort: handleContexts(options.sortContexts),
85
+ detail: handleContexts(options.detailContexts),
86
+ download: handleContexts(options.downloadContexts),
87
+ label: handleContexts(options.labelContexts),
88
+ prominent: handleContexts(options.prominentContexts)
89
+ };
90
+ }
91
+ function parseWebsiteScopes(scopes, options) {
92
+ if (scopes == null) return null;
93
+ return Array.from(scopes.scope, (scope) => ({
94
+ uuid: scope.uuid.payload,
95
+ type: scope.uuid.type,
96
+ identification: parseIdentification(scope.identification, options)
97
+ }));
98
+ }
99
+ function parseWebsiteOptions(rawOptions, options) {
100
+ const parsedOptions = {
101
+ scopes: parseWebsiteScopes(rawOptions?.scopes, options),
102
+ contextTree: rawOptions == null ? null : parseAllOptionContexts(rawOptions, options),
103
+ labels: { title: null }
104
+ };
105
+ const notes = parseNotes(rawOptions?.notes, options);
106
+ for (const note of notes) {
107
+ if (note.title?.getText() !== "Title label") continue;
108
+ parsedOptions.labels.title = note.content;
109
+ break;
110
+ }
111
+ return parsedOptions;
112
+ }
113
+ //#endregion
114
+ export { parseWebsiteOptions };
@@ -0,0 +1,12 @@
1
+ import { WebSidebar, Website } from "../../types/website.mjs";
2
+ import { XMLWebsiteProperties, XMLWebsiteTree } from "../../xml/types.mjs";
3
+ import { ParserOptions } from "../helpers.mjs";
4
+ //#region src/parsers/website/properties.d.ts
5
+ /**
6
+ * Parses raw website properties into a standardized Website properties structure
7
+ *
8
+ * @param properties - Array of raw website properties in OCHRE format
9
+ * @returns Parsed WebsiteProperties object
10
+ */
11
+ export declare function parseWebsiteProperties<T extends ReadonlyArray<string>>(properties: XMLWebsiteProperties["property"], websiteTree: XMLWebsiteTree, sidebar: WebSidebar<T> | null, options: ParserOptions<T>, parent: Website<T>["properties"] | null): Website<T>["properties"];
12
+ //#endregion
@@ -0,0 +1,158 @@
1
+ import { parseSimplifiedProperties } from "../index.mjs";
2
+ import { parseWebsiteOptions } from "./options.mjs";
3
+ import { websitePresentationReader } from "./reader.mjs";
4
+ import { parseStylesheets } from "./styles.mjs";
5
+ //#region src/parsers/website/properties.ts
6
+ /**
7
+ * Parses raw website properties into a standardized Website properties structure
8
+ *
9
+ * @param properties - Array of raw website properties in OCHRE format
10
+ * @returns Parsed WebsiteProperties object
11
+ */
12
+ function parseWebsiteProperties(properties, websiteTree, sidebar, options, parent) {
13
+ const mainProperties = parseSimplifiedProperties({ property: properties }, options);
14
+ const websiteReader = websitePresentationReader(mainProperties).nested("presentation");
15
+ const returnProperties = {
16
+ type: parent?.type ?? "traditional",
17
+ status: parent?.status ?? "development",
18
+ versionLabel: parent?.versionLabel ?? "release",
19
+ privacy: parent?.privacy ?? "public",
20
+ contact: parent?.contact ?? null,
21
+ loadingVariant: "spinner",
22
+ theme: {
23
+ isThemeToggleDisplayed: true,
24
+ defaultTheme: "system"
25
+ },
26
+ icon: {
27
+ logoUuid: null,
28
+ faviconUuid: null,
29
+ appleTouchIconUuid: null
30
+ },
31
+ navbar: {
32
+ isDisplayed: true,
33
+ variant: "default",
34
+ alignment: "start",
35
+ isProjectDisplayed: true,
36
+ searchBarBoundElementUuid: null,
37
+ items: parent?.navbar.items ?? null
38
+ },
39
+ footer: {
40
+ isDisplayed: true,
41
+ logoUuid: null,
42
+ items: parent?.footer.items ?? null
43
+ },
44
+ sidebar: sidebar ?? parent?.sidebar ?? null,
45
+ itemPage: {
46
+ isMainContentDisplayed: parent?.itemPage.isMainContentDisplayed ?? true,
47
+ description: {
48
+ isDisplayed: parent?.itemPage.description.isDisplayed ?? true,
49
+ isHeaderDisplayed: parent?.itemPage.description.isHeaderDisplayed ?? true
50
+ },
51
+ document: {
52
+ isDisplayed: parent?.itemPage.document.isDisplayed ?? true,
53
+ isHeaderDisplayed: parent?.itemPage.document.isHeaderDisplayed ?? true
54
+ },
55
+ notes: {
56
+ isDisplayed: parent?.itemPage.notes.isDisplayed ?? true,
57
+ isHeaderDisplayed: parent?.itemPage.notes.isHeaderDisplayed ?? true,
58
+ variant: parent?.itemPage.notes.variant ?? "discrete"
59
+ },
60
+ events: {
61
+ isDisplayed: parent?.itemPage.events.isDisplayed ?? true,
62
+ isHeaderDisplayed: parent?.itemPage.events.isHeaderDisplayed ?? true,
63
+ variant: parent?.itemPage.events.variant ?? "tabular"
64
+ },
65
+ periods: {
66
+ isDisplayed: parent?.itemPage.periods.isDisplayed ?? true,
67
+ isHeaderDisplayed: parent?.itemPage.periods.isHeaderDisplayed ?? true
68
+ },
69
+ isPropertiesDisplayed: parent?.itemPage.isPropertiesDisplayed ?? true,
70
+ bibliography: {
71
+ isDisplayed: parent?.itemPage.bibliography.isDisplayed ?? true,
72
+ isHeaderDisplayed: parent?.itemPage.bibliography.isHeaderDisplayed ?? true
73
+ },
74
+ isPropertyValuesGrouped: parent?.itemPage.isPropertyValuesGrouped ?? true,
75
+ isPublicationDateTimeDisplayed: parent?.itemPage.isPublicationDateTimeDisplayed ?? true,
76
+ isPersistentIdentifierDisplayed: parent?.itemPage.isPersistentIdentifierDisplayed ?? true,
77
+ iiifViewer: parent?.itemPage.iiifViewer ?? "universal-viewer"
78
+ },
79
+ options: {
80
+ contextTree: parent?.options.contextTree ?? null,
81
+ scopes: parent?.options.scopes ?? null,
82
+ labels: { title: parent?.options.labels.title ?? null },
83
+ stylesheets: { properties: parent?.options.stylesheets.properties ?? [] }
84
+ }
85
+ };
86
+ const contactProperty = websiteReader.property("contact");
87
+ if (contactProperty !== null) {
88
+ const contactContent = contactProperty.values[0]?.content.toString().split(";") ?? [];
89
+ if (contactContent.length === 2) returnProperties.contact = {
90
+ name: contactContent[0],
91
+ email: contactContent[1] ?? null
92
+ };
93
+ else throw new Error(`Contact property must use “name;email”, got “${contactProperty.values[0]?.content}” (website uuid “${websiteTree.uuid}”)`, { cause: websiteTree });
94
+ }
95
+ websiteReader.readAll(returnProperties, {
96
+ type: "webUI",
97
+ status: "status",
98
+ versionLabel: "version-label",
99
+ privacy: "privacy",
100
+ loadingVariant: "loading-variant"
101
+ });
102
+ websiteReader.readAll(returnProperties.theme, {
103
+ isThemeToggleDisplayed: "supports-theme-toggle",
104
+ defaultTheme: "default-theme"
105
+ });
106
+ websiteReader.readAllUuids(returnProperties.icon, {
107
+ logoUuid: "navbar-logo",
108
+ faviconUuid: "favicon-ico",
109
+ appleTouchIconUuid: "favicon-img"
110
+ });
111
+ websiteReader.readAll(returnProperties.navbar, {
112
+ isDisplayed: "navbar-displayed",
113
+ variant: "navbar-variant",
114
+ alignment: "navbar-alignment",
115
+ isProjectDisplayed: "navbar-project-displayed"
116
+ });
117
+ websiteReader.readAllUuids(returnProperties.navbar, { searchBarBoundElementUuid: "bound-element-navbar-search-bar" });
118
+ websiteReader.readAll(returnProperties.footer, { isDisplayed: "footer-displayed" });
119
+ websiteReader.readAllUuids(returnProperties.footer, { logoUuid: "footer-logo" });
120
+ const itemPageReader = websiteReader.nestedByValue("page-type", "item-page");
121
+ if (itemPageReader.size > 0) {
122
+ for (const [key, slug] of [
123
+ ["description", "description"],
124
+ ["document", "document"],
125
+ ["notes", "notes"],
126
+ ["events", "events"],
127
+ ["periods", "periods"],
128
+ ["bibliography", "bibliography"]
129
+ ]) {
130
+ const section = returnProperties.itemPage[key];
131
+ itemPageReader.readAll(section, {
132
+ isDisplayed: `item-page-${slug}-displayed`,
133
+ isHeaderDisplayed: `item-page-${slug}-header-displayed`
134
+ });
135
+ }
136
+ itemPageReader.readAll(returnProperties.itemPage.notes, { variant: "item-page-notes-display-variant" });
137
+ itemPageReader.readAll(returnProperties.itemPage.events, { variant: "item-page-events-display-variant" });
138
+ itemPageReader.readAll(returnProperties.itemPage, {
139
+ isPropertyValuesGrouped: "item-page-property-values-grouped",
140
+ isPublicationDateTimeDisplayed: "item-page-publication-date-time-displayed",
141
+ isPersistentIdentifierDisplayed: "item-page-persistent-identifier-displayed",
142
+ iiifViewer: "item-page-iiif-viewer"
143
+ });
144
+ }
145
+ if (websiteTree.options != null) {
146
+ const parsedOptions = parseWebsiteOptions(websiteTree.options, options);
147
+ returnProperties.options.scopes = (parsedOptions.scopes != null && parsedOptions.scopes.length > 0 ? parsedOptions : returnProperties.options).scopes;
148
+ returnProperties.options.contextTree = parsedOptions.contextTree ?? returnProperties.options.contextTree;
149
+ returnProperties.options.labels = { title: parsedOptions.labels.title ?? returnProperties.options.labels.title };
150
+ }
151
+ if ("styleOptions" in websiteTree && websiteTree.styleOptions != null) {
152
+ const stylesheetProperties = parseStylesheets(websiteTree.styleOptions.style);
153
+ if (stylesheetProperties.length > 0) returnProperties.options.stylesheets.properties = stylesheetProperties;
154
+ }
155
+ return returnProperties;
156
+ }
157
+ //#endregion
158
+ export { parseWebsiteProperties };
@@ -3,24 +3,74 @@ import { LanguageCodes, PropertyValueContent, SimplifiedProperty } from "../../t
3
3
  import { ParserOptions } from "../helpers.mjs";
4
4
  //#region src/parsers/website/reader.d.ts
5
5
  type WebsitePropertyContent<T extends LanguageCodes> = PropertyValueContent<T>["content"];
6
+ /**
7
+ * The fields of an object that hold a UUID, which are the only ones
8
+ * {@link WebsitePresentationReader.readAllUuids} can write
9
+ */
10
+ type UuidKeys<O> = { [K in keyof O]-?: O[K] extends string | null ? K : never; }[keyof O];
11
+ /**
12
+ * Reads a website's presentation properties
13
+ *
14
+ * OCHRE stores everything a website declares about itself as labeled property
15
+ * values nested under a "presentation" property, so every read is "find the
16
+ * property with this label, then take its first value". This owns that walk
17
+ * and the coercions that go with it, and hands back readers for nested levels
18
+ * so the caller never touches the raw property array.
19
+ */
6
20
  export declare class WebsitePresentationReader<T extends LanguageCodes> {
7
21
  private readonly sourceProperties;
8
22
  constructor(sourceProperties: ReadonlyArray<SimplifiedProperty<T>>);
23
+ private propertyByValue;
24
+ /**
25
+ * Overwrite one field from a labeled OCHRE property
26
+ *
27
+ * Private because {@link readAll} covers every caller: a single-field read
28
+ * is a one-entry label map, and going through the map keeps the field's type
29
+ * coming from the target rather than from a type argument.
30
+ */
31
+ private readOne;
9
32
  property(label: string): SimplifiedProperty<T> | null;
10
33
  requiredProperty(label: string, message: string): SimplifiedProperty<T>;
11
- propertyByValue(label: string, value: WebsitePropertyContent<T>): SimplifiedProperty<T> | null;
12
34
  valueNode(label: string): PropertyValueContent<T> | null;
13
- values(label: string): Array<PropertyValueContent<T>>;
14
35
  value<U = WebsitePropertyContent<T>>(label: string): U | null;
15
36
  valueOr<U>(label: string, fallback: U): U;
37
+ /**
38
+ * Read a property whose field is a string but whose OCHRE value may not be
39
+ *
40
+ * OCHRE types a dimension like "width" from what was entered, so the same
41
+ * property arrives as `"50%"` on one resource and `50` on another. Anything
42
+ * that is neither is treated as unset rather than stringified, so a stray
43
+ * boolean does not become the literal text "true".
44
+ * @param label - The OCHRE property label to read
45
+ * @returns The string, or null when the property is unset or not string-like
46
+ */
16
47
  stringValue(label: string): string | null;
17
- numberValue(label: string): number | null;
18
48
  uuid(label: string): string | null;
19
49
  multilingualValue(label: string, options: ParserOptions<T>): MultilingualString<T> | null;
50
+ /**
51
+ * Overwrite several fields from the OCHRE properties naming them
52
+ *
53
+ * The defaults object carries the shape and the fallback values, and the
54
+ * label map carries the OCHRE names, so a component states each property
55
+ * twice rather than five times: once as a default and once as a label. The
56
+ * field's type comes from the defaults object, so no type argument is needed.
57
+ * @param target - The object holding the fields, pre-filled with defaults
58
+ * @param labels - The OCHRE property label for each field to overwrite
59
+ */
60
+ readAll<O extends object>(target: O, labels: Partial<Record<keyof O, string>>): void;
61
+ /**
62
+ * Overwrite several UUID fields from the UUIDs labeled OCHRE properties
63
+ * point at
64
+ *
65
+ * The same shape as {@link readAll}, for the fields that take the value's
66
+ * target rather than its content.
67
+ * @param target - The object holding the fields, pre-filled with defaults
68
+ * @param labels - The OCHRE property label for each field to overwrite
69
+ */
70
+ readAllUuids<O extends object>(target: O, labels: Partial<Record<UuidKeys<O>, string>>): void;
20
71
  nested(label: string): WebsitePresentationReader<T>;
21
72
  nestedByValue(label: string, value: WebsitePropertyContent<T>): WebsitePresentationReader<T>;
22
73
  get size(): number;
23
- get properties(): ReadonlyArray<SimplifiedProperty<T>>;
24
74
  }
25
75
  export declare function websitePresentationReader<T extends LanguageCodes>(properties: ReadonlyArray<SimplifiedProperty<T>>): WebsitePresentationReader<T>;
26
76
  //#endregion
@@ -1,27 +1,46 @@
1
- import { getPropertyByVariableLabel, getPropertyByVariableLabelAndValueContent, getPropertyValueByVariableLabel } from "../../getters.mjs";
1
+ import { getProperty, getPropertyValue } from "../../getters.mjs";
2
2
  import { multilingualFromText } from "../helpers.mjs";
3
3
  //#region src/parsers/website/reader.ts
4
+ /**
5
+ * Reads a website's presentation properties
6
+ *
7
+ * OCHRE stores everything a website declares about itself as labeled property
8
+ * values nested under a "presentation" property, so every read is "find the
9
+ * property with this label, then take its first value". This owns that walk
10
+ * and the coercions that go with it, and hands back readers for nested levels
11
+ * so the caller never touches the raw property array.
12
+ */
4
13
  var WebsitePresentationReader = class WebsitePresentationReader {
5
14
  sourceProperties;
6
15
  constructor(sourceProperties) {
7
16
  this.sourceProperties = sourceProperties;
8
17
  }
18
+ propertyByValue(label, value) {
19
+ return getProperty(this.sourceProperties, {
20
+ label,
21
+ valueContent: value
22
+ });
23
+ }
24
+ /**
25
+ * Overwrite one field from a labeled OCHRE property
26
+ *
27
+ * Private because {@link readAll} covers every caller: a single-field read
28
+ * is a one-entry label map, and going through the map keeps the field's type
29
+ * coming from the target rather than from a type argument.
30
+ */
31
+ readOne(target, key, label) {
32
+ target[key] = this.valueOr(label, target[key]);
33
+ }
9
34
  property(label) {
10
- return getPropertyByVariableLabel(this.sourceProperties, label);
35
+ return getProperty(this.sourceProperties, { label });
11
36
  }
12
37
  requiredProperty(label, message) {
13
38
  const property = this.property(label);
14
39
  if (property === null) throw new Error(message, { cause: this.sourceProperties });
15
40
  return property;
16
41
  }
17
- propertyByValue(label, value) {
18
- return getPropertyByVariableLabelAndValueContent(this.sourceProperties, label, value);
19
- }
20
42
  valueNode(label) {
21
- return getPropertyValueByVariableLabel(this.sourceProperties, label);
22
- }
23
- values(label) {
24
- return this.property(label)?.values ?? [];
43
+ return getPropertyValue(this.sourceProperties, { label });
25
44
  }
26
45
  value(label) {
27
46
  return this.valueNode(label)?.content ?? null;
@@ -29,16 +48,20 @@ var WebsitePresentationReader = class WebsitePresentationReader {
29
48
  valueOr(label, fallback) {
30
49
  return this.value(label) ?? fallback;
31
50
  }
51
+ /**
52
+ * Read a property whose field is a string but whose OCHRE value may not be
53
+ *
54
+ * OCHRE types a dimension like "width" from what was entered, so the same
55
+ * property arrives as `"50%"` on one resource and `50` on another. Anything
56
+ * that is neither is treated as unset rather than stringified, so a stray
57
+ * boolean does not become the literal text "true".
58
+ * @param label - The OCHRE property label to read
59
+ * @returns The string, or null when the property is unset or not string-like
60
+ */
32
61
  stringValue(label) {
33
62
  const value = this.value(label);
34
- return value == null ? null : value.toString();
35
- }
36
- numberValue(label) {
37
- const value = this.value(label);
38
- if (typeof value === "number") return value;
39
- if (typeof value !== "string" || value.trim() === "") return null;
40
- const parsedValue = Number(value);
41
- return Number.isNaN(parsedValue) ? null : parsedValue;
63
+ if (typeof value === "string") return value;
64
+ return typeof value === "number" ? value.toString() : null;
42
65
  }
43
66
  uuid(label) {
44
67
  return this.valueNode(label)?.uuid ?? null;
@@ -49,6 +72,31 @@ var WebsitePresentationReader = class WebsitePresentationReader {
49
72
  if (value.label != null) return value.label;
50
73
  return typeof value.content === "string" ? multilingualFromText(value.content, options) : null;
51
74
  }
75
+ /**
76
+ * Overwrite several fields from the OCHRE properties naming them
77
+ *
78
+ * The defaults object carries the shape and the fallback values, and the
79
+ * label map carries the OCHRE names, so a component states each property
80
+ * twice rather than five times: once as a default and once as a label. The
81
+ * field's type comes from the defaults object, so no type argument is needed.
82
+ * @param target - The object holding the fields, pre-filled with defaults
83
+ * @param labels - The OCHRE property label for each field to overwrite
84
+ */
85
+ readAll(target, labels) {
86
+ for (const [key, label] of Object.entries(labels)) if (label != null) this.readOne(target, key, label);
87
+ }
88
+ /**
89
+ * Overwrite several UUID fields from the UUIDs labeled OCHRE properties
90
+ * point at
91
+ *
92
+ * The same shape as {@link readAll}, for the fields that take the value's
93
+ * target rather than its content.
94
+ * @param target - The object holding the fields, pre-filled with defaults
95
+ * @param labels - The OCHRE property label for each field to overwrite
96
+ */
97
+ readAllUuids(target, labels) {
98
+ for (const [key, label] of Object.entries(labels)) if (label != null) target[key] = this.uuid(label) ?? target[key];
99
+ }
52
100
  nested(label) {
53
101
  return new WebsitePresentationReader(this.property(label)?.properties ?? []);
54
102
  }
@@ -58,9 +106,6 @@ var WebsitePresentationReader = class WebsitePresentationReader {
58
106
  get size() {
59
107
  return this.sourceProperties.length;
60
108
  }
61
- get properties() {
62
- return this.sourceProperties;
63
- }
64
109
  };
65
110
  function websitePresentationReader(properties) {
66
111
  return new WebsitePresentationReader(properties);