ochre-sdk 1.0.13 → 1.0.14

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 (51) hide show
  1. package/dist/constants.d.mts +17 -0
  2. package/dist/constants.mjs +85 -0
  3. package/dist/fetchers/gallery.d.mts +38 -0
  4. package/dist/fetchers/gallery.mjs +91 -0
  5. package/dist/fetchers/item-links.d.mts +32 -0
  6. package/dist/fetchers/item-links.mjs +120 -0
  7. package/dist/fetchers/item.d.mts +74 -0
  8. package/dist/fetchers/item.mjs +146 -0
  9. package/dist/fetchers/set/items.d.mts +48 -0
  10. package/dist/fetchers/set/items.mjs +268 -0
  11. package/dist/fetchers/set/property-values.d.mts +46 -0
  12. package/dist/fetchers/set/property-values.mjs +514 -0
  13. package/dist/fetchers/website.d.mts +25 -0
  14. package/dist/fetchers/website.mjs +38 -0
  15. package/dist/getters.d.mts +193 -0
  16. package/dist/getters.mjs +341 -0
  17. package/dist/helpers.d.mts +18 -0
  18. package/dist/helpers.mjs +33 -0
  19. package/dist/index.d.mts +12 -1971
  20. package/dist/index.mjs +9 -7236
  21. package/dist/parsers/helpers.d.mts +27 -0
  22. package/dist/parsers/helpers.mjs +53 -0
  23. package/dist/parsers/index.d.mts +65 -0
  24. package/dist/parsers/index.mjs +1338 -0
  25. package/dist/parsers/mdx.d.mts +4 -0
  26. package/dist/parsers/mdx.mjs +9 -0
  27. package/dist/parsers/multilingual.d.mts +189 -0
  28. package/dist/parsers/multilingual.mjs +410 -0
  29. package/dist/parsers/string.d.mts +29 -0
  30. package/dist/parsers/string.mjs +477 -0
  31. package/dist/parsers/website/index.d.mts +20 -0
  32. package/dist/parsers/website/index.mjs +1245 -0
  33. package/dist/parsers/website/reader.d.mts +29 -0
  34. package/dist/parsers/website/reader.mjs +75 -0
  35. package/dist/query.d.mts +13 -0
  36. package/dist/query.mjs +827 -0
  37. package/dist/schemas.d.mts +84 -0
  38. package/dist/schemas.mjs +232 -0
  39. package/dist/types/index.d.mts +840 -0
  40. package/dist/types/index.mjs +1 -0
  41. package/dist/types/website.d.mts +501 -0
  42. package/dist/types/website.mjs +1 -0
  43. package/dist/utils.d.mts +34 -0
  44. package/dist/utils.mjs +172 -0
  45. package/dist/xml/metadata.d.mts +5 -0
  46. package/dist/xml/metadata.mjs +30 -0
  47. package/dist/xml/schemas.d.mts +13 -0
  48. package/dist/xml/schemas.mjs +849 -0
  49. package/dist/xml/types.d.mts +901 -0
  50. package/dist/xml/types.mjs +1 -0
  51. package/package.json +19 -17
