ochre-sdk 1.0.78 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +31 -3
  2. package/dist/_virtual/_rolldown/runtime.mjs +13 -0
  3. package/dist/categories.d.mts +249 -0
  4. package/dist/categories.mjs +259 -0
  5. package/dist/constants.d.mts +14 -0
  6. package/dist/constants.mjs +18 -1
  7. package/dist/errors.d.mts +23 -0
  8. package/dist/{utilities.mjs → errors.mjs} +33 -84
  9. package/dist/fetchers/gallery.mjs +15 -51
  10. package/dist/fetchers/item-children.mjs +20 -66
  11. package/dist/fetchers/item-links.mjs +26 -79
  12. package/dist/fetchers/item-ocr-data.d.mts +2 -2
  13. package/dist/fetchers/item-ocr-data.mjs +11 -15
  14. package/dist/fetchers/item.d.mts +0 -15
  15. package/dist/fetchers/item.mjs +20 -72
  16. package/dist/fetchers/request.d.mts +70 -0
  17. package/dist/fetchers/request.mjs +100 -0
  18. package/dist/fetchers/set/items.mjs +27 -73
  19. package/dist/fetchers/set/property-values.d.mts +2 -3
  20. package/dist/fetchers/set/property-values.mjs +96 -130
  21. package/dist/fetchers/website-metadata.mjs +35 -57
  22. package/dist/fetchers/website.d.mts +2 -3
  23. package/dist/fetchers/website.mjs +22 -31
  24. package/dist/getters.d.mts +78 -148
  25. package/dist/getters.mjs +127 -208
  26. package/dist/helpers.d.mts +0 -4
  27. package/dist/helpers.mjs +19 -6
  28. package/dist/index.d.mts +8 -6
  29. package/dist/index.mjs +6 -4
  30. package/dist/ocr.d.mts +37 -0
  31. package/dist/ocr.mjs +52 -0
  32. package/dist/parsers/helpers.d.mts +21 -1
  33. package/dist/parsers/helpers.mjs +26 -6
  34. package/dist/parsers/index.d.mts +0 -8
  35. package/dist/parsers/index.mjs +118 -259
  36. package/dist/parsers/languages.d.mts +72 -0
  37. package/dist/parsers/languages.mjs +132 -0
  38. package/dist/parsers/multilingual.d.mts +49 -74
  39. package/dist/parsers/multilingual.mjs +88 -189
  40. package/dist/parsers/property-token.d.mts +34 -0
  41. package/dist/parsers/property-token.mjs +29 -0
  42. package/dist/parsers/string.d.mts +19 -0
  43. package/dist/parsers/string.mjs +45 -25
  44. package/dist/parsers/website/bounds.d.mts +10 -0
  45. package/dist/parsers/website/bounds.mjs +28 -0
  46. package/dist/parsers/website/components.d.mts +91 -0
  47. package/dist/parsers/website/components.mjs +681 -0
  48. package/dist/parsers/website/index.d.mts +0 -7
  49. package/dist/parsers/website/index.mjs +92 -1153
  50. package/dist/parsers/website/links.d.mts +36 -0
  51. package/dist/parsers/website/links.mjs +58 -0
  52. package/dist/parsers/website/messages.d.mts +24 -0
  53. package/dist/parsers/website/messages.mjs +31 -0
  54. package/dist/parsers/website/options.d.mts +6 -0
  55. package/dist/parsers/website/options.mjs +114 -0
  56. package/dist/parsers/website/properties.d.mts +12 -0
  57. package/dist/parsers/website/properties.mjs +158 -0
  58. package/dist/parsers/website/reader.d.mts +54 -4
  59. package/dist/parsers/website/reader.mjs +65 -20
  60. package/dist/parsers/website/slug.d.mts +64 -0
  61. package/dist/parsers/website/slug.mjs +82 -0
  62. package/dist/parsers/website/styles.d.mts +28 -0
  63. package/dist/parsers/website/styles.mjs +103 -0
  64. package/dist/parsers/website/walk.d.mts +68 -0
  65. package/dist/parsers/website/walk.mjs +116 -0
  66. package/dist/query.d.mts +66 -18
  67. package/dist/query.mjs +202 -48
  68. package/dist/reflection.d.mts +64 -0
  69. package/dist/reflection.mjs +79 -0
  70. package/dist/schemas.d.mts +7 -0
  71. package/dist/schemas.mjs +12 -3
  72. package/dist/types/index.d.mts +1 -31
  73. package/dist/types/utilities.d.mts +9 -0
  74. package/dist/types/utilities.mjs +1 -0
  75. package/dist/types/website.d.mts +49 -58
  76. package/dist/xml/metadata.d.mts +16 -0
  77. package/dist/xml/metadata.mjs +32 -11
  78. package/dist/xml/schemas.d.mts +5970 -3
  79. package/dist/xml/schemas.mjs +43 -45
  80. package/dist/xml/types.d.mts +13 -30
  81. package/dist/xquery.d.mts +46 -0
  82. package/dist/xquery.mjs +66 -0
  83. package/package.json +3 -3
  84. package/dist/utilities.d.mts +0 -54
