@digitalculture/ochre-sdk 0.1.20 → 0.1.21

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,1969 @@
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
+ ],
524
+ { message: "Invalid component" }
525
+ );
526
+ function parseIdentification(identification) {
527
+ try {
528
+ const returnIdentification = {
529
+ label: parseStringContent(identification.label),
530
+ abbreviation: ""
531
+ };
532
+ for (const key of Object.keys(identification).filter(
533
+ (key2) => key2 !== "label"
534
+ )) {
535
+ returnIdentification[key] = parseStringContent(
536
+ identification[key]
537
+ );
538
+ }
539
+ return returnIdentification;
540
+ } catch (error) {
541
+ console.error(error);
542
+ return {
543
+ label: "",
544
+ abbreviation: ""
545
+ };
546
+ }
547
+ }
548
+ function parseLanguages(language) {
549
+ if (Array.isArray(language)) {
550
+ return language.map((lang) => parseStringContent(lang));
551
+ } else {
552
+ return [parseStringContent(language)];
553
+ }
554
+ }
555
+ function parseMetadata(metadata) {
556
+ let identification = {
557
+ label: "",
558
+ abbreviation: ""
559
+ };
560
+ if (metadata.item) {
561
+ if (metadata.item.label || metadata.item.abbreviation) {
562
+ let label = "";
563
+ let abbreviation = "";
564
+ if (metadata.item.label) {
565
+ label = parseStringContent(metadata.item.label);
566
+ }
567
+ if (metadata.item.abbreviation) {
568
+ abbreviation = parseStringContent(metadata.item.abbreviation);
569
+ }
570
+ identification = { label, abbreviation };
571
+ } else {
572
+ identification = parseIdentification(metadata.item.identification);
573
+ }
574
+ }
575
+ let projectIdentification = null;
576
+ const baseProjectIdentification = metadata.project?.identification ? parseIdentification(metadata.project.identification) : null;
577
+ if (baseProjectIdentification) {
578
+ projectIdentification = {
579
+ ...baseProjectIdentification,
580
+ website: metadata.project?.identification.website ?? null
581
+ };
582
+ }
583
+ return {
584
+ project: projectIdentification ? { identification: projectIdentification } : null,
585
+ item: metadata.item ? {
586
+ identification,
587
+ category: metadata.item.category,
588
+ type: metadata.item.type,
589
+ maxLength: metadata.item.maxLength ?? null
590
+ } : null,
591
+ dataset: parseStringContent(metadata.dataset),
592
+ publisher: parseStringContent(metadata.publisher),
593
+ languages: parseLanguages(metadata.language),
594
+ identifier: parseStringContent(metadata.identifier),
595
+ description: parseStringContent(metadata.description)
596
+ };
597
+ }
598
+ function parseContextItem(contextItem) {
599
+ return {
600
+ uuid: contextItem.uuid,
601
+ publicationDateTime: contextItem.publicationDateTime != null ? new Date(contextItem.publicationDateTime) : null,
602
+ number: contextItem.n,
603
+ content: parseFakeString(contextItem.content)
604
+ };
605
+ }
606
+ function parseContext(context) {
607
+ const contexts = Array.isArray(context.context) ? context.context : [context.context];
608
+ const returnContexts = {
609
+ nodes: contexts.map((context2) => {
610
+ const spatialUnit = [];
611
+ if ("spatialUnit" in context2 && context2.spatialUnit) {
612
+ const contextsToParse = Array.isArray(context2.spatialUnit) ? context2.spatialUnit : [context2.spatialUnit];
613
+ for (const contextItem of contextsToParse) {
614
+ spatialUnit.push(parseContextItem(contextItem));
615
+ }
616
+ }
617
+ return {
618
+ tree: parseContextItem(context2.tree),
619
+ project: parseContextItem(context2.project),
620
+ spatialUnit
621
+ };
622
+ }),
623
+ displayPath: context.displayPath
624
+ };
625
+ return returnContexts;
626
+ }
627
+ function parseLicense(license) {
628
+ if (typeof license.license === "string") {
629
+ return null;
630
+ }
631
+ return {
632
+ content: license.license.content,
633
+ url: license.license.target
634
+ };
635
+ }
636
+ function parsePersons(persons) {
637
+ const returnPersons = [];
638
+ for (const person of persons) {
639
+ returnPersons.push({
640
+ uuid: person.uuid,
641
+ publicationDateTime: person.publicationDateTime != null ? new Date(person.publicationDateTime) : null,
642
+ type: person.type ?? null,
643
+ date: person.date != null ? new Date(person.date) : null,
644
+ identification: person.identification ? parseIdentification(person.identification) : null,
645
+ content: person.content != null ? parseFakeString(person.content) : null
646
+ });
647
+ }
648
+ return returnPersons;
649
+ }
650
+ function parseLink(linkRaw) {
651
+ 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;
652
+ if (!links) {
653
+ throw new Error(
654
+ `Invalid link provided: ${JSON.stringify(linkRaw, null, 2)}`
655
+ );
656
+ }
657
+ const linksToParse = Array.isArray(links) ? links : [links];
658
+ const returnLinks = [];
659
+ for (const link of linksToParse) {
660
+ const returnLink = {
661
+ 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,
662
+ content: "content" in link ? link.content != null ? parseFakeString(link.content) : null : null,
663
+ uuid: link.uuid,
664
+ type: link.type ?? null,
665
+ identification: link.identification ? parseIdentification(link.identification) : null,
666
+ image: null,
667
+ bibliographies: "bibliography" in linkRaw ? parseBibliographies(
668
+ Array.isArray(linkRaw.bibliography) ? linkRaw.bibliography : [linkRaw.bibliography]
669
+ ) : null,
670
+ publicationDateTime: link.publicationDateTime != null ? new Date(link.publicationDateTime) : null
671
+ };
672
+ if ("height" in link && link.height != null && link.width != null && link.heightPreview != null && link.widthPreview != null) {
673
+ returnLink.image = {
674
+ isInline: link.rend === "inline",
675
+ heightPreview: link.heightPreview,
676
+ widthPreview: link.widthPreview,
677
+ height: link.height,
678
+ width: link.width
679
+ };
680
+ }
681
+ returnLinks.push(returnLink);
682
+ }
683
+ return returnLinks;
684
+ }
685
+ function parseLinks(links) {
686
+ const returnLinks = [];
687
+ for (const link of links) {
688
+ returnLinks.push(...parseLink(link));
689
+ }
690
+ return returnLinks;
691
+ }
692
+ function parseDocument(document, language = "eng") {
693
+ let returnString = "";
694
+ const footnotes = [];
695
+ const documentWithLanguage = Array.isArray(document) ? document.find((doc) => doc.lang === language) : document;
696
+ if (typeof documentWithLanguage.string === "string" || typeof documentWithLanguage.string === "number" || typeof documentWithLanguage.string === "boolean") {
697
+ returnString += parseEmailAndUrl(
698
+ parseFakeString(documentWithLanguage.string)
699
+ );
700
+ } else {
701
+ const documentItems = Array.isArray(documentWithLanguage.string) ? documentWithLanguage.string : [documentWithLanguage.string];
702
+ for (const item of documentItems) {
703
+ returnString += parseStringDocumentItem(item, footnotes);
704
+ }
705
+ }
706
+ returnString = trimEndLineBreaks(returnString);
707
+ return { content: returnString, footnotes };
708
+ }
709
+ function parseImage(image) {
710
+ return {
711
+ publicationDateTime: image.publicationDateTime != null ? new Date(image.publicationDateTime) : null,
712
+ identification: image.identification ? parseIdentification(image.identification) : null,
713
+ url: image.href ?? (image.htmlImgSrcPrefix == null && image.content != null ? parseFakeString(image.content) : null),
714
+ htmlPrefix: image.htmlImgSrcPrefix ?? null,
715
+ content: image.htmlImgSrcPrefix != null && image.content != null ? parseFakeString(image.content) : null
716
+ };
717
+ }
718
+ function parseNotes(notes, language = "eng") {
719
+ const returnNotes = [];
720
+ for (const note of notes) {
721
+ if (typeof note === "string") {
722
+ if (note === "") {
723
+ continue;
724
+ }
725
+ returnNotes.push({
726
+ number: -1,
727
+ title: null,
728
+ content: note
729
+ });
730
+ continue;
731
+ }
732
+ let content = "";
733
+ const notesToParse = Array.isArray(note.content) ? note.content : [note.content];
734
+ let noteWithLanguage = notesToParse.find((item) => item.lang === language);
735
+ if (!noteWithLanguage) {
736
+ noteWithLanguage = notesToParse[0];
737
+ if (!noteWithLanguage) {
738
+ throw new Error(
739
+ `Note does not have a valid content item: ${JSON.stringify(
740
+ note,
741
+ null,
742
+ 2
743
+ )}`
744
+ );
745
+ }
746
+ }
747
+ if (typeof noteWithLanguage.string === "string" || typeof noteWithLanguage.string === "number" || typeof noteWithLanguage.string === "boolean") {
748
+ content = parseEmailAndUrl(parseFakeString(noteWithLanguage.string));
749
+ } else {
750
+ content = parseEmailAndUrl(parseDocument(noteWithLanguage).content);
751
+ }
752
+ returnNotes.push({
753
+ number: note.noteNo,
754
+ title: noteWithLanguage.title != null ? parseFakeString(noteWithLanguage.title) : null,
755
+ content
756
+ });
757
+ }
758
+ return returnNotes;
759
+ }
760
+ function parseCoordinates(coordinates) {
761
+ return {
762
+ latitude: coordinates.latitude,
763
+ longitude: coordinates.longitude,
764
+ type: coordinates.coord?.coordType ?? null,
765
+ label: coordinates.coord?.coordLabel != null ? parseFakeString(coordinates.coord.coordLabel) : null
766
+ };
767
+ }
768
+ function parseObservation(observation) {
769
+ return {
770
+ number: observation.observationNo,
771
+ date: observation.date != null ? new Date(observation.date) : null,
772
+ observers: observation.observers != null ? parseFakeString(observation.observers).split(";").map((observer) => observer.trim()) : [],
773
+ notes: observation.notes ? parseNotes(
774
+ Array.isArray(observation.notes.note) ? observation.notes.note : [observation.notes.note]
775
+ ) : [],
776
+ links: observation.links ? parseLinks(
777
+ Array.isArray(observation.links) ? observation.links : [observation.links]
778
+ ) : [],
779
+ properties: observation.properties ? parseProperties(
780
+ Array.isArray(observation.properties.property) ? observation.properties.property : [observation.properties.property]
781
+ ) : []
782
+ };
783
+ }
784
+ function parseObservations(observations) {
785
+ const returnObservations = [];
786
+ for (const observation of observations) {
787
+ returnObservations.push(parseObservation(observation));
788
+ }
789
+ return returnObservations;
790
+ }
791
+ function parseEvents(events) {
792
+ const returnEvents = [];
793
+ for (const event of events) {
794
+ returnEvents.push({
795
+ date: event.dateTime != null ? new Date(event.dateTime) : null,
796
+ label: parseStringContent(event.label),
797
+ agent: event.agent ? {
798
+ uuid: event.agent.uuid,
799
+ content: parseFakeString(event.agent.content)
800
+ } : null
801
+ });
802
+ }
803
+ return returnEvents;
804
+ }
805
+ function parseProperties(properties, language = "eng") {
806
+ const returnProperties = [];
807
+ for (const property of properties) {
808
+ const valuesToParse = "value" in property && property.value ? Array.isArray(property.value) ? property.value : [property.value] : [];
809
+ const values = valuesToParse.map((value) => ({
810
+ content: parseStringContent(value),
811
+ type: value.type,
812
+ category: value.category !== "value" ? value.category ?? null : null,
813
+ uuid: value.uuid ?? null,
814
+ publicationDateTime: value.publicationDateTime != null ? new Date(value.publicationDateTime) : null
815
+ }));
816
+ returnProperties.push({
817
+ label: parseStringContent(property.label, language).replace(/\s*\.{3}$/, "").trim(),
818
+ values,
819
+ comment: property.comment != null ? parseFakeString(property.comment) : null,
820
+ properties: property.property ? parseProperties(
821
+ Array.isArray(property.property) ? property.property : [property.property]
822
+ ) : []
823
+ });
824
+ }
825
+ return returnProperties;
826
+ }
827
+ function parseInterpretations(interpretations) {
828
+ const returnInterpretations = [];
829
+ for (const interpretation of interpretations) {
830
+ returnInterpretations.push({
831
+ date: new Date(interpretation.date),
832
+ number: interpretation.interpretationNo,
833
+ properties: interpretation.properties ? parseProperties(
834
+ Array.isArray(interpretation.properties.property) ? interpretation.properties.property : [interpretation.properties.property]
835
+ ) : []
836
+ });
837
+ }
838
+ return returnInterpretations;
839
+ }
840
+ function parseImageMap(imageMap) {
841
+ const returnImageMap = {
842
+ area: [],
843
+ width: imageMap.width,
844
+ height: imageMap.height
845
+ };
846
+ const imageMapAreasToParse = Array.isArray(imageMap.area) ? imageMap.area : [imageMap.area];
847
+ for (const area of imageMapAreasToParse) {
848
+ returnImageMap.area.push({
849
+ uuid: area.uuid,
850
+ publicationDateTime: area.publicationDateTime != null ? new Date(area.publicationDateTime) : null,
851
+ type: area.type,
852
+ title: parseFakeString(area.title),
853
+ shape: area.shape === "rect" ? "rectangle" : "polygon",
854
+ coords: area.coords.split(",").map((coord) => Number.parseInt(coord))
855
+ });
856
+ }
857
+ return returnImageMap;
858
+ }
859
+ function parsePeriod(period) {
860
+ return {
861
+ uuid: period.uuid,
862
+ publicationDateTime: period.publicationDateTime != null ? new Date(period.publicationDateTime) : null,
863
+ type: period.type ?? null,
864
+ number: period.n ?? null,
865
+ identification: parseIdentification(period.identification),
866
+ description: period.description ? parseStringContent(period.description) : null
867
+ };
868
+ }
869
+ function parsePeriods(periods) {
870
+ const returnPeriods = [];
871
+ for (const period of periods) {
872
+ returnPeriods.push(parsePeriod(period));
873
+ }
874
+ return returnPeriods;
875
+ }
876
+ function parseBibliography(bibliography) {
877
+ let resource = null;
878
+ if (bibliography.source?.resource) {
879
+ resource = {
880
+ uuid: bibliography.source.resource.uuid,
881
+ publicationDateTime: bibliography.source.resource.publicationDateTime ? new Date(bibliography.source.resource.publicationDateTime) : null,
882
+ type: bibliography.source.resource.type,
883
+ identification: parseIdentification(
884
+ bibliography.source.resource.identification
885
+ )
886
+ };
887
+ }
888
+ return {
889
+ uuid: bibliography.uuid,
890
+ publicationDateTime: bibliography.publicationDateTime != null ? new Date(bibliography.publicationDateTime) : null,
891
+ type: bibliography.type ?? null,
892
+ number: bibliography.n ?? null,
893
+ identification: bibliography.identification ? parseIdentification(bibliography.identification) : null,
894
+ projectIdentification: bibliography.project?.identification ? parseIdentification(bibliography.project.identification) : null,
895
+ context: bibliography.context ? parseContext(bibliography.context) : null,
896
+ citation: {
897
+ format: bibliography.citationFormat ?? null,
898
+ short: bibliography.citationFormatSpan ? parseFakeString(
899
+ "default:span" in bibliography.citationFormatSpan ? bibliography.citationFormatSpan["default:span"].content : bibliography.citationFormatSpan.span.content
900
+ ) : null,
901
+ long: bibliography.referenceFormatDiv ? parseFakeString(
902
+ "default:div" in bibliography.referenceFormatDiv ? bibliography.referenceFormatDiv["default:div"]["default:div"].content : bibliography.referenceFormatDiv.div.div.content
903
+ ) : null
904
+ },
905
+ publicationInfo: {
906
+ publishers: bibliography.publicationInfo?.publishers ? parsePersons(
907
+ Array.isArray(
908
+ bibliography.publicationInfo.publishers.publishers.person
909
+ ) ? bibliography.publicationInfo.publishers.publishers.person : [bibliography.publicationInfo.publishers.publishers.person]
910
+ ) : [],
911
+ startDate: bibliography.publicationInfo?.startDate ? new Date(
912
+ bibliography.publicationInfo.startDate.year,
913
+ bibliography.publicationInfo.startDate.month,
914
+ bibliography.publicationInfo.startDate.day
915
+ ) : null
916
+ },
917
+ entryInfo: bibliography.entryInfo ? {
918
+ startIssue: parseFakeString(bibliography.entryInfo.startIssue),
919
+ startVolume: parseFakeString(bibliography.entryInfo.startVolume)
920
+ } : null,
921
+ source: {
922
+ resource,
923
+ documentUrl: bibliography.sourceDocument ? `https://ochre.lib.uchicago.edu/ochre?uuid=${bibliography.sourceDocument.uuid}&load` : null
924
+ },
925
+ authors: bibliography.authors ? parsePersons(
926
+ Array.isArray(bibliography.authors.person) ? bibliography.authors.person : [bibliography.authors.person]
927
+ ) : [],
928
+ properties: bibliography.properties ? parseProperties(
929
+ Array.isArray(bibliography.properties.property) ? bibliography.properties.property : [bibliography.properties.property]
930
+ ) : []
931
+ };
932
+ }
933
+ function parseBibliographies(bibliographies) {
934
+ const returnBibliographies = [];
935
+ for (const bibliography of bibliographies) {
936
+ returnBibliographies.push(parseBibliography(bibliography));
937
+ }
938
+ return returnBibliographies;
939
+ }
940
+ function parseTree(tree) {
941
+ let creators = [];
942
+ if (tree.creators) {
943
+ creators = parsePersons(
944
+ Array.isArray(tree.creators.creator) ? tree.creators.creator : [tree.creators.creator]
945
+ );
946
+ }
947
+ let date = null;
948
+ if (tree.date != null) {
949
+ date = new Date(tree.date);
950
+ }
951
+ let resources = [];
952
+ let spatialUnits = [];
953
+ let concepts = [];
954
+ let periods = [];
955
+ if (typeof tree.items !== "string" && "resource" in tree.items) {
956
+ resources = parseResources(
957
+ Array.isArray(tree.items.resource) ? tree.items.resource : [tree.items.resource]
958
+ );
959
+ }
960
+ if (typeof tree.items !== "string" && "spatialUnit" in tree.items) {
961
+ spatialUnits = parseSpatialUnits(
962
+ Array.isArray(tree.items.spatialUnit) ? tree.items.spatialUnit : [tree.items.spatialUnit]
963
+ );
964
+ }
965
+ if (typeof tree.items !== "string" && "concept" in tree.items) {
966
+ concepts = parseConcepts(
967
+ Array.isArray(tree.items.concept) ? tree.items.concept : [tree.items.concept]
968
+ );
969
+ }
970
+ if (typeof tree.items !== "string" && "period" in tree.items) {
971
+ periods = parsePeriods(
972
+ Array.isArray(tree.items.period) ? tree.items.period : [tree.items.period]
973
+ );
974
+ }
975
+ const returnTree = {
976
+ uuid: tree.uuid,
977
+ category: "tree",
978
+ publicationDateTime: new Date(tree.publicationDateTime),
979
+ identification: parseIdentification(tree.identification),
980
+ creators,
981
+ license: parseLicense(tree.availability),
982
+ date,
983
+ type: tree.type,
984
+ number: tree.n,
985
+ items: {
986
+ resources,
987
+ spatialUnits,
988
+ concepts,
989
+ periods
990
+ },
991
+ properties: tree.properties ? parseProperties(
992
+ Array.isArray(tree.properties.property) ? tree.properties.property : [tree.properties.property]
993
+ ) : []
994
+ };
995
+ return returnTree;
996
+ }
997
+ function parseSet(set) {
998
+ let resources = [];
999
+ let spatialUnits = [];
1000
+ let concepts = [];
1001
+ let periods = [];
1002
+ if (typeof set.items !== "string" && "resource" in set.items) {
1003
+ resources = parseResources(
1004
+ Array.isArray(set.items.resource) ? set.items.resource : [set.items.resource],
1005
+ true
1006
+ );
1007
+ }
1008
+ if (typeof set.items !== "string" && "spatialUnit" in set.items) {
1009
+ spatialUnits = parseSpatialUnits(
1010
+ Array.isArray(set.items.spatialUnit) ? set.items.spatialUnit : [set.items.spatialUnit],
1011
+ true
1012
+ );
1013
+ }
1014
+ if (typeof set.items !== "string" && "concept" in set.items) {
1015
+ concepts = parseConcepts(
1016
+ Array.isArray(set.items.concept) ? set.items.concept : [set.items.concept],
1017
+ true
1018
+ );
1019
+ }
1020
+ if (typeof set.items !== "string" && "period" in set.items) {
1021
+ periods = parsePeriods(
1022
+ Array.isArray(set.items.period) ? set.items.period : [set.items.period]
1023
+ );
1024
+ }
1025
+ return {
1026
+ uuid: set.uuid,
1027
+ category: "set",
1028
+ publicationDateTime: set.publicationDateTime ? new Date(set.publicationDateTime) : null,
1029
+ date: set.date != null ? new Date(set.date) : null,
1030
+ license: parseLicense(set.availability),
1031
+ identification: parseIdentification(set.identification),
1032
+ isSuppressingBlanks: set.suppressBlanks ?? false,
1033
+ description: set.description ? parseStringContent(set.description) : "",
1034
+ creators: set.creators ? parsePersons(
1035
+ Array.isArray(set.creators.creator) ? set.creators.creator : [set.creators.creator]
1036
+ ) : [],
1037
+ type: set.type,
1038
+ number: set.n,
1039
+ items: {
1040
+ resources,
1041
+ spatialUnits,
1042
+ concepts,
1043
+ periods
1044
+ }
1045
+ };
1046
+ }
1047
+ function parseResource(resource, isNested = false) {
1048
+ const returnResource = {
1049
+ uuid: resource.uuid,
1050
+ category: "resource",
1051
+ publicationDateTime: resource.publicationDateTime ? new Date(resource.publicationDateTime) : null,
1052
+ type: resource.type,
1053
+ number: resource.n,
1054
+ format: resource.format ?? null,
1055
+ context: "context" in resource && resource.context ? parseContext(resource.context) : null,
1056
+ license: "availability" in resource && resource.availability ? parseLicense(resource.availability) : null,
1057
+ copyright: "copyright" in resource && resource.copyright != null ? parseFakeString(resource.copyright) : null,
1058
+ identification: parseIdentification(resource.identification),
1059
+ date: resource.date != null ? new Date(resource.date) : null,
1060
+ image: resource.image ? parseImage(resource.image) : null,
1061
+ creators: resource.creators ? parsePersons(
1062
+ Array.isArray(resource.creators.creator) ? resource.creators.creator : [resource.creators.creator]
1063
+ ) : [],
1064
+ notes: (
1065
+ // TODO: Remove this check once the { rend: "splitNotes" } issue is fixed
1066
+ resource.notes && "note" in resource.notes ? parseNotes(
1067
+ Array.isArray(resource.notes.note) ? resource.notes.note : [resource.notes.note]
1068
+ ) : []
1069
+ ),
1070
+ description: resource.description ? parseStringContent(resource.description) : "",
1071
+ document: resource.document ? parseDocument(resource.document.content) : null,
1072
+ href: resource.href ?? null,
1073
+ imageMap: resource.imagemap ? parseImageMap(resource.imagemap) : null,
1074
+ periods: resource.periods ? parsePeriods(
1075
+ Array.isArray(resource.periods.period) ? resource.periods.period : [resource.periods.period]
1076
+ ) : [],
1077
+ links: resource.links ? parseLinks(
1078
+ Array.isArray(resource.links) ? resource.links : [resource.links]
1079
+ ) : [],
1080
+ reverseLinks: resource.reverseLinks ? parseLinks(
1081
+ Array.isArray(resource.reverseLinks) ? resource.reverseLinks : [resource.reverseLinks]
1082
+ ) : [],
1083
+ properties: resource.properties ? parseProperties(
1084
+ Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
1085
+ ) : [],
1086
+ citedBibliographies: resource.citedBibliography ? parseBibliographies(
1087
+ Array.isArray(resource.citedBibliography.reference) ? resource.citedBibliography.reference : [resource.citedBibliography.reference]
1088
+ ) : [],
1089
+ resources: resource.resource ? parseResources(
1090
+ Array.isArray(resource.resource) ? resource.resource : [resource.resource],
1091
+ true
1092
+ ) : []
1093
+ };
1094
+ if (isNested) {
1095
+ const returnNestedResource = {
1096
+ ...returnResource,
1097
+ publicationDateTime: null,
1098
+ context: null,
1099
+ license: null,
1100
+ copyright: null
1101
+ };
1102
+ delete returnNestedResource.publicationDateTime;
1103
+ delete returnNestedResource.license;
1104
+ delete returnNestedResource.copyright;
1105
+ return returnNestedResource;
1106
+ }
1107
+ return returnResource;
1108
+ }
1109
+ function parseResources(resources, isNested = false) {
1110
+ const returnResources = [];
1111
+ const resourcesToParse = Array.isArray(resources) ? resources : [resources];
1112
+ for (const resource of resourcesToParse) {
1113
+ returnResources.push(parseResource(resource, isNested));
1114
+ }
1115
+ return returnResources;
1116
+ }
1117
+ function parseSpatialUnit(spatialUnit, isNested = false) {
1118
+ const returnSpatialUnit = {
1119
+ uuid: spatialUnit.uuid,
1120
+ category: "spatialUnit",
1121
+ publicationDateTime: spatialUnit.publicationDateTime != null ? new Date(spatialUnit.publicationDateTime) : null,
1122
+ type: spatialUnit.type,
1123
+ number: spatialUnit.n,
1124
+ context: "context" in spatialUnit && spatialUnit.context ? parseContext(spatialUnit.context) : null,
1125
+ license: "availability" in spatialUnit && spatialUnit.availability ? parseLicense(spatialUnit.availability) : null,
1126
+ identification: parseIdentification(spatialUnit.identification),
1127
+ image: spatialUnit.image ? parseImage(spatialUnit.image) : null,
1128
+ description: spatialUnit.description ? parseStringContent(spatialUnit.description) : "",
1129
+ coordinates: spatialUnit.coordinates ? parseCoordinates(spatialUnit.coordinates) : null,
1130
+ observations: "observations" in spatialUnit && spatialUnit.observations ? parseObservations(
1131
+ Array.isArray(spatialUnit.observations.observation) ? spatialUnit.observations.observation : [spatialUnit.observations.observation]
1132
+ ) : spatialUnit.observation ? [parseObservation(spatialUnit.observation)] : [],
1133
+ events: "events" in spatialUnit && spatialUnit.events ? parseEvents(
1134
+ Array.isArray(spatialUnit.events.event) ? spatialUnit.events.event : [spatialUnit.events.event]
1135
+ ) : []
1136
+ };
1137
+ if (isNested) {
1138
+ const returnNestedSpatialUnit = {
1139
+ ...returnSpatialUnit,
1140
+ publicationDateTime: null,
1141
+ license: null,
1142
+ properties: "properties" in spatialUnit && spatialUnit.properties ? parseProperties(
1143
+ Array.isArray(spatialUnit.properties.property) ? spatialUnit.properties.property : [spatialUnit.properties.property]
1144
+ ) : []
1145
+ };
1146
+ delete returnNestedSpatialUnit.publicationDateTime;
1147
+ delete returnNestedSpatialUnit.license;
1148
+ return returnNestedSpatialUnit;
1149
+ }
1150
+ return returnSpatialUnit;
1151
+ }
1152
+ function parseSpatialUnits(spatialUnits, isNested = false) {
1153
+ const returnSpatialUnits = [];
1154
+ const spatialUnitsToParse = Array.isArray(spatialUnits) ? spatialUnits : [spatialUnits];
1155
+ for (const spatialUnit of spatialUnitsToParse) {
1156
+ returnSpatialUnits.push(
1157
+ parseSpatialUnit(spatialUnit, isNested)
1158
+ );
1159
+ }
1160
+ return returnSpatialUnits;
1161
+ }
1162
+ function parseConcept(concept, isNested = false) {
1163
+ const returnConcept = {
1164
+ uuid: concept.uuid,
1165
+ category: "concept",
1166
+ publicationDateTime: concept.publicationDateTime ? new Date(concept.publicationDateTime) : null,
1167
+ number: concept.n,
1168
+ license: "availability" in concept && concept.availability ? parseLicense(concept.availability) : null,
1169
+ context: "context" in concept && concept.context ? parseContext(concept.context) : null,
1170
+ identification: parseIdentification(concept.identification),
1171
+ interpretations: parseInterpretations(
1172
+ Array.isArray(concept.interpretations.interpretation) ? concept.interpretations.interpretation : [concept.interpretations.interpretation]
1173
+ )
1174
+ };
1175
+ if (isNested) {
1176
+ const returnNestedConcept = {
1177
+ ...returnConcept,
1178
+ publicationDateTime: null,
1179
+ context: null,
1180
+ license: null
1181
+ };
1182
+ delete returnNestedConcept.publicationDateTime;
1183
+ delete returnNestedConcept.license;
1184
+ return returnNestedConcept;
1185
+ }
1186
+ return returnConcept;
1187
+ }
1188
+ var parseWebpageResources = async (webpageResources, type) => {
1189
+ const returnElements = [];
1190
+ for (const resource of webpageResources) {
1191
+ const resourceProperties = resource.properties ? parseProperties(
1192
+ Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
1193
+ ) : [];
1194
+ const resourceProperty = resourceProperties.find(
1195
+ (property) => property.label === "presentation" && property.values[0].content === type
1196
+ );
1197
+ if (!resourceProperty) continue;
1198
+ if (type === "element") {
1199
+ const element = await parseWebElement(
1200
+ resource,
1201
+ resourceProperty.properties
1202
+ );
1203
+ returnElements.push(
1204
+ element
1205
+ );
1206
+ } else {
1207
+ const webpage = await parseWebpage(resource);
1208
+ if (webpage) {
1209
+ returnElements.push(
1210
+ webpage
1211
+ );
1212
+ }
1213
+ }
1214
+ }
1215
+ return returnElements;
1216
+ };
1217
+ function parseConcepts(concepts, isNested = false) {
1218
+ const returnConcepts = [];
1219
+ const conceptsToParse = Array.isArray(concepts) ? concepts : [concepts];
1220
+ for (const concept of conceptsToParse) {
1221
+ returnConcepts.push(parseConcept(concept, isNested));
1222
+ }
1223
+ return returnConcepts;
1224
+ }
1225
+ async function parseWebElementProperties(componentProperty, elementResource) {
1226
+ const componentName = componentSchema.parse(
1227
+ componentProperty.values[0].content
1228
+ );
1229
+ const properties = {
1230
+ component: componentName
1231
+ };
1232
+ const links = elementResource.links ? parseLinks(
1233
+ Array.isArray(elementResource.links) ? elementResource.links : [elementResource.links]
1234
+ ) : [];
1235
+ const imageLink = links.find((link) => link.type === "image");
1236
+ let document = elementResource.document ? parseDocument(elementResource.document.content) : null;
1237
+ if (document === null) {
1238
+ const documentLink = links.find((link) => link.type === "internalDocument");
1239
+ if (documentLink) {
1240
+ const documentResource = await fetchResource(documentLink.uuid);
1241
+ if (documentResource === null) {
1242
+ throw new Error("Failed to fetch OCHRE data");
1243
+ }
1244
+ document = documentResource.resource.document;
1245
+ }
1246
+ }
1247
+ switch (componentName) {
1248
+ case "annotated-document": {
1249
+ if (!document) {
1250
+ throw new Error(
1251
+ `Document not found for the following component: \u201C${componentName}\u201D`
1252
+ );
1253
+ }
1254
+ properties.document = document;
1255
+ break;
1256
+ }
1257
+ case "annotated-image": {
1258
+ if (!imageLink) {
1259
+ throw new Error(
1260
+ `Image link not found for the following component: \u201C${componentName}\u201D`
1261
+ );
1262
+ }
1263
+ let isSearchable = getPropertyValueByLabel(
1264
+ componentProperty.properties,
1265
+ "is-searchable"
1266
+ ) === "Yes";
1267
+ if (!isSearchable) {
1268
+ isSearchable = false;
1269
+ }
1270
+ properties.imageUuid = imageLink.uuid;
1271
+ properties.isSearchable = isSearchable;
1272
+ break;
1273
+ }
1274
+ case "bibliography": {
1275
+ const bibliographyLink = links.find(
1276
+ (link) => link.category === "bibliography"
1277
+ );
1278
+ if (!bibliographyLink) {
1279
+ throw new Error(
1280
+ `Bibliography link not found for the following component: \u201C${componentName}\u201D`
1281
+ );
1282
+ }
1283
+ if (!bibliographyLink.bibliographies) {
1284
+ throw new Error(
1285
+ `Bibliography not found for the following component: \u201C${componentName}\u201D`
1286
+ );
1287
+ }
1288
+ let layout = getPropertyValueByLabel(
1289
+ componentProperty.properties,
1290
+ "layout"
1291
+ );
1292
+ if (layout === null) {
1293
+ layout = "long";
1294
+ }
1295
+ properties.bibliographies = bibliographyLink.bibliographies;
1296
+ properties.layout = layout;
1297
+ break;
1298
+ }
1299
+ case "blog": {
1300
+ const blogLink = links.find((link) => link.category === "tree");
1301
+ if (!blogLink) {
1302
+ throw new Error(
1303
+ `Blog link not found for the following component: \u201C${componentName}\u201D`
1304
+ );
1305
+ }
1306
+ properties.blogId = blogLink.uuid;
1307
+ break;
1308
+ }
1309
+ case "button": {
1310
+ let isExternal = false;
1311
+ let href = getPropertyValueByLabel(
1312
+ componentProperty.properties,
1313
+ "navigate-to"
1314
+ );
1315
+ if (href === null) {
1316
+ href = getPropertyValueByLabel(componentProperty.properties, "link-to");
1317
+ if (href === null) {
1318
+ throw new Error(
1319
+ `Properties \u201Cnavigate-to\u201D or \u201Clink-to\u201D not found for the following component: \u201C${componentName}\u201D`
1320
+ );
1321
+ } else {
1322
+ isExternal = true;
1323
+ }
1324
+ }
1325
+ properties.href = href;
1326
+ properties.isExternal = isExternal;
1327
+ properties.label = parseStringContent(
1328
+ elementResource.identification.label
1329
+ );
1330
+ break;
1331
+ }
1332
+ case "collection": {
1333
+ let variant = getPropertyValueByLabel(
1334
+ componentProperty.properties,
1335
+ "variant"
1336
+ );
1337
+ if (variant === null) {
1338
+ variant = "full";
1339
+ }
1340
+ let layout = getPropertyValueByLabel(
1341
+ componentProperty.properties,
1342
+ "layout"
1343
+ );
1344
+ if (layout === null) {
1345
+ layout = "image-start";
1346
+ }
1347
+ const collectionLink = links.find((link) => link.category === "set");
1348
+ if (!collectionLink) {
1349
+ throw new Error(
1350
+ `Collection link not found for the following component: \u201C${componentName}\u201D`
1351
+ );
1352
+ }
1353
+ properties.variant = variant;
1354
+ properties.layout = layout;
1355
+ properties.collectionId = collectionLink.uuid;
1356
+ break;
1357
+ }
1358
+ case "iiif-viewer": {
1359
+ const manifestLink = links.find((link) => link.type === "IIIF");
1360
+ if (!manifestLink) {
1361
+ throw new Error(
1362
+ `Manifest link not found for the following component: \u201C${componentName}\u201D`
1363
+ );
1364
+ }
1365
+ properties.IIIFId = manifestLink.uuid;
1366
+ break;
1367
+ }
1368
+ case "image": {
1369
+ if (!imageLink) {
1370
+ throw new Error(
1371
+ `Image link not found for the following component: \u201C${componentName}\u201D`
1372
+ );
1373
+ }
1374
+ properties.image = {
1375
+ url: `https://ochre.lib.uchicago.edu/ochre?uuid=${imageLink.uuid}&load`,
1376
+ label: imageLink.identification?.label ?? null,
1377
+ width: imageLink.image?.width ?? 0,
1378
+ height: imageLink.image?.height ?? 0
1379
+ };
1380
+ break;
1381
+ }
1382
+ case "image-gallery": {
1383
+ const galleryLink = links.find((link) => link.category === "tree");
1384
+ if (!galleryLink) {
1385
+ throw new Error(
1386
+ `Image gallery link not found for the following component: \u201C${componentName}\u201D`
1387
+ );
1388
+ }
1389
+ properties.galleryId = galleryLink.uuid;
1390
+ break;
1391
+ }
1392
+ case "interactive-chapter-table": {
1393
+ break;
1394
+ }
1395
+ case "item-gallery": {
1396
+ const galleryLink = links.find((link) => link.category === "tree");
1397
+ if (!galleryLink) {
1398
+ throw new Error(
1399
+ `Item gallery link not found for the following component: \u201C${componentName}\u201D`
1400
+ );
1401
+ }
1402
+ properties.galleryId = galleryLink.uuid;
1403
+ break;
1404
+ }
1405
+ case "menu": {
1406
+ break;
1407
+ }
1408
+ case "menu-item": {
1409
+ break;
1410
+ }
1411
+ case "n-columns": {
1412
+ const subElements = elementResource.resource ? await parseWebpageResources(
1413
+ Array.isArray(elementResource.resource) ? elementResource.resource : [elementResource.resource],
1414
+ "element"
1415
+ ) : [];
1416
+ properties.columns = subElements;
1417
+ break;
1418
+ }
1419
+ case "n-rows": {
1420
+ const subElements = elementResource.resource ? await parseWebpageResources(
1421
+ Array.isArray(elementResource.resource) ? elementResource.resource : [elementResource.resource],
1422
+ "element"
1423
+ ) : [];
1424
+ properties.rows = subElements;
1425
+ break;
1426
+ }
1427
+ case "network-graph": {
1428
+ break;
1429
+ }
1430
+ case "table": {
1431
+ const tableLink = links.find((link) => link.category === "set");
1432
+ if (!tableLink) {
1433
+ throw new Error(
1434
+ `Table link not found for the following component: \u201C${componentName}\u201D`
1435
+ );
1436
+ }
1437
+ properties.tableId = tableLink.uuid;
1438
+ break;
1439
+ }
1440
+ case "text": {
1441
+ if (!document) {
1442
+ throw new Error(
1443
+ `Document not found for the following component: \u201C${componentName}\u201D`
1444
+ );
1445
+ }
1446
+ let variant = getPropertyValueByLabel(
1447
+ componentProperty.properties,
1448
+ "variant"
1449
+ );
1450
+ if (variant === null) {
1451
+ variant = "block";
1452
+ }
1453
+ properties.variant = variant;
1454
+ properties.content = document.content;
1455
+ break;
1456
+ }
1457
+ case "text-image": {
1458
+ if (!document) {
1459
+ throw new Error(
1460
+ `Document not found for the following component: \u201C${componentName}\u201D`
1461
+ );
1462
+ }
1463
+ let variant = getPropertyValueByLabel(
1464
+ componentProperty.properties,
1465
+ "variant"
1466
+ );
1467
+ if (variant === null) {
1468
+ variant = "block";
1469
+ }
1470
+ let layout = getPropertyValueByLabel(
1471
+ componentProperty.properties,
1472
+ "layout"
1473
+ );
1474
+ if (layout === null) {
1475
+ layout = "image-start";
1476
+ }
1477
+ let captionLayout = getPropertyValueByLabel(
1478
+ componentProperty.properties,
1479
+ "caption-layout"
1480
+ );
1481
+ if (captionLayout === null) {
1482
+ captionLayout = "bottom";
1483
+ }
1484
+ const imageLink2 = links.find(
1485
+ (link) => link.type === "image" || link.type === "IIIF"
1486
+ );
1487
+ if (!imageLink2) {
1488
+ throw new Error(
1489
+ `Image link not found for the following component: \u201C${componentName}\u201D: ${JSON.stringify(
1490
+ links
1491
+ )}`
1492
+ );
1493
+ }
1494
+ properties.variant = variant;
1495
+ properties.layout = layout;
1496
+ properties.captionLayout = captionLayout;
1497
+ properties.content = document.content;
1498
+ properties.image = {
1499
+ url: `https://ochre.lib.uchicago.edu/ochre?uuid=${imageLink2.uuid}&preview`,
1500
+ label: imageLink2.identification?.label ?? null,
1501
+ width: imageLink2.image?.width ?? 0,
1502
+ height: imageLink2.image?.height ?? 0
1503
+ };
1504
+ properties.imageOpacity = null;
1505
+ break;
1506
+ }
1507
+ default: {
1508
+ console.warn(
1509
+ `Invalid or non-implemented component name \u201C${componentName}\u201D for the following element: \u201C${parseStringContent(
1510
+ elementResource.identification.label
1511
+ )}\u201D`
1512
+ );
1513
+ }
1514
+ }
1515
+ return properties;
1516
+ }
1517
+ async function parseWebElement(elementResource, elementProperties) {
1518
+ const identification = parseIdentification(elementResource.identification);
1519
+ const componentProperty = elementProperties.find(
1520
+ (property) => property.label === "component"
1521
+ );
1522
+ if (!componentProperty) {
1523
+ throw new Error(
1524
+ `Component for element \u201C${identification.label}\u201D not found`
1525
+ );
1526
+ }
1527
+ const properties = await parseWebElementProperties(
1528
+ componentProperty,
1529
+ elementResource
1530
+ );
1531
+ const elementResourceProperties = elementResource.properties?.property ? parseProperties(
1532
+ Array.isArray(elementResource.properties.property) ? elementResource.properties.property : [elementResource.properties.property]
1533
+ ) : [];
1534
+ const cssProperties = elementResourceProperties.find(
1535
+ (property) => property.label === "presentation" && property.values[0].content === "css"
1536
+ )?.properties ?? [];
1537
+ const cssStyles = [];
1538
+ for (const property of cssProperties) {
1539
+ const cssStyle = property.values[0].content;
1540
+ cssStyles.push({ label: property.label, value: cssStyle });
1541
+ }
1542
+ return {
1543
+ uuid: elementResource.uuid,
1544
+ title: identification.label,
1545
+ cssStyles,
1546
+ ...properties
1547
+ };
1548
+ }
1549
+ async function parseWebpage(webpageResource) {
1550
+ const webpageProperties = webpageResource.properties ? parseProperties(
1551
+ Array.isArray(webpageResource.properties.property) ? webpageResource.properties.property : [webpageResource.properties.property]
1552
+ ) : [];
1553
+ if (webpageProperties.length === 0 || webpageProperties.find((property) => property.label === "presentation")?.values[0]?.content !== "page") {
1554
+ return null;
1555
+ }
1556
+ const identification = parseIdentification(webpageResource.identification);
1557
+ const slug = webpageResource.slug === "/" ? "" : webpageResource.slug;
1558
+ if (slug === void 0) {
1559
+ throw new Error(`Slug not found for page \u201C${identification.label}\u201D`);
1560
+ }
1561
+ const links = webpageResource.links ? parseLinks(
1562
+ Array.isArray(webpageResource.links) ? webpageResource.links : [webpageResource.links]
1563
+ ) : [];
1564
+ const imageLink = links.find(
1565
+ (link) => link.type === "image" || link.type === "IIIF"
1566
+ );
1567
+ const elements = webpageResource.resource ? await parseWebpageResources(
1568
+ Array.isArray(webpageResource.resource) ? webpageResource.resource : [webpageResource.resource],
1569
+ "element"
1570
+ ) : [];
1571
+ const webpages = webpageResource.resource ? await parseWebpageResources(
1572
+ Array.isArray(webpageResource.resource) ? webpageResource.resource : [webpageResource.resource],
1573
+ "page"
1574
+ ) : [];
1575
+ let displayedInHeader = true;
1576
+ let width = "default";
1577
+ let variant = "default";
1578
+ const webpageSubProperties = webpageProperties.find(
1579
+ (property) => property.label === "presentation" && property.values[0]?.content === "page"
1580
+ )?.properties;
1581
+ if (webpageSubProperties) {
1582
+ const headerProperty = webpageSubProperties.find(
1583
+ (property) => property.label === "header"
1584
+ )?.values[0];
1585
+ if (headerProperty) {
1586
+ displayedInHeader = headerProperty.content === "Yes";
1587
+ }
1588
+ const widthProperty = webpageSubProperties.find(
1589
+ (property) => property.label === "width"
1590
+ )?.values[0];
1591
+ if (widthProperty) {
1592
+ width = widthProperty.content;
1593
+ }
1594
+ const variantProperty = webpageSubProperties.find(
1595
+ (property) => property.label === "variant"
1596
+ )?.values[0];
1597
+ if (variantProperty) {
1598
+ variant = variantProperty.content;
1599
+ }
1600
+ }
1601
+ const cssStyleSubProperties = webpageProperties.find(
1602
+ (property) => property.label === "presentation" && property.values[0]?.content === "css"
1603
+ )?.properties;
1604
+ const cssStyles = [];
1605
+ if (cssStyleSubProperties) {
1606
+ for (const property of cssStyleSubProperties) {
1607
+ cssStyles.push({
1608
+ label: property.label,
1609
+ value: property.values[0].content
1610
+ });
1611
+ }
1612
+ }
1613
+ return {
1614
+ title: identification.label,
1615
+ slug,
1616
+ elements,
1617
+ properties: {
1618
+ displayedInHeader,
1619
+ width,
1620
+ variant,
1621
+ backgroundImageUrl: imageLink ? `https://ochre.lib.uchicago.edu/ochre?uuid=${imageLink.uuid}&preview` : null,
1622
+ cssStyles
1623
+ },
1624
+ webpages
1625
+ };
1626
+ }
1627
+ async function parseWebpages(webpageResources) {
1628
+ const returnPages = [];
1629
+ const pagesToParse = Array.isArray(webpageResources) ? webpageResources : [webpageResources];
1630
+ for (const page of pagesToParse) {
1631
+ const webpage = await parseWebpage(page);
1632
+ if (webpage) {
1633
+ returnPages.push(webpage);
1634
+ }
1635
+ }
1636
+ return returnPages;
1637
+ }
1638
+ function parseWebsiteProperties(properties) {
1639
+ const mainProperties = parseProperties(properties);
1640
+ const websiteProperties = mainProperties.find(
1641
+ (property) => property.label === "presentation"
1642
+ )?.properties;
1643
+ if (!websiteProperties) {
1644
+ throw new Error("Presentation property not found");
1645
+ }
1646
+ const type = websiteProperties.find((property) => property.label === "webUI")?.values[0]?.content;
1647
+ if (type == null) {
1648
+ throw new Error("Website type not found");
1649
+ }
1650
+ const status = websiteProperties.find(
1651
+ (property) => property.label === "status"
1652
+ )?.values[0]?.content;
1653
+ if (status == null) {
1654
+ throw new Error("Website status not found");
1655
+ }
1656
+ let privacy = websiteProperties.find(
1657
+ (property) => property.label === "privacy"
1658
+ )?.values[0]?.content;
1659
+ if (privacy == null) {
1660
+ privacy = "public";
1661
+ }
1662
+ const result = websiteSchema.safeParse({
1663
+ type,
1664
+ status,
1665
+ privacy
1666
+ });
1667
+ if (!result.success) {
1668
+ throw new Error(`Invalid website properties: ${result.error.message}`);
1669
+ }
1670
+ const logoUuid = websiteProperties.find((property) => property.label === "logo")?.values[0]?.uuid ?? null;
1671
+ let isHeaderDisplayed = true;
1672
+ let headerVariant = "default";
1673
+ let isFooterDisplayed = true;
1674
+ let isSidebarDisplayed = false;
1675
+ let searchCollectionUuid = null;
1676
+ const headerProperty = websiteProperties.find(
1677
+ (property) => property.label === "navbar-visible"
1678
+ )?.values[0];
1679
+ if (headerProperty) {
1680
+ isHeaderDisplayed = headerProperty.content === "Yes";
1681
+ }
1682
+ const headerVariantProperty = websiteProperties.find(
1683
+ (property) => property.label === "navbar-variant"
1684
+ )?.values[0];
1685
+ if (headerVariantProperty) {
1686
+ headerVariant = headerVariantProperty.content;
1687
+ }
1688
+ const footerProperty = websiteProperties.find(
1689
+ (property) => property.label === "footer-visible"
1690
+ )?.values[0];
1691
+ if (footerProperty) {
1692
+ isFooterDisplayed = footerProperty.content === "Yes";
1693
+ }
1694
+ const sidebarProperty = websiteProperties.find(
1695
+ (property) => property.label === "sidebar-visible"
1696
+ )?.values[0];
1697
+ if (sidebarProperty) {
1698
+ isSidebarDisplayed = sidebarProperty.content === "Yes";
1699
+ }
1700
+ const collectionSearchProperty = websiteProperties.find(
1701
+ (property) => property.label === "search-collection"
1702
+ )?.values[0];
1703
+ if (collectionSearchProperty) {
1704
+ searchCollectionUuid = collectionSearchProperty.uuid;
1705
+ }
1706
+ const {
1707
+ type: validatedType,
1708
+ status: validatedStatus,
1709
+ privacy: validatedPrivacy
1710
+ } = result.data;
1711
+ return {
1712
+ type: validatedType,
1713
+ privacy: validatedPrivacy,
1714
+ status: validatedStatus,
1715
+ isHeaderDisplayed,
1716
+ headerVariant,
1717
+ isFooterDisplayed,
1718
+ isSidebarDisplayed,
1719
+ searchCollectionUuid,
1720
+ logoUrl: logoUuid !== null ? `https://ochre.lib.uchicago.edu/ochre?uuid=${logoUuid}&load` : null
1721
+ };
1722
+ }
1723
+ async function parseWebsite(websiteTree, projectName, website) {
1724
+ if (!websiteTree.properties) {
1725
+ throw new Error("Website properties not found");
1726
+ }
1727
+ const properties = parseWebsiteProperties(
1728
+ Array.isArray(websiteTree.properties.property) ? websiteTree.properties.property : [websiteTree.properties.property]
1729
+ );
1730
+ if (typeof websiteTree.items === "string" || !("resource" in websiteTree.items)) {
1731
+ throw new Error("Website pages not found");
1732
+ }
1733
+ const resources = Array.isArray(websiteTree.items.resource) ? websiteTree.items.resource : [websiteTree.items.resource];
1734
+ const pages = await parseWebpages(resources);
1735
+ const sidebarElements = [];
1736
+ const sidebar = resources.find((resource) => {
1737
+ const resourceProperties = resource.properties ? parseProperties(
1738
+ Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
1739
+ ) : [];
1740
+ return resourceProperties.some(
1741
+ (property) => property.label === "presentation" && property.values[0]?.content === "element" && property.properties[0]?.label === "component" && property.properties[0]?.values[0]?.content === "sidebar"
1742
+ );
1743
+ });
1744
+ if (sidebar) {
1745
+ const sidebarResources = sidebar.resource ? Array.isArray(sidebar.resource) ? sidebar.resource : [sidebar.resource] : [];
1746
+ for (const resource of sidebarResources) {
1747
+ const sidebarResourceProperties = resource.properties ? parseProperties(
1748
+ Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property]
1749
+ ) : [];
1750
+ const element = await parseWebElement(
1751
+ resource,
1752
+ sidebarResourceProperties.find(
1753
+ (property) => property.label === "presentation" && property.values[0]?.content === "element"
1754
+ )?.properties ?? []
1755
+ );
1756
+ sidebarElements.push(element);
1757
+ }
1758
+ }
1759
+ return {
1760
+ uuid: websiteTree.uuid,
1761
+ publicationDateTime: websiteTree.publicationDateTime ? new Date(websiteTree.publicationDateTime) : null,
1762
+ identification: parseIdentification(websiteTree.identification),
1763
+ project: {
1764
+ name: parseFakeString(projectName),
1765
+ website: website !== null ? parseFakeString(website) : null
1766
+ },
1767
+ creators: websiteTree.creators ? parsePersons(
1768
+ Array.isArray(websiteTree.creators.creator) ? websiteTree.creators.creator : [websiteTree.creators.creator]
1769
+ ) : [],
1770
+ license: parseLicense(websiteTree.availability),
1771
+ pages,
1772
+ sidebarElements,
1773
+ properties
1774
+ };
1775
+ }
1776
+
1777
+ // src/utils/fetchers/concept.ts
1778
+ async function fetchConcept(uuid) {
1779
+ try {
1780
+ const [error, dataRaw] = await fetchByUuid(uuid);
1781
+ if (error !== null) {
1782
+ throw new Error(error);
1783
+ }
1784
+ if (!("concept" in dataRaw.ochre)) {
1785
+ throw new Error("Invalid OCHRE data: API response missing 'concept' key");
1786
+ }
1787
+ const conceptItem = parseConcept(dataRaw.ochre.concept);
1788
+ const data = {
1789
+ uuid: parseFakeString(dataRaw.ochre.uuid),
1790
+ publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
1791
+ belongsTo: {
1792
+ uuid: dataRaw.ochre.uuidBelongsTo,
1793
+ abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
1794
+ },
1795
+ metadata: parseMetadata(dataRaw.ochre.metadata),
1796
+ item: conceptItem
1797
+ };
1798
+ return { metadata: data.metadata, concept: data.item };
1799
+ } catch (error) {
1800
+ console.error(error);
1801
+ return null;
1802
+ }
1803
+ }
1804
+
1805
+ // src/utils/fetchers/set.ts
1806
+ async function fetchSet(uuid) {
1807
+ try {
1808
+ const [error, dataRaw] = await fetchByUuid(uuid);
1809
+ if (error !== null) {
1810
+ throw new Error(error);
1811
+ }
1812
+ if (!("set" in dataRaw.ochre)) {
1813
+ throw new Error("Invalid OCHRE data: API response missing 'set' key");
1814
+ }
1815
+ const setItem = parseSet(dataRaw.ochre.set);
1816
+ const data = {
1817
+ uuid: parseFakeString(dataRaw.ochre.uuid),
1818
+ publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
1819
+ belongsTo: {
1820
+ uuid: dataRaw.ochre.uuidBelongsTo,
1821
+ abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
1822
+ },
1823
+ metadata: parseMetadata(dataRaw.ochre.metadata),
1824
+ item: setItem
1825
+ };
1826
+ return { metadata: data.metadata, set: data.item };
1827
+ } catch (error) {
1828
+ console.error(error);
1829
+ return null;
1830
+ }
1831
+ }
1832
+
1833
+ // src/utils/fetchers/spatial-unit.ts
1834
+ async function fetchSpatialUnit(uuid) {
1835
+ try {
1836
+ const [error, dataRaw] = await fetchByUuid(uuid);
1837
+ if (error !== null) {
1838
+ throw new Error(error);
1839
+ }
1840
+ if (!("spatialUnit" in dataRaw.ochre)) {
1841
+ throw new Error(
1842
+ "Invalid OCHRE data: API response missing 'spatialUnit' key"
1843
+ );
1844
+ }
1845
+ const spatialUnitItem = parseSpatialUnit(dataRaw.ochre.spatialUnit);
1846
+ const data = {
1847
+ uuid: parseFakeString(dataRaw.ochre.uuid),
1848
+ publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
1849
+ belongsTo: {
1850
+ uuid: dataRaw.ochre.uuidBelongsTo,
1851
+ abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
1852
+ },
1853
+ metadata: parseMetadata(dataRaw.ochre.metadata),
1854
+ item: spatialUnitItem
1855
+ };
1856
+ return { metadata: data.metadata, spatialUnit: data.item };
1857
+ } catch (error) {
1858
+ console.error(error);
1859
+ return null;
1860
+ }
1861
+ }
1862
+
1863
+ // src/utils/fetchers/tree.ts
1864
+ async function fetchTree(uuid) {
1865
+ try {
1866
+ const [error, dataRaw] = await fetchByUuid(uuid);
1867
+ if (error !== null) {
1868
+ throw new Error(error);
1869
+ }
1870
+ if (!("tree" in dataRaw.ochre)) {
1871
+ throw new Error("Invalid OCHRE data: API response missing 'tree' key");
1872
+ }
1873
+ const tree = parseTree(dataRaw.ochre.tree);
1874
+ if (!tree) {
1875
+ throw new Error("Invalid OCHRE data: Could not parse tree");
1876
+ }
1877
+ const data = {
1878
+ uuid: parseFakeString(dataRaw.ochre.uuid),
1879
+ publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
1880
+ belongsTo: {
1881
+ uuid: dataRaw.ochre.uuidBelongsTo,
1882
+ abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
1883
+ },
1884
+ metadata: parseMetadata(dataRaw.ochre.metadata),
1885
+ item: tree
1886
+ };
1887
+ return { metadata: data.metadata, tree: data.item };
1888
+ } catch (error) {
1889
+ console.error(error);
1890
+ return null;
1891
+ }
1892
+ }
1893
+
1894
+ // src/utils/fetchers/website.ts
1895
+ async function fetchWebsite(abbreviation) {
1896
+ try {
1897
+ const response = await fetch(
1898
+ `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`
1899
+ );
1900
+ if (!response.ok) {
1901
+ throw new Error("Failed to fetch website");
1902
+ }
1903
+ const data = await response.json();
1904
+ if (!("ochre" in data.result) || !("tree" in data.result.ochre)) {
1905
+ throw new Error("Failed to fetch website");
1906
+ }
1907
+ const projectIdentification = data.result.ochre.metadata.project?.identification ? parseIdentification(data.result.ochre.metadata.project.identification) : null;
1908
+ const website = await parseWebsite(
1909
+ data.result.ochre.tree,
1910
+ projectIdentification?.label ?? "",
1911
+ data.result.ochre.metadata.project?.identification.website ?? null
1912
+ );
1913
+ return website;
1914
+ } catch (error) {
1915
+ console.error(error);
1916
+ return null;
1917
+ }
1918
+ }
1919
+ export {
1920
+ fetchByUuid,
1921
+ fetchConcept,
1922
+ fetchResource,
1923
+ fetchSet,
1924
+ fetchSpatialUnit,
1925
+ fetchTree,
1926
+ fetchWebsite,
1927
+ filterProperties,
1928
+ getAllPropertyLabels,
1929
+ getPropertyByLabel,
1930
+ getPropertyValueByLabel,
1931
+ getPropertyValuesByLabel,
1932
+ parseBibliographies,
1933
+ parseBibliography,
1934
+ parseConcept,
1935
+ parseConcepts,
1936
+ parseContext,
1937
+ parseCoordinates,
1938
+ parseDocument,
1939
+ parseEmailAndUrl,
1940
+ parseEvents,
1941
+ parseFakeString,
1942
+ parseIdentification,
1943
+ parseImage,
1944
+ parseImageMap,
1945
+ parseInterpretations,
1946
+ parseLanguages,
1947
+ parseLicense,
1948
+ parseLink,
1949
+ parseLinks,
1950
+ parseMetadata,
1951
+ parseNotes,
1952
+ parseObservation,
1953
+ parseObservations,
1954
+ parsePeriod,
1955
+ parsePeriods,
1956
+ parsePersons,
1957
+ parseProperties,
1958
+ parseResource,
1959
+ parseResources,
1960
+ parseSet,
1961
+ parseSpatialUnit,
1962
+ parseSpatialUnits,
1963
+ parseStringContent,
1964
+ parseStringDocumentItem,
1965
+ parseStringItem,
1966
+ parseTree,
1967
+ parseWebsite,
1968
+ trimEndLineBreaks
1969
+ };