@digitalculture/ochre-sdk 0.5.18 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,52 +1,238 @@
1
- // src/utils/parse.ts
2
- import { z as z3 } from "zod";
3
-
4
- // src/utils/fetchers/generic.ts
1
+ // src/schemas.ts
5
2
  import { z } from "zod";
6
3
  var uuidSchema = z.string().uuid({ message: "Invalid UUID provided" });
7
- async function fetchByUuid(uuid) {
8
- try {
9
- const result = uuidSchema.safeParse(uuid);
10
- if (!result.success) {
11
- throw new Error(result.error.issues[0]?.message);
12
- }
13
- const response = await fetch(
14
- `https://ochre.lib.uchicago.edu/ochre?uuid=${uuid}&format=json&lang="*"`
15
- );
16
- if (!response.ok) {
17
- throw new Error("Failed to fetch OCHRE data");
18
- }
19
- const dataRaw = await response.json();
20
- if (!("ochre" in dataRaw)) {
21
- throw new Error("Invalid OCHRE data: API response missing 'ochre' key");
22
- }
23
- return [null, dataRaw];
24
- } catch (error) {
25
- return [error instanceof Error ? error.message : "Unknown error", null];
26
- }
27
- }
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([
4
+ var websiteSchema = z.object({
5
+ type: z.enum(
6
+ [
7
+ "traditional",
8
+ "digital-collection",
9
+ "plum",
10
+ "cedar",
11
+ "elm",
12
+ "maple",
13
+ "oak",
14
+ "palm"
15
+ ],
16
+ { message: "Invalid website type" }
17
+ ),
18
+ status: z.enum(
19
+ ["development", "preview", "production"],
20
+ { message: "Invalid website status" }
21
+ ),
22
+ privacy: z.enum(
23
+ ["public", "password", "private"],
24
+ { message: "Invalid website privacy" }
25
+ )
26
+ });
27
+ var componentSchema = z.enum(
28
+ [
29
+ "annotated-document",
30
+ "annotated-image",
31
+ "bibliography",
32
+ "blog",
33
+ "button",
34
+ "collection",
35
+ "empty-space",
36
+ "filter-categories",
37
+ "iframe",
38
+ "iiif-viewer",
39
+ "image",
40
+ "image-gallery",
41
+ "n-columns",
42
+ "n-rows",
43
+ "network-graph",
44
+ "search-bar",
45
+ "table",
46
+ "text",
47
+ "timeline",
48
+ "video"
49
+ ],
50
+ { message: "Invalid component" }
51
+ );
52
+ var categorySchema = z.enum([
53
+ "resource",
54
+ "spatialUnit",
55
+ "concept",
56
+ "period",
57
+ "bibliography",
58
+ "person",
59
+ "propertyValue",
60
+ "set",
61
+ "tree"
62
+ ]);
63
+ var renderOptionsSchema = z.string().transform((str) => str.split(" ")).pipe(
64
+ z.array(
65
+ z.enum([
34
66
  "bold",
35
67
  "italic",
36
68
  "underline"
37
69
  ])
38
70
  )
39
71
  );
40
- var whitespaceSchema = z2.string().transform((str) => str.split(" ")).pipe(
41
- z2.array(
42
- z2.enum([
72
+ var whitespaceSchema = z.string().transform((str) => str.split(" ")).pipe(
73
+ z.array(
74
+ z.enum([
43
75
  "newline",
44
76
  "trailing",
45
77
  "leading"
46
78
  ])
47
79
  )
48
80
  );
49
- var emailSchema = z2.string().email({ message: "Invalid email" });
81
+ var emailSchema = z.string().email({ message: "Invalid email" });
82
+
83
+ // src/utils/helpers.ts
84
+ function getItemCategory(keys) {
85
+ const categoryFound = keys.find(
86
+ (key) => categorySchema.safeParse(key).success
87
+ );
88
+ if (!categoryFound) {
89
+ const unknownKey = keys.find(
90
+ (key) => ![
91
+ "uuid",
92
+ "uuidBelongsTo",
93
+ "belongsTo",
94
+ "publicationDateTime",
95
+ "metadata",
96
+ "languages"
97
+ ].includes(key)
98
+ );
99
+ throw new Error(`Invalid OCHRE data; found unexpected "${unknownKey}" key`);
100
+ }
101
+ const categoryKey = categorySchema.parse(categoryFound);
102
+ return categoryKey;
103
+ }
104
+
105
+ // src/utils/getters.ts
106
+ var DEFAULT_OPTIONS = {
107
+ searchNestedProperties: false
108
+ };
109
+ function getPropertyByLabel(properties, label, options = DEFAULT_OPTIONS) {
110
+ const { searchNestedProperties } = options;
111
+ const property = properties.find((property2) => property2.label === label);
112
+ if (property) {
113
+ return property;
114
+ }
115
+ if (searchNestedProperties) {
116
+ for (const property2 of properties) {
117
+ if (property2.properties.length > 0) {
118
+ const nestedResult = getPropertyByLabel(property2.properties, label, {
119
+ searchNestedProperties
120
+ });
121
+ if (nestedResult) {
122
+ return nestedResult;
123
+ }
124
+ }
125
+ }
126
+ }
127
+ return null;
128
+ }
129
+ function getPropertyValuesByLabel(properties, label, options = DEFAULT_OPTIONS) {
130
+ const { searchNestedProperties } = options;
131
+ const property = properties.find((property2) => property2.label === label);
132
+ if (property) {
133
+ return property.values.map((value) => value.content);
134
+ }
135
+ if (searchNestedProperties) {
136
+ for (const property2 of properties) {
137
+ if (property2.properties.length > 0) {
138
+ const nestedResult = getPropertyValuesByLabel(
139
+ property2.properties,
140
+ label,
141
+ { searchNestedProperties }
142
+ );
143
+ if (nestedResult) {
144
+ return nestedResult;
145
+ }
146
+ }
147
+ }
148
+ }
149
+ return null;
150
+ }
151
+ function getPropertyValueByLabel(properties, label, options = DEFAULT_OPTIONS) {
152
+ const { searchNestedProperties } = options;
153
+ const values = getPropertyValuesByLabel(properties, label, {
154
+ searchNestedProperties
155
+ });
156
+ if (values !== null && values.length > 0) {
157
+ return values[0];
158
+ }
159
+ if (searchNestedProperties) {
160
+ for (const property of properties) {
161
+ if (property.properties.length > 0) {
162
+ const nestedResult = getPropertyValueByLabel(
163
+ property.properties,
164
+ label,
165
+ { searchNestedProperties }
166
+ );
167
+ if (nestedResult !== null) {
168
+ return nestedResult;
169
+ }
170
+ }
171
+ }
172
+ }
173
+ return null;
174
+ }
175
+ function getAllPropertyLabels(properties, options = DEFAULT_OPTIONS) {
176
+ const { searchNestedProperties } = options;
177
+ const labels = /* @__PURE__ */ new Set();
178
+ for (const property of properties) {
179
+ labels.add(property.label);
180
+ if (property.properties.length > 0 && searchNestedProperties) {
181
+ const nestedLabels = getAllPropertyLabels(property.properties, {
182
+ searchNestedProperties: true
183
+ });
184
+ for (const label of nestedLabels) {
185
+ labels.add(label);
186
+ }
187
+ }
188
+ }
189
+ return [...labels];
190
+ }
191
+ function filterProperties(property, filter, options = DEFAULT_OPTIONS) {
192
+ const { searchNestedProperties } = options;
193
+ const isAllFields = filter.label.toLocaleLowerCase("en-US") === "all fields";
194
+ if (isAllFields || property.label.toLocaleLowerCase("en-US") === filter.label.toLocaleLowerCase("en-US")) {
195
+ let isFound = property.values.some((value) => {
196
+ if (value.content === null) {
197
+ return false;
198
+ }
199
+ if (typeof value.content === "string") {
200
+ if (typeof filter.value !== "string") {
201
+ return false;
202
+ }
203
+ return value.content.toLocaleLowerCase("en-US").includes(filter.value.toLocaleLowerCase("en-US"));
204
+ }
205
+ if (typeof value.content === "number") {
206
+ if (typeof filter.value !== "number") {
207
+ return false;
208
+ }
209
+ return value.content === filter.value;
210
+ }
211
+ if (typeof value.content === "boolean") {
212
+ if (typeof filter.value !== "boolean") {
213
+ return false;
214
+ }
215
+ return value.booleanValue === filter.value;
216
+ }
217
+ if (value.content instanceof Date) {
218
+ if (!(filter.value instanceof Date)) {
219
+ return false;
220
+ }
221
+ return value.content.getTime() === filter.value.getTime();
222
+ }
223
+ return false;
224
+ });
225
+ if (!isFound && searchNestedProperties) {
226
+ isFound = property.properties.some(
227
+ (property2) => filterProperties(property2, filter, { searchNestedProperties: true })
228
+ );
229
+ }
230
+ return isFound;
231
+ }
232
+ return false;
233
+ }
234
+
235
+ // src/utils/string.ts
50
236
  function getStringItemByLanguage(content, language) {
51
237
  const stringItemToFind = content.find((item) => item.lang === language);
52
238
  return stringItemToFind ?? null;
@@ -289,267 +475,42 @@ function parseStringDocumentItem(item, footnotes) {
289
475
  }
290
476
  }
291
477
  return returnString.replaceAll(/^(\d+)\./gm, String.raw`$1\.`);
292
- }
293
- function parseStringContent(content, language = "eng") {
294
- switch (typeof content.content) {
295
- case "string":
296
- case "number":
297
- case "boolean": {
298
- return parseFakeString(content.content);
299
- }
300
- case "object": {
301
- if (Array.isArray(content.content)) {
302
- const stringItem = getStringItemByLanguage(content.content, language);
303
- if (stringItem) {
304
- return parseStringItem(stringItem);
305
- } else {
306
- const returnStringItem = content.content[0];
307
- if (!returnStringItem) {
308
- throw new Error(
309
- `No string item found for language \u201C${language}\u201D in the following content:
310
- ${JSON.stringify(
311
- content.content
312
- )}.`
313
- );
314
- }
315
- return parseStringItem(returnStringItem);
316
- }
317
- } else {
318
- return parseStringItem(content.content);
319
- }
320
- }
321
- default: {
322
- return String(content.content).replaceAll(/^(\d+)\./gm, String.raw`$1\.`);
323
- }
324
- }
325
- }
326
-
327
- // src/utils/fetchers/resource.ts
328
- async function fetchResource(uuid) {
329
- try {
330
- const [error, dataRaw] = await fetchByUuid(uuid);
331
- if (error !== null) {
332
- throw new Error(error);
333
- }
334
- if (!("resource" in dataRaw.ochre)) {
335
- throw new Error(
336
- "Invalid OCHRE data: API response missing 'resource' key"
337
- );
338
- }
339
- const resourceItem = parseResource(dataRaw.ochre.resource);
340
- const data = {
341
- uuid: parseFakeString(dataRaw.ochre.uuid),
342
- publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
343
- belongsTo: {
344
- uuid: dataRaw.ochre.uuidBelongsTo,
345
- abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
346
- },
347
- metadata: parseMetadata(dataRaw.ochre.metadata),
348
- item: resourceItem
349
- };
350
- return { metadata: data.metadata, resource: data.item };
351
- } catch (error) {
352
- console.error(error);
353
- return null;
354
- }
355
- }
356
-
357
- // src/utils/getters.ts
358
- var DEFAULT_OPTIONS = {
359
- searchNestedProperties: false
360
- };
361
- function getPropertyByLabel(properties, label, options = DEFAULT_OPTIONS) {
362
- const { searchNestedProperties } = options;
363
- const property = properties.find((property2) => property2.label === label);
364
- if (property) {
365
- return property;
366
- }
367
- if (searchNestedProperties) {
368
- for (const property2 of properties) {
369
- if (property2.properties.length > 0) {
370
- const nestedResult = getPropertyByLabel(property2.properties, label, {
371
- searchNestedProperties
372
- });
373
- if (nestedResult) {
374
- return nestedResult;
375
- }
376
- }
377
- }
378
- }
379
- return null;
380
- }
381
- function getPropertyValuesByLabel(properties, label, options = DEFAULT_OPTIONS) {
382
- const { searchNestedProperties } = options;
383
- const property = properties.find((property2) => property2.label === label);
384
- if (property) {
385
- return property.values.map((value) => value.content);
386
- }
387
- if (searchNestedProperties) {
388
- for (const property2 of properties) {
389
- if (property2.properties.length > 0) {
390
- const nestedResult = getPropertyValuesByLabel(
391
- property2.properties,
392
- label,
393
- { searchNestedProperties }
394
- );
395
- if (nestedResult) {
396
- return nestedResult;
397
- }
398
- }
399
- }
400
- }
401
- return null;
402
- }
403
- function getPropertyValueByLabel(properties, label, options = DEFAULT_OPTIONS) {
404
- const { searchNestedProperties } = options;
405
- const values = getPropertyValuesByLabel(properties, label, {
406
- searchNestedProperties
407
- });
408
- if (values !== null && values.length > 0) {
409
- return values[0];
410
- }
411
- if (searchNestedProperties) {
412
- for (const property of properties) {
413
- if (property.properties.length > 0) {
414
- const nestedResult = getPropertyValueByLabel(
415
- property.properties,
416
- label,
417
- { searchNestedProperties }
418
- );
419
- if (nestedResult !== null) {
420
- return nestedResult;
421
- }
422
- }
423
- }
424
- }
425
- return null;
426
- }
427
- function getAllPropertyLabels(properties, options = DEFAULT_OPTIONS) {
428
- const { searchNestedProperties } = options;
429
- const labels = /* @__PURE__ */ new Set();
430
- for (const property of properties) {
431
- labels.add(property.label);
432
- if (property.properties.length > 0 && searchNestedProperties) {
433
- const nestedLabels = getAllPropertyLabels(property.properties, {
434
- searchNestedProperties: true
435
- });
436
- for (const label of nestedLabels) {
437
- labels.add(label);
438
- }
439
- }
440
- }
441
- return [...labels];
442
- }
443
- function filterProperties(property, filter, options = DEFAULT_OPTIONS) {
444
- const { searchNestedProperties } = options;
445
- const isAllFields = filter.label.toLocaleLowerCase("en-US") === "all fields";
446
- if (isAllFields || property.label.toLocaleLowerCase("en-US") === filter.label.toLocaleLowerCase("en-US")) {
447
- let isFound = property.values.some((value) => {
448
- if (value.content === null) {
449
- return false;
450
- }
451
- if (typeof value.content === "string") {
452
- if (typeof filter.value !== "string") {
453
- return false;
454
- }
455
- return value.content.toLocaleLowerCase("en-US").includes(filter.value.toLocaleLowerCase("en-US"));
456
- }
457
- if (typeof value.content === "number") {
458
- if (typeof filter.value !== "number") {
459
- return false;
460
- }
461
- return value.content === filter.value;
462
- }
463
- if (typeof value.content === "boolean") {
464
- if (typeof filter.value !== "boolean") {
465
- return false;
466
- }
467
- return value.booleanValue === filter.value;
468
- }
469
- if (value.content instanceof Date) {
470
- if (!(filter.value instanceof Date)) {
471
- return false;
472
- }
473
- return value.content.getTime() === filter.value.getTime();
474
- }
475
- return false;
476
- });
477
- if (!isFound && searchNestedProperties) {
478
- isFound = property.properties.some(
479
- (property2) => filterProperties(property2, filter, { searchNestedProperties: true })
480
- );
481
- }
482
- return isFound;
483
- }
484
- return false;
485
- }
486
-
487
- // src/utils/parse.ts
488
- var websiteSchema = z3.object({
489
- type: z3.enum(
490
- [
491
- "traditional",
492
- "digital-collection",
493
- "plum",
494
- "cedar",
495
- "elm",
496
- "maple",
497
- "oak",
498
- "palm"
499
- ],
500
- { message: "Invalid website type" }
501
- ),
502
- status: z3.enum(
503
- ["development", "preview", "production"],
504
- { message: "Invalid website status" }
505
- ),
506
- privacy: z3.enum(
507
- ["public", "password", "private"],
508
- { message: "Invalid website privacy" }
509
- )
510
- });
511
- var resourceTypeSchema = z3.enum(
512
- [
513
- "audio",
514
- "document",
515
- "drawing",
516
- "FITS",
517
- "geospatial",
518
- "IIIF",
519
- "image",
520
- "model",
521
- "PTM",
522
- "TEI",
523
- "video",
524
- "webpage"
525
- ],
526
- { message: "Invalid resource type" }
527
- );
528
- var componentSchema = z3.enum(
529
- [
530
- "annotated-document",
531
- "annotated-image",
532
- "bibliography",
533
- "blog",
534
- "button",
535
- "collection",
536
- "empty-space",
537
- "filter-categories",
538
- "iframe",
539
- "iiif-viewer",
540
- "image",
541
- "image-gallery",
542
- "n-columns",
543
- "n-rows",
544
- "network-graph",
545
- "search-bar",
546
- "table",
547
- "text",
548
- "timeline",
549
- "video"
550
- ],
551
- { message: "Invalid component" }
552
- );
478
+ }
479
+ function parseStringContent(content, language = "eng") {
480
+ switch (typeof content.content) {
481
+ case "string":
482
+ case "number":
483
+ case "boolean": {
484
+ return parseFakeString(content.content);
485
+ }
486
+ case "object": {
487
+ if (Array.isArray(content.content)) {
488
+ const stringItem = getStringItemByLanguage(content.content, language);
489
+ if (stringItem) {
490
+ return parseStringItem(stringItem);
491
+ } else {
492
+ const returnStringItem = content.content[0];
493
+ if (!returnStringItem) {
494
+ throw new Error(
495
+ `No string item found for language \u201C${language}\u201D in the following content:
496
+ ${JSON.stringify(
497
+ content.content
498
+ )}.`
499
+ );
500
+ }
501
+ return parseStringItem(returnStringItem);
502
+ }
503
+ } else {
504
+ return parseStringItem(content.content);
505
+ }
506
+ }
507
+ default: {
508
+ return String(content.content).replaceAll(/^(\d+)\./gm, String.raw`$1\.`);
509
+ }
510
+ }
511
+ }
512
+
513
+ // src/utils/parse.ts
553
514
  function parseIdentification(identification) {
554
515
  try {
555
516
  const returnIdentification = {
@@ -959,13 +920,10 @@ function parsePeriods(periods) {
959
920
  function parseBibliography(bibliography) {
960
921
  let resource = null;
961
922
  if (bibliography.source?.resource) {
962
- const resourceType = resourceTypeSchema.parse(
963
- bibliography.source.resource.type
964
- );
965
923
  resource = {
966
924
  uuid: bibliography.source.resource.uuid,
967
925
  publicationDateTime: bibliography.source.resource.publicationDateTime ? new Date(bibliography.source.resource.publicationDateTime) : null,
968
- type: resourceType,
926
+ type: bibliography.source.resource.type,
969
927
  identification: parseIdentification(
970
928
  bibliography.source.resource.identification
971
929
  )
@@ -1137,54 +1095,83 @@ function parseTree(tree) {
1137
1095
  return returnTree;
1138
1096
  }
1139
1097
  function parseSet(set) {
1140
- let resources = [];
1141
- let spatialUnits = [];
1142
- let concepts = [];
1143
- let periods = [];
1144
- let bibliographies = [];
1145
- let persons = [];
1146
- let propertyValues = [];
1147
- if (typeof set.items !== "string" && "resource" in set.items) {
1148
- resources = parseResources(
1149
- Array.isArray(set.items.resource) ? set.items.resource : [set.items.resource],
1150
- true
1151
- );
1152
- }
1153
- if (typeof set.items !== "string" && "spatialUnit" in set.items) {
1154
- spatialUnits = parseSpatialUnits(
1155
- Array.isArray(set.items.spatialUnit) ? set.items.spatialUnit : [set.items.spatialUnit],
1156
- true
1157
- );
1158
- }
1159
- if (typeof set.items !== "string" && "concept" in set.items) {
1160
- concepts = parseConcepts(
1161
- Array.isArray(set.items.concept) ? set.items.concept : [set.items.concept],
1162
- true
1163
- );
1164
- }
1165
- if (typeof set.items !== "string" && "period" in set.items) {
1166
- periods = parsePeriods(
1167
- Array.isArray(set.items.period) ? set.items.period : [set.items.period]
1168
- );
1169
- }
1170
- if (typeof set.items !== "string" && "bibliography" in set.items) {
1171
- bibliographies = parseBibliographies(
1172
- Array.isArray(set.items.bibliography) ? set.items.bibliography : [set.items.bibliography]
1173
- );
1174
- }
1175
- if (typeof set.items !== "string" && "person" in set.items) {
1176
- persons = parsePersons(
1177
- Array.isArray(set.items.person) ? set.items.person : [set.items.person]
1178
- );
1179
- }
1180
- if (typeof set.items !== "string" && "propertyValue" in set.items) {
1181
- propertyValues = parsePropertyValues(
1182
- Array.isArray(set.items.propertyValue) ? set.items.propertyValue : [set.items.propertyValue]
1183
- );
1098
+ if (typeof set.items === "string") {
1099
+ throw new TypeError("Invalid OCHRE data: Set has no items");
1100
+ }
1101
+ const itemCategory = getItemCategory(Object.keys(set.items));
1102
+ let items = [];
1103
+ switch (itemCategory) {
1104
+ case "resource": {
1105
+ if (!("resource" in set.items)) {
1106
+ throw new Error("Invalid OCHRE data: Set has no resources");
1107
+ }
1108
+ items = parseResources(
1109
+ Array.isArray(set.items.resource) ? set.items.resource : [set.items.resource]
1110
+ );
1111
+ break;
1112
+ }
1113
+ case "spatialUnit": {
1114
+ if (!("spatialUnit" in set.items)) {
1115
+ throw new Error("Invalid OCHRE data: Set has no spatial units");
1116
+ }
1117
+ items = parseSpatialUnits(
1118
+ Array.isArray(set.items.spatialUnit) ? set.items.spatialUnit : [set.items.spatialUnit]
1119
+ );
1120
+ break;
1121
+ }
1122
+ case "concept": {
1123
+ if (!("concept" in set.items)) {
1124
+ throw new Error("Invalid OCHRE data: Set has no concepts");
1125
+ }
1126
+ items = parseConcepts(
1127
+ Array.isArray(set.items.concept) ? set.items.concept : [set.items.concept]
1128
+ );
1129
+ break;
1130
+ }
1131
+ case "period": {
1132
+ if (!("period" in set.items)) {
1133
+ throw new Error("Invalid OCHRE data: Set has no periods");
1134
+ }
1135
+ items = parsePeriods(
1136
+ Array.isArray(set.items.period) ? set.items.period : [set.items.period]
1137
+ );
1138
+ break;
1139
+ }
1140
+ case "bibliography": {
1141
+ if (!("bibliography" in set.items)) {
1142
+ throw new Error("Invalid OCHRE data: Set has no bibliographies");
1143
+ }
1144
+ items = parseBibliographies(
1145
+ Array.isArray(set.items.bibliography) ? set.items.bibliography : [set.items.bibliography]
1146
+ );
1147
+ break;
1148
+ }
1149
+ case "person": {
1150
+ if (!("person" in set.items)) {
1151
+ throw new Error("Invalid OCHRE data: Set has no persons");
1152
+ }
1153
+ items = parsePersons(
1154
+ Array.isArray(set.items.person) ? set.items.person : [set.items.person]
1155
+ );
1156
+ break;
1157
+ }
1158
+ case "propertyValue": {
1159
+ if (!("propertyValue" in set.items)) {
1160
+ throw new Error("Invalid OCHRE data: Set has no property values");
1161
+ }
1162
+ items = parsePropertyValues(
1163
+ Array.isArray(set.items.propertyValue) ? set.items.propertyValue : [set.items.propertyValue]
1164
+ );
1165
+ break;
1166
+ }
1167
+ default: {
1168
+ throw new Error("Invalid OCHRE data: Set has no items or is malformed");
1169
+ }
1184
1170
  }
1185
1171
  return {
1186
1172
  uuid: set.uuid,
1187
1173
  category: "set",
1174
+ itemCategory,
1188
1175
  publicationDateTime: set.publicationDateTime ? new Date(set.publicationDateTime) : null,
1189
1176
  date: set.date != null ? new Date(set.date) : null,
1190
1177
  license: parseLicense(set.availability),
@@ -1196,24 +1183,15 @@ function parseSet(set) {
1196
1183
  ) : [],
1197
1184
  type: set.type,
1198
1185
  number: set.n,
1199
- items: {
1200
- resources,
1201
- spatialUnits,
1202
- concepts,
1203
- periods,
1204
- bibliographies,
1205
- persons,
1206
- propertyValues
1207
- }
1186
+ items
1208
1187
  };
1209
1188
  }
1210
- function parseResource(resource, isNested = false) {
1211
- const resourceType = resourceTypeSchema.parse(resource.type);
1189
+ function parseResource(resource) {
1212
1190
  const returnResource = {
1213
1191
  uuid: resource.uuid,
1214
1192
  category: "resource",
1215
1193
  publicationDateTime: resource.publicationDateTime ? new Date(resource.publicationDateTime) : null,
1216
- type: resourceType,
1194
+ type: resource.type,
1217
1195
  number: resource.n,
1218
1196
  format: resource.format ?? null,
1219
1197
  context: "context" in resource && resource.context ? parseContext(resource.context) : null,
@@ -1251,34 +1229,20 @@ function parseResource(resource, isNested = false) {
1251
1229
  Array.isArray(resource.citedBibliography.reference) ? resource.citedBibliography.reference : [resource.citedBibliography.reference]
1252
1230
  ) : [],
1253
1231
  resources: resource.resource ? parseResources(
1254
- Array.isArray(resource.resource) ? resource.resource : [resource.resource],
1255
- true
1232
+ Array.isArray(resource.resource) ? resource.resource : [resource.resource]
1256
1233
  ) : []
1257
1234
  };
1258
- if (isNested) {
1259
- const returnNestedResource = {
1260
- ...returnResource,
1261
- publicationDateTime: null,
1262
- context: null,
1263
- license: null,
1264
- copyright: null
1265
- };
1266
- delete returnNestedResource.publicationDateTime;
1267
- delete returnNestedResource.license;
1268
- delete returnNestedResource.copyright;
1269
- return returnNestedResource;
1270
- }
1271
1235
  return returnResource;
1272
1236
  }
1273
- function parseResources(resources, isNested = false) {
1237
+ function parseResources(resources) {
1274
1238
  const returnResources = [];
1275
1239
  const resourcesToParse = Array.isArray(resources) ? resources : [resources];
1276
1240
  for (const resource of resourcesToParse) {
1277
- returnResources.push(parseResource(resource, isNested));
1241
+ returnResources.push(parseResource(resource));
1278
1242
  }
1279
1243
  return returnResources;
1280
1244
  }
1281
- function parseSpatialUnit(spatialUnit, isNested = false) {
1245
+ function parseSpatialUnit(spatialUnit) {
1282
1246
  const returnSpatialUnit = {
1283
1247
  uuid: spatialUnit.uuid,
1284
1248
  category: "spatialUnit",
@@ -1298,34 +1262,22 @@ function parseSpatialUnit(spatialUnit, isNested = false) {
1298
1262
  ) : spatialUnit.observation ? [parseObservation(spatialUnit.observation)] : [],
1299
1263
  events: "events" in spatialUnit && spatialUnit.events ? parseEvents(
1300
1264
  Array.isArray(spatialUnit.events.event) ? spatialUnit.events.event : [spatialUnit.events.event]
1265
+ ) : [],
1266
+ properties: "properties" in spatialUnit && spatialUnit.properties ? parseProperties(
1267
+ Array.isArray(spatialUnit.properties.property) ? spatialUnit.properties.property : [spatialUnit.properties.property]
1301
1268
  ) : []
1302
1269
  };
1303
- if (isNested) {
1304
- const returnNestedSpatialUnit = {
1305
- ...returnSpatialUnit,
1306
- publicationDateTime: null,
1307
- license: null,
1308
- properties: "properties" in spatialUnit && spatialUnit.properties ? parseProperties(
1309
- Array.isArray(spatialUnit.properties.property) ? spatialUnit.properties.property : [spatialUnit.properties.property]
1310
- ) : []
1311
- };
1312
- delete returnNestedSpatialUnit.publicationDateTime;
1313
- delete returnNestedSpatialUnit.license;
1314
- return returnNestedSpatialUnit;
1315
- }
1316
1270
  return returnSpatialUnit;
1317
1271
  }
1318
- function parseSpatialUnits(spatialUnits, isNested = false) {
1272
+ function parseSpatialUnits(spatialUnits) {
1319
1273
  const returnSpatialUnits = [];
1320
1274
  const spatialUnitsToParse = Array.isArray(spatialUnits) ? spatialUnits : [spatialUnits];
1321
1275
  for (const spatialUnit of spatialUnitsToParse) {
1322
- returnSpatialUnits.push(
1323
- parseSpatialUnit(spatialUnit, isNested)
1324
- );
1276
+ returnSpatialUnits.push(parseSpatialUnit(spatialUnit));
1325
1277
  }
1326
1278
  return returnSpatialUnits;
1327
1279
  }
1328
- function parseConcept(concept, isNested = false) {
1280
+ function parseConcept(concept) {
1329
1281
  const returnConcept = {
1330
1282
  uuid: concept.uuid,
1331
1283
  category: "concept",
@@ -1338,17 +1290,6 @@ function parseConcept(concept, isNested = false) {
1338
1290
  Array.isArray(concept.interpretations.interpretation) ? concept.interpretations.interpretation : [concept.interpretations.interpretation]
1339
1291
  )
1340
1292
  };
1341
- if (isNested) {
1342
- const returnNestedConcept = {
1343
- ...returnConcept,
1344
- publicationDateTime: null,
1345
- context: null,
1346
- license: null
1347
- };
1348
- delete returnNestedConcept.publicationDateTime;
1349
- delete returnNestedConcept.license;
1350
- return returnNestedConcept;
1351
- }
1352
1293
  return returnConcept;
1353
1294
  }
1354
1295
  var parseWebpageResources = async (webpageResources, type) => {
@@ -1391,11 +1332,11 @@ var parseWebpageResources = async (webpageResources, type) => {
1391
1332
  }
1392
1333
  return returnElements;
1393
1334
  };
1394
- function parseConcepts(concepts, isNested = false) {
1335
+ function parseConcepts(concepts) {
1395
1336
  const returnConcepts = [];
1396
1337
  const conceptsToParse = Array.isArray(concepts) ? concepts : [concepts];
1397
1338
  for (const concept of conceptsToParse) {
1398
- returnConcepts.push(parseConcept(concept, isNested));
1339
+ returnConcepts.push(parseConcept(concept));
1399
1340
  }
1400
1341
  return returnConcepts;
1401
1342
  }
@@ -1416,11 +1357,11 @@ async function parseWebElementProperties(componentProperty, elementResource) {
1416
1357
  if (document === null) {
1417
1358
  const documentLink = links.find((link) => link.type === "internalDocument");
1418
1359
  if (documentLink) {
1419
- const documentResource = await fetchResource(documentLink.uuid);
1420
- if (documentResource === null) {
1360
+ const { item, error } = await fetchItem(documentLink.uuid, "resource");
1361
+ if (error !== null) {
1421
1362
  throw new Error("Failed to fetch OCHRE data");
1422
1363
  }
1423
- document = documentResource.resource.document;
1364
+ document = item.document;
1424
1365
  }
1425
1366
  }
1426
1367
  switch (componentName) {
@@ -2461,300 +2402,144 @@ async function parseWebsite(websiteTree, projectName, website) {
2461
2402
  };
2462
2403
  }
2463
2404
 
2464
- // src/utils/fetchers/bibliography.ts
2465
- async function fetchBibliography(uuid) {
2466
- try {
2467
- const [error, dataRaw] = await fetchByUuid(uuid);
2468
- if (error !== null) {
2469
- throw new Error(error);
2470
- }
2471
- if (!("bibliography" in dataRaw.ochre)) {
2472
- throw new Error(
2473
- "Invalid OCHRE data: API response missing 'bibliography' key"
2474
- );
2475
- }
2476
- const bibliographyItem = parseBibliography(dataRaw.ochre.bibliography);
2477
- const data = {
2478
- uuid: parseFakeString(dataRaw.ochre.uuid),
2479
- publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
2480
- belongsTo: {
2481
- uuid: dataRaw.ochre.uuidBelongsTo,
2482
- abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
2483
- },
2484
- metadata: parseMetadata(dataRaw.ochre.metadata),
2485
- item: bibliographyItem
2486
- };
2487
- return { metadata: data.metadata, bibliography: data.item };
2488
- } catch (error) {
2489
- console.error(error);
2490
- return null;
2491
- }
2492
- }
2493
-
2494
- // src/utils/fetchers/concept.ts
2495
- async function fetchConcept(uuid) {
2496
- try {
2497
- const [error, dataRaw] = await fetchByUuid(uuid);
2498
- if (error !== null) {
2499
- throw new Error(error);
2500
- }
2501
- if (!("concept" in dataRaw.ochre)) {
2502
- throw new Error("Invalid OCHRE data: API response missing 'concept' key");
2503
- }
2504
- const conceptItem = parseConcept(dataRaw.ochre.concept);
2505
- const data = {
2506
- uuid: parseFakeString(dataRaw.ochre.uuid),
2507
- publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
2508
- belongsTo: {
2509
- uuid: dataRaw.ochre.uuidBelongsTo,
2510
- abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
2511
- },
2512
- metadata: parseMetadata(dataRaw.ochre.metadata),
2513
- item: conceptItem
2514
- };
2515
- return { metadata: data.metadata, concept: data.item };
2516
- } catch (error) {
2517
- console.error(error);
2518
- return null;
2519
- }
2520
- }
2521
-
2522
- // src/utils/fetchers/gallery.ts
2523
- import { z as z4 } from "zod";
2524
- var gallerySchema = z4.object({
2525
- uuid: z4.string().uuid({ message: "Invalid UUID" }),
2526
- filter: z4.string().optional(),
2527
- page: z4.number().positive({ message: "Page must be positive" }),
2528
- perPage: z4.number().positive({ message: "Per page must be positive" })
2529
- }).strict();
2530
- async function fetchGallery(uuid, filter, page, perPage) {
2405
+ // src/utils/fetchers/uuid.ts
2406
+ async function fetchByUuid(uuid) {
2531
2407
  try {
2532
- const parsed = gallerySchema.safeParse({ uuid, filter, page, perPage });
2533
- if (!parsed.success) {
2534
- throw new Error(parsed.error.message);
2535
- }
2408
+ const parsedUuid = uuidSchema.parse(uuid);
2536
2409
  const response = await fetch(
2537
- `https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(`
2538
- for $q in input()/ochre[@uuid='${uuid}']
2539
- let $filtered := $q/tree/items/resource[contains(lower-case(identification/label), lower-case('${filter}'))]
2540
- let $maxLength := count($filtered)
2541
- return <gallery maxLength='{$maxLength}'>
2542
- {$q/metadata/project}
2543
- {$q/metadata/item}
2544
- {$filtered[position() >= ${((page - 1) * perPage + 1).toString()} and position() < ${(page * perPage + 1).toString()}]}
2545
- </gallery>
2546
- `)}&format=json`
2410
+ `https://ochre.lib.uchicago.edu/ochre?uuid=${parsedUuid}&format=json&lang="*"`
2547
2411
  );
2548
2412
  if (!response.ok) {
2549
- throw new Error("Error fetching gallery items, please try again later.");
2550
- }
2551
- const data = await response.json();
2552
- if (!("gallery" in data.result)) {
2553
- throw new Error("Failed to fetch gallery");
2413
+ throw new Error("Failed to fetch OCHRE data");
2554
2414
  }
2555
- const galleryIdentification = parseIdentification(
2556
- data.result.gallery.item.identification
2557
- );
2558
- const galleryProjectIdentification = parseIdentification(
2559
- data.result.gallery.project.identification
2560
- );
2561
- const gallery = {
2562
- identification: galleryIdentification,
2563
- projectIdentification: galleryProjectIdentification,
2564
- resources: data.result.gallery.resource ? Array.isArray(data.result.gallery.resource) ? parseResources(data.result.gallery.resource) : [parseResource(data.result.gallery.resource)] : [],
2565
- maxLength: data.result.gallery.maxLength
2566
- };
2567
- return gallery;
2568
- } catch (error) {
2569
- console.error(error);
2570
- return null;
2571
- }
2572
- }
2573
-
2574
- // src/utils/fetchers/period.ts
2575
- async function fetchPeriod(uuid) {
2576
- try {
2577
- const [error, dataRaw] = await fetchByUuid(uuid);
2578
- if (error !== null) {
2579
- throw new Error(error);
2415
+ const dataRaw = await response.json();
2416
+ if (!("ochre" in dataRaw)) {
2417
+ throw new Error("Invalid OCHRE data: API response missing 'ochre' key");
2580
2418
  }
2581
- if (!("period" in dataRaw.ochre)) {
2582
- throw new Error("Invalid OCHRE data: API response missing 'period' key");
2583
- }
2584
- const periodItem = parsePeriod(dataRaw.ochre.period);
2585
- const data = {
2586
- uuid: parseFakeString(dataRaw.ochre.uuid),
2587
- publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
2588
- belongsTo: {
2589
- uuid: dataRaw.ochre.uuidBelongsTo,
2590
- abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
2591
- },
2592
- metadata: parseMetadata(dataRaw.ochre.metadata),
2593
- item: periodItem
2594
- };
2595
- return { metadata: data.metadata, period: data.item };
2419
+ return [null, dataRaw];
2596
2420
  } catch (error) {
2597
- console.error(error);
2598
- return null;
2421
+ return [error instanceof Error ? error.message : "Unknown error", null];
2599
2422
  }
2600
2423
  }
2601
2424
 
2602
- // src/utils/fetchers/property-value.ts
2603
- async function fetchPropertyValue(uuid) {
2425
+ // src/utils/fetchers/item.ts
2426
+ async function fetchItem(uuid, category) {
2604
2427
  try {
2605
- const [error, dataRaw] = await fetchByUuid(uuid);
2428
+ const [error, data] = await fetchByUuid(uuid);
2606
2429
  if (error !== null) {
2607
2430
  throw new Error(error);
2608
2431
  }
2609
- if (!("propertyValue" in dataRaw.ochre)) {
2610
- throw new Error(
2611
- "Invalid OCHRE data: API response missing 'propertyValue' key"
2612
- );
2613
- }
2614
- const propertyValueItem = parsePropertyValue(dataRaw.ochre.propertyValue);
2615
- const data = {
2616
- uuid: parseFakeString(dataRaw.ochre.uuid),
2617
- publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
2618
- belongsTo: {
2619
- uuid: dataRaw.ochre.uuidBelongsTo,
2620
- abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
2621
- },
2622
- metadata: parseMetadata(dataRaw.ochre.metadata),
2623
- item: propertyValueItem
2624
- };
2625
- return { metadata: data.metadata, propertyValue: data.item };
2626
- } catch (error) {
2627
- console.error(error);
2628
- return null;
2629
- }
2630
- }
2631
-
2632
- // src/utils/fetchers/set.ts
2633
- async function fetchSet(uuid) {
2634
- try {
2635
- const [error, dataRaw] = await fetchByUuid(uuid);
2636
- if (error !== null) {
2637
- throw new Error(error);
2432
+ const categoryKey = getItemCategory(Object.keys(data.ochre));
2433
+ let item;
2434
+ switch (categoryKey) {
2435
+ case "resource": {
2436
+ if (!("resource" in data.ochre)) {
2437
+ throw new Error(
2438
+ "Invalid OCHRE data: API response missing 'resource' key"
2439
+ );
2440
+ }
2441
+ item = parseResource(data.ochre.resource);
2442
+ break;
2443
+ }
2444
+ case "spatialUnit": {
2445
+ if (!("spatialUnit" in data.ochre)) {
2446
+ throw new Error(
2447
+ "Invalid OCHRE data: API response missing 'spatialUnit' key"
2448
+ );
2449
+ }
2450
+ item = parseSpatialUnit(data.ochre.spatialUnit);
2451
+ break;
2452
+ }
2453
+ case "concept": {
2454
+ if (!("concept" in data.ochre)) {
2455
+ throw new Error(
2456
+ "Invalid OCHRE data: API response missing 'concept' key"
2457
+ );
2458
+ }
2459
+ item = parseConcept(data.ochre.concept);
2460
+ break;
2461
+ }
2462
+ case "period": {
2463
+ if (!("period" in data.ochre)) {
2464
+ throw new Error(
2465
+ "Invalid OCHRE data: API response missing 'period' key"
2466
+ );
2467
+ }
2468
+ item = parsePeriod(data.ochre.period);
2469
+ break;
2470
+ }
2471
+ case "bibliography": {
2472
+ if (!("bibliography" in data.ochre)) {
2473
+ throw new Error(
2474
+ "Invalid OCHRE data: API response missing 'bibliography' key"
2475
+ );
2476
+ }
2477
+ item = parseBibliography(data.ochre.bibliography);
2478
+ break;
2479
+ }
2480
+ case "person": {
2481
+ if (!("person" in data.ochre)) {
2482
+ throw new Error(
2483
+ "Invalid OCHRE data: API response missing 'person' key"
2484
+ );
2485
+ }
2486
+ item = parsePerson(data.ochre.person);
2487
+ break;
2488
+ }
2489
+ case "propertyValue": {
2490
+ if (!("propertyValue" in data.ochre)) {
2491
+ throw new Error(
2492
+ "Invalid OCHRE data: API response missing 'propertyValue' key"
2493
+ );
2494
+ }
2495
+ item = parsePropertyValue(data.ochre.propertyValue);
2496
+ break;
2497
+ }
2498
+ case "set": {
2499
+ if (!("set" in data.ochre)) {
2500
+ throw new Error("Invalid OCHRE data: API response missing 'set' key");
2501
+ }
2502
+ item = parseSet(data.ochre.set);
2503
+ break;
2504
+ }
2505
+ case "tree": {
2506
+ if (!("tree" in data.ochre)) {
2507
+ throw new Error(
2508
+ "Invalid OCHRE data: API response missing 'tree' key"
2509
+ );
2510
+ }
2511
+ item = parseTree(data.ochre.tree);
2512
+ break;
2513
+ }
2514
+ default: {
2515
+ throw new Error("Invalid category");
2516
+ }
2638
2517
  }
2639
- if (!("set" in dataRaw.ochre)) {
2640
- throw new Error("Invalid OCHRE data: API response missing 'set' key");
2641
- }
2642
- const setItem = parseSet(dataRaw.ochre.set);
2643
- const data = {
2644
- uuid: parseFakeString(dataRaw.ochre.uuid),
2645
- publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
2646
- belongsTo: {
2647
- uuid: dataRaw.ochre.uuidBelongsTo,
2648
- abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
2649
- },
2650
- metadata: parseMetadata(dataRaw.ochre.metadata),
2651
- item: setItem
2518
+ const metadata = parseMetadata(data.ochre.metadata);
2519
+ const belongsTo = {
2520
+ uuid: data.ochre.uuidBelongsTo,
2521
+ abbreviation: parseFakeString(data.ochre.belongsTo)
2652
2522
  };
2653
- return { metadata: data.metadata, set: data.item };
2654
- } catch (error) {
2655
- console.error(error);
2656
- return null;
2657
- }
2658
- }
2659
-
2660
- // src/utils/fetchers/spatial-unit.ts
2661
- async function fetchSpatialUnit(uuid) {
2662
- try {
2663
- const [error, dataRaw] = await fetchByUuid(uuid);
2664
- if (error !== null) {
2665
- throw new Error(error);
2666
- }
2667
- if (!("spatialUnit" in dataRaw.ochre)) {
2668
- throw new Error(
2669
- "Invalid OCHRE data: API response missing 'spatialUnit' key"
2670
- );
2671
- }
2672
- const spatialUnitItem = parseSpatialUnit(dataRaw.ochre.spatialUnit);
2673
- const data = {
2674
- uuid: parseFakeString(dataRaw.ochre.uuid),
2675
- publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
2676
- belongsTo: {
2677
- uuid: dataRaw.ochre.uuidBelongsTo,
2678
- abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
2679
- },
2680
- metadata: parseMetadata(dataRaw.ochre.metadata),
2681
- item: spatialUnitItem
2523
+ return {
2524
+ error: null,
2525
+ metadata,
2526
+ belongsTo,
2527
+ item,
2528
+ category
2682
2529
  };
2683
- return { metadata: data.metadata, spatialUnit: data.item };
2684
2530
  } catch (error) {
2685
- console.error(error);
2686
- return null;
2687
- }
2688
- }
2689
-
2690
- // src/utils/fetchers/tree.ts
2691
- async function fetchTree(uuid) {
2692
- try {
2693
- const [error, dataRaw] = await fetchByUuid(uuid);
2694
- if (error !== null) {
2695
- throw new Error(error);
2696
- }
2697
- if (!("tree" in dataRaw.ochre)) {
2698
- throw new Error("Invalid OCHRE data: API response missing 'tree' key");
2699
- }
2700
- const tree = parseTree(dataRaw.ochre.tree);
2701
- if (!tree) {
2702
- throw new Error("Invalid OCHRE data: Could not parse tree");
2703
- }
2704
- const data = {
2705
- uuid: parseFakeString(dataRaw.ochre.uuid),
2706
- publicationDateTime: new Date(dataRaw.ochre.publicationDateTime),
2707
- belongsTo: {
2708
- uuid: dataRaw.ochre.uuidBelongsTo,
2709
- abbreviation: parseFakeString(dataRaw.ochre.belongsTo)
2710
- },
2711
- metadata: parseMetadata(dataRaw.ochre.metadata),
2712
- item: tree
2531
+ return {
2532
+ error: error instanceof Error ? error.message : "Unknown error",
2533
+ metadata: void 0,
2534
+ belongsTo: void 0,
2535
+ item: void 0,
2536
+ category: void 0
2713
2537
  };
2714
- return { metadata: data.metadata, tree: data.item };
2715
- } catch (error) {
2716
- console.error(error);
2717
- return null;
2718
- }
2719
- }
2720
-
2721
- // src/utils/fetchers/website.ts
2722
- async function fetchWebsite(abbreviation) {
2723
- try {
2724
- const response = await fetch(
2725
- `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`
2726
- );
2727
- if (!response.ok) {
2728
- throw new Error("Failed to fetch website");
2729
- }
2730
- const data = await response.json();
2731
- if (!("ochre" in data.result) || !("tree" in data.result.ochre)) {
2732
- throw new Error("Failed to fetch website");
2733
- }
2734
- const projectIdentification = data.result.ochre.metadata.project?.identification ? parseIdentification(data.result.ochre.metadata.project.identification) : null;
2735
- const website = await parseWebsite(
2736
- data.result.ochre.tree,
2737
- projectIdentification?.label ?? "",
2738
- data.result.ochre.metadata.project?.identification.website ?? null
2739
- );
2740
- return website;
2741
- } catch (error) {
2742
- console.error(error);
2743
- return null;
2744
2538
  }
2745
2539
  }
2746
2540
  export {
2747
- fetchBibliography,
2748
2541
  fetchByUuid,
2749
- fetchConcept,
2750
- fetchGallery,
2751
- fetchPeriod,
2752
- fetchPropertyValue,
2753
- fetchResource,
2754
- fetchSet,
2755
- fetchSpatialUnit,
2756
- fetchTree,
2757
- fetchWebsite,
2542
+ fetchItem,
2758
2543
  filterProperties,
2759
2544
  getAllPropertyLabels,
2760
2545
  getPropertyByLabel,