@@ -1,4 +1,5 @@
1
1
  import { DEFAULT_LANGUAGES } from "../constants.mjs";
2
+ import { readProperty } from "../reflection.mjs";
2
3
  import { serializeMDXText } from "./mdx.mjs";
3
4
  //#region src/parsers/multilingual.ts
4
5
  const MULTILINGUAL_STRING_INTERNAL_INIT = Symbol("MultilingualString.internalInit");
@@ -56,8 +57,28 @@ function getImplicitLanguages(content, options) {
56
57
  for (const language of Object.keys(content)) if (!languages.includes(language)) languages.push(language);
57
58
  return languages.length > 0 ? languages : [...DEFAULT_LANGUAGES];
58
59
  }
60
+ /**
61
+ * Pick the language reads should fall back to first
62
+ * @param availableLanguages - The languages carrying content, in order
63
+ * @param supportedLanguages - Every language the string was created for
64
+ * @returns The default language
65
+ */
66
+ function resolveDefaultLanguageOption(availableLanguages, supportedLanguages) {
67
+ return availableLanguages[0] ?? supportedLanguages[0] ?? DEFAULT_LANGUAGES[0];
68
+ }
69
+ /**
70
+ * Whether a constructor argument is the module's own normalized init
71
+ *
72
+ * The assertion is earned by the brand rather than by inspecting the fields:
73
+ * {@link MULTILINGUAL_STRING_INTERNAL_INIT} is a module-private symbol, so no
74
+ * caller outside this file can produce a value carrying it, and the single
75
+ * producer is `fromNormalized`. That is what makes the narrowing sound; a
76
+ * field-by-field check would only re-state what the brand already guarantees.
77
+ * @param value - The constructor argument
78
+ * @returns True when the value carries the internal brand
79
+ */
59
80
  function isInternalInit(value) {
60
- return typeof value === "object" && value != null && Object.hasOwn(value, MULTILINGUAL_STRING_INTERNAL_INIT);
81
+ return readProperty(value, MULTILINGUAL_STRING_INTERNAL_INIT) === true;
61
82
  }
62
83
  /**
63
84
  * Multilingual string
@@ -84,7 +105,7 @@ var MultilingualString = class MultilingualString {
84
105
  for (const language of actualLanguages) normalizedContent[language] = entriesFromTexts(content[language] ?? []);
85
106
  const availableLanguages = getLanguagesWithEntries(normalizedContent, actualLanguages);
86
107
  const defaultOptions = {
87
- defaultLanguage: options.defaultLanguage ?? availableLanguages[0] ?? actualLanguages[0],
108
+ defaultLanguage: options.defaultLanguage ?? resolveDefaultLanguageOption(availableLanguages, actualLanguages),
88
109
  availableLanguages: actualLanguages,
89
110
  aliases: normalizeAliases(options.aliases)
90
111
  };
@@ -97,7 +118,7 @@ var MultilingualString = class MultilingualString {
97
118
  }
98
119
  const availableLanguages = getLanguagesWithEntries(normalizedContent, languages);
99
120
  const defaultOptions = {
100
- defaultLanguage: options.defaultLanguage ?? availableLanguages[0] ?? languages[0],
121
+ defaultLanguage: options.defaultLanguage ?? resolveDefaultLanguageOption(availableLanguages, languages),
101
122
  availableLanguages: languages,
102
123
  aliases: normalizeAliases(options.aliases)
103
124
  };
@@ -120,6 +141,7 @@ var MultilingualString = class MultilingualString {
120
141
  static fromJSON(json, languages, options = {}) {
121
142
  const content = json.content;
122
143
  const mergedOptions = {
144
+ defaultLanguage: json.defaultLanguage,
123
145
  ...options,
124
146
  aliases: json.aliases
125
147
  };
@@ -144,256 +166,133 @@ var MultilingualString = class MultilingualString {
144
166
  this._availableLanguages = parsed._availableLanguages;
145
167
  this._aliases = parsed._aliases;
146
168
  }
147
- getPrimaryEntry(language) {
148
- const entries = this._content[language] ?? [];
169
+ /**
170
+ * Resolve the entries to read for a language
171
+ *
172
+ * The fallback order is requested language, then the dataset's default
173
+ * language, then the first language that has any content. This is the only
174
+ * place that order is written down; every reader below is a projection of it.
175
+ */
176
+ resolveEntries(language, isExact) {
177
+ const candidateLanguages = isExact ? [language] : [
178
+ language,
179
+ this._options.defaultLanguage,
180
+ ...this._availableLanguages
181
+ ];
182
+ for (const candidateLanguage of candidateLanguages) {
183
+ if (candidateLanguage == null) continue;
184
+ const entries = this._content[candidateLanguage] ?? [];
185
+ if (entries.length > 0) return entries;
186
+ }
187
+ return [];
188
+ }
189
+ resolvePrimaryEntry(language, isExact) {
190
+ const entries = this.resolveEntries(language, isExact);
149
191
  for (const entry of entries) if (entry.isPrimary) return entry;
150
192
  return entries[0] ?? null;
151
193
  }
