@digitalculture/ochre-sdk 0.1.20 → 0.1.22

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/dist/index.js ADDED
@@ -0,0 +1,2045 @@
1
+ // src/utils/fetchers/generic.ts
2
+ import { z } from "zod";
3
+ var uuidSchema = z.string().uuid({ message: "Invalid UUID provided" });
4
+ async function fetchByUuid(uuid) {
5
+ try {
6
+ const result = uuidSchema.safeParse(uuid);
7
+ if (!result.success) {
8
+ throw new Error(result.error.issues[0]?.message);
9
+ }
10
+ const response = await fetch(
11
+ `https://ochre.lib.uchicago.edu/ochre?uuid=${uuid}&format=json&lang="*"`
12
+ );
13
+ if (!response.ok) {
14
+ throw new Error("Failed to fetch OCHRE data");
15
+ }
16
+ const dataRaw = await response.json();
17
+ if (!("ochre" in dataRaw)) {
18
+ throw new Error("Invalid OCHRE data: API response missing 'ochre' key");
19
+ }
20
+ return [null, dataRaw];
21
+ } catch (error) {
22
+ return [error instanceof Error ? error.message : "Unknown error", null];
23
+ }
24
+ }
25
+
26
+ // src/utils/parse.ts
27
+ import { z as z3 } from "zod";
28
+
29
+ // src/utils/string.ts
30
+ import { z as z2 } from "zod";
31
+ var renderOptionsSchema = z2.string().transform((str) => str.split(" ")).pipe(
32
+ z2.array(
33
+ z2.enum([
34
+ "bold",
35
+ "italic",
36
+ "underline"
37
+ ])
38
+ )
39
+ );
40
+ var whitespaceSchema = z2.string().transform((str) => str.split(" ")).pipe(
41
+ z2.array(
42
+ z2.enum([
43
+ "newline",
44
+ "trailing",
45
+ "leading"
46
+ ])
47
+ )
48
+ );
49
+ var emailSchema = z2.string().email({ message: "Invalid email" });
50
+ var urlSchema = z2.string().refine((v) => v ? isUrlValid(v) : false, {
51
+ message: "Invalid URL"
52
+ });
53
+ function isUrlValid(url) {
54
+ const pattern = (
55
+ // eslint-disable-next-line regexp/no-useless-quantifier, regexp/no-unused-capturing-group
56
+ /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\w$&+,:;=-]+@)?[\d.A-Za-z-]+(:\d+)?|(?:www.|[\w$&+,:;=-]+@)[\d.A-Za-z-]+)((?:\/[\w%+./~-]*)?\??[\w%&+.;=@-]*#?\w*)?)/
57
+ );
58
+ return !!pattern.test(url);
59
+ }
60
+ function getStringItemByLanguage(content, language) {
61
+ const stringItemToFind = content.find((item) => item.lang === language);
62
+ return stringItemToFind ?? null;
63
+ }
64
+ function parseEmailAndUrl(string) {
65
+ const splitString = string.split(" ");
66
+ const returnSplitString = [];
67
+ for (const string2 of splitString) {
68
+ const cleanString = string2.replaceAll(/(?<=\s|^)[([{]+|[)\]}]+(?=\s|$)/g, "").replace(/[!),.:;?\]]$/, "");
69
+ const index = string2.indexOf(cleanString);
70
+ const before = string2.slice(0, index);
71
+ const after = string2.slice(index + cleanString.length);
72
+ const isEmail = emailSchema.safeParse(cleanString).success;
73
+ if (isEmail) {
74
+ returnSplitString.push(
75
+ before,
76
+ `${before}<ExternalLink href="mailto:${cleanString}">${cleanString}</ExternalLink>${after}`
77
+ );
78
+ continue;
79
+ }
80
+ const isUrl = urlSchema.safeParse(cleanString).success;
81
+ if (isUrl) {
82
+ returnSplitString.push(
83
+ `${before}<ExternalLink href="${cleanString}">${cleanString}</ExternalLink>${after}`
84
+ );
85
+ continue;
86
+ }
87
+ returnSplitString.push(string2);
88
+ }
89
+ return returnSplitString.join(" ");
90
+ }
91
+ function parseRenderOptions(contentString, renderString) {
92
+ let returnString = contentString;
93
+ const result = renderOptionsSchema.safeParse(renderString);
94
+ if (!result.success) {
95
+ console.warn(`Invalid render options string provided: \u201C${renderString}\u201D`);
96
+ return contentString;
97
+ }
98
+ for (const option of result.data) {
99
+ switch (option) {
100
+ case "bold": {
101
+ returnString = `**${returnString}**`;
102
+ break;
103
+ }
104
+ case "italic": {
105
+ returnString = `*${returnString}*`;
106
+ break;
107
+ }
108
+ case "underline": {
109
+ returnString = `_${returnString}_`;
110
+ break;
111
+ }
112
+ }
113
+ }
114
+ return returnString.replaceAll("&#39;", "'");
115
+ }
116
+ function parseWhitespace(contentString, whitespace) {
117
+ let returnString = contentString;
118
+ const result = whitespaceSchema.safeParse(whitespace);
119
+ if (!result.success) {
120
+ console.warn(`Invalid whitespace string provided: \u201C${whitespace}\u201D`);
121
+ return contentString;
122
+ }
123
+ for (const option of result.data) {
124
+ switch (option) {
125
+ case "newline": {
126
+ returnString = `${returnString}
127
+ <br />`;
128
+ break;
129
+ }
130
+ case "trailing": {
131
+ returnString = `${returnString} `;
132
+ break;
133
+ }
134
+ case "leading": {
135
+ returnString = ` ${returnString}`;
136
+ break;
137
+ }
138
+ }
139
+ }
140
+ return returnString.replaceAll("&#39;", "'");
141
+ }
142
+ function parseFakeString(string) {
143
+ let returnString = "";
144
+ if (typeof string === "string") {
145
+ returnString = string;
146
+ } else if (typeof string === "number") {
147
+ returnString = string.toString();
148
+ } else if (typeof string === "boolean") {
149
+ returnString = string ? "Yes" : "No";
150
+ }
151
+ return returnString.replaceAll("&#39;", "'");
152
+ }
153
+ function parseStringItem(item) {
154
+ let returnString = "";
155
+ switch (typeof item.string) {
156
+ case "string": {
157
+ returnString = item.string;
158
+ break;
159
+ }
160
+ case "number":
161
+ case "boolean": {
162
+ returnString = parseFakeString(item.string);
163
+ break;
164
+ }
165
+ case "object": {
166
+ const stringItems = Array.isArray(item.string) ? item.string : [item.string];
167
+ for (const stringItem of stringItems) {
168
+ const renderedText = stringItem.rend != null ? parseRenderOptions(
169
+ parseFakeString(stringItem.content),
170
+ stringItem.rend
171
+ ) : parseFakeString(stringItem.content);
172
+ const whitespacedText = stringItem.whitespace != null ? parseWhitespace(renderedText, stringItem.whitespace) : renderedText;
173
+ returnString += whitespacedText;
174
+ }
175
+ break;
176
+ }
177
+ default: {
178
+ returnString = "";
179
+ break;
180
+ }
181
+ }
182
+ return returnString.replaceAll("&#39;", "'");
183
+ }
184
+ function parseStringDocumentItem(item, footnotes) {
185
+ if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") {
186
+ return parseEmailAndUrl(parseFakeString(item));
187
+ }
188
+ if ("whitespace" in item && !("content" in item) && !("string" in item)) {
189
+ if (item.whitespace === "newline") {
190
+ return " \n";
191
+ } else {
192
+ return "";
193
+ }
194
+ }
195
+ if ("links" in item) {
196
+ const itemString = parseFakeString(item.string).replaceAll("<", String.raw`\<`).replaceAll("{", String.raw`\{`);
197
+ const itemLinks = Array.isArray(item.links) ? item.links : [item.links];
198
+ for (const link of itemLinks) {
199
+ if ("resource" in link) {
200
+ const linkResource = Array.isArray(link.resource) ? link.resource[0] : link.resource;
201
+ let linkContent = null;
202
+ if (linkResource.content != null) {
203
+ linkContent = parseFakeString(linkResource.content).replaceAll("<", String.raw`\<`).replaceAll("{", String.raw`\{`);
204
+ }
205
+ switch (linkResource.type) {
206
+ case "image": {
207
+ if (linkResource.rend === "inline") {
208
+ return `<InlineImage uuid="${linkResource.uuid}" ${linkContent !== null ? `content="${linkContent}"` : ""} height={${linkResource.height?.toString() ?? "null"}} width={${linkResource.width?.toString() ?? "null"}} />`;
209
+ } else if (linkResource.publicationDateTime != null) {
210
+ return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkResource.uuid}" type="image"${linkContent !== null ? ` content="${linkContent}"` : ""}>${itemString}</ExternalLink>`;
211
+ } else {
212
+ return `<TooltipSpan type="image" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</TooltipSpan>`;
213
+ }
214
+ }
215
+ case "internalDocument": {
216
+ const isFootnote = linkContent?.toLocaleLowerCase("en-US").includes("footnote");
217
+ if (isFootnote) {
218
+ if (footnotes) {
219
+ footnotes.push({
220
+ uuid: linkResource.uuid,
221
+ label: itemString,
222
+ content: ""
223
+ });
224
+ }
225
+ return ` <Footnote uuid="${linkResource.uuid}"${itemString ? ` label="${itemString}"` : ""}${linkContent !== null ? ` content="${linkContent}"` : ""} />`;
226
+ } else {
227
+ return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkResource.uuid}" type="internalDocument" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</ExternalLink>`;
228
+ }
229
+ }
230
+ case "externalDocument": {
231
+ if (linkResource.publicationDateTime != null) {
232
+ return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkResource.uuid}" type="externalDocument" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</ExternalLink>`;
233
+ } else {
234
+ return `<TooltipSpan type="externalDocument" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</TooltipSpan>`;
235
+ }
236
+ }
237
+ default: {
238
+ return "";
239
+ }
240
+ }
241
+ } else if ("concept" in link) {
242
+ const linkConcept = Array.isArray(link.concept) ? link.concept[0] : link.concept;
243
+ if (linkConcept.publicationDateTime != null) {
244
+ return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkConcept.uuid}" type="concept">${itemString}</ExternalLink>`;
245
+ } else {
246
+ return `<TooltipSpan type="concept">${itemString}</TooltipSpan>`;
247
+ }
248
+ } else if ("set" in link) {
249
+ const linkSet = Array.isArray(link.set) ? link.set[0] : link.set;
250
+ if (linkSet.publicationDateTime != null) {
251
+ return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkSet.uuid}" type="set">${itemString}</ExternalLink>`;
252
+ } else {
253
+ return `<TooltipSpan type="set">${itemString}</TooltipSpan>`;
254
+ }
255
+ } else if ("person" in link) {
256
+ const linkPerson = Array.isArray(link.person) ? link.person[0] : link.person;
257
+ const linkContent = linkPerson.identification ? parseStringContent(linkPerson.identification.label) : null;
258
+ if (linkPerson.publicationDateTime != null) {
259
+ return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkPerson.uuid}" type="${linkPerson.type ?? "person"}" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</ExternalLink>`;
260
+ } else {
261
+ return `<TooltipSpan type="${linkPerson.type ?? "person"}" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</TooltipSpan>`;
262
+ }
263
+ } else if ("bibliography" in link) {
264
+ const linkBibliography = Array.isArray(link.bibliography) ? link.bibliography[0] : link.bibliography;
265
+ if (linkBibliography.publicationDateTime != null) {
266
+ return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkBibliography.uuid}" type="${linkBibliography.type ?? "bibliography"}">${itemString}</ExternalLink>`;
267
+ } else {
268
+ return `<TooltipSpan type="bibliography">${itemString}</TooltipSpan>`;
269
+ }
270
+ }
271
+ }
272
+ }
273
+ let returnString = "";
274
+ if ("string" in item) {
275
+ const stringItems = Array.isArray(item.string) ? item.string : [item.string];
276
+ for (const stringItem of stringItems) {
277
+ returnString += parseStringDocumentItem(stringItem, footnotes);
278
+ }
279
+ if ("whitespace" in item && item.whitespace != null) {
280
+ returnString = parseWhitespace(
281
+ parseEmailAndUrl(returnString),
282
+ item.whitespace
283
+ );
284
+ }
285
+ return returnString.replaceAll("&#39;", "'");
286
+ } else {
287
+ returnString = parseFakeString(item.content);
288
+ if (item.rend != null) {
289
+ returnString = parseRenderOptions(
290
+ parseEmailAndUrl(returnString),
291
+ item.rend
292
+ );
293
+ }
294
+ if (item.whitespace != null) {
295
+ returnString = parseWhitespace(
296
+ parseEmailAndUrl(returnString),
297
+ item.whitespace
298
+ );
299
+ }
300
+ }
301
+ return returnString.replaceAll("&#39;", "'");
302
+ }
303
+ function trimEndLineBreaks(string) {
304
+ const trimmedString = string.replaceAll(/^\n<br \/>|\n<br \/>$/g, "");
305
+ const finalLineBreaks = /\n<br \/>$/.exec(trimmedString);
306
+ if (finalLineBreaks) {
307
+ return trimEndLineBreaks(trimmedString);
308
+ }
309
+ return trimmedString;
310
+ }
311
+ function parseStringContent(content, language = "eng") {
312
+ switch (typeof content.content) {
313
+ case "string":
314
+ case "number":
315
+ case "boolean": {
316
+ return parseFakeString(content.content);
317
+ }
318
+ case "object": {
319
+ if (Array.isArray(content.content)) {
320
+ const stringItem = getStringItemByLanguage(content.content, language);
321
+ if (stringItem) {
322
+ return parseStringItem(stringItem);
323
+ } else {
324
+ const returnStringItem = content.content[0];
325
+ if (!returnStringItem) {
326
+ throw new Error(
327
+ `No string item found for language \u201C${language}\u201D in the following content:
328
+ ${JSON.stringify(
329
+ content.content
330
+ )}.`
331
+ );
332
+ }
333
+ return parseStringItem(returnStringItem);
334
+ }
335
+ } else {
336
+ return parseStringItem(content.content);
337
+ }
338
+ }
339
+ default: {
340
+ return String(content.content);
341
+ }
342
+ }
343
+ }
344
+
345
+ // src/utils/fetchers/resource.ts
346
+ async function fetchResource(uuid) {
347
+ try {
348
+ const [error, dataRaw] = await fetchByUuid(uuid);
349
+ if (error !== null) {
350
+ throw new Error(error);
351
+ }
352
+ if (!("resource" in dataRaw.ochre)) {
353
+ throw new Error(
354
+ "Invalid OCHRE data: API response missing 'resource' key"
355
+ );
356
+ }
357
+ const resourceItem = parseResource(dataRaw.ochre.resource);
358
+ const data = {
359
+ uuid: parseFakeString(dataRaw.ochre.uuid),
360
+ publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
361
+ belongsTo: {
362
+ uuid: dataRaw.ochre.uuidBelongsTo,
363
+ abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
364
+ },
365
+ metadata: parseMetadata(dataRaw.ochre.metadata),
366
+ item: resourceItem
367
+ };
368
+ return { metadata: data.metadata, resource: data.item };
369
+ } catch (error) {
370
+ console.error(error);
371
+ return null;
372
+ }
373
+ }
374
+
375
+ // src/utils/getters.ts
376
+ var DEFAULT_OPTIONS = {
377
+ searchNestedProperties: false
378
+ };
379
+ function getPropertyByLabel(properties, label, options = DEFAULT_OPTIONS) {
380
+ const { searchNestedProperties } = options;
381
+ const property = properties.find((property2) => property2.label === label);
382
+ if (property) {
383
+ return property;
384
+ }
385
+ if (searchNestedProperties) {
386
+ for (const property2 of properties) {
387
+ if (property2.properties.length > 0) {
388
+ const nestedResult = getPropertyByLabel(property2.properties, label, {
389
+ searchNestedProperties
390
+ });
391
+ if (nestedResult) {
392
+ return nestedResult;
393
+ }
394
+ }
395
+ }
396
+ }
397
+ return null;
398
+ }
399
+ function getPropertyValuesByLabel(properties, label, options = DEFAULT_OPTIONS) {
400
+ const { searchNestedProperties } = options;
401
+ const property = properties.find((property2) => property2.label === label);
402
+ if (property) {
403
+ return property.values.map((value) => value.content);
404
+ }
405
+ if (searchNestedProperties) {
406
+ for (const property2 of properties) {
407
+ if (property2.properties.length > 0) {
408
+ const nestedResult = getPropertyValuesByLabel(
409
+ property2.properties,
410
+ label,
411
+ { searchNestedProperties }
412
+ );
413
+ if (nestedResult) {
414
+ return nestedResult;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ return null;
420
+ }
421
+ function getPropertyValueByLabel(properties, label, options = DEFAULT_OPTIONS) {
422
+ const { searchNestedProperties } = options;
423
+ const values = getPropertyValuesByLabel(properties, label, {
424
+ searchNestedProperties
425
+ });
426
+ if (values !== null && values.length > 0) {
427
+ return values[0];
428
+ }
429
+ if (searchNestedProperties) {
430
+ for (const property of properties) {
431
+ if (property.properties.length > 0) {
432
+ const nestedResult = getPropertyValueByLabel(
433
+ property.properties,
434
+ label,
435
+ { searchNestedProperties }
436
+ );
437
+ if (nestedResult !== null) {
438
+ return nestedResult;
439
+ }
440
+ }
441
+ }
442
+ }
443
+ return null;
444
+ }
445
+ function getAllPropertyLabels(properties, options = DEFAULT_OPTIONS) {
446
+ const { searchNestedProperties } = options;
447
+ const labels = /* @__PURE__ */ new Set();
448
+ for (const property of properties) {
449
+ labels.add(property.label);
450
+ if (property.properties.length > 0 && searchNestedProperties) {
451
+ const nestedLabels = getAllPropertyLabels(property.properties, {
452
+ searchNestedProperties: true
453
+ });
454
+ for (const label of nestedLabels) {
455
+ labels.add(label);
456
+ }
457
+ }
458
+ }
459
+ return [...labels];
460
+ }
461
+ function filterProperties(property, filter, options = DEFAULT_OPTIONS) {
462
+ const { searchNestedProperties } = options;
463
+ const isAllFields = filter.label.toLocaleLowerCase("en-US") === "all fields";
464
+ if (isAllFields || property.label.toLocaleLowerCase("en-US") === filter.label.toLocaleLowerCase("en-US")) {
465
+ let isFound = property.values.some(
466
+ (value) => value.content.toLocaleLowerCase("en-US").includes(filter.value.toLocaleLowerCase("en-US"))
467
+ );
468
+ if (!isFound && searchNestedProperties) {
469
+ isFound = property.properties.some(
470
+ (property2) => filterProperties(property2, filter, { searchNestedProperties: true })
471
+ );
472
+ }
473
+ return isFound;
474
+ }
475
+ return false;
476
+ }
477
+
478
+ // src/utils/parse.ts
479
+ var websiteSchema = z3.object({
480
+ type: z3.enum(
481
+ [
482
+ "traditional",
483
+ "digital-collection",
484
+ "plum",
485
+ "cedar",
486
+ "elm",
487
+ "maple",
488
+ "oak",
489
+ "palm"
490
+ ],
491
+ { message: "Invalid website type" }
492
+ ),
493
+ status: z3.enum(
494
+ ["development", "preview", "production"],
495
+ { message: "Invalid website status" }
496
+ ),
497
+ privacy: z3.enum(
498
+ ["public", "password", "private"],
499
+ { message: "Invalid website privacy" }
500
+ )
501
+ });
502
+ var componentSchema = z3.enum(
503
+ [
504
+ "annotated-document",
505
+ "annotated-image",
506
+ "bibliography",
507
+ "blog",
508
+ "button",
509
+ "collection",
510
+ "iiif-viewer",
511
+ "image",
512
+ "image-gallery",
513
+ "interactive-chapter-table",
514
+ "item-gallery",
515
+ "menu",
516
+ "menu-item",
517
+ "n-columns",
518
+ "n-rows",
519
+ "network-graph",
520
+ "table",
521
+ "text",
522
+ "text-image",
523
+ "timeline"
524
+ ],
525
+ { message: "Invalid component" }
526
+ );
527
+ function parseIdentification(identification) {
528
+ try {
529
+ const returnIdentification = {
530
+ label: parseStringContent(identification.label),
531
+ abbreviation: ""
532
+ };
533
+ for (const key of Object.keys(identification).filter(
534
+ (key2) => key2 !== "label"
535
+ )) {
536
+ returnIdentification[key] = parseStringContent(
537
+ identification[key]
538
+ );
539
+ }
540
+ return returnIdentification;
541
+ } catch (error) {
542
+ console.error(error);
543
+ return {
544
+ label: "",
545
+ abbreviation: ""
546
+ };
547
+ }
548
+ }
549
+ function parseLanguages(language) {
550
+ if (Array.isArray(language)) {
551
+ return language.map((lang) => parseStringContent(lang));
552
+ } else {
553
+ return [parseStringContent(language)];
554
+ }
555
+ }
556
+ function parseMetadata(metadata) {
557
+ let identification = {
558
+ label: "",
559
+ abbreviation: ""
560
+ };
561
+ if (metadata.item) {
562
+ if (metadata.item.label || metadata.item.abbreviation) {
563
+ let label = "";
564
+ let abbreviation = "";
565
+ if (metadata.item.label) {
566
+ label = parseStringContent(metadata.item.label);
567
+ }
568
+ if (metadata.item.abbreviation) {
569
+ abbreviation = parseStringContent(metadata.item.abbreviation);
570
+ }
571
+ identification = { label, abbreviation };
572
+ } else {
573
+ identification = parseIdentification(metadata.item.identification);
574
+ }
575
+ }
576
+ let projectIdentification = null;
577
+ const baseProjectIdentification = metadata.project?.identification ? parseIdentification(metadata.project.identification) : null;
578
+ if (baseProjectIdentification) {
579
+ projectIdentification = {
580
+ ...baseProjectIdentification,
581
+ website: metadata.project?.identification.website ?? null
582
+ };
583
+ }
584
+ return {
585
+ project: projectIdentification ? { identification: projectIdentification } : null,
586
+ item: metadata.item ? {
587
+ identification,
588
+ category: metadata.item.category,
589
+ type: metadata.item.type,
590
+ maxLength: metadata.item.maxLength ?? null
591
+ } : null,
592
+ dataset: parseStringContent(metadata.dataset),
593
+ publisher: parseStringContent(metadata.publisher),
594
+ languages: parseLanguages(metadata.language),
595
+ identifier: parseStringContent(metadata.identifier),
596
+ description: parseStringContent(metadata.description)
597
+ };
598
+ }
599
+ function parseContextItem(contextItem) {
600
+ return {
601
+ uuid: contextItem.uuid,
602
+ publicationDateTime: contextItem.publicationDateTime != null ? new Date(contextItem.publicationDateTime) : null,
603
+ number: contextItem.n,
604
+ content: parseFakeString(contextItem.content)
605
+ };
606
+ }
607
+ function parseContext(context) {
608
+ const contexts = Array.isArray(context.context) ? context.context : [context.context];
609
+ const returnContexts = {
610
+ nodes: contexts.map((context2) => {
611
+ const spatialUnit = [];
612
+ if ("spatialUnit" in context2 && context2.spatialUnit) {
613
+ const contextsToParse = Array.isArray(context2.spatialUnit) ? context2.spatialUnit : [context2.spatialUnit];
614
+ for (const contextItem of contextsToParse) {
615
+ spatialUnit.push(parseContextItem(contextItem));
616
+ }
617
+ }
618
+ return {
619
+ tree: parseContextItem(context2.tree),
620
+ project: parseContextItem(context2.project),
621
+ spatialUnit
622
+ };
623
+ }),
624
+ displayPath: context.displayPath
625
+ };
626
+ return returnContexts;
627
+ }
628
+ function parseLicense(license) {
629
+ if (typeof license.license === "string") {
630
+ return null;
631
+ }
632
+ return {
633
+ content: license.license.content,
634
+ url: license.license.target
635
+ };
636
+ }
637
+ function parsePersons(persons) {
638
+ const returnPersons = [];
639
+ for (const person of persons) {
640
+ returnPersons.push({
641
+ uuid: person.uuid,
642
+ publicationDateTime: person.publicationDateTime != null ? new Date(person.publicationDateTime) : null,
643
+ type: person.type ?? null,
644
+ date: person.date != null ? new Date(person.date) : null,
645
+ identification: person.identification ? parseIdentification(person.identification) : null,
646
+ content: person.content != null ? parseFakeString(person.content) : null
647
+ });
648
+ }
649
+ return returnPersons;
650
+ }
651
+ function parseLink(linkRaw) {
652
+ const links = "resource" in linkRaw ? linkRaw.resource : "concept" in linkRaw ? linkRaw.concept : "set" in linkRaw ? linkRaw.set : "tree" in linkRaw ? linkRaw.tree : "person" in linkRaw ? linkRaw.person : "bibliography" in linkRaw ? linkRaw.bibliography : "epigraphicUnit" in linkRaw ? linkRaw.epigraphicUnit : null;
653
+ if (!links) {
654
+ throw new Error(
655
+ `Invalid link provided: ${JSON.stringify(linkRaw, null, 2)}`
656
+ );
657
+ }
658
+ const linksToParse = Array.isArray(links) ? links : [links];
659
+ const returnLinks = [];
660
+ for (const link of linksToParse) {
661
+ const returnLink = {
662
+ category: "resource" in linkRaw ? "resource" : "concept" in linkRaw ? "concept" : "set" in linkRaw ? "set" : "person" in linkRaw ? "person" : "tree" in linkRaw ? "tree" : "bibliography" in linkRaw ? "bibliography" : "epigraphicUnit" in linkRaw ? "epigraphicUnit" : null,
663
+ content: "content" in link ? link.content != null ? parseFakeString(link.content) : null : null,
664
+ uuid: link.uuid,
665
+ type: link.type ?? null,
666
+ identification: link.identification ? parseIdentification(link.identification) : null,
667
+ image: null,
668
+ bibliographies: "bibliography" in linkRaw ? parseBibliographies(
669
+ Array.isArray(linkRaw.bibliography) ? linkRaw.bibliography : [linkRaw.bibliography]
670
+ ) : null,
671
+ publicationDateTime: link.publicationDateTime != null ? new Date(link.publicationDateTime) : null
672
+ };
673
+ if ("height" in link && link.height != null && link.width != null && link.heightPreview != null && link.widthPreview != null) {
674
+ returnLink.image = {
675
+ isInline: link.rend === "inline",
676
+ heightPreview: link.heightPreview,
677
+ widthPreview: link.widthPreview,
678
+ height: link.height,
679
+ width: link.width
680
+ };
681
+ }
682
+ returnLinks.push(returnLink);
683
+ }
684
+ return returnLinks;
685
+ }
686
+ function parseLinks(links) {
687
+ const returnLinks = [];
688
+ for (const link of links) {
689
+ returnLinks.push(...parseLink(link));
690
+ }
691
+ return returnLinks;
692
+ }
693
+ function parseDocument(document, language = "eng") {
694
+ let returnString = "";
695
+ const footnotes = [];
696
+ const documentWithLanguage = Array.isArray(document) ? document.find((doc) => doc.lang === language) : document;
697
+ if (typeof documentWithLanguage.string === "string" || typeof documentWithLanguage.string === "number" || typeof documentWithLanguage.string === "boolean") {
698
+ returnString += parseEmailAndUrl(
699
+ parseFakeString(documentWithLanguage.string)
700
+ );
701
+ } else {
702
+ const documentItems = Array.isArray(documentWithLanguage.string) ? documentWithLanguage.string : [documentWithLanguage.string];
703
+ for (const item of documentItems) {
704
+ returnString += parseStringDocumentItem(item, footnotes);
705
+ }
706
+ }
707
+ returnString = trimEndLineBreaks(returnString);
708
+ return { content: returnString, footnotes };
709
+ }
710
+ function parseImage(image) {
711
+ return {
712
+ publicationDateTime: image.publicationDateTime != null ? new Date(image.publicationDateTime) : null,
713
+ identification: image.identification ? parseIdentification(image.identification) : null,
714
+ url: image.href ?? (image.htmlImgSrcPrefix == null && image.content != null ? parseFakeString(image.content) : null),
715
+ htmlPrefix: image.htmlImgSrcPrefix ?? null,
716
+ content: image.htmlImgSrcPrefix != null && image.content != null ? parseFakeString(image.content) : null
717
+ };
718
+ }
719
+ function parseNotes(notes, language = "eng") {
720
+ const returnNotes = [];
721
+ for (const note of notes) {
722
+ if (typeof note === "string") {
723
+ if (note === "") {
724
+ continue;
725
+ }
726
+ returnNotes.push({
727
+ number: -1,
728
+ title: null,
729
+ content: note
730
+ });
731
+ continue;
732
+ }
733
+ let content = "";
734
+ const notesToParse = Array.isArray(note.content) ? note.content : [note.content];
735
+ let noteWithLanguage = notesToParse.find((item) => item.lang === language);
736
+ if (!noteWithLanguage) {
737
+ noteWithLanguage = notesToParse[0];
738
+ if (!noteWithLanguage) {
739
+ throw new Error(
740
+ `Note does not have a valid content item: ${JSON.stringify(
741
+ note,
742
+ null,
743
+ 2
744
+ )}`
745
+ );
746
+ }
747
+ }
748
+ if (typeof noteWithLanguage.string === "string" || typeof noteWithLanguage.string === "number" || typeof noteWithLanguage.string === "boolean") {
749
+ content = parseEmailAndUrl(parseFakeString(noteWithLanguage.string));
750
+ } else {
751
+ content = parseEmailAndUrl(parseDocument(noteWithLanguage).content);
752
+ }
753
+ returnNotes.push({
754
+ number: note.noteNo,
755
+ title: noteWithLanguage.title != null ? parseFakeString(noteWithLanguage.title) : null,
756
+ content
757
+ });
758
+ }
759
+ return returnNotes;
760
+ }
761
+ function parseCoordinates(coordinates) {
762
+ return {
763
+ latitude: coordinates.latitude,
764
+ longitude: coordinates.longitude,
765
+ type: coordinates.coord?.coordType ?? null,
766
+ label: coordinates.coord?.coordLabel != null ? parseFakeString(coordinates.coord.coordLabel) : null
767
+ };
768
+ }
769
+ function parseObservation(observation) {
770
+ return {
771
+ number: observation.observationNo,
772
+ date: observation.date != null ? new Date(observation.date) : null,
773
+ observers: observation.observers != null ? parseFakeString(observation.observers).split(";").map((observer) => observer.trim()) : [],
774
+ notes: observation.notes ? parseNotes(
775
+ Array.isArray(observation.notes.note) ? observation.notes.note : [observation.notes.note]
776
+ ) : [],
777
+ links: observation.links ? parseLinks(
778
+ Array.isArray(observation.links) ? observation.links : [observation.links]
779
+ ) : [],
780
+ properties: observation.properties ? parseProperties(
781
+ Array.isArray(observation.properties.property) ? observation.properties.property : [observation.properties.property]
782
+ ) : []
783
+ };
784
+ }
785
+ function parseObservations(observations) {
786
+ const returnObservations = [];
787
+ for (const observation of observations) {
788
+ returnObservations.push(parseObservation(observation));
789
+ }
790
+ return returnObservations;
791
+ }
792
+ function parseEvents(events) {
793
+ const returnEvents = [];
794
+ for (const event of events) {
795
+ returnEvents.push({
796
+ date: event.dateTime != null ? new Date(event.dateTime) : null,
797
+ label: parseStringContent(event.label),
798
+ agent: event.agent ? {
799
+ uuid: event.agent.uuid,
800
+ content: parseFakeString(event.agent.content)
801
+ } : null
802
+ });
803
+ }
804
+ return returnEvents;
805
+ }
806
+ function parseProperties(properties, language = "eng") {
807
+ const returnProperties = [];
808
+ for (const property of properties) {
809
+ const valuesToParse = "value" in property && property.value ? Array.isArray(property.value) ? property.value : [property.value] : [];
810
+ const values = valuesToParse.map((value) => ({
811
+ content: parseStringContent(value),
812
+ type: value.type,
813
+ category: value.category !== "value" ? value.category ?? null : null,
814
+ uuid: value.uuid ?? null,
815
+ publicationDateTime: value.publicationDateTime != null ? new Date(value.publicationDateTime) : null
816
+ }));
817
+ returnProperties.push({
818
+ label: parseStringContent(property.label, language).replace(/\s*\.{3}$/, "").trim(),
819
+ values,
820
+ comment: property.comment != null ? parseFakeString(property.comment) : null,
821
+ properties: property.property ? parseProperties(
822
+ Array.isArray(property.property) ? property.property : [property.property]
823
+ ) : []
824
+ });
825
+ }
826
+ return returnProperties;
827
+ }
828
+ function parseInterpretations(interpretations) {
829
+ const returnInterpretations = [];
830
+ for (const interpretation of interpretations) {
831
+ returnInterpretations.push({
832
+ date: new Date(interpretation.date),
833
+ number: interpretation.interpretationNo,
834
+ properties: interpretation.properties ? parseProperties(
835
+ Array.isArray(interpretation.properties.property) ? interpretation.properties.property : [interpretation.properties.property]
836
+ ) : []
837
+ });
838
+ }
839
+ return returnInterpretations;
840
+ }
841
+ function parseImageMap(imageMap) {
842
+ const returnImageMap = {
843
+ area: [],
844
+ width: imageMap.width,
845
+ height: imageMap.height
846
+ };
847
+ const imageMapAreasToParse = Array.isArray(imageMap.area) ? imageMap.area : [imageMap.area];
848
+ for (const area of imageMapAreasToParse) {
849
+ returnImageMap.area.push({
850
+ uuid: area.uuid,
851
+ publicationDateTime: area.publicationDateTime != null ? new Date(area.publicationDateTime) : null,
852
+ type: area.type,
853
+ title: parseFakeString(area.title),
854
+ shape: area.shape === "rect" ? "rectangle" : "polygon",
855
+ coords: area.coords.split(",").map((coord) => Number.parseInt(coord))
856
+ });
857
+ }
858
+ return returnImageMap;
859
+ }
860
+ function parsePeriod(period) {
861
+ return {
862
+ uuid: period.uuid,
863
+ publicationDateTime: period.publicationDateTime != null ? new Date(period.publicationDateTime) : null,
864
+ type: period.type ?? null,
865
+ number: period.n ?? null,
866
+ identification: parseIdentification(period.identification),
867
+ description: period.description ? parseStringContent(period.description) : null
868
+ };
869
+ }
870
+ function parsePeriods(periods) {
871
+ const returnPeriods = [];
872
+ for (const period of periods) {
873
+ returnPeriods.push(parsePeriod(period));
874
+ }
875
+ return returnPeriods;
876
+ }
877
+ function parseBibliography(bibliography) {
878
+ let resource = null;
879
+ if (bibliography.source?.resource) {
880
+ resource = {
881
+ uuid: bibliography.source.resource.uuid,
882
+ publicationDateTime: bibliography.source.resource.publicationDateTime ? new Date(bibliography.source.resource.publicationDateTime) : null,
883
+ type: bibliography.source.resource.type,
884
+ identification: parseIdentification(
885
+ bibliography.source.resource.identification
886
+ )
887
+ };
888
+ }
889
+ return {
890
+ uuid: bibliography.uuid,
891
+ publicationDateTime: bibliography.publicationDateTime != null ? new Date(bibliography.publicationDateTime) : null,
892
+ type: bibliography.type ?? null,
893
+ number: bibliography.n ?? null,
894
+ identification: bibliography.identification ? parseIdentification(bibliography.identification) : null,
895
+ projectIdentification: bibliography.project?.identification ? parseIdentification(bibliography.project.identification) : null,
896
+ context: bibliography.context ? parseContext(bibliography.context) : null,
897
+ citation: {
898
+ format: bibliography.citationFormat ?? null,
899
+ short: bibliography.citationFormatSpan ? parseFakeString(
900
+ "default:span" in bibliography.citationFormatSpan ? bibliography.citationFormatSpan["default:span"].content : bibliography.citationFormatSpan.span.content
901
+ ) : null,
902
+ long: bibliography.referenceFormatDiv ? parseFakeString(
903
+ "default:div" in bibliography.referenceFormatDiv ? bibliography.referenceFormatDiv["default:div"]["default:div"].content : bibliography.referenceFormatDiv.div.div.content
904
+ ) : null
905
+ },
906
+ publicationInfo: {
907
+ publishers: bibliography.publicationInfo?.publishers ? parsePersons(
908
+ Array.isArray(
909
+ bibliography.publicationInfo.publishers.publishers.person
910
+ ) ? bibliography.publicationInfo.publishers.publishers.person : [bibliography.publicationInfo.publishers.publishers.person]
911
+ ) : [],
912
+ startDate: bibliography.publicationInfo?.startDate ? new Date(
913
+ bibliography.publicationInfo.startDate.year,
914
+ bibliography.publicationInfo.startDate.month,
915
+ bibliography.publicationInfo.startDate.day
916
+ ) : null
917
+ },
918
+ entryInfo: bibliography.entryInfo ? {
919
+ startIssue: parseFakeString(bibliography.entryInfo.startIssue),
920
+ startVolume: parseFakeString(bibliography.entryInfo.startVolume)
921
+ } : null,
922
+ source: {
923
+ resource,
924
+ documentUrl: bibliography.sourceDocument ? `https://ochre.lib.uchicago.edu/ochre?uuid=${bibliography.sourceDocument.uuid}&load` : null
925
+ },
926
+ authors: bibliography.authors ? parsePersons(
927
+ Array.isArray(bibliography.authors.person) ? bibliography.authors.person : [bibliography.authors.person]
928
+ ) : [],
929
+ properties: bibliography.properties ? parseProperties(
930
+ Array.isArray(bibliography.properties.property) ? bibliography.properties.property : [bibliography.properties.property]
931
+ ) : []
932
+ };
933
+ }
934
+ function parseBibliographies(bibliographies) {
935
+ const returnBibliographies = [];
936
+ for (const bibliography of bibliographies) {
937
+ returnBibliographies.push(parseBibliography(bibliography));
938
+ }
939
+ return returnBibliographies;
940
+ }
941
+ function parseTree(tree) {
942
+ let creators = [];
943
+ if (tree.creators) {
944
+ creators = parsePersons(
945
+ Array.isArray(tree.creators.creator) ? tree.creators.creator : [tree.creators.creator]
946
+ );
947
+ }
948
+ let date = null;
949
+ if (tree.date != null) {
950
+ date = new Date(tree.date);
951
+ }
952
+ let resources = [];
953
+ let spatialUnits = [];
954
+ let concepts = [];
955
+ let periods = [];
956
+ let bibliographies = [];
957
+ if (typeof tree.items !== "string" && "resource" in tree.items) {
958
+ resources = parseResources(
959
+ Array.isArray(tree.items.resource) ? tree.items.resource : [tree.items.resource]
960
+ );
961
+ }
962
+ if (typeof tree.items !== "string" && "spatialUnit" in tree.items) {
963
+ spatialUnits = parseSpatialUnits(
964
+ Array.isArray(tree.items.spatialUnit) ? tree.items.spatialUnit : [tree.items.spatialUnit]
965
+ );
966
+ }
967
+ if (typeof tree.items !== "string" && "concept" in tree.items) {
968
+ concepts = parseConcepts(
969
+ Array.isArray(tree.items.concept) ? tree.items.concept : [tree.items.concept]
970
+ );
971
+ }
972
+ if (typeof tree.items !== "string" && "period" in tree.items) {
973
+ periods = parsePeriods(
974
+ Array.isArray(tree.items.period) ? tree.items.period : [tree.items.period]
975
+ );
976
+ }
977
+ if (typeof tree.items !== "string" && "bibliography" in tree.items) {
978
+ bibliographies = parseBibliographies(
979
+ Array.isArray(tree.items.bibliography) ? tree.items.bibliography : [tree.items.bibliography]
980
+ );
981
+ }
982
+ const returnTree = {
983
+ uuid: tree.uuid,
984
+ category: "tree",
985
+ publicationDateTime: new Date(tree.publicationDateTime),
986
+ identification: parseIdentification(tree.identification),
987
+ creators,
988
+ license: parseLicense(tree.availability),
989
+ date,
990
+ type: tree.type,
991
+ number: tree.n,
992
+ items: {
993
+ resources,
994
+ spatialUnits,
995
+ concepts,
996
+ periods,
997
+ bibliographies
998
+ },
999
+ properties: tree.properties ? parseProperties(
1000
+ Array.isArray(tree.properties.property) ? tree.properties.property : [tree.properties.property]
1001
+ ) : []
1002
+ };
1003
+ return returnTree;
1004
+ }
1005
+ function parseSet(set) {
1006
+ let resources = [];
1007
+ let spatialUnits = [];
1008
+ let concepts = [];
1009
+ let periods = [];
1010
+ let bibliographies = [];
1011
+ if (typeof set.items !== "string" && "resource" in set.items) {
1012
+ resources = parseResources(
1013
+ Array.isArray(set.items.resource) ? set.items.resource : [set.items.resource],
1014
+ true
1015
+ );
1016
+ }
1017
+ if (typeof set.items !== "string" && "spatialUnit" in set.items) {
1018
+ spatialUnits = parseSpatialUnits(
1019
+ Array.isArray(set.items.spatialUnit) ? set.items.spatialUnit : [set.items.spatialUnit],
1020
+ true
1021
+ );
1022
+ }
1023
+ if (typeof set.items !== "string" && "concept" in set.items) {
1024
+ concepts = parseConcepts(
1025
+ Array.isArray(set.items.concept) ? set.items.concept : [set.items.concept],
1026
+ true
1027
+ );
1028
+ }
1029
+ if (typeof set.items !== "string" && "period" in set.items) {
1030
+ periods = parsePeriods(
1031
+ Array.isArray(set.items.period) ? set.items.period : [set.items.period]
1032
+ );
1033
+ }
1034
+ if (typeof set.items !== "string" && "bibliography" in set.items) {
1035
+ bibliographies = parseBibliographies(
1036
+ Array.isArray(set.items.bibliography) ? set.items.bibliography : [set.items.bibliography]
1037
+ );
1038
+ }
1039
+ return {
1040
+ uuid: set.uuid,
1041
+ category: "set",
1042
+ publicationDateTime: set.publicationDateTime ? new Date(set.publicationDateTime) : null,
1043
+ date: set.date != null ? new Date(set.date) : null,
1044
+ license: parseLicense(set.availability),
1045
+ identification: parseIdentification(set.identification),
1046
+ isSuppressingBlanks: set.suppressBlanks ?? false,
1047
+ description: set.description ? parseStringContent(set.description) : "",
1048
+ creators: set.creators ? parsePersons(
1049
+ Array.isArray(set.creators.creator) ? set.creators.creator : [set.creators.creator]
1050
+ ) : [],
1051
+ type: set.type,
1052
+ number: set.n,
1053
+ items: {
1054
+ resources,
1055
+ spatialUnits,
1056
+ concepts,
1057
+ periods,
1058
+ bibliographies
1059
+ }
1060
+ };
1061
+ }
1062
+ function parseResource(resource, isNested = false) {
1063
+ const returnResource = {
1064
+ uuid: resource.uuid,
1065
+ category: "resource",
1066
+ publicationDateTime: resource.publicationDateTime ? new Date(resource.publicationDateTime) : null,
1067
+ type: resource.type,
1068
+ number: resource.n,
1069
+ format: resource.format ?? null,
1070
+ context: "context" in resource && resource.context ? parseContext(resource.context) : null,
1071
+ license: "availability" in resource && resource.availability ? parseLicense(resource.availability) : null,
1072
+ copyright: "copyright" in resource && resource.copyright != null ? parseFakeString(resource.copyright) : null,
1073
+ identification: parseIdentification(resource.identification),
1074
+ date: resource.date != null ? new Date(resource.date) : null,
1075
+ image: resource.image ? parseImage(resource.image) : null,
1076
+ creators: resource.creators ? parsePersons(
1077
+ Array.isArray(resource.creators.creator) ? resource.creators.creator : [resource.creators.creator]
1078
+ ) : [],
1079
+ notes: (
1080
+ // TODO: Remove this check once the { rend: "splitNotes" } issue is fixed
1081
+ resource.notes && "note" in resource.notes ? parseNotes(
1082
+ Array.isArray(resource.notes.note) ? resource.notes.note : [resource.notes.note]
1083
+ ) : []
1084
+ ),
1085
+ description: resource.description ? parseStringContent(resource.description) : "",
1086
+ document: resource.document ? parseDocument(resource.document.content) : null,
1087
+ href: resource.href ?? null,
1088
+ imageMap: resource.imagemap ? parseImageMap(resource.imagemap) : null,
1089
+ periods: resource.periods ? parsePeriods(
1090
+ Array.isArray(resource.periods.period) ? resource.periods.period : [resource.periods.period]
1091
+ ) : [],
1092
+ links: resource.links ? parseLinks(
1093
+ Array.isArray(resource.links) ? resource.links : [resource.links]
1094
+ ) : [],
1095
+ reverseLinks: resource.reverseLinks ? parseLinks(
1096
+ Array.isArray(resource.reverseLinks) ? resource.reverseLinks : [resource.reverseLinks]
1097
+ ) : [],
1098
+ properties: resource.properties ? parseProperties(
1099
+ Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
1100
+ ) : [],
1101
+ citedBibliographies: resource.citedBibliography ? parseBibliographies(
1102
+ Array.isArray(resource.citedBibliography.reference) ? resource.citedBibliography.reference : [resource.citedBibliography.reference]
1103
+ ) : [],
1104
+ resources: resource.resource ? parseResources(
1105
+ Array.isArray(resource.resource) ? resource.resource : [resource.resource],
1106
+ true
1107
+ ) : []
1108
+ };
1109
+ if (isNested) {
1110
+ const returnNestedResource = {
1111
+ ...returnResource,
1112
+ publicationDateTime: null,
1113
+ context: null,
1114
+ license: null,
1115
+ copyright: null
1116
+ };
1117
+ delete returnNestedResource.publicationDateTime;
1118
+ delete returnNestedResource.license;
1119
+ delete returnNestedResource.copyright;
1120
+ return returnNestedResource;
1121
+ }
1122
+ return returnResource;
1123
+ }
1124
+ function parseResources(resources, isNested = false) {
1125
+ const returnResources = [];
1126
+ const resourcesToParse = Array.isArray(resources) ? resources : [resources];
1127
+ for (const resource of resourcesToParse) {
1128
+ returnResources.push(parseResource(resource, isNested));
1129
+ }
1130
+ return returnResources;
1131
+ }
1132
+ function parseSpatialUnit(spatialUnit, isNested = false) {
1133
+ const returnSpatialUnit = {
1134
+ uuid: spatialUnit.uuid,
1135
+ category: "spatialUnit",
1136
+ publicationDateTime: spatialUnit.publicationDateTime != null ? new Date(spatialUnit.publicationDateTime) : null,
1137
+ type: spatialUnit.type,
1138
+ number: spatialUnit.n,
1139
+ context: "context" in spatialUnit && spatialUnit.context ? parseContext(spatialUnit.context) : null,
1140
+ license: "availability" in spatialUnit && spatialUnit.availability ? parseLicense(spatialUnit.availability) : null,
1141
+ identification: parseIdentification(spatialUnit.identification),
1142
+ image: spatialUnit.image ? parseImage(spatialUnit.image) : null,
1143
+ description: spatialUnit.description ? parseStringContent(spatialUnit.description) : "",
1144
+ coordinates: spatialUnit.coordinates ? parseCoordinates(spatialUnit.coordinates) : null,
1145
+ observations: "observations" in spatialUnit && spatialUnit.observations ? parseObservations(
1146
+ Array.isArray(spatialUnit.observations.observation) ? spatialUnit.observations.observation : [spatialUnit.observations.observation]
1147
+ ) : spatialUnit.observation ? [parseObservation(spatialUnit.observation)] : [],
1148
+ events: "events" in spatialUnit && spatialUnit.events ? parseEvents(
1149
+ Array.isArray(spatialUnit.events.event) ? spatialUnit.events.event : [spatialUnit.events.event]
1150
+ ) : []
1151
+ };
1152
+ if (isNested) {
1153
+ const returnNestedSpatialUnit = {
1154
+ ...returnSpatialUnit,
1155
+ publicationDateTime: null,
1156
+ license: null,
1157
+ properties: "properties" in spatialUnit && spatialUnit.properties ? parseProperties(
1158
+ Array.isArray(spatialUnit.properties.property) ? spatialUnit.properties.property : [spatialUnit.properties.property]
1159
+ ) : []
1160
+ };
1161
+ delete returnNestedSpatialUnit.publicationDateTime;
1162
+ delete returnNestedSpatialUnit.license;
1163
+ return returnNestedSpatialUnit;
1164
+ }
1165
+ return returnSpatialUnit;
1166
+ }
1167
+ function parseSpatialUnits(spatialUnits, isNested = false) {
1168
+ const returnSpatialUnits = [];
1169
+ const spatialUnitsToParse = Array.isArray(spatialUnits) ? spatialUnits : [spatialUnits];
1170
+ for (const spatialUnit of spatialUnitsToParse) {
1171
+ returnSpatialUnits.push(
1172
+ parseSpatialUnit(spatialUnit, isNested)
1173
+ );
1174
+ }
1175
+ return returnSpatialUnits;
1176
+ }
1177
+ function parseConcept(concept, isNested = false) {
1178
+ const returnConcept = {
1179
+ uuid: concept.uuid,
1180
+ category: "concept",
1181
+ publicationDateTime: concept.publicationDateTime ? new Date(concept.publicationDateTime) : null,
1182
+ number: concept.n,
1183
+ license: "availability" in concept && concept.availability ? parseLicense(concept.availability) : null,
1184
+ context: "context" in concept && concept.context ? parseContext(concept.context) : null,
1185
+ identification: parseIdentification(concept.identification),
1186
+ interpretations: parseInterpretations(
1187
+ Array.isArray(concept.interpretations.interpretation) ? concept.interpretations.interpretation : [concept.interpretations.interpretation]
1188
+ )
1189
+ };
1190
+ if (isNested) {
1191
+ const returnNestedConcept = {
1192
+ ...returnConcept,
1193
+ publicationDateTime: null,
1194
+ context: null,
1195
+ license: null
1196
+ };
1197
+ delete returnNestedConcept.publicationDateTime;
1198
+ delete returnNestedConcept.license;
1199
+ return returnNestedConcept;
1200
+ }
1201
+ return returnConcept;
1202
+ }
1203
+ var parseWebpageResources = async (webpageResources, type) => {
1204
+ const returnElements = [];
1205
+ for (const resource of webpageResources) {
1206
+ const resourceProperties = resource.properties ? parseProperties(
1207
+ Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
1208
+ ) : [];
1209
+ const resourceProperty = resourceProperties.find(
1210
+ (property) => property.label === "presentation" && property.values[0].content === type
1211
+ );
1212
+ if (!resourceProperty) continue;
1213
+ if (type === "element") {
1214
+ const element = await parseWebElement(
1215
+ resource,
1216
+ resourceProperty.properties
1217
+ );
1218
+ returnElements.push(
1219
+ element
1220
+ );
1221
+ } else {
1222
+ const webpage = await parseWebpage(resource);
1223
+ if (webpage) {
1224
+ returnElements.push(
1225
+ webpage
1226
+ );
1227
+ }
1228
+ }
1229
+ }
1230
+ return returnElements;
1231
+ };
1232
+ function parseConcepts(concepts, isNested = false) {
1233
+ const returnConcepts = [];
1234
+ const conceptsToParse = Array.isArray(concepts) ? concepts : [concepts];
1235
+ for (const concept of conceptsToParse) {
1236
+ returnConcepts.push(parseConcept(concept, isNested));
1237
+ }
1238
+ return returnConcepts;
1239
+ }
1240
+ async function parseWebElementProperties(componentProperty, elementResource) {
1241
+ const componentName = componentSchema.parse(
1242
+ componentProperty.values[0].content
1243
+ );
1244
+ const properties = {
1245
+ component: componentName
1246
+ };
1247
+ const links = elementResource.links ? parseLinks(
1248
+ Array.isArray(elementResource.links) ? elementResource.links : [elementResource.links]
1249
+ ) : [];
1250
+ const imageLink = links.find((link) => link.type === "image");
1251
+ let document = elementResource.document ? parseDocument(elementResource.document.content) : null;
1252
+ if (document === null) {
1253
+ const documentLink = links.find((link) => link.type === "internalDocument");
1254
+ if (documentLink) {
1255
+ const documentResource = await fetchResource(documentLink.uuid);
1256
+ if (documentResource === null) {
1257
+ throw new Error("Failed to fetch OCHRE data");
1258
+ }
1259
+ document = documentResource.resource.document;
1260
+ }
1261
+ }
1262
+ switch (componentName) {
1263
+ case "annotated-document": {
1264
+ if (!document) {
1265
+ throw new Error(
1266
+ `Document not found for the following component: \u201C${componentName}\u201D`
1267
+ );
1268
+ }
1269
+ properties.document = document;
1270
+ break;
1271
+ }
1272
+ case "annotated-image": {
1273
+ if (!imageLink) {
1274
+ throw new Error(
1275
+ `Image link not found for the following component: \u201C${componentName}\u201D`
1276
+ );
1277
+ }
1278
+ let isSearchable = getPropertyValueByLabel(
1279
+ componentProperty.properties,
1280
+ "is-searchable"
1281
+ ) === "Yes";
1282
+ if (!isSearchable) {
1283
+ isSearchable = false;
1284
+ }
1285
+ properties.imageUuid = imageLink.uuid;
1286
+ properties.isSearchable = isSearchable;
1287
+ break;
1288
+ }
1289
+ case "bibliography": {
1290
+ const bibliographyLink = links.find(
1291
+ (link) => link.category === "bibliography"
1292
+ );
1293
+ if (!bibliographyLink) {
1294
+ throw new Error(
1295
+ `Bibliography link not found for the following component: \u201C${componentName}\u201D`
1296
+ );
1297
+ }
1298
+ if (!bibliographyLink.bibliographies) {
1299
+ throw new Error(
1300
+ `Bibliography not found for the following component: \u201C${componentName}\u201D`
1301
+ );
1302
+ }
1303
+ let layout = getPropertyValueByLabel(
1304
+ componentProperty.properties,
1305
+ "layout"
1306
+ );
1307
+ if (layout === null) {
1308
+ layout = "long";
1309
+ }
1310
+ properties.bibliographies = bibliographyLink.bibliographies;
1311
+ properties.layout = layout;
1312
+ break;
1313
+ }
1314
+ case "blog": {
1315
+ const blogLink = links.find((link) => link.category === "tree");
1316
+ if (!blogLink) {
1317
+ throw new Error(
1318
+ `Blog link not found for the following component: \u201C${componentName}\u201D`
1319
+ );
1320
+ }
1321
+ properties.blogId = blogLink.uuid;
1322
+ break;
1323
+ }
1324
+ case "button": {
1325
+ let isExternal = false;
1326
+ let href = getPropertyValueByLabel(
1327
+ componentProperty.properties,
1328
+ "navigate-to"
1329
+ );
1330
+ if (href === null) {
1331
+ href = getPropertyValueByLabel(componentProperty.properties, "link-to");
1332
+ if (href === null) {
1333
+ throw new Error(
1334
+ `Properties \u201Cnavigate-to\u201D or \u201Clink-to\u201D not found for the following component: \u201C${componentName}\u201D`
1335
+ );
1336
+ } else {
1337
+ isExternal = true;
1338
+ }
1339
+ }
1340
+ properties.href = href;
1341
+ properties.isExternal = isExternal;
1342
+ properties.label = parseStringContent(
1343
+ elementResource.identification.label
1344
+ );
1345
+ break;
1346
+ }
1347
+ case "collection": {
1348
+ let variant = getPropertyValueByLabel(
1349
+ componentProperty.properties,
1350
+ "variant"
1351
+ );
1352
+ if (variant === null) {
1353
+ variant = "full";
1354
+ }
1355
+ let layout = getPropertyValueByLabel(
1356
+ componentProperty.properties,
1357
+ "layout"
1358
+ );
1359
+ if (layout === null) {
1360
+ layout = "image-start";
1361
+ }
1362
+ const collectionLink = links.find((link) => link.category === "set");
1363
+ if (!collectionLink) {
1364
+ throw new Error(
1365
+ `Collection link not found for the following component: \u201C${componentName}\u201D`
1366
+ );
1367
+ }
1368
+ properties.variant = variant;
1369
+ properties.layout = layout;
1370
+ properties.collectionId = collectionLink.uuid;
1371
+ break;
1372
+ }
1373
+ case "iiif-viewer": {
1374
+ const manifestLink = links.find((link) => link.type === "IIIF");
1375
+ if (!manifestLink) {
1376
+ throw new Error(
1377
+ `Manifest link not found for the following component: \u201C${componentName}\u201D`
1378
+ );
1379
+ }
1380
+ properties.IIIFId = manifestLink.uuid;
1381
+ break;
1382
+ }
1383
+ case "image": {
1384
+ if (!imageLink) {
1385
+ throw new Error(
1386
+ `Image link not found for the following component: \u201C${componentName}\u201D`
1387
+ );
1388
+ }
1389
+ properties.image = {
1390
+ url: `https://ochre.lib.uchicago.edu/ochre?uuid=${imageLink.uuid}&load`,
1391
+ label: imageLink.identification?.label ?? null,
1392
+ width: imageLink.image?.width ?? 0,
1393
+ height: imageLink.image?.height ?? 0
1394
+ };
1395
+ break;
1396
+ }
1397
+ case "image-gallery": {
1398
+ const galleryLink = links.find((link) => link.category === "tree");
1399
+ if (!galleryLink) {
1400
+ throw new Error(
1401
+ `Image gallery link not found for the following component: \u201C${componentName}\u201D`
1402
+ );
1403
+ }
1404
+ properties.galleryId = galleryLink.uuid;
1405
+ break;
1406
+ }
1407
+ case "interactive-chapter-table": {
1408
+ break;
1409
+ }
1410
+ case "item-gallery": {
1411
+ const galleryLink = links.find((link) => link.category === "tree");
1412
+ if (!galleryLink) {
1413
+ throw new Error(
1414
+ `Item gallery link not found for the following component: \u201C${componentName}\u201D`
1415
+ );
1416
+ }
1417
+ properties.galleryId = galleryLink.uuid;
1418
+ break;
1419
+ }
1420
+ case "menu": {
1421
+ break;
1422
+ }
1423
+ case "menu-item": {
1424
+ break;
1425
+ }
1426
+ case "n-columns": {
1427
+ const subElements = elementResource.resource ? await parseWebpageResources(
1428
+ Array.isArray(elementResource.resource) ? elementResource.resource : [elementResource.resource],
1429
+ "element"
1430
+ ) : [];
1431
+ properties.columns = subElements;
1432
+ break;
1433
+ }
1434
+ case "n-rows": {
1435
+ const subElements = elementResource.resource ? await parseWebpageResources(
1436
+ Array.isArray(elementResource.resource) ? elementResource.resource : [elementResource.resource],
1437
+ "element"
1438
+ ) : [];
1439
+ properties.rows = subElements;
1440
+ break;
1441
+ }
1442
+ case "network-graph": {
1443
+ break;
1444
+ }
1445
+ case "table": {
1446
+ const tableLink = links.find((link) => link.category === "set");
1447
+ if (!tableLink) {
1448
+ throw new Error(
1449
+ `Table link not found for the following component: \u201C${componentName}\u201D`
1450
+ );
1451
+ }
1452
+ properties.tableId = tableLink.uuid;
1453
+ break;
1454
+ }
1455
+ case "text": {
1456
+ if (!document) {
1457
+ throw new Error(
1458
+ `Document not found for the following component: \u201C${componentName}\u201D`
1459
+ );
1460
+ }
1461
+ let variant = getPropertyValueByLabel(
1462
+ componentProperty.properties,
1463
+ "variant"
1464
+ );
1465
+ if (variant === null) {
1466
+ variant = "block";
1467
+ }
1468
+ properties.variant = variant;
1469
+ properties.content = document.content;
1470
+ break;
1471
+ }
1472
+ case "text-image": {
1473
+ if (!document) {
1474
+ throw new Error(
1475
+ `Document not found for the following component: \u201C${componentName}\u201D`
1476
+ );
1477
+ }
1478
+ let variant = getPropertyValueByLabel(
1479
+ componentProperty.properties,
1480
+ "variant"
1481
+ );
1482
+ if (variant === null) {
1483
+ variant = "block";
1484
+ }
1485
+ let layout = getPropertyValueByLabel(
1486
+ componentProperty.properties,
1487
+ "layout"
1488
+ );
1489
+ if (layout === null) {
1490
+ layout = "image-start";
1491
+ }
1492
+ let captionLayout = getPropertyValueByLabel(
1493
+ componentProperty.properties,
1494
+ "caption-layout"
1495
+ );
1496
+ if (captionLayout === null) {
1497
+ captionLayout = "bottom";
1498
+ }
1499
+ const imageLink2 = links.find(
1500
+ (link) => link.type === "image" || link.type === "IIIF"
1501
+ );
1502
+ if (!imageLink2) {
1503
+ throw new Error(
1504
+ `Image link not found for the following component: \u201C${componentName}\u201D: ${JSON.stringify(
1505
+ links
1506
+ )}`
1507
+ );
1508
+ }
1509
+ properties.variant = variant;
1510
+ properties.layout = layout;
1511
+ properties.captionLayout = captionLayout;
1512
+ properties.content = document.content;
1513
+ properties.image = {
1514
+ url: `https://ochre.lib.uchicago.edu/ochre?uuid=${imageLink2.uuid}&preview`,
1515
+ label: imageLink2.identification?.label ?? null,
1516
+ width: imageLink2.image?.width ?? 0,
1517
+ height: imageLink2.image?.height ?? 0
1518
+ };
1519
+ properties.imageOpacity = null;
1520
+ break;
1521
+ }
1522
+ case "timeline": {
1523
+ const timelineLink = links.find((link) => link.category === "tree");
1524
+ if (!timelineLink) {
1525
+ throw new Error(
1526
+ `Timeline link not found for the following component: \u201C${componentName}\u201D`
1527
+ );
1528
+ }
1529
+ properties.timelineId = timelineLink.uuid;
1530
+ break;
1531
+ }
1532
+ default: {
1533
+ console.warn(
1534
+ `Invalid or non-implemented component name \u201C${componentName}\u201D for the following element: \u201C${parseStringContent(
1535
+ elementResource.identification.label
1536
+ )}\u201D`
1537
+ );
1538
+ }
1539
+ }
1540
+ return properties;
1541
+ }
1542
+ async function parseWebElement(elementResource, elementProperties) {
1543
+ const identification = parseIdentification(elementResource.identification);
1544
+ const componentProperty = elementProperties.find(
1545
+ (property) => property.label === "component"
1546
+ );
1547
+ if (!componentProperty) {
1548
+ throw new Error(
1549
+ `Component for element \u201C${identification.label}\u201D not found`
1550
+ );
1551
+ }
1552
+ const properties = await parseWebElementProperties(
1553
+ componentProperty,
1554
+ elementResource
1555
+ );
1556
+ const elementResourceProperties = elementResource.properties?.property ? parseProperties(
1557
+ Array.isArray(elementResource.properties.property) ? elementResource.properties.property : [elementResource.properties.property]
1558
+ ) : [];
1559
+ const cssProperties = elementResourceProperties.find(
1560
+ (property) => property.label === "presentation" && property.values[0].content === "css"
1561
+ )?.properties ?? [];
1562
+ const cssStyles = [];
1563
+ for (const property of cssProperties) {
1564
+ const cssStyle = property.values[0].content;
1565
+ cssStyles.push({ label: property.label, value: cssStyle });
1566
+ }
1567
+ return {
1568
+ uuid: elementResource.uuid,
1569
+ title: identification.label,
1570
+ cssStyles,
1571
+ ...properties
1572
+ };
1573
+ }
1574
+ async function parseWebpage(webpageResource) {
1575
+ const webpageProperties = webpageResource.properties ? parseProperties(
1576
+ Array.isArray(webpageResource.properties.property) ? webpageResource.properties.property : [webpageResource.properties.property]
1577
+ ) : [];
1578
+ if (webpageProperties.length === 0 || webpageProperties.find((property) => property.label === "presentation")?.values[0]?.content !== "page") {
1579
+ return null;
1580
+ }
1581
+ const identification = parseIdentification(webpageResource.identification);
1582
+ const slug = webpageResource.slug === "/" ? "" : webpageResource.slug;
1583
+ if (slug === void 0) {
1584
+ throw new Error(`Slug not found for page \u201C${identification.label}\u201D`);
1585
+ }
1586
+ const links = webpageResource.links ? parseLinks(
1587
+ Array.isArray(webpageResource.links) ? webpageResource.links : [webpageResource.links]
1588
+ ) : [];
1589
+ const imageLink = links.find(
1590
+ (link) => link.type === "image" || link.type === "IIIF"
1591
+ );
1592
+ const elements = webpageResource.resource ? await parseWebpageResources(
1593
+ Array.isArray(webpageResource.resource) ? webpageResource.resource : [webpageResource.resource],
1594
+ "element"
1595
+ ) : [];
1596
+ const webpages = webpageResource.resource ? await parseWebpageResources(
1597
+ Array.isArray(webpageResource.resource) ? webpageResource.resource : [webpageResource.resource],
1598
+ "page"
1599
+ ) : [];
1600
+ let displayedInHeader = true;
1601
+ let width = "default";
1602
+ let variant = "default";
1603
+ const webpageSubProperties = webpageProperties.find(
1604
+ (property) => property.label === "presentation" && property.values[0]?.content === "page"
1605
+ )?.properties;
1606
+ if (webpageSubProperties) {
1607
+ const headerProperty = webpageSubProperties.find(
1608
+ (property) => property.label === "header"
1609
+ )?.values[0];
1610
+ if (headerProperty) {
1611
+ displayedInHeader = headerProperty.content === "Yes";
1612
+ }
1613
+ const widthProperty = webpageSubProperties.find(
1614
+ (property) => property.label === "width"
1615
+ )?.values[0];
1616
+ if (widthProperty) {
1617
+ width = widthProperty.content;
1618
+ }
1619
+ const variantProperty = webpageSubProperties.find(
1620
+ (property) => property.label === "variant"
1621
+ )?.values[0];
1622
+ if (variantProperty) {
1623
+ variant = variantProperty.content;
1624
+ }
1625
+ }
1626
+ const cssStyleSubProperties = webpageProperties.find(
1627
+ (property) => property.label === "presentation" && property.values[0]?.content === "css"
1628
+ )?.properties;
1629
+ const cssStyles = [];
1630
+ if (cssStyleSubProperties) {
1631
+ for (const property of cssStyleSubProperties) {
1632
+ cssStyles.push({
1633
+ label: property.label,
1634
+ value: property.values[0].content
1635
+ });
1636
+ }
1637
+ }
1638
+ return {
1639
+ title: identification.label,
1640
+ slug,
1641
+ elements,
1642
+ properties: {
1643
+ displayedInHeader,
1644
+ width,
1645
+ variant,
1646
+ backgroundImageUrl: imageLink ? `https://ochre.lib.uchicago.edu/ochre?uuid=${imageLink.uuid}&preview` : null,
1647
+ cssStyles
1648
+ },
1649
+ webpages
1650
+ };
1651
+ }
1652
+ async function parseWebpages(webpageResources) {
1653
+ const returnPages = [];
1654
+ const pagesToParse = Array.isArray(webpageResources) ? webpageResources : [webpageResources];
1655
+ for (const page of pagesToParse) {
1656
+ const webpage = await parseWebpage(page);
1657
+ if (webpage) {
1658
+ returnPages.push(webpage);
1659
+ }
1660
+ }
1661
+ return returnPages;
1662
+ }
1663
+ function parseWebsiteProperties(properties) {
1664
+ const mainProperties = parseProperties(properties);
1665
+ const websiteProperties = mainProperties.find(
1666
+ (property) => property.label === "presentation"
1667
+ )?.properties;
1668
+ if (!websiteProperties) {
1669
+ throw new Error("Presentation property not found");
1670
+ }
1671
+ const type = websiteProperties.find((property) => property.label === "webUI")?.values[0]?.content;
1672
+ if (type == null) {
1673
+ throw new Error("Website type not found");
1674
+ }
1675
+ const status = websiteProperties.find(
1676
+ (property) => property.label === "status"
1677
+ )?.values[0]?.content;
1678
+ if (status == null) {
1679
+ throw new Error("Website status not found");
1680
+ }
1681
+ let privacy = websiteProperties.find(
1682
+ (property) => property.label === "privacy"
1683
+ )?.values[0]?.content;
1684
+ if (privacy == null) {
1685
+ privacy = "public";
1686
+ }
1687
+ const result = websiteSchema.safeParse({
1688
+ type,
1689
+ status,
1690
+ privacy
1691
+ });
1692
+ if (!result.success) {
1693
+ throw new Error(`Invalid website properties: ${result.error.message}`);
1694
+ }
1695
+ const logoUuid = websiteProperties.find((property) => property.label === "logo")?.values[0]?.uuid ?? null;
1696
+ let isHeaderDisplayed = true;
1697
+ let headerVariant = "default";
1698
+ let isFooterDisplayed = true;
1699
+ let isSidebarDisplayed = false;
1700
+ let searchCollectionUuid = null;
1701
+ const headerProperty = websiteProperties.find(
1702
+ (property) => property.label === "navbar-visible"
1703
+ )?.values[0];
1704
+ if (headerProperty) {
1705
+ isHeaderDisplayed = headerProperty.content === "Yes";
1706
+ }
1707
+ const headerVariantProperty = websiteProperties.find(
1708
+ (property) => property.label === "navbar-variant"
1709
+ )?.values[0];
1710
+ if (headerVariantProperty) {
1711
+ headerVariant = headerVariantProperty.content;
1712
+ }
1713
+ const footerProperty = websiteProperties.find(
1714
+ (property) => property.label === "footer-visible"
1715
+ )?.values[0];
1716
+ if (footerProperty) {
1717
+ isFooterDisplayed = footerProperty.content === "Yes";
1718
+ }
1719
+ const sidebarProperty = websiteProperties.find(
1720
+ (property) => property.label === "sidebar-visible"
1721
+ )?.values[0];
1722
+ if (sidebarProperty) {
1723
+ isSidebarDisplayed = sidebarProperty.content === "Yes";
1724
+ }
1725
+ const collectionSearchProperty = websiteProperties.find(
1726
+ (property) => property.label === "search-collection"
1727
+ )?.values[0];
1728
+ if (collectionSearchProperty) {
1729
+ searchCollectionUuid = collectionSearchProperty.uuid;
1730
+ }
1731
+ const {
1732
+ type: validatedType,
1733
+ status: validatedStatus,
1734
+ privacy: validatedPrivacy
1735
+ } = result.data;
1736
+ return {
1737
+ type: validatedType,
1738
+ privacy: validatedPrivacy,
1739
+ status: validatedStatus,
1740
+ isHeaderDisplayed,
1741
+ headerVariant,
1742
+ isFooterDisplayed,
1743
+ isSidebarDisplayed,
1744
+ searchCollectionUuid,
1745
+ logoUrl: logoUuid !== null ? `https://ochre.lib.uchicago.edu/ochre?uuid=${logoUuid}&load` : null
1746
+ };
1747
+ }
1748
+ async function parseWebsite(websiteTree, projectName, website) {
1749
+ if (!websiteTree.properties) {
1750
+ throw new Error("Website properties not found");
1751
+ }
1752
+ const properties = parseWebsiteProperties(
1753
+ Array.isArray(websiteTree.properties.property) ? websiteTree.properties.property : [websiteTree.properties.property]
1754
+ );
1755
+ if (typeof websiteTree.items === "string" || !("resource" in websiteTree.items)) {
1756
+ throw new Error("Website pages not found");
1757
+ }
1758
+ const resources = Array.isArray(websiteTree.items.resource) ? websiteTree.items.resource : [websiteTree.items.resource];
1759
+ const pages = await parseWebpages(resources);
1760
+ const sidebarElements = [];
1761
+ const sidebar = resources.find((resource) => {
1762
+ const resourceProperties = resource.properties ? parseProperties(
1763
+ Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
1764
+ ) : [];
1765
+ return resourceProperties.some(
1766
+ (property) => property.label === "presentation" && property.values[0]?.content === "element" && property.properties[0]?.label === "component" && property.properties[0]?.values[0]?.content === "sidebar"
1767
+ );
1768
+ });
1769
+ if (sidebar) {
1770
+ const sidebarResources = sidebar.resource ? Array.isArray(sidebar.resource) ? sidebar.resource : [sidebar.resource] : [];
1771
+ for (const resource of sidebarResources) {
1772
+ const sidebarResourceProperties = resource.properties ? parseProperties(
1773
+ Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
1774
+ ) : [];
1775
+ const element = await parseWebElement(
1776
+ resource,
1777
+ sidebarResourceProperties.find(
1778
+ (property) => property.label === "presentation" && property.values[0]?.content === "element"
1779
+ )?.properties ?? []
1780
+ );
1781
+ sidebarElements.push(element);
1782
+ }
1783
+ }
1784
+ return {
1785
+ uuid: websiteTree.uuid,
1786
+ publicationDateTime: websiteTree.publicationDateTime ? new Date(websiteTree.publicationDateTime) : null,
1787
+ identification: parseIdentification(websiteTree.identification),
1788
+ project: {
1789
+ name: parseFakeString(projectName),
1790
+ website: website !== null ? parseFakeString(website) : null
1791
+ },
1792
+ creators: websiteTree.creators ? parsePersons(
1793
+ Array.isArray(websiteTree.creators.creator) ? websiteTree.creators.creator : [websiteTree.creators.creator]
1794
+ ) : [],
1795
+ license: parseLicense(websiteTree.availability),
1796
+ pages,
1797
+ sidebarElements,
1798
+ properties
1799
+ };
1800
+ }
1801
+
1802
+ // src/utils/fetchers/concept.ts
1803
+ async function fetchConcept(uuid) {
1804
+ try {
1805
+ const [error, dataRaw] = await fetchByUuid(uuid);
1806
+ if (error !== null) {
1807
+ throw new Error(error);
1808
+ }
1809
+ if (!("concept" in dataRaw.ochre)) {
1810
+ throw new Error("Invalid OCHRE data: API response missing 'concept' key");
1811
+ }
1812
+ const conceptItem = parseConcept(dataRaw.ochre.concept);
1813
+ const data = {
1814
+ uuid: parseFakeString(dataRaw.ochre.uuid),
1815
+ publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
1816
+ belongsTo: {
1817
+ uuid: dataRaw.ochre.uuidBelongsTo,
1818
+ abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
1819
+ },
1820
+ metadata: parseMetadata(dataRaw.ochre.metadata),
1821
+ item: conceptItem
1822
+ };
1823
+ return { metadata: data.metadata, concept: data.item };
1824
+ } catch (error) {
1825
+ console.error(error);
1826
+ return null;
1827
+ }
1828
+ }
1829
+
1830
+ // src/utils/fetchers/gallery.ts
1831
+ import { z as z4 } from "zod";
1832
+ var gallerySchema = z4.object({
1833
+ uuid: z4.string().uuid({ message: "Invalid UUID" }),
1834
+ filter: z4.string().optional(),
1835
+ page: z4.number().positive({ message: "Page must be positive" }),
1836
+ perPage: z4.number().positive({ message: "Per page must be positive" })
1837
+ }).strict();
1838
+ async function fetchGallery(uuid, filter, page, perPage) {
1839
+ try {
1840
+ const parsed = gallerySchema.safeParse({ uuid, filter, page, perPage });
1841
+ if (!parsed.success) {
1842
+ throw new Error(parsed.error.message);
1843
+ }
1844
+ const response = await fetch(
1845
+ `https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(`
1846
+ for $q in input()/ochre[@uuid='${uuid}']
1847
+ let $filtered := $q/tree/items/resource[contains(lower-case(identification/label), lower-case('${filter}'))]
1848
+ let $maxLength := count($filtered)
1849
+ return <gallery maxLength='{$maxLength}'>
1850
+ {$q/metadata/project}
1851
+ {$q/metadata/item}
1852
+ {$filtered[position() >= ${((page - 1) * perPage + 1).toString()} and position() < ${(page * perPage + 1).toString()}]}
1853
+ </gallery>
1854
+ `)}&format=json`
1855
+ );
1856
+ if (!response.ok) {
1857
+ throw new Error("Error fetching gallery items, please try again later.");
1858
+ }
1859
+ const data = await response.json();
1860
+ if (!("gallery" in data.result)) {
1861
+ throw new Error("Failed to fetch gallery");
1862
+ }
1863
+ const galleryIdentification = parseIdentification(data.result.gallery.item);
1864
+ const galleryProjectIdentification = parseIdentification(
1865
+ data.result.gallery.project.identification
1866
+ );
1867
+ const gallery = {
1868
+ identification: galleryIdentification,
1869
+ projectIdentification: galleryProjectIdentification,
1870
+ resources: Array.isArray(data.result.gallery.resource) ? parseResources(data.result.gallery.resource) : [parseResource(data.result.gallery.resource)],
1871
+ maxLength: data.result.gallery.maxLength
1872
+ };
1873
+ return gallery;
1874
+ } catch (error) {
1875
+ console.error(error);
1876
+ return null;
1877
+ }
1878
+ }
1879
+
1880
+ // src/utils/fetchers/set.ts
1881
+ async function fetchSet(uuid) {
1882
+ try {
1883
+ const [error, dataRaw] = await fetchByUuid(uuid);
1884
+ if (error !== null) {
1885
+ throw new Error(error);
1886
+ }
1887
+ if (!("set" in dataRaw.ochre)) {
1888
+ throw new Error("Invalid OCHRE data: API response missing 'set' key");
1889
+ }
1890
+ const setItem = parseSet(dataRaw.ochre.set);
1891
+ const data = {
1892
+ uuid: parseFakeString(dataRaw.ochre.uuid),
1893
+ publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
1894
+ belongsTo: {
1895
+ uuid: dataRaw.ochre.uuidBelongsTo,
1896
+ abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
1897
+ },
1898
+ metadata: parseMetadata(dataRaw.ochre.metadata),
1899
+ item: setItem
1900
+ };
1901
+ return { metadata: data.metadata, set: data.item };
1902
+ } catch (error) {
1903
+ console.error(error);
1904
+ return null;
1905
+ }
1906
+ }
1907
+
1908
+ // src/utils/fetchers/spatial-unit.ts
1909
+ async function fetchSpatialUnit(uuid) {
1910
+ try {
1911
+ const [error, dataRaw] = await fetchByUuid(uuid);
1912
+ if (error !== null) {
1913
+ throw new Error(error);
1914
+ }
1915
+ if (!("spatialUnit" in dataRaw.ochre)) {
1916
+ throw new Error(
1917
+ "Invalid OCHRE data: API response missing 'spatialUnit' key"
1918
+ );
1919
+ }
1920
+ const spatialUnitItem = parseSpatialUnit(dataRaw.ochre.spatialUnit);
1921
+ const data = {
1922
+ uuid: parseFakeString(dataRaw.ochre.uuid),
1923
+ publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
1924
+ belongsTo: {
1925
+ uuid: dataRaw.ochre.uuidBelongsTo,
1926
+ abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
1927
+ },
1928
+ metadata: parseMetadata(dataRaw.ochre.metadata),
1929
+ item: spatialUnitItem
1930
+ };
1931
+ return { metadata: data.metadata, spatialUnit: data.item };
1932
+ } catch (error) {
1933
+ console.error(error);
1934
+ return null;
1935
+ }
1936
+ }
1937
+
1938
+ // src/utils/fetchers/tree.ts
1939
+ async function fetchTree(uuid) {
1940
+ try {
1941
+ const [error, dataRaw] = await fetchByUuid(uuid);
1942
+ if (error !== null) {
1943
+ throw new Error(error);
1944
+ }
1945
+ if (!("tree" in dataRaw.ochre)) {
1946
+ throw new Error("Invalid OCHRE data: API response missing 'tree' key");
1947
+ }
1948
+ const tree = parseTree(dataRaw.ochre.tree);
1949
+ if (!tree) {
1950
+ throw new Error("Invalid OCHRE data: Could not parse tree");
1951
+ }
1952
+ const data = {
1953
+ uuid: parseFakeString(dataRaw.ochre.uuid),
1954
+ publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
1955
+ belongsTo: {
1956
+ uuid: dataRaw.ochre.uuidBelongsTo,
1957
+ abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
1958
+ },
1959
+ metadata: parseMetadata(dataRaw.ochre.metadata),
1960
+ item: tree
1961
+ };
1962
+ return { metadata: data.metadata, tree: data.item };
1963
+ } catch (error) {
1964
+ console.error(error);
1965
+ return null;
1966
+ }
1967
+ }
1968
+
1969
+ // src/utils/fetchers/website.ts
1970
+ async function fetchWebsite(abbreviation) {
1971
+ try {
1972
+ const response = await fetch(
1973
+ `https://ochre.lib.uchicago.edu/ochre?xquery=for $q in input()/ochre[tree[@type='lesson'][identification/abbreviation='${abbreviation.toLocaleLowerCase("en-US")}']] return $q&format=json`
1974
+ );
1975
+ if (!response.ok) {
1976
+ throw new Error("Failed to fetch website");
1977
+ }
1978
+ const data = await response.json();
1979
+ if (!("ochre" in data.result) || !("tree" in data.result.ochre)) {
1980
+ throw new Error("Failed to fetch website");
1981
+ }
1982
+ const projectIdentification = data.result.ochre.metadata.project?.identification ? parseIdentification(data.result.ochre.metadata.project.identification) : null;
1983
+ const website = await parseWebsite(
1984
+ data.result.ochre.tree,
1985
+ projectIdentification?.label ?? "",
1986
+ data.result.ochre.metadata.project?.identification.website ?? null
1987
+ );
1988
+ return website;
1989
+ } catch (error) {
1990
+ console.error(error);
1991
+ return null;
1992
+ }
1993
+ }
1994
+ export {
1995
+ fetchByUuid,
1996
+ fetchConcept,
1997
+ fetchGallery,
1998
+ fetchResource,
1999
+ fetchSet,
2000
+ fetchSpatialUnit,
2001
+ fetchTree,
2002
+ fetchWebsite,
2003
+ filterProperties,
2004
+ getAllPropertyLabels,
2005
+ getPropertyByLabel,
2006
+ getPropertyValueByLabel,
2007
+ getPropertyValuesByLabel,
2008
+ parseBibliographies,
2009
+ parseBibliography,
2010
+ parseConcept,
2011
+ parseConcepts,
2012
+ parseContext,
2013
+ parseCoordinates,
2014
+ parseDocument,
2015
+ parseEmailAndUrl,
2016
+ parseEvents,
2017
+ parseFakeString,
2018
+ parseIdentification,
2019
+ parseImage,
2020
+ parseImageMap,
2021
+ parseInterpretations,
2022
+ parseLanguages,
2023
+ parseLicense,
2024
+ parseLink,
2025
+ parseLinks,
2026
+ parseMetadata,
2027
+ parseNotes,
2028
+ parseObservation,
2029
+ parseObservations,
2030
+ parsePeriod,
2031
+ parsePeriods,
2032
+ parsePersons,
2033
+ parseProperties,
2034
+ parseResource,
2035
+ parseResources,
2036
+ parseSet,
2037
+ parseSpatialUnit,
2038
+ parseSpatialUnits,
2039
+ parseStringContent,
2040
+ parseStringDocumentItem,
2041
+ parseStringItem,
2042
+ parseTree,
2043
+ parseWebsite,
2044
+ trimEndLineBreaks
2045
+ };