@@ -0,0 +1,477 @@
1
+ import { TEXT_ANNOTATION_UUID } from "../constants.mjs";
2
+ import { serializeMDXText } from "./mdx.mjs";
3
+ import { MultilingualString } from "./multilingual.mjs";
4
+ import { renderOptionsSchema, whitespaceSchema } from "../schemas.mjs";
5
+ import { getXMLSourceIndex } from "../xml/metadata.mjs";
6
+ import * as v from "valibot";
7
+ //#region src/parsers/string.ts
8
+ const TEXT_ANNOTATION_TOKEN = "text-annotation";
9
+ const TEXT_STYLING_TOKEN = "text-styling";
10
+ const HOVER_CARD_TOKEN = "hover-card";
11
+ const ITEM_PAGE_TOKEN = "item-page";
12
+ const ENTRY_PAGE_TOKEN = "entry-page";
13
+ const VARIANT_TOKEN = "variant";
14
+ const HEADING_LEVEL_TOKEN = "heading-level";
15
+ const MDX_QUOTED_ATTRIBUTE_ESCAPE_REGEX = /[\n\r"]/;
16
+ const MDX_RENDER_ELEMENTS = {
17
+ bold: "strong",
18
+ italic: "em",
19
+ underline: "u"
20
+ };
21
+ function isXMLRichTextLink(value) {
22
+ return typeof value === "object" && value != null;
23
+ }
24
+ function getLinkStringProperty(link, property) {
25
+ switch (property) {
26
+ case "uuid": return "uuid" in link && typeof link.uuid === "string" ? link.uuid : null;
27
+ case "href": return "href" in link && typeof link.href === "string" ? link.href : null;
28
+ case "height": return "height" in link && link.height != null ? link.height.toString() : null;
29
+ case "width": return "width" in link && link.width != null ? link.width.toString() : null;
30
+ }
31
+ }
32
+ function transformPermanentIdentificationUrl(url) {
33
+ return url.replace("https://pi.lib.uchicago.edu/1001/org/ochre/", "https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=");
34
+ }
35
+ /**
36
+ * Applies text rendering options (bold, italic, underline) to a string
37
+ *
38
+ * @param contentString - The string content to render
39
+ * @param renderString - Space-separated string of render options
40
+ * @param rendering - Which text rendering to produce
41
+ * @returns String with rich-text formatting applied
42
+ * @internal
43
+ */
44
+ function parseRenderOptions(contentString, renderString, rendering) {
45
+ const { success, output } = v.safeParse(renderOptionsSchema, renderString);
46
+ if (!success) return contentString;
47
+ if (rendering !== "rich") return contentString.replaceAll("'", "'");
48
+ return applyRichRenderOptions(contentString, output).replaceAll("'", "'");
49
+ }
50
+ function applyRichRenderOptions(contentString, options) {
51
+ const withoutLeadingWhitespace = contentString.trimStart();
52
+ const leadingWhitespace = contentString.slice(0, contentString.length - withoutLeadingWhitespace.length);
53
+ const renderableContent = withoutLeadingWhitespace.trimEnd();
54
+ const trailingWhitespace = withoutLeadingWhitespace.slice(renderableContent.length);
55
+ if (renderableContent === "") return contentString;
56
+ return `${leadingWhitespace}${applyMDXRenderElements(renderableContent, options)}${trailingWhitespace}`;
57
+ }
58
+ function applyMDXRenderElements(contentString, options) {
59
+ let result = contentString;
60
+ for (const option of options) {
61
+ const element = MDX_RENDER_ELEMENTS[option];
62
+ result = `<${element}>${result}</${element}>`;
63
+ }
64
+ return result;
65
+ }
66
+ /**
67
+ * Applies whitespace options to a string (newline)
68
+ *
69
+ * @param contentString - The string content to modify
70
+ * @param whitespace - Space-separated string of whitespace options
71
+ * @param rendering - Which text rendering to produce
72
+ * @returns String with whitespace modifications applied
73
+ *
74
+ * @internal
75
+ */
76
+ function parseWhitespace(contentString, whitespace, rendering) {
77
+ let returnString = contentString;
78
+ const { success, output } = v.safeParse(whitespaceSchema, whitespace);
79
+ if (!success) return contentString;
80
+ for (const option of output) switch (option) {
81
+ case "newline":
82
+ if (rendering === "rich") returnString = returnString.trim() === "***" ? `${returnString}\n` : `<br />\n${returnString}`;
83
+ else returnString = `\n${returnString}`;
84
+ break;
85
+ case "trailing":
86
+ returnString = `${returnString} `;
87
+ break;
88
+ case "leading":
89
+ returnString = ` ${returnString}`;
90
+ break;
91
+ }
92
+ return returnString.replaceAll("&#39;", "'");
93
+ }
94
+ /**
95
+ * Parses XML string into a formatted string with whitespace and rendering options
96
+ *
97
+ * @param string - XML string to parse
98
+ * @param options - Options for parsing
99
+ * @param options.rendering - Which text rendering to produce
100
+ * @returns Formatted string with whitespace and rendering options
101
+ *
102
+ * @internal
103
+ */
104
+ function parseXMLStringVariant(string, options) {
105
+ let returnString = parseXMLStringPayload(string, options);
106
+ if (string.rend != null) returnString = parseRenderOptions(returnString, string.rend, options.rendering);
107
+ if (string.whitespace != null) returnString = parseWhitespace(returnString, string.whitespace, options.rendering);
108
+ return returnString;
109
+ }
110
+ function parseXMLStringPayload(string, options) {
111
+ const payload = string.payload ?? "";
112
+ return options.rendering === "rich" ? serializeMDXText(payload) : payload;
113
+ }
114
+ function parseXMLString(string) {
115
+ return {
116
+ text: parseXMLStringVariant(string, { rendering: "plain" }),
117
+ richText: parseXMLStringVariant(string, { rendering: "rich" })
118
+ };
119
+ }
120
+ /**
121
+ * Creates an MDX component based on the variant
122
+ *
123
+ * @param variant - The variant of the component
124
+ * @param properties - The properties of the component
125
+ * @param properties.uuid - The UUID of the component
126
+ * @param properties.href - The href of the component
127
+ * @param properties.height - The height of the component
128
+ * @param properties.width - The width of the component
129
+ * @param properties.content - The content of the component
130
+ * @param properties.text - The text of the component
131
+ * @returns The MDX component as a string
132
+ *
133
+ * @internal
134
+ */
135
+ function createMDXComponent(variant, properties) {
136
+ const { uuid, href, height, width, content, text } = properties;
137
+ const tooltipContent = getDistinctTooltipContent(content, text);
138
+ let returnString = "";
139
+ switch (variant) {
140
+ case "inlineImage":
141
+ returnString = `<InlineImage${createMDXStringAttribute("uuid", uuid ?? "null")}${createMDXStringAttribute("content", content)} height={${height ?? "null"}} width={${width ?? "null"}} />`;
142
+ break;
143
+ case "internalLink":
144
+ returnString = `<InternalLink${createMDXStringAttribute("uuid", uuid ?? "null")}${createMDXStringAttribute("content", tooltipContent)}>${text}</InternalLink>`;
145
+ break;
146
+ case "externalLink":
147
+ returnString = `<ExternalLink${createMDXStringAttribute("href", href == null ? "#" : transformPermanentIdentificationUrl(href))}${createMDXStringAttribute("content", tooltipContent)}>${text}</ExternalLink>`;
148
+ break;
149
+ case "documentLink":
150
+ returnString = `<ExternalLink${createMDXStringAttribute("href", `https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=${uuid}&load`)}${createMDXStringAttribute("content", tooltipContent)}>${text}</ExternalLink>`;
151
+ break;
152
+ case "tooltipSpan":
153
+ returnString = `<TooltipSpan${createMDXStringAttribute("content", content)}>${text}</TooltipSpan>`;
154
+ break;
155
+ }
156
+ return returnString;
157
+ }
158
+ function getDistinctTooltipContent(content, textContent) {
159
+ return content === textContent ? void 0 : content;
160
+ }
161
+ function createMDXStringAttribute(name, value) {
162
+ if (value == null || value === "") return "";
163
+ return ` ${name}=${MDX_QUOTED_ATTRIBUTE_ESCAPE_REGEX.test(value) ? `{${JSON.stringify(value)}}` : `"${value}"`}`;
164
+ }
165
+ function applyWhitespaceToResult(result, whitespace, rendering) {
166
+ return whitespace == null ? result : parseWhitespace(result, whitespace, rendering);
167
+ }
168
+ function getPropertyValueUuid(property) {
169
+ const value = property?.value?.[0];
170
+ return value?.uuid == null || value.uuid === "" ? null : value.uuid;
171
+ }
172
+ function getFirstPropertyMetadata(item) {
173
+ const itemProperty = item.properties?.property[0];
174
+ if (itemProperty == null) return null;
175
+ return {
176
+ labelUuid: itemProperty.label.uuid,
177
+ valueUuid: getPropertyValueUuid(itemProperty)
178
+ };
179
+ }
180
+ function parseContentLikeForLanguage(value, options) {
181
+ if (value == null) return "";
182
+ if (!("content" in value)) return parseXMLString(value).text;
183
+ const contentItem = value.content.find((item) => item.lang === options.language) ?? value.content[0];
184
+ if (contentItem == null) return "";
185
+ const languages = [contentItem.lang];
186
+ return parseXMLContent({ content: [contentItem] }, { languages }).getText(contentItem.lang);
187
+ }
188
+ function parsePropertyValueText(property, options) {
189
+ const value = property?.value?.[0];
190
+ if (value == null) return "";
191
+ if (value.rawValue != null) return value.rawValue;
192
+ if (value.payload != null) return value.payload;
193
+ if (value.slug != null) return value.slug;
194
+ if (value.content != null) return parseContentLikeForLanguage(value, options);
195
+ return "";
196
+ }
197
+ function normalizePropertyToken(value) {
198
+ return value.trim().toLowerCase().replaceAll(/[\s_]+/g, "-");
199
+ }
200
+ function propertyLabelMatches(property, uuid, tokens, options) {
201
+ if (uuid !== "" && property.label.uuid === uuid) return true;
202
+ const label = normalizePropertyToken(parseContentLikeForLanguage(property.label, options));
203
+ return tokens.includes(label);
204
+ }
205
+ function propertyValueMatches(property, uuid, tokens, options) {
206
+ if (uuid !== "" && getPropertyValueUuid(property) === uuid) return true;
207
+ const value = normalizePropertyToken(parsePropertyValueText(property, options));
208
+ return tokens.includes(value);
209
+ }
210
+ function extractAnnotationMetadata(item, options) {
211
+ const result = {
212
+ linkVariant: null,
213
+ textStyling: null
214
+ };
215
+ const itemProperty = item.properties?.property[0];
216
+ if (itemProperty == null) return result;
217
+ if (!propertyLabelMatches(itemProperty, "f1c131b6-1498-48a4-95bf-a9edae9fd518", ["presentation"], options) || !propertyValueMatches(itemProperty, "b9ca2732-78f4-416e-b77f-dae7647e68a9", [TEXT_ANNOTATION_TOKEN], options)) return result;
218
+ for (const textAnnotationProperty of itemProperty.property ?? []) {
219
+ if (propertyValueMatches(textAnnotationProperty, "c7f6a08a-f07b-49b6-bcb1-af485da3c58f", [HOVER_CARD_TOKEN], options)) {
220
+ result.linkVariant = "hover-card";
221
+ continue;
222
+ }
223
+ if (propertyValueMatches(textAnnotationProperty, "bf4476ab-6bc8-40d0-a001-1446213c72ce", [ITEM_PAGE_TOKEN], options)) {
224
+ result.linkVariant = "item-page";
225
+ continue;
226
+ }
227
+ if (propertyValueMatches(textAnnotationProperty, "9d52db95-a9cf-45f7-a0bf-fc9ba9f0aae0", [ENTRY_PAGE_TOKEN], options)) {
228
+ result.linkVariant = "entry-page";
229
+ continue;
230
+ }
231
+ if (propertyValueMatches(textAnnotationProperty, "3e6f86ab-df81-45ae-8257-e2867357df56", [TEXT_STYLING_TOKEN], options)) {
232
+ let variant = "block";
233
+ let size = "md";
234
+ let headingLevel = null;
235
+ const cssStyles = [];
236
+ const textStylingProperties = textAnnotationProperty.property ?? [];
237
+ for (const textStylingProperty of textStylingProperties) {
238
+ if (propertyLabelMatches(textStylingProperty, "e1647bef-d801-4100-bdde-d081c422f763", [VARIANT_TOKEN], options)) {
239
+ variant = parsePropertyValueText(textStylingProperty, options);
240
+ for (const nestedProperty of textStylingProperty.property ?? []) if (propertyLabelMatches(nestedProperty, "", ["size"], options)) size = parsePropertyValueText(nestedProperty, options);
241
+ continue;
242
+ }
243
+ if (propertyLabelMatches(textStylingProperty, "d4266f0b-3f8d-4b32-8c15-4b229c8bb11e", [HEADING_LEVEL_TOKEN], options)) {
244
+ headingLevel = parsePropertyValueText(textStylingProperty, options);
245
+ continue;
246
+ }
247
+ cssStyles.push({
248
+ label: parseContentLikeForLanguage(textStylingProperty.label, options),
249
+ value: parsePropertyValueText(textStylingProperty, options)
250
+ });
251
+ }
252
+ result.textStyling = {
253
+ variant,
254
+ size,
255
+ headingLevel,
256
+ cssStyles
257
+ };
258
+ continue;
259
+ }
260
+ }
261
+ return result;
262
+ }
263
+ function hasRichTextEnvelope(item) {
264
+ return item.properties?.property[0] != null || getXMLRichTextLinks(item).length > 0;
265
+ }
266
+ function parseXMLStringItem(item, contentItem, options) {
267
+ if (!(item.payload != null || item.string != null) && getXMLRichTextLinks(item).length === 0) return item.whitespace == null ? "" : parseWhitespace("", item.whitespace, options.rendering);
268
+ if (hasRichTextEnvelope(item)) {
269
+ let linkString = item.payload != null ? parseXMLStringPayload(item, { rendering: options.rendering }) : parseNestedStringItems(item.string ?? [], contentItem, { ...options });
270
+ if (item.rend != null) linkString = parseRenderOptions(linkString, item.rend, options.rendering);
271
+ if (options.rendering === "plain") return applyWhitespaceToResult(linkString, item.whitespace, options.rendering);
272
+ return renderRichTextItem(item, linkString, contentItem, options);
273
+ }
274
+ if (item.payload != null) return parseXMLStringVariant(item, { rendering: options.rendering });
275
+ let result = parseNestedStringItems(item.string ?? [], contentItem, options);
276
+ if (item.rend != null) result = parseRenderOptions(result, item.rend, options.rendering);
277
+ return applyWhitespaceToResult(result, item.whitespace, options.rendering);
278
+ }
279
+ function parseNestedStringItems(items, contentItem, options) {
280
+ let result = "";
281
+ for (const item of items) result += parseXMLStringItem(item, contentItem, options);
282
+ return result;
283
+ }
284
+ function isTextAnnotationMarkerLink(link) {
285
+ return getLinkStringProperty(link, "uuid") === TEXT_ANNOTATION_UUID;
286
+ }
287
+ function wrapWithTextStyling(content, textStyling) {
288
+ if (textStyling == null) return content;
289
+ return `<Annotation type="text-styling"${createMDXStringAttribute("variant", textStyling.variant)}${createMDXStringAttribute("size", textStyling.size)}${createMDXStringAttribute("headingLevel", textStyling.headingLevel ?? void 0)}${textStyling.cssStyles.length > 0 ? ` cssStyles={{default: ${JSON.stringify(textStyling.cssStyles)}, tablet: [], mobile: []}}` : ""}>${content}</Annotation>`;
290
+ }
291
+ function createInternalLinkComponent(properties) {
292
+ const innerContent = wrapWithTextStyling(properties.text, properties.annotationMetadata.textStyling);
293
+ switch (properties.annotationMetadata.linkVariant) {
294
+ case "hover-card": return `<Annotation type="hover-card"${createMDXStringAttribute("uuid", properties.uuid ?? "null")}>${innerContent}</Annotation>`;
295
+ case "item-page": return `<InternalLink type="item"${createMDXStringAttribute("uuid", properties.uuid ?? "null")}>${innerContent}</InternalLink>`;
296
+ case "entry-page": return `<InternalLink type="entry"${createMDXStringAttribute("uuid", properties.uuid ?? "null")}>${innerContent}</InternalLink>`;
297
+ default: return `<InternalLink${createMDXStringAttribute("uuid", properties.uuid ?? "null")}${properties.propertyMetadata != null ? `${createMDXStringAttribute("properties", properties.propertyMetadata.labelUuid)}${createMDXStringAttribute("value", properties.propertyMetadata.valueUuid ?? void 0)}` : ""}${createMDXStringAttribute("content", getDistinctTooltipContent(properties.content, properties.text))}>${innerContent}</InternalLink>`;
298
+ }
299
+ }
300
+ function getXMLRichTextLinks(item) {
301
+ const links = [];
302
+ let fallbackIndex = 0;
303
+ for (const rawLinks of Object.values(item.links ?? {})) {
304
+ if (!Array.isArray(rawLinks)) continue;
305
+ for (const rawLink of rawLinks) if (isXMLRichTextLink(rawLink) && !isTextAnnotationMarkerLink(rawLink)) {
306
+ links.push({
307
+ link: rawLink,
308
+ fallbackIndex
309
+ });
310
+ fallbackIndex += 1;
311
+ }
312
+ }
313
+ links.sort((left, right) => {
314
+ const leftIndex = getXMLSourceIndex(left.link);
315
+ const rightIndex = getXMLSourceIndex(right.link);
316
+ if (leftIndex != null && rightIndex != null && leftIndex !== rightIndex) return leftIndex - rightIndex;
317
+ return left.fallbackIndex - right.fallbackIndex;
318
+ });
319
+ const sortedLinks = [];
320
+ for (const { link } of links) sortedLinks.push(link);
321
+ return sortedLinks;
322
+ }
323
+ function renderRichTextItem(item, linkString, contentItem, options) {
324
+ const { languages, rendering } = options;
325
+ const annotationMetadata = extractAnnotationMetadata(item, { language: contentItem.lang });
326
+ const links = getXMLRichTextLinks(item);
327
+ if (links.length === 0) return applyWhitespaceToResult(wrapWithTextStyling(linkString, annotationMetadata.textStyling), item.whitespace, rendering);
328
+ let result = "";
329
+ for (const link of links) {
330
+ const linkContent = link.identification != null ? "content" in link.identification.label ? parseXMLContent(link.identification.label, { languages }) : MultilingualString.create(contentItem.lang, parseXMLString(link.identification.label), languages) : MultilingualString.create(contentItem.lang, "", languages);
331
+ const contentText = (rendering === "rich" ? linkContent.getExactRichText(contentItem.lang) : linkContent.getExactText(contentItem.lang)) ?? "";
332
+ if ("type" in link && link.type != null) switch (link.type) {
333
+ case "IIIF":
334
+ case "image":
335
+ if ("rend" in link && link.rend === "inline") {
336
+ const component = createMDXComponent("inlineImage", {
337
+ uuid: getLinkStringProperty(link, "uuid"),
338
+ href: getLinkStringProperty(link, "href") ?? void 0,
339
+ height: getLinkStringProperty(link, "height") ?? void 0,
340
+ width: getLinkStringProperty(link, "width") ?? void 0,
341
+ content: contentText,
342
+ text: linkString
343
+ });
344
+ result += applyWhitespaceToResult(component, item.whitespace, rendering);
345
+ } else if (link.publicationDateTime != null) {
346
+ const component = createInternalLinkComponent({
347
+ uuid: getLinkStringProperty(link, "uuid"),
348
+ text: linkString,
349
+ content: contentText,
350
+ annotationMetadata
351
+ });
352
+ result += applyWhitespaceToResult(component, item.whitespace, rendering);
353
+ } else {
354
+ const component = createMDXComponent("tooltipSpan", {
355
+ uuid: getLinkStringProperty(link, "uuid"),
356
+ text: linkString,
357
+ content: contentText
358
+ });
359
+ result += applyWhitespaceToResult(component, item.whitespace, rendering);
360
+ }
361
+ break;
362
+ case "internalDocument": {
363
+ const component = createInternalLinkComponent({
364
+ uuid: getLinkStringProperty(link, "uuid"),
365
+ text: linkString,
366
+ content: contentText,
367
+ annotationMetadata,
368
+ propertyMetadata: getFirstPropertyMetadata(item)
369
+ });
370
+ result += applyWhitespaceToResult(component, item.whitespace, rendering);
371
+ break;
372
+ }
373
+ case "externalDocument": {
374
+ const component = link.publicationDateTime != null ? createMDXComponent("documentLink", {
375
+ uuid: getLinkStringProperty(link, "uuid"),
376
+ text: linkString,
377
+ content: contentText
378
+ }) : createMDXComponent("tooltipSpan", {
379
+ uuid: getLinkStringProperty(link, "uuid"),
380
+ text: linkString,
381
+ content: contentText
382
+ });
383
+ result += applyWhitespaceToResult(component, item.whitespace, rendering);
384
+ break;
385
+ }
386
+ case "webpage": {
387
+ const component = createMDXComponent("externalLink", {
388
+ uuid: getLinkStringProperty(link, "uuid"),
389
+ href: getLinkStringProperty(link, "href") ?? "#",
390
+ text: linkString,
391
+ content: contentText
392
+ });
393
+ result += applyWhitespaceToResult(component, item.whitespace, rendering);
394
+ break;
395
+ }
396
+ }
397
+ else if (link.publicationDateTime != null) {
398
+ const component = createInternalLinkComponent({
399
+ uuid: getLinkStringProperty(link, "uuid"),
400
+ text: linkString,
401
+ content: contentText,
402
+ annotationMetadata
403
+ });
404
+ result += applyWhitespaceToResult(component, item.whitespace, rendering);
405
+ } else {
406
+ const component = createMDXComponent("tooltipSpan", {
407
+ uuid: getLinkStringProperty(link, "uuid"),
408
+ text: linkString,
409
+ content: contentText
410
+ });
411
+ result += applyWhitespaceToResult(component, item.whitespace, rendering);
412
+ }
413
+ }
414
+ return result;
415
+ }
416
+ /**
417
+ * Parses rich text content into a formatted string with links and annotations
418
+ *
419
+ * @param item - XML-based rich text item to parse
420
+ * @param options - Options for parsing
421
+ * @param options.languages - Languages of the content
422
+ * @returns Plain and rich formatted strings
423
+ *
424
+ * @internal
425
+ */
426
+ function parseXMLContent(item, options) {
427
+ const { languages } = options;
428
+ const aliases = extractAliases(item) ?? [];
429
+ const content = {};
430
+ for (const contentItem of item.content) {
431
+ if (contentItem.lang === "zxx" || !languages.includes(contentItem.lang)) continue;
432
+ const language = contentItem.lang;
433
+ const entries = content[language] ?? [];
434
+ entries.push(parseXMLContentItem(contentItem, { languages }));
435
+ content[language] = entries;
436
+ }
437
+ if (Object.keys(content).length > 0) return MultilingualString.fromEntries(content, languages, { aliases });
438
+ for (const contentItem of item.content) {
439
+ if (contentItem.lang === "zxx") continue;
440
+ const fallbackText = parseXMLContentItem(contentItem, { languages: [contentItem.lang] });
441
+ const fallbackContent = {};
442
+ for (const language of languages) fallbackContent[language] = [fallbackText];
443
+ return MultilingualString.fromEntries(fallbackContent, languages, { aliases });
444
+ }
445
+ return MultilingualString.empty(languages, { aliases });
446
+ }
447
+ function parseXMLContentItem(contentItem, options) {
448
+ return {
449
+ text: parseNestedStringItems(contentItem.string, contentItem, {
450
+ ...options,
451
+ rendering: "plain"
452
+ }),
453
+ richText: parseNestedStringItems(contentItem.string, contentItem, {
454
+ ...options,
455
+ rendering: "rich"
456
+ })
457
+ };
458
+ }
459
+ /**
460
+ * Extracts alias strings from XMLContent where lang="zxx"
461
+ * @param content - The XMLContent to extract aliases from
462
+ * @returns Array of alias strings, or null if none found
463
+ *
464
+ * @internal
465
+ */
466
+ function extractAliases(content) {
467
+ if (content == null) return null;
468
+ const aliases = [];
469
+ for (const contentItem of content.content) {
470
+ if (contentItem.lang !== "zxx") continue;
471
+ const alias = parseXMLContentItem(contentItem, { languages: ["zxx"] }).text;
472
+ if (alias !== "") aliases.push(alias);
473
+ }
474
+ return aliases.length > 0 ? aliases : null;
475
+ }
476
+ //#endregion
477
+ export { extractAliases, parseXMLContent, parseXMLString, transformPermanentIdentificationUrl };
@@ -0,0 +1,20 @@
1
+ import { Webpage, Website } from "../../types/website.mjs";
2
+ import { XMLWebsiteData, XMLWebsiteResource } from "../../xml/types.mjs";
3
+ import { ParserOptions } from "../helpers.mjs";
4
+
5
+ //#region src/parsers/website/index.d.ts
6
+ /**
7
+ * Parses raw bounds data into a standardized bounds structure
8
+ *
9
+ * @param bounds - Raw bounds data in OCHRE format
10
+ * @returns Parsed bounds object
11
+ */
12
+ declare function parseBounds(bounds: string): [[number, number], [number, number]];
13
+ declare function parseWebpageView<T extends ReadonlyArray<string>>(view: {
14
+ resource?: Array<XMLWebsiteResource>;
15
+ } | undefined, options: ParserOptions<T>, context: Pick<Website<T>, "belongsTo" | "metadata">): Webpage<T> | null;
16
+ declare function parseWebsite<const T extends ReadonlyArray<string> = ReadonlyArray<string>>(data: XMLWebsiteData, options?: {
17
+ languages?: T;
18
+ }): Website<T>;
19
+ //#endregion
20
+ export { parseBounds, parseWebpageView, parseWebsite };