152
194
  /**
153
- * Get text in a specific language with automatic fallback
195
+ * Get text in a specific language, falling back when it has none
154
196
  */
155
197
  getText(language) {
156
- if (language == null) {
157
- const defaultEntry = this.getPrimaryEntry(this._options.defaultLanguage);
158
- if (defaultEntry != null) return defaultEntry.text;
159
- }
160
- if (language != null) {
161
- const requestedEntry = this.getPrimaryEntry(language);
162
- if (requestedEntry != null) return requestedEntry.text;
163
- }
164
- const defaultEntry = this.getPrimaryEntry(this._options.defaultLanguage);
165
- if (defaultEntry != null) return defaultEntry.text;
166
- for (const availableLanguage of this._availableLanguages) {
167
- const entry = this.getPrimaryEntry(availableLanguage);
168
- if (entry != null) return entry.text;
169
- }
170
- return "";
198
+ return this.resolvePrimaryEntry(language, false)?.text ?? "";
171
199
  }
172
200
  /**
173
- * Get rich text in a specific language with automatic fallback
201
+ * Get rich text in a specific language, falling back when it has none
174
202
  */
175
203
  getRichText(language) {
176
- if (language == null) {
177
- const defaultEntry = this.getPrimaryEntry(this._options.defaultLanguage);
178
- if (defaultEntry != null) return defaultEntry.richText;
179
- }
180
- if (language != null) {
181
- const requestedEntry = this.getPrimaryEntry(language);
182
- if (requestedEntry != null) return requestedEntry.richText;
183
- }
184
- const defaultEntry = this.getPrimaryEntry(this._options.defaultLanguage);
185
- if (defaultEntry != null) return defaultEntry.richText;
186
- for (const availableLanguage of this._availableLanguages) {
187
- const entry = this.getPrimaryEntry(availableLanguage);
188
- if (entry != null) return entry.richText;
189
- }
190
- return "";
204
+ return this.resolvePrimaryEntry(language, false)?.richText ?? "";
191
205
  }
192
206
  /**
193
- * Get primary text in a specific language without fallback
207
+ * Get text in a specific language, with no fallback
194
208
  */
195
209
  getExactText(language) {
196
- return this.getPrimaryEntry(language)?.text ?? null;
210
+ return this.resolvePrimaryEntry(language, true)?.text ?? "";
197
211
  }
198
212
  /**
199
- * Get primary rich text in a specific language without fallback
213
+ * Get rich text in a specific language, with no fallback
200
214
  */
201
215
  getExactRichText(language) {
202
- return this.getPrimaryEntry(language)?.richText ?? null;
203
- }
204
- /**
205
- * Get all text entries in a specific language without fallback
206
- */
207
- getExactTexts(language) {
208
- return Array.from(this._content[language] ?? [], (entry) => entry.text);
216
+ return this.resolvePrimaryEntry(language, true)?.richText ?? "";
209
217
  }
210
218
  /**
211
- * Get all rich text entries in a specific language without fallback
212
- */
213
- getExactRichTexts(language) {
214
- return Array.from(this._content[language] ?? [], (entry) => entry.richText);
215
- }
216
- /**
217
- * Get all text entries in a specific language with fallback
218
- */
219
- getTexts(language) {
220
- if (language != null && (this._content[language]?.length ?? 0) > 0) return this.getExactTexts(language);
221
- const defaultLanguage = this._options.defaultLanguage;
222
- if ((this._content[defaultLanguage]?.length ?? 0) > 0) return this.getExactTexts(defaultLanguage);
223
- const firstLanguage = this._availableLanguages[0];
224
- return firstLanguage == null ? [] : this.getExactTexts(firstLanguage);
225
- }
226
- /**
227
- * Get all rich text entries in a specific language with fallback
228
- */
229
- getRichTexts(language) {
230
- if (language != null && (this._content[language]?.length ?? 0) > 0) return this.getExactRichTexts(language);
231
- const defaultLanguage = this._options.defaultLanguage;
232
- if ((this._content[defaultLanguage]?.length ?? 0) > 0) return this.getExactRichTexts(defaultLanguage);
233
- const firstLanguage = this._availableLanguages[0];
234
- return firstLanguage == null ? [] : this.getExactRichTexts(firstLanguage);
235
- }
236
- /**
237
- * Get all entries in a specific language without fallback
219
+ * Get every entry for a specific language, with no fallback
238
220
  */
239
221
  getExactEntries(language) {
240
- return Array.from(this._content[language] ?? [], (entry) => ({
241
- text: entry.text,
242
- richText: entry.richText,
243
- isPrimary: entry.isPrimary
244
- }));
222
+ return Array.from(this.resolveEntries(language, true), (entry) => ({ ...entry }));
245
223
  }
246
224
  /**
247
- * Get all entries in a specific language with fallback
248
- */
249
- getEntries(language) {
250
- if (language != null && (this._content[language]?.length ?? 0) > 0) return this.getExactEntries(language);
251
- const defaultLanguage = this._options.defaultLanguage;
252
- if ((this._content[defaultLanguage]?.length ?? 0) > 0) return this.getExactEntries(defaultLanguage);
253
- const firstLanguage = this._availableLanguages[0];
254
- return firstLanguage == null ? [] : this.getExactEntries(firstLanguage);
255
- }
256
- /**
257
- * Get aliases carried by OCHRE as zxx content
225
+ * Get the alias values OCHRE carries as `zxx` content
258
226
  */
259
227
  getAliases() {
260
228
  return [...this._aliases];
261
229
  }
262
230
  /**
263
- * Check if text exists for a specific language
264
- */
265
- hasLanguage(language) {
266
- return (this._content[language]?.length ?? 0) > 0;
267
- }
268
- /**
269
- * Check if aliases exist
270
- */
271
- hasAliases() {
272
- return this._aliases.length > 0;
273
- }
274
- /**
275
- * Get all available languages
231
+ * Get the languages that actually carry content
276
232
  */
277
233
  getAvailableLanguages() {
278
- return this._availableLanguages;
234
+ return [...this._availableLanguages];
279
235
  }
280
236
  /**
281
- * Get all supported languages (the full language array passed to constructor)
282
- */
283
- getSupportedLanguages() {
284
- return this._options.availableLanguages;
285
- }
286
- /**
287
- * Check if the multilingual string is empty (no content in any language)
288
- */
289
- isEmpty() {
290
- return this._availableLanguages.length === 0;
291
- }
292
- /**
293
- * Check if the multilingual string has any content
294
- */
295
- hasContent() {
296
- for (const language of this._availableLanguages) {
297
- const entries = this._content[language] ?? [];
298
- for (const entry of entries) if (entry.text.trim().length > 0 || entry.richText.trim().length > 0) return true;
299
- }
300
- return false;
301
- }
302
- /**
303
- * Get the default language
237
+ * Get the language reads fall back to before trying the rest
304
238
  */
305
239
  getDefaultLanguage() {
306
240
  return this._options.defaultLanguage;
307
241
  }
308
242
  /**
309
- * Add or update the primary text for a language (returns new instance)
243
+ * Set the text for a language, or append another entry to it
244
+ * @param language - The language to write
245
+ * @param text - The text to write
246
+ * @param options - Write options
247
+ * @param options.shouldAppend - Append as an additional entry instead of replacing
248
+ * @returns A new multilingual string
310
249
  */
