ochre-sdk 0.20.20 → 0.20.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1223,7 +1223,6 @@ declare function fetchItem<T extends DataCategory = DataCategory, U extends Data
1223
1223
  *
1224
1224
  * @param params - The parameters for the fetch
1225
1225
  * @param params.setScopeUuids - The Set scope UUIDs to filter by
1226
- * @param params.belongsToCollectionScopeUuids - The collection scope UUIDs to filter by
1227
1226
  * @param params.propertyVariableUuids - The property variable UUIDs to filter by
1228
1227
  * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
1229
1228
  * @param params.sort - Optional sorting configuration applied before pagination.
@@ -1238,7 +1237,6 @@ declare function fetchItem<T extends DataCategory = DataCategory, U extends Data
1238
1237
  */
1239
1238
  declare function fetchSetItems<U extends Array<DataCategory> = Array<DataCategory>>(params: {
1240
1239
  setScopeUuids: Array<string>;
1241
- belongsToCollectionScopeUuids: Array<string>;
1242
1240
  propertyVariableUuids: Array<string>;
1243
1241
  queries: Array<Query>;
1244
1242
  sort?: SetItemsSort;
@@ -1267,7 +1265,6 @@ declare function fetchSetItems<U extends Array<DataCategory> = Array<DataCategor
1267
1265
  *
1268
1266
  * @param params - The parameters for the fetch
1269
1267
  * @param params.setScopeUuids - An array of set scope UUIDs to filter by
1270
- * @param params.belongsToCollectionScopeUuids - The collection scope UUIDs to filter by
1271
1268
  * @param params.propertyVariableUuids - The property variable UUIDs to query by
1272
1269
  * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
1273
1270
  * @param params.attributes - Whether to return values for bibliographies and periods
@@ -1282,7 +1279,6 @@ declare function fetchSetItems<U extends Array<DataCategory> = Array<DataCategor
1282
1279
  */
1283
1280
  declare function fetchSetPropertyValuesByPropertyVariables(params: {
1284
1281
  setScopeUuids: Array<string>;
1285
- belongsToCollectionScopeUuids: Array<string>;
1286
1282
  propertyVariableUuids: Array<string>;
1287
1283
  queries?: Array<Query>;
1288
1284
  attributes?: {
package/dist/index.mjs CHANGED
@@ -16,6 +16,9 @@ const TEXT_ANNOTATION_TEXT_STYLING_HEADING_LEVEL_UUID = "d4266f0b-3f8d-4b32-8c15
16
16
 
17
17
  //#endregion
18
18
  //#region src/utils/string.ts
19
+ const EMAIL_BRACKET_CLEANUP_REGEX = /(?<=\s|^)[([{]+|[)\]}]+(?=\s|$)/g;
20
+ const EMAIL_PUNCTUATION_CLEANUP_REGEX = /[!),:;?\]]/g;
21
+ const EMAIL_TRAILING_PERIOD_REGEX = /\.$/;
19
22
  /**
20
23
  * Finds a string item in an array by language code
21
24
  *
@@ -46,7 +49,7 @@ function parseEmail(string) {
46
49
  const splitString = string.split(" ");
47
50
  const returnSplitString = [];
48
51
  for (const string of splitString) {
49
- const cleanString = transformPermanentIdentificationUrl(string).replaceAll(/(?<=\s|^)[([{]+|[)\]}]+(?=\s|$)/g, "").replaceAll(/[!),:;?\]]/g, "").replace(/\.$/, "");
52
+ const cleanString = transformPermanentIdentificationUrl(string).replaceAll(EMAIL_BRACKET_CLEANUP_REGEX, "").replaceAll(EMAIL_PUNCTUATION_CLEANUP_REGEX, "").replace(EMAIL_TRAILING_PERIOD_REGEX, "");
50
53
  const index = string.indexOf(cleanString);
51
54
  const before = string.slice(0, index);
52
55
  const after = string.slice(index + cleanString.length);
@@ -442,6 +445,7 @@ function parseStringContent(content, language = "eng") {
442
445
 
443
446
  //#endregion
444
447
  //#region src/utils/internal.ts
448
+ const PSEUDO_UUID_REGEX = /^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/i;
445
449
  /**
446
450
  * Get the category of an item from the OCHRE API response
447
451
  * @param keys - The keys of the OCHRE API response
@@ -481,7 +485,7 @@ function getItemCategories(keys) {
481
485
  * @internal
482
486
  */
483
487
  function isPseudoUuid(value) {
484
- return /^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/i.test(value);
488
+ return PSEUDO_UUID_REGEX.test(value);
485
489
  }
486
490
  /**
487
491
  * Flatten a properties array
@@ -900,6 +904,7 @@ const setItemsParamsSchema = z.object({
900
904
 
901
905
  //#endregion
902
906
  //#region src/utils/parse/index.ts
907
+ const TRAILING_ELLIPSIS_REGEX = /\s*\.{3}$/;
903
908
  /**
904
909
  * Parses raw identification data into the standardized Identification type
905
910
  *
@@ -1406,7 +1411,7 @@ function parseProperty(property, language = "eng") {
1406
1411
  });
1407
1412
  return {
1408
1413
  uuid: property.label.uuid,
1409
- label: parseStringContent(property.label, language).replace(/\s*\.{3}$/, "").trim(),
1414
+ label: parseStringContent(property.label, language).replace(TRAILING_ELLIPSIS_REGEX, "").trim(),
1410
1415
  values,
1411
1416
  comment: property.comment != null ? parseStringContent(property.comment) : null,
1412
1417
  properties: property.property ? parseProperties(ensureArray(property.property)) : []
@@ -2142,10 +2147,18 @@ async function fetchItem(uuid, category, itemCategories, options) {
2142
2147
 
2143
2148
  //#endregion
2144
2149
  //#region src/utils/fetchers/set/query-helpers.ts
2145
- /**
2146
- * Build a string match predicate for an XQuery string
2147
- */
2148
- function buildStringMatchPredicate(params) {
2150
+ const CTS_INCLUDES_STOP_WORDS = [
2151
+ "of",
2152
+ "the",
2153
+ "and",
2154
+ "in",
2155
+ "it"
2156
+ ];
2157
+ const CTS_INCLUDES_STOP_WORDS_VAR = "$ctsIncludesStopWords";
2158
+ /**
2159
+ * Build a string match predicate for an XQuery string.
2160
+ */
2161
+ function buildRawStringMatchPredicate(params) {
2149
2162
  const { path, value, matchMode, isCaseSensitive } = params;
2150
2163
  const comparedPath = isCaseSensitive ? path : `lower-case(${path})`;
2151
2164
  const comparedValueLiteral = stringLiteral(isCaseSensitive ? value : value.toLowerCase());
@@ -2153,6 +2166,77 @@ function buildStringMatchPredicate(params) {
2153
2166
  return `${comparedPath} = ${comparedValueLiteral}`;
2154
2167
  }
2155
2168
  /**
2169
+ * Build CTS word-query options for API v2 includes search.
2170
+ */
2171
+ function buildCtsQueryOptionsExpression(isCaseSensitive) {
2172
+ return `(${[
2173
+ isCaseSensitive ? "case-sensitive" : "case-insensitive",
2174
+ "diacritic-insensitive",
2175
+ "punctuation-insensitive",
2176
+ "whitespace-insensitive",
2177
+ "stemmed"
2178
+ ].map((option) => stringLiteral(option)).join(", ")})`;
2179
+ }
2180
+ /**
2181
+ * Build a CTS-backed includes predicate for an XQuery string.
2182
+ */
2183
+ function buildCtsIncludesPredicate(params) {
2184
+ const { path, value, isCaseSensitive, queryIndex } = params;
2185
+ const searchStringVar = `$query${queryIndex}SearchString`;
2186
+ const rawTermsVar = `$query${queryIndex}RawTerms`;
2187
+ const termsVar = `$query${queryIndex}Terms`;
2188
+ const ctsQueryVar = `$query${queryIndex}CtsQuery`;
2189
+ const ctsOptionsExpression = buildCtsQueryOptionsExpression(isCaseSensitive);
2190
+ const fallbackPredicate = buildRawStringMatchPredicate({
2191
+ path,
2192
+ value,
2193
+ matchMode: "includes",
2194
+ isCaseSensitive
2195
+ });
2196
+ return {
2197
+ declarations: [
2198
+ `let ${searchStringVar} := ${stringLiteral(value)}`,
2199
+ String.raw`let ${rawTermsVar} := fn:tokenize(${searchStringVar}, "\W+")`,
2200
+ `let ${termsVar} :=
2201
+ for $term in ${rawTermsVar}
2202
+ let $normalizedTerm := fn:lower-case($term)
2203
+ where $normalizedTerm ne "" and not($normalizedTerm = ${CTS_INCLUDES_STOP_WORDS_VAR})
2204
+ return ${isCaseSensitive ? "$term" : "$normalizedTerm"}`,
2205
+ `let ${ctsQueryVar} :=
2206
+ if (count(${termsVar}) = 1)
2207
+ then cts:word-query(${termsVar}[1], ${ctsOptionsExpression})
2208
+ else if (count(${termsVar}) gt 1)
2209
+ then cts:near-query((
2210
+ for $term in ${termsVar}
2211
+ return cts:word-query($term, ${ctsOptionsExpression})
2212
+ ), 5, ("unordered"))
2213
+ else ()`
2214
+ ],
2215
+ predicate: `(if (exists(${ctsQueryVar})) then cts:contains(${path}, ${ctsQueryVar}) else ${fallbackPredicate})`
2216
+ };
2217
+ }
2218
+ /**
2219
+ * Build a string match predicate for an XQuery string.
2220
+ */
2221
+ function buildStringMatchPredicate(params) {
2222
+ const { path, value, matchMode, isCaseSensitive, version, queryIndex } = params;
2223
+ if (matchMode === "includes" && version === 2) return buildCtsIncludesPredicate({
2224
+ path,
2225
+ value,
2226
+ isCaseSensitive,
2227
+ queryIndex
2228
+ });
2229
+ return {
2230
+ declarations: [],
2231
+ predicate: buildRawStringMatchPredicate({
2232
+ path,
2233
+ value,
2234
+ matchMode,
2235
+ isCaseSensitive
2236
+ })
2237
+ };
2238
+ }
2239
+ /**
2156
2240
  * Build a date/dateTime range predicate for an XQuery string.
2157
2241
  */
2158
2242
  function buildDateRangePredicate(params) {
@@ -2163,76 +2247,136 @@ function buildDateRangePredicate(params) {
2163
2247
  return conditions.join(" and ");
2164
2248
  }
2165
2249
  /**
2166
- * Build a property value predicate for an XQuery string
2250
+ * Build a property value predicate for an XQuery string.
2167
2251
  */
2168
- function buildPropertyValuePredicate(query) {
2169
- if (query.dataType === "IDREF") return `.//properties//property[value[@uuid=${stringLiteral(query.value)}]]`;
2170
- if (query.dataType === "date" || query.dataType === "dateTime") return `.//properties//property[(label/@uuid=${stringLiteral(query.value)}) and ${buildDateRangePredicate({
2171
- from: query.from,
2172
- to: query.to
2173
- })}]`;
2174
- if (query.dataType === "time" || query.dataType === "integer" || query.dataType === "decimal" || query.dataType === "boolean") return `.//properties//property[value[@rawValue=${stringLiteral(query.value)}]]`;
2175
- return `.//properties//property[${buildStringMatchPredicate({
2252
+ function buildPropertyValuePredicate(params) {
2253
+ const { query, version, queryIndex } = params;
2254
+ if (query.dataType === "IDREF") return {
2255
+ declarations: [],
2256
+ predicate: `.//properties//property[value[@uuid=${stringLiteral(query.value)}]]`
2257
+ };
2258
+ if (query.dataType === "date" || query.dataType === "dateTime") return {
2259
+ declarations: [],
2260
+ predicate: `.//properties//property[(label/@uuid=${stringLiteral(query.value)}) and ${buildDateRangePredicate({
2261
+ from: query.from,
2262
+ to: query.to
2263
+ })}]`
2264
+ };
2265
+ if (query.dataType === "time" || query.dataType === "integer" || query.dataType === "decimal" || query.dataType === "boolean") return {
2266
+ declarations: [],
2267
+ predicate: `.//properties//property[value[@rawValue=${stringLiteral(query.value)}]]`
2268
+ };
2269
+ const compiledStringPredicate = buildStringMatchPredicate({
2176
2270
  path: `string-join(value/content[@xml:lang="${query.language}"]/string, "")`,
2177
2271
  value: query.value,
2178
2272
  matchMode: query.matchMode,
2179
- isCaseSensitive: query.isCaseSensitive
2180
- })}]`;
2273
+ isCaseSensitive: query.isCaseSensitive,
2274
+ version,
2275
+ queryIndex
2276
+ });
2277
+ return {
2278
+ declarations: compiledStringPredicate.declarations,
2279
+ predicate: `.//properties//property[${compiledStringPredicate.predicate}]`
2280
+ };
2181
2281
  }
2182
2282
  /**
2183
- * Build a query predicate for an XQuery string
2283
+ * Build a query predicate for an XQuery string.
2184
2284
  */
2185
- function buildQueryPredicate(query) {
2285
+ function buildQueryPredicate(params) {
2286
+ const { query, version, queryIndex } = params;
2186
2287
  switch (query.target) {
2187
2288
  case "title": return buildStringMatchPredicate({
2188
2289
  path: `string-join(identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2189
2290
  value: query.value,
2190
2291
  matchMode: query.matchMode,
2191
- isCaseSensitive: query.isCaseSensitive
2292
+ isCaseSensitive: query.isCaseSensitive,
2293
+ version,
2294
+ queryIndex
2192
2295
  });
2193
2296
  case "description": return buildStringMatchPredicate({
2194
2297
  path: `string-join(description/content[@xml:lang="${query.language}"]/string, "")`,
2195
2298
  value: query.value,
2196
2299
  matchMode: query.matchMode,
2197
- isCaseSensitive: query.isCaseSensitive
2300
+ isCaseSensitive: query.isCaseSensitive,
2301
+ version,
2302
+ queryIndex
2198
2303
  });
2199
2304
  case "periods": return buildStringMatchPredicate({
2200
2305
  path: `string-join(periods/period/identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2201
2306
  value: query.value,
2202
2307
  matchMode: query.matchMode,
2203
- isCaseSensitive: query.isCaseSensitive
2308
+ isCaseSensitive: query.isCaseSensitive,
2309
+ version,
2310
+ queryIndex
2204
2311
  });
2205
2312
  case "bibliography": return buildStringMatchPredicate({
2206
2313
  path: `string-join(bibliographies/bibliography/identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2207
2314
  value: query.value,
2208
2315
  matchMode: query.matchMode,
2209
- isCaseSensitive: query.isCaseSensitive
2316
+ isCaseSensitive: query.isCaseSensitive,
2317
+ version,
2318
+ queryIndex
2210
2319
  });
2211
2320
  case "image": return buildStringMatchPredicate({
2212
2321
  path: `string-join(image/identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2213
2322
  value: query.value,
2214
2323
  matchMode: query.matchMode,
2215
- isCaseSensitive: query.isCaseSensitive
2324
+ isCaseSensitive: query.isCaseSensitive,
2325
+ version,
2326
+ queryIndex
2327
+ });
2328
+ case "propertyValue": return buildPropertyValuePredicate({
2329
+ query,
2330
+ version,
2331
+ queryIndex
2216
2332
  });
2217
- case "propertyValue": return buildPropertyValuePredicate(query);
2218
2333
  }
2219
2334
  }
2220
2335
  /**
2221
2336
  * Build a boolean query clause for an XQuery string.
2222
2337
  */
2223
- function buildBooleanQueryClause(query) {
2224
- const baseClause = `(${buildQueryPredicate(query)})`;
2225
- return query.isNegated ? `not(${baseClause})` : baseClause;
2338
+ function buildBooleanQueryClause(params) {
2339
+ const { query, version, queryIndex } = params;
2340
+ const compiledQueryPredicate = buildQueryPredicate({
2341
+ query,
2342
+ version,
2343
+ queryIndex
2344
+ });
2345
+ const baseClause = `(${compiledQueryPredicate.predicate})`;
2346
+ return {
2347
+ declarations: compiledQueryPredicate.declarations,
2348
+ predicate: query.isNegated ? `not(${baseClause})` : baseClause
2349
+ };
2226
2350
  }
2227
2351
  /**
2228
2352
  * Build query filters for an XQuery string.
2229
2353
  */
2230
- function buildQueryFilters(queries) {
2231
- return queries.map((query, index) => {
2232
- const clause = buildBooleanQueryClause(query);
2233
- if (index === 0) return clause;
2234
- return `${query.operator === "AND" ? "and" : "or"} ${clause}`;
2235
- }).join(" ");
2354
+ function buildQueryFilters(params) {
2355
+ const { queries, version } = params;
2356
+ const declarations = [];
2357
+ const predicateParts = [];
2358
+ let hasCtsIncludesClauses = false;
2359
+ for (const [index, query] of queries.entries()) {
2360
+ const compiledClause = buildBooleanQueryClause({
2361
+ query,
2362
+ version,
2363
+ queryIndex: index + 1
2364
+ });
2365
+ if (compiledClause.declarations.length > 0) {
2366
+ hasCtsIncludesClauses = true;
2367
+ declarations.push(...compiledClause.declarations);
2368
+ }
2369
+ if (index === 0) {
2370
+ predicateParts.push(compiledClause.predicate);
2371
+ continue;
2372
+ }
2373
+ predicateParts.push(`${query.operator === "AND" ? "and" : "or"} ${compiledClause.predicate}`);
2374
+ }
2375
+ if (hasCtsIncludesClauses) declarations.unshift(`let ${CTS_INCLUDES_STOP_WORDS_VAR} := (${CTS_INCLUDES_STOP_WORDS.map((stopWord) => stringLiteral(stopWord)).join(", ")})`);
2376
+ return {
2377
+ declarations,
2378
+ predicate: predicateParts.join(" ")
2379
+ };
2236
2380
  }
2237
2381
 
2238
2382
  //#endregion
@@ -2328,7 +2472,11 @@ function buildXQuery$1(params, options) {
2328
2472
  const startPosition = (page - 1) * pageSize + 1;
2329
2473
  const endPosition = page * pageSize;
2330
2474
  const setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
2331
- const queryFilters = buildQueryFilters(queries);
2475
+ const compiledQueryFilters = buildQueryFilters({
2476
+ queries,
2477
+ version
2478
+ });
2479
+ const queryFilterDeclarations = compiledQueryFilters.declarations.length > 0 ? `${compiledQueryFilters.declarations.join("\n")}\n\n` : "";
2332
2480
  const filterPredicates = [];
2333
2481
  if (belongsToCollectionScopeUuids.length > 0) {
2334
2482
  const belongsToCollectionScopeValues = belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
@@ -2338,10 +2486,10 @@ function buildXQuery$1(params, options) {
2338
2486
  const propertyVariables = propertyVariableUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
2339
2487
  filterPredicates.push(`.//properties//property[label[${propertyVariables}]]`);
2340
2488
  }
2341
- if (queryFilters.length > 0) filterPredicates.push(`(${queryFilters})`);
2489
+ if (compiledQueryFilters.predicate.length > 0) filterPredicates.push(`(${compiledQueryFilters.predicate})`);
2342
2490
  const itemFilters = filterPredicates.length > 0 ? `[${filterPredicates.join(" and ")}]` : "";
2343
2491
  const orderedItemsClause = buildOrderedItemsClause(sort);
2344
- return `<ochre>{${`let $items := ${version === 2 ? "doc()" : "input()"}/ochre
2492
+ return `<ochre>{${`${queryFilterDeclarations}let $items := ${version === 2 ? "doc()" : "input()"}/ochre
2345
2493
  ${setScopeFilter}
2346
2494
  ${itemFilters}
2347
2495
 
@@ -2360,7 +2508,6 @@ function buildXQuery$1(params, options) {
2360
2508
  *
2361
2509
  * @param params - The parameters for the fetch
2362
2510
  * @param params.setScopeUuids - The Set scope UUIDs to filter by
2363
- * @param params.belongsToCollectionScopeUuids - The collection scope UUIDs to filter by
2364
2511
  * @param params.propertyVariableUuids - The property variable UUIDs to filter by
2365
2512
  * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
2366
2513
  * @param params.sort - Optional sorting configuration applied before pagination.
@@ -2386,7 +2533,13 @@ async function fetchSetItems(params, itemCategories, options) {
2386
2533
  page,
2387
2534
  pageSize
2388
2535
  }, { version });
2389
- const response = await (options?.fetch ?? fetch)(version === 2 ? `https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"` : `https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"`);
2536
+ let response;
2537
+ if (version === 2) response = await (options?.fetch ?? fetch)("https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery&format=json", {
2538
+ method: "POST",
2539
+ body: xquery,
2540
+ headers: { "Content-Type": "application/xquery" }
2541
+ });
2542
+ else response = await (options?.fetch ?? fetch)(`https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"`);
2390
2543
  if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`);
2391
2544
  const data = await response.json();
2392
2545
  if (Array.isArray(data.result) || Object.keys(data.result).length === 0) throw new Error("No items found");
@@ -2622,13 +2775,17 @@ function buildXQuery(params, options) {
2622
2775
  let setScopeFilter = "/set/items/*";
2623
2776
  if (setScopeUuids.length > 0) setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
2624
2777
  const propertyVariableFilters = propertyVariableUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
2625
- const queryFilters = buildQueryFilters(queries);
2778
+ const compiledQueryFilters = buildQueryFilters({
2779
+ queries,
2780
+ version
2781
+ });
2782
+ const queryFilterDeclarations = compiledQueryFilters.declarations.length > 0 ? `${compiledQueryFilters.declarations.join("\n")}\n\n` : "";
2626
2783
  const filterPredicates = [];
2627
2784
  if (belongsToCollectionScopeUuids.length > 0) {
2628
2785
  const belongsToCollectionScopeValues = belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
2629
2786
  filterPredicates.push(`.//properties[property[label/@uuid="${BELONGS_TO_COLLECTION_UUID}" and value[${belongsToCollectionScopeValues}]]]`);
2630
2787
  }
2631
- if (queryFilters.length > 0) filterPredicates.push(`(${queryFilters})`);
2788
+ if (compiledQueryFilters.predicate.length > 0) filterPredicates.push(`(${compiledQueryFilters.predicate})`);
2632
2789
  const itemFilters = filterPredicates.length > 0 ? `[${filterPredicates.join(" and ")}]` : "";
2633
2790
  const queryBlocks = [`let $matching-props := $items//property[label[${propertyVariableFilters}]]
2634
2791
 
@@ -2659,7 +2816,7 @@ let $property-values :=
2659
2816
  return <attributeValue attributeType="periods" itemUuid="{$item/@uuid}" content="{$label}" />`);
2660
2817
  returnedSequences.push("$period-values");
2661
2818
  }
2662
- return `<ochre>{${`let $items := ${version === 2 ? "doc()" : "input()"}/ochre
2819
+ return `<ochre>{${`${queryFilterDeclarations}let $items := ${version === 2 ? "doc()" : "input()"}/ochre
2663
2820
  ${setScopeFilter}
2664
2821
  ${itemFilters}
2665
2822
 
@@ -2672,7 +2829,6 @@ return (${returnedSequences.join(", ")})`}}</ochre>`;
2672
2829
  *
2673
2830
  * @param params - The parameters for the fetch
2674
2831
  * @param params.setScopeUuids - An array of set scope UUIDs to filter by
2675
- * @param params.belongsToCollectionScopeUuids - The collection scope UUIDs to filter by
2676
2832
  * @param params.propertyVariableUuids - The property variable UUIDs to query by
2677
2833
  * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
2678
2834
  * @param params.attributes - Whether to return values for bibliographies and periods
@@ -2697,7 +2853,13 @@ async function fetchSetPropertyValuesByPropertyVariables(params, options) {
2697
2853
  attributes,
2698
2854
  isLimitedToLeafPropertyValues
2699
2855
  }, { version });
2700
- const response = await (options?.fetch ?? fetch)(version === 2 ? `https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"` : `https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"`);
2856
+ let response;
2857
+ if (version === 2) response = await (options?.fetch ?? fetch)("https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery&format=json", {
2858
+ method: "POST",
2859
+ body: xquery,
2860
+ headers: { "Content-Type": "application/xquery" }
2861
+ });
2862
+ else response = await (options?.fetch ?? fetch)(`https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"`);
2701
2863
  if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`);
2702
2864
  const data = await response.json();
2703
2865
  const parsedResultRaw = responseSchema.parse(data);
@@ -3006,6 +3168,8 @@ function filterProperties(property, filter, options = DEFAULT_OPTIONS) {
3006
3168
 
3007
3169
  //#endregion
3008
3170
  //#region src/utils/parse/website.ts
3171
+ const SEGMENT_UNIQUE_SLUG_PREFIX_REGEX = /^\$[^-]*-/;
3172
+ const TRAILING_SLASH_REGEX = /\/$/;
3009
3173
  /**
3010
3174
  * Extracts CSS style properties for a given presentation variant.
3011
3175
  *
@@ -3726,13 +3890,13 @@ function parseWebpage(webpageResource, slugPrefix) {
3726
3890
  const webpageProperties = webpageResource.properties ? parseProperties(ensureArray(webpageResource.properties.property)) : [];
3727
3891
  if (webpageProperties.length === 0 || getPropertyValueByLabel(webpageProperties, "presentation") !== "page") return null;
3728
3892
  const identification = parseIdentification(webpageResource.identification);
3729
- const slug = webpageResource.slug?.replace(/^\$[^-]*-/, "") ?? null;
3893
+ const slug = webpageResource.slug?.replace(SEGMENT_UNIQUE_SLUG_PREFIX_REGEX, "") ?? null;
3730
3894
  if (slug == null) throw new Error(`Slug not found for page “${identification.label}”`);
3731
3895
  const returnWebpage = {
3732
3896
  uuid: webpageResource.uuid,
3733
3897
  type: "page",
3734
3898
  title: identification.label,
3735
- slug: slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug,
3899
+ slug: slugPrefix != null ? `${slugPrefix}/${slug}`.replace(TRAILING_SLASH_REGEX, "") : slug,
3736
3900
  publicationDateTime: parseOptionalDate(webpageResource.publicationDateTime),
3737
3901
  items: [],
3738
3902
  properties: {
@@ -3831,7 +3995,7 @@ function parseWebSegment(segmentResource, slugPrefix) {
3831
3995
  publicationDateTime: parseOptionalDate(segmentResource.publicationDateTime),
3832
3996
  items: []
3833
3997
  };
3834
- returnSegment.items = parseWebSegmentItems(segmentResource.resource ? ensureArray(segmentResource.resource) : [], slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug);
3998
+ returnSegment.items = parseWebSegmentItems(segmentResource.resource ? ensureArray(segmentResource.resource) : [], slugPrefix != null ? `${slugPrefix}/${slug}`.replace(TRAILING_SLASH_REGEX, "") : slug);
3835
3999
  return returnSegment;
3836
4000
  }
3837
4001
  /**
@@ -3869,7 +4033,7 @@ function parseWebSegmentItem(segmentItemResource, slugPrefix) {
3869
4033
  items: []
3870
4034
  };
3871
4035
  const resources = segmentItemResource.resource ? ensureArray(segmentItemResource.resource) : [];
3872
- returnSegmentItem.items.push(...parseWebpages(resources, slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug), ...parseSegments(resources, slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug));
4036
+ returnSegmentItem.items.push(...parseWebpages(resources, slugPrefix != null ? `${slugPrefix}/${slug}`.replace(TRAILING_SLASH_REGEX, "") : slug), ...parseSegments(resources, slugPrefix != null ? `${slugPrefix}/${slug}`.replace(TRAILING_SLASH_REGEX, "") : slug));
3873
4037
  return returnSegmentItem;
3874
4038
  }
3875
4039
  /**
@@ -4275,6 +4439,7 @@ function parseWebsite(websiteTree, metadata, belongsTo, { version = DEFAULT_API_
4275
4439
 
4276
4440
  //#endregion
4277
4441
  //#region src/utils/fetchers/website.ts
4442
+ const API_VERSION_SUFFIX_REGEX = /-v\d+$/;
4278
4443
  /**
4279
4444
  * Parses the version suffix from an API abbreviation
4280
4445
  *
@@ -4282,7 +4447,7 @@ function parseWebsite(websiteTree, metadata, belongsTo, { version = DEFAULT_API_
4282
4447
  * @returns The parsed abbreviation and API version
4283
4448
  */
4284
4449
  function parseApiVersionSuffix(abbreviation) {
4285
- if (!/-v\d+$/.test(abbreviation)) return {
4450
+ if (!API_VERSION_SUFFIX_REGEX.test(abbreviation)) return {
4286
4451
  abbreviation,
4287
4452
  version: DEFAULT_API_VERSION
4288
4453
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ochre-sdk",
3
- "version": "0.20.20",
3
+ "version": "0.20.22",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Node.js library for working with OCHRE (Online Cultural and Historical Research Environment) data",
@@ -46,8 +46,8 @@
46
46
  "zod": "^4.3.6"
47
47
  },
48
48
  "devDependencies": {
49
- "@antfu/eslint-config": "^7.6.1",
50
- "@types/node": "^24.11.0",
49
+ "@antfu/eslint-config": "^7.7.0",
50
+ "@types/node": "^24.12.0",
51
51
  "bumpp": "^10.4.1",
52
52
  "eslint": "^10.0.2",
53
53
  "prettier": "^3.8.1",