311
- withText(language, text) {
250
+ with(language, text, options = {}) {
312
251
  const newContent = cloneContent(this._content);
313
- newContent[language] = entriesFromTexts([text]);
314
- const newAvailableLanguages = getLanguagesWithEntries(newContent, this._options.availableLanguages);
315
- return MultilingualString.fromNormalized(newContent, this._options, newAvailableLanguages);
316
- }
317
- /**
318
- * Add another text entry for a language (returns new instance)
319
- */
320
- withEntry(language, text) {
321
- const newContent = cloneContent(this._content);
322
- newContent[language] = normalizePrimary([...newContent[language] ?? [], {
252
+ newContent[language] = options.shouldAppend === true ? normalizePrimary([...newContent[language] ?? [], {
323
253
  ...normalizeInputText(text),
324
254
  isPrimary: false
325
- }]);
326
- const newAvailableLanguages = getLanguagesWithEntries(newContent, this._options.availableLanguages);
327
- return MultilingualString.fromNormalized(newContent, this._options, newAvailableLanguages);
255
+ }]) : entriesFromTexts([text]);
256
+ return MultilingualString.fromNormalized(newContent, this._options, getLanguagesWithEntries(newContent, this._options.availableLanguages));
328
257
  }
329
258
  /**
330
- * Replace aliases (returns new instance)
259
+ * Remove the content for a language
260
+ *
261
+ * When the removed language was the default, the default moves to the first
262
+ * language that still has content, so reads keep resolving.
263
+ * @param language - The language to remove
264
+ * @returns A new multilingual string
331
265
  */
332
- withAliases(aliases) {
333
- return MultilingualString.fromNormalized(this._content, {
334
- ...this._options,
335
- aliases: normalizeAliases(aliases)
336
- }, this._availableLanguages);
337
- }
338
- /**
339
- * Remove text for a language (returns new instance)
340
- */
341
- withoutLanguage(language) {
342
- const newContent = {};
266
+ without(language) {
343
267
  const currentContent = cloneContent(this._content);
344
- for (const supportedLanguage of this._options.availableLanguages) if (supportedLanguage !== language) newContent[supportedLanguage] = normalizePrimary(currentContent[supportedLanguage] ?? []);
345
- const newAvailableLanguages = getLanguagesWithEntries(newContent, this._options.availableLanguages);
346
- const newDefaultLanguage = this._options.defaultLanguage === language ? newAvailableLanguages[0] ?? this._options.availableLanguages[0] : this._options.defaultLanguage;
347
- return MultilingualString.fromNormalized(newContent, {
348
- ...this._options,
349
- defaultLanguage: newDefaultLanguage
350
- }, newAvailableLanguages);
351
- }
352
- /**
353
- * Transform all language versions (returns new instance)
354
- */
355
- map(function_) {
356
- const newContent = {};
357
- for (const language of this._availableLanguages) newContent[language] = normalizePrimary(Array.from(this._content[language] ?? [], (entry) => ({
358
- text: function_(entry.text, language),
359
- richText: function_(entry.richText, language),
360
- isPrimary: entry.isPrimary
361
- })));
362
- return MultilingualString.fromNormalized(newContent, this._options, this._availableLanguages);
363
- }
364
- /**
365
- * Filter languages based on predicate (returns new instance)
366
- */
367
- filter(shouldInclude) {
368
268
  const newContent = {};
369
- for (const language of this._availableLanguages) {
370
- const entries = [];
371
- const languageEntries = this._content[language] ?? [];
372
- for (const entry of languageEntries) if (shouldInclude(entry.text, language)) entries.push(entry);
373
- newContent[language] = normalizePrimary(entries);
374
- }
269
+ for (const supportedLanguage of this._options.availableLanguages) if (supportedLanguage !== language) newContent[supportedLanguage] = normalizePrimary(currentContent[supportedLanguage] ?? []);
375
270
  const newAvailableLanguages = getLanguagesWithEntries(newContent, this._options.availableLanguages);
376
- const newDefaultLanguage = (newContent[this._options.defaultLanguage]?.length ?? 0) > 0 ? this._options.defaultLanguage : newAvailableLanguages[0] ?? this._options.availableLanguages[0];
377
271
  return MultilingualString.fromNormalized(newContent, {
378
272
  ...this._options,
379
- defaultLanguage: newDefaultLanguage
273
+ defaultLanguage: this._options.defaultLanguage === language ? resolveDefaultLanguageOption(newAvailableLanguages, this._options.availableLanguages) : this._options.defaultLanguage
380
274
  }, newAvailableLanguages);
381
275
  }
382
276
  /**
383
- * Get the string representation (uses default language)
277
+ * Get the string representation, using the default language
384
278
  */
385
279
  toString() {
386
280
  return this.getText();
387
281
  }
388
282
  /**
389
- * Get JSON representation
283
+ * Get the JSON representation
284
+ *
285
+ * Carries `defaultLanguage`, because it is not derivable from the content:
286
+ * without it {@link MultilingualString.fromJSON} would fall back to the first
287
+ * available language and `getText()` would resolve differently.
390
288
  */
391
289
  toJSON() {
392
290
  const content = {};
393
291
  for (const language of this._availableLanguages) content[language] = this.getExactEntries(language);
394
292
  return {
395
293
  content,
396
- aliases: this.getAliases()
294
+ aliases: this.getAliases(),
295
+ defaultLanguage: this._options.defaultLanguage
397
296
  };
398
297
  }
399
298
  };
@@ -0,0 +1,34 @@
1
+ //#region src/parsers/property-token.d.ts
2
+ /**
3
+ * The attributes a property value can carry its text in
4
+ */
5
+ type PropertyValueTextSource = {
6
+ rawValue?: string;
7
+ payload?: string;
8
+ slug?: string;
9
+ };
10
+ /**
11
+ * The text a property value stands for
12
+ *
13
+ * OCHRE spreads that text over four places, so which one wins is real domain
14
+ * knowledge and this is the only place it is written down. The content step is
15
+ * injected because the two callers reach it differently: the item parser builds
16
+ * a full multilingual string, while the rich-text parser needs one language's
17
+ * plain text. Reading the sources in a different order is what let the two
18
+ * disagree for a value carrying both a `slug` and `content`.
19
+ * @param value - The raw property value
20
+ * @param readContentText - Reads the value's `content` as plain text, or null when it has none
21
+ * @returns The text, or an empty string when the value carries none
22
+ * @internal
23
+ */
24
+ export declare function readPropertyValueText(value: PropertyValueTextSource, readContentText: () => string | null): string;
25
+ /**
26
+ * The UUID a property value points at, treating an empty attribute as absent
27
+ * @param value - The raw property value
28
+ * @returns The UUID, or null when there is none
29
+ * @internal
30
+ */
31
+ export declare function readPropertyValueUuid(value: {
32
+ uuid?: string;
33
+ } | undefined): string | null;
34
+ //#endregion
@@ -0,0 +1,29 @@
1
+ //#region src/parsers/property-token.ts
2
+ /**
3
+ * The text a property value stands for
4
+ *
5
+ * OCHRE spreads that text over four places, so which one wins is real domain
6
+ * knowledge and this is the only place it is written down. The content step is
7
+ * injected because the two callers reach it differently: the item parser builds
8
+ * a full multilingual string, while the rich-text parser needs one language's
9
+ * plain text. Reading the sources in a different order is what let the two
10
+ * disagree for a value carrying both a `slug` and `content`.
11
+ * @param value - The raw property value
12
+ * @param readContentText - Reads the value's `content` as plain text, or null when it has none
13
+ * @returns The text, or an empty string when the value carries none
14
+ * @internal
15
+ */
16
+ function readPropertyValueText(value, readContentText) {
17
+ return value.rawValue ?? value.payload ?? readContentText() ?? value.slug ?? "";
18
+ }
19
+ /**
20
+ * The UUID a property value points at, treating an empty attribute as absent
21
+ * @param value - The raw property value
22
+ * @returns The UUID, or null when there is none
23
+ * @internal
24
+ */
25
+ function readPropertyValueUuid(value) {
26
+ return value?.uuid == null || value.uuid === "" ? null : value.uuid;
27
+ }
28
+ //#endregion
29
+ export { readPropertyValueText, readPropertyValueUuid };
@@ -1,7 +1,24 @@
1
1
  import { MultilingualString, MultilingualStringText } from "./multilingual.mjs";
2
2
  import { XMLContent, XMLString } from "../xml/types.mjs";
3
3
  //#region src/parsers/string.d.ts
4
+ /**
5
+ * Rewrite a permanent identification URL as an OCHRE API endpoint
6
+ *
7
+ * The counterpart of {@link transformPermanentIdentificationUrlToItemLink}:
8
+ * the same prefix becomes an API call here and a route into a consuming
9
+ * website there, so which one a caller wants depends on who reads the result.
10
+ * @param url - The permanent identification URL
11
+ * @returns The API URL
12
+ * @internal
13
+ */
4
14
  export declare function transformPermanentIdentificationUrl(url: string): string;
15
+ /**
16
+ * Rewrite a permanent identification URL as a website item route
17
+ * @param url - The permanent identification URL
18
+ * @returns The item route
19
+ * @internal
20
+ */
21
+ export declare function transformPermanentIdentificationUrlToItemLink(url: string): string;
5
22
  export declare function parseXMLString(string: XMLString): MultilingualStringText;
6
23
  /**
7
24
  * Parses rich text content into a formatted string with links and annotations
@@ -9,12 +26,14 @@ export declare function parseXMLString(string: XMLString): MultilingualStringTex
9
26
  * @param item - XML-based rich text item to parse
10
27
  * @param options - Options for parsing
11
28
  * @param options.languages - Languages of the content
29
+ * @param options.defaultLanguage - The language the dataset declares as its default
12
30
  * @returns Plain and rich formatted strings
13
31
  *
14
32
  * @internal
15
33
  */
16
34
  export declare function parseXMLContent<V extends ReadonlyArray<string>>(item: XMLContent, options: {
17
35
  languages: V;
36
+ defaultLanguage?: V[number];
18
37
  }): MultilingualString<V>;
19
38
  /**
20
39
  * Extracts alias strings from XMLContent where lang="zxx"
@@ -1,8 +1,10 @@
1
- import { TEXT_ANNOTATION_UUID } from "../constants.mjs";
1
+ import { OCHRE_ENDPOINT, PERMANENT_IDENTIFICATION_URL_PREFIX, TEXT_ANNOTATION_UUID, getOchreItemUrl } from "../constants.mjs";
2
+ import { normalizePropertyVariableLabel } from "../getters.mjs";
3
+ import { getXMLSourceIndex } from "../xml/metadata.mjs";
2
4
  import { serializeMDXContent, serializeMDXText } from "./mdx.mjs";
3
5
  import { MultilingualString } from "./multilingual.mjs";
6
+ import { readPropertyValueText, readPropertyValueUuid } from "./property-token.mjs";
4
7
  import { renderOptionsSchema } from "../schemas.mjs";
5
- import { getXMLSourceIndex } from "../xml/metadata.mjs";
6
8
  import * as v from "valibot";
7
9
  //#region src/parsers/string.ts
8
10
  const TEXT_ANNOTATION_TOKEN = "text-annotation";
@@ -21,9 +23,6 @@ const MDX_RENDER_ELEMENTS = {
21
23
  italic: "em",
22
24
  underline: "u"
23
25
  };
24
- function isXMLRichTextLink(value) {
25
- return typeof value === "object" && value != null;
26
- }
27
26
  function getLinkStringProperty(link, property) {
28
27
  switch (property) {
29
28
  case "uuid": return "uuid" in link && typeof link.uuid === "string" ? link.uuid : null;
@@ -32,8 +31,27 @@ function getLinkStringProperty(link, property) {
32
31
  case "width": return "width" in link && link.width != null ? link.width.toString() : null;
33
32
  }
34
33
  }
34
+ /**
35
+ * Rewrite a permanent identification URL as an OCHRE API endpoint
36
+ *
37
+ * The counterpart of {@link transformPermanentIdentificationUrlToItemLink}:
38
+ * the same prefix becomes an API call here and a route into a consuming
39
+ * website there, so which one a caller wants depends on who reads the result.
40
+ * @param url - The permanent identification URL
41
+ * @returns The API URL
42
+ * @internal
43
+ */
35
44
  function transformPermanentIdentificationUrl(url) {
36
- return url.replace("https://pi.lib.uchicago.edu/1001/org/ochre/", "https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=");
45
+ return url.replace(PERMANENT_IDENTIFICATION_URL_PREFIX, () => `${OCHRE_ENDPOINT}?uuid=`);
46
+ }
47
+ /**
48
+ * Rewrite a permanent identification URL as a website item route
49
+ * @param url - The permanent identification URL
50
+ * @returns The item route
51
+ * @internal
52
+ */
53
+ function transformPermanentIdentificationUrlToItemLink(url) {
54
+ return url.replace(PERMANENT_IDENTIFICATION_URL_PREFIX, "/item/");
37
55
  }
38
56
  /**
39
57
  * Applies text rendering options (bold, italic, underline) to a string
@@ -126,7 +144,7 @@ function createMDXComponent(variant, properties) {
126
144
  returnString = `<ExternalLink${createMDXStringAttribute("href", href == null ? "#" : transformPermanentIdentificationUrl(href))}>${text}</ExternalLink>`;
127
145
  break;
128
146
  case "documentLink":
129
- returnString = `<ExternalLink${createMDXStringAttribute("href", `https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=${uuid}&load`)}>${text}</ExternalLink>`;
147
+ returnString = `<ExternalLink${createMDXStringAttribute("href", getOchreItemUrl(uuid ?? "null", "load"))}>${text}</ExternalLink>`;
130
148
  break;
131
149
  case "tooltipSpan": returnString = `<TooltipSpan${createMDXStringAttribute("content", content)}>${text}</TooltipSpan>`;
132
150
  }
@@ -137,8 +155,7 @@ function createMDXStringAttribute(name, value) {
137
155
  return ` ${name}=${MDX_QUOTED_ATTRIBUTE_ESCAPE_REGEX.test(value) ? `{${JSON.stringify(value)}}` : `"${value}"`}`;
138
156
  }
139
157
  function getPropertyValueUuid(property) {
140
- const value = property?.value?.[0];
141
- return value?.uuid == null || value.uuid === "" ? null : value.uuid;
158
+ return readPropertyValueUuid(property?.value?.[0]);
142
159
  }
143
160
  function getFirstPropertyMetadata(item) {
144
161
  const itemProperty = item.properties?.property[0];
@@ -159,23 +176,16 @@ function parseContentLikeForLanguage(value, options) {
159
176
  function parsePropertyValueText(property, options) {
160
177
  const value = property?.value?.[0];
161
178
  if (value == null) return "";
162
- if (value.rawValue != null) return value.rawValue;
163
- if (value.payload != null) return value.payload;
164
- if (value.slug != null) return value.slug;
165
- if (value.content != null) return parseContentLikeForLanguage(value, options);
166
- return "";
167
- }
168
- function normalizePropertyToken(value) {
169
- return value.trim().toLowerCase().replaceAll(/[\s_]+/g, "-");
179
+ return readPropertyValueText(value, () => value.content == null ? null : parseContentLikeForLanguage(value, options));
170
180
  }
171
181
  function hasMatchingPropertyLabel(property, uuid, tokens, options) {
172
182
  if (uuid !== "" && property.label.uuid === uuid) return true;
173
- const label = normalizePropertyToken(parseContentLikeForLanguage(property.label, options));
183
+ const label = normalizePropertyVariableLabel(parseContentLikeForLanguage(property.label, options));
174
184
  return tokens.includes(label);
175
185
  }
176
186
  function hasMatchingPropertyValue(property, uuid, tokens, options) {
177
187
  if (uuid !== "" && getPropertyValueUuid(property) === uuid) return true;
178
- const value = normalizePropertyToken(parsePropertyValueText(property, options));
188
+ const value = normalizePropertyVariableLabel(parsePropertyValueText(property, options));
179
189
  return tokens.includes(value);
180
190
  }
181
191
  function parseTextStylingSize(textStylingProperty, options) {
@@ -336,7 +346,7 @@ function getXMLRichTextLinks(item) {
336
346
  for (const rawLinks of linkGroups) {
337
347
  if (!Array.isArray(rawLinks)) continue;
338
348
  for (const rawLink of rawLinks) {
339
- if (!isXMLRichTextLink(rawLink) || isTextAnnotationMarkerLink(rawLink)) continue;
349
+ if (isTextAnnotationMarkerLink(rawLink)) continue;
340
350
  links.push({
341
351
  link: rawLink,
342
352
  fallbackIndex
@@ -408,7 +418,7 @@ function renderRichTextItem(item, linkString, contentItem, options) {
408
418
  let result = "";
409
419
  for (const link of links) {
410
420
  const linkContent = link.identification != null ? "content" in link.identification.label ? parseXMLContent(link.identification.label, { languages }) : MultilingualString.create(contentItem.lang, parseXMLString(link.identification.label), languages) : MultilingualString.create(contentItem.lang, "", languages);
411
- const contentText = (rendering === "plain" ? linkContent.getExactText(contentItem.lang) : linkContent.getExactRichText(contentItem.lang)) ?? "";
421
+ const contentText = rendering === "plain" ? linkContent.getExactText(contentItem.lang) : linkContent.getExactRichText(contentItem.lang);
412
422
  result += renderRichTextLink(item, link, linkString, contentText, annotationMetadata);
413
423
  }
414
424
  return applyNewlineWhitespace(result, item.whitespace, rendering);
@@ -419,6 +429,7 @@ function renderRichTextItem(item, linkString, contentItem, options) {
419
429
  * @param item - XML-based rich text item to parse
420
430
  * @param options - Options for parsing
421
431
  * @param options.languages - Languages of the content
432
+ * @param options.defaultLanguage - The language the dataset declares as its default
422
433
  * @returns Plain and rich formatted strings
423
434
  *
424
435
  * @internal
@@ -434,15 +445,24 @@ function parseXMLContent(item, options) {
434
445
  entries.push(parseXMLContentItem(contentItem, { languages }));
435
446
  content[language] = entries;
436
447
  }
437
- if (Object.keys(content).length > 0) return MultilingualString.fromEntries(content, languages, { aliases });
448
+ if (Object.keys(content).length > 0) return MultilingualString.fromEntries(content, languages, {
449
+ aliases,
450
+ defaultLanguage: options.defaultLanguage
451
+ });
438
452
  for (const contentItem of item.content) {
439
453
  if (contentItem.lang === "zxx") continue;
440
454
  const fallbackText = parseXMLContentItem(contentItem, { languages: [contentItem.lang] });
441
455
  const fallbackContent = {};
442
456
  for (const language of languages) fallbackContent[language] = [fallbackText];
443
- return MultilingualString.fromEntries(fallbackContent, languages, { aliases });
457
+ return MultilingualString.fromEntries(fallbackContent, languages, {
458
+ aliases,
459
+ defaultLanguage: options.defaultLanguage
460
+ });
444
461
  }
445
- return MultilingualString.empty(languages, { aliases });
462
+ return MultilingualString.empty(languages, {
463
+ aliases,
464
+ defaultLanguage: options.defaultLanguage
465
+ });
446
466
  }
447
467
  function parseXMLContentItem(contentItem, options) {
448
468
  const rawMDXBlocks = [];
@@ -483,4 +503,4 @@ function extractAliases(content) {
483
503
  return aliases.length > 0 ? aliases : null;
484
504
  }
485
505
  //#endregion
486
- export { extractAliases, parseXMLContent, parseXMLString, transformPermanentIdentificationUrl };
506
+ export { extractAliases, parseXMLContent, parseXMLString, transformPermanentIdentificationUrl, transformPermanentIdentificationUrlToItemLink };
@@ -0,0 +1,10 @@
1
+ //#region src/parsers/website/bounds.d.ts
2
+ /**
3
+ * Parses raw bounds data into a standardized bounds structure
4
+ *
5
+ * @param bounds - Raw bounds data in OCHRE format
6
+ * @returns Parsed bounds object
7
+ * @internal
8
+ */
9
+ export declare function parseBounds(bounds: string): [[number, number], [number, number]];
10
+ //#endregion