ochre-sdk 0.20.21 → 0.20.23

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
@@ -706,6 +706,14 @@ type Query = {
706
706
  language: string;
707
707
  operator?: "AND" | "OR";
708
708
  isNegated?: boolean;
709
+ } | {
710
+ target: "string";
711
+ value: string;
712
+ matchMode: "includes" | "exact";
713
+ isCaseSensitive: boolean;
714
+ language: string;
715
+ operator?: "AND" | "OR";
716
+ isNegated?: boolean;
709
717
  } | {
710
718
  target: "title" | "description" | "image" | "periods" | "bibliography";
711
719
  value: string;
package/dist/index.mjs CHANGED
@@ -2,18 +2,13 @@ import * as z from "zod";
2
2
  import { parseISO, set } from "date-fns";
3
3
  import { UTCDate } from "@date-fns/utc";
4
4
  import { deepEqual } from "fast-equals";
5
-
6
5
  //#region src/constants.ts
7
6
  const BELONGS_TO_COLLECTION_UUID = "30054cb2-909a-4f34-8db9-8fe7369d691d";
8
- const PRESENTATION_ITEM_UUID = "f1c131b6-1498-48a4-95bf-a9edae9fd518";
9
- const TEXT_ANNOTATION_UUID = "b9ca2732-78f4-416e-b77f-dae7647e68a9";
10
7
  const TEXT_ANNOTATION_HOVER_CARD_UUID = "c7f6a08a-f07b-49b6-bcb1-af485da3c58f";
11
8
  const TEXT_ANNOTATION_ITEM_PAGE_VARIANT_UUID = "bf4476ab-6bc8-40d0-a001-1446213c72ce";
12
9
  const TEXT_ANNOTATION_ENTRY_PAGE_VARIANT_UUID = "9d52db95-a9cf-45f7-a0bf-fc9ba9f0aae0";
13
- const TEXT_ANNOTATION_TEXT_STYLING_UUID = "3e6f86ab-df81-45ae-8257-e2867357df56";
14
10
  const TEXT_ANNOTATION_TEXT_STYLING_VARIANT_UUID = "e1647bef-d801-4100-bdde-d081c422f763";
15
11
  const TEXT_ANNOTATION_TEXT_STYLING_HEADING_LEVEL_UUID = "d4266f0b-3f8d-4b32-8c15-4b229c8bb11e";
16
-
17
12
  //#endregion
18
13
  //#region src/utils/string.ts
19
14
  const EMAIL_BRACKET_CLEANUP_REGEX = /(?<=\s|^)[([{]+|[)\]}]+(?=\s|$)/g;
@@ -180,7 +175,7 @@ function extractAnnotationMetadata(item) {
180
175
  if (itemProperty == null) return result;
181
176
  const itemPropertyLabelUuid = itemProperty.label.uuid;
182
177
  const itemPropertyValueUuid = typeof itemProperty.value === "object" && "uuid" in itemProperty.value && itemProperty.value.uuid != null ? itemProperty.value.uuid : null;
183
- if (itemPropertyLabelUuid !== PRESENTATION_ITEM_UUID || itemPropertyValueUuid !== TEXT_ANNOTATION_UUID) return result;
178
+ if (itemPropertyLabelUuid !== "f1c131b6-1498-48a4-95bf-a9edae9fd518" || itemPropertyValueUuid !== "b9ca2732-78f4-416e-b77f-dae7647e68a9") return result;
184
179
  const textAnnotationProperties = itemProperty.property != null ? Array.isArray(itemProperty.property) ? itemProperty.property : [itemProperty.property] : [];
185
180
  for (const textAnnotationProperty of textAnnotationProperties) {
186
181
  const textAnnotationPropertyValueUuid = typeof textAnnotationProperty.value === "object" && "uuid" in textAnnotationProperty.value && textAnnotationProperty.value.uuid != null ? textAnnotationProperty.value.uuid : null;
@@ -194,7 +189,7 @@ function extractAnnotationMetadata(item) {
194
189
  case TEXT_ANNOTATION_ENTRY_PAGE_VARIANT_UUID:
195
190
  result.linkVariant = "entry-page";
196
191
  break;
197
- default: if (textAnnotationPropertyValueUuid === TEXT_ANNOTATION_TEXT_STYLING_UUID && textAnnotationProperty.property != null) {
192
+ default: if (textAnnotationPropertyValueUuid === "3e6f86ab-df81-45ae-8257-e2867357df56" && textAnnotationProperty.property != null) {
198
193
  let textStylingVariant = "block";
199
194
  let textStylingSize = "md";
200
195
  let textStylingHeadingLevel = null;
@@ -211,7 +206,7 @@ function extractAnnotationMetadata(item) {
211
206
  }
212
207
  const textStylingHeadingLevelProperty = textStylingProperties.find((property) => property.label.uuid === TEXT_ANNOTATION_TEXT_STYLING_HEADING_LEVEL_UUID);
213
208
  if (textStylingHeadingLevelProperty != null) textStylingHeadingLevel = parseFakeString(textStylingHeadingLevelProperty.value.content);
214
- const textStylingCssProperties = textStylingProperties.filter((property) => property.label.uuid !== TEXT_ANNOTATION_TEXT_STYLING_VARIANT_UUID && property.label.uuid !== TEXT_ANNOTATION_TEXT_STYLING_HEADING_LEVEL_UUID);
209
+ const textStylingCssProperties = textStylingProperties.filter((property) => property.label.uuid !== "e1647bef-d801-4100-bdde-d081c422f763" && property.label.uuid !== "d4266f0b-3f8d-4b32-8c15-4b229c8bb11e");
215
210
  if (textStylingCssProperties.length > 0) textStylingCss = textStylingCssProperties.map((property) => ({
216
211
  label: parseFakeString(property.label.content),
217
212
  value: parseFakeString(property.value.content)
@@ -371,10 +366,10 @@ function parseStringDocumentItem(item) {
371
366
  if (itemProperty != null) {
372
367
  const itemPropertyLabelUuid = itemProperty.label.uuid;
373
368
  const itemPropertyValueUuid = typeof itemProperty.value === "object" && "uuid" in itemProperty.value && itemProperty.value.uuid != null ? itemProperty.value.uuid : null;
374
- if (itemPropertyLabelUuid === PRESENTATION_ITEM_UUID && itemPropertyValueUuid === TEXT_ANNOTATION_UUID) {
369
+ if (itemPropertyLabelUuid === "f1c131b6-1498-48a4-95bf-a9edae9fd518" && itemPropertyValueUuid === "b9ca2732-78f4-416e-b77f-dae7647e68a9") {
375
370
  const textAnnotationProperty = itemProperty.property != null ? Array.isArray(itemProperty.property) ? itemProperty.property[0] : itemProperty.property : null;
376
371
  if (textAnnotationProperty != null) {
377
- if ((typeof textAnnotationProperty.value === "object" && "uuid" in textAnnotationProperty.value && textAnnotationProperty.value.uuid != null ? textAnnotationProperty.value.uuid : null) === TEXT_ANNOTATION_TEXT_STYLING_UUID && textAnnotationProperty.property != null) {
372
+ if ((typeof textAnnotationProperty.value === "object" && "uuid" in textAnnotationProperty.value && textAnnotationProperty.value.uuid != null ? textAnnotationProperty.value.uuid : null) === "3e6f86ab-df81-45ae-8257-e2867357df56" && textAnnotationProperty.property != null) {
378
373
  const textStylingType = "text-styling";
379
374
  let textStylingVariant = "block";
380
375
  let textStylingSize = "md";
@@ -391,7 +386,7 @@ function parseStringDocumentItem(item) {
391
386
  }
392
387
  const textStylingHeadingLevelProperty = textStylingProperties.find((property) => property.label.uuid === TEXT_ANNOTATION_TEXT_STYLING_HEADING_LEVEL_UUID);
393
388
  if (textStylingHeadingLevelProperty != null) textStylingHeadingLevel = parseFakeString(textStylingHeadingLevelProperty.value.content);
394
- const textStylingCssProperties = textStylingProperties.filter((property) => property.label.uuid !== TEXT_ANNOTATION_TEXT_STYLING_VARIANT_UUID && property.label.uuid !== TEXT_ANNOTATION_TEXT_STYLING_HEADING_LEVEL_UUID);
389
+ const textStylingCssProperties = textStylingProperties.filter((property) => property.label.uuid !== "e1647bef-d801-4100-bdde-d081c422f763" && property.label.uuid !== "d4266f0b-3f8d-4b32-8c15-4b229c8bb11e");
395
390
  if (textStylingCssProperties.length > 0) textStylingCss = textStylingCssProperties.map((property) => ({
396
391
  label: parseFakeString(property.label.content),
397
392
  value: parseFakeString(property.value.content)
@@ -442,7 +437,6 @@ function parseStringContent(content, language = "eng") {
442
437
  default: return String(content.content);
443
438
  }
444
439
  }
445
-
446
440
  //#endregion
447
441
  //#region src/utils/internal.ts
448
442
  const PSEUDO_UUID_REGEX = /^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/i;
@@ -572,7 +566,6 @@ function cleanObject(object) {
572
566
  function stringLiteral(value) {
573
567
  return `"${value.replaceAll("\"", "\"\"")}"`;
574
568
  }
575
-
576
569
  //#endregion
577
570
  //#region src/utils/helpers.ts
578
571
  /**
@@ -606,7 +599,6 @@ function flattenItemProperties(item) {
606
599
  properties: flattenProperties(allProperties)
607
600
  };
608
601
  }
609
-
610
602
  //#endregion
611
603
  //#region src/schemas.ts
612
604
  /**
@@ -636,25 +628,13 @@ const richTextStringSchema = z.object({
636
628
  ]),
637
629
  lang: z.string().optional()
638
630
  });
639
- /**
640
- * Schema for validating identification
641
- * @internal
642
- */
643
- const identificationSchema = z.object({
631
+ z.object({
644
632
  label: z.object({ content: z.union([richTextStringSchema, z.array(richTextStringSchema)]) }),
645
633
  abbreviation: z.object({ content: z.union([richTextStringSchema, z.array(richTextStringSchema)]).optional() }),
646
634
  code: z.string().optional()
647
635
  });
648
- /**
649
- * Schema for validating filters
650
- * @internal
651
- */
652
- const filterSchema = z.string().optional();
653
- /**
654
- * Schema for validating data options
655
- * @internal
656
- */
657
- const dataOptionsSchema = z.object({
636
+ z.string().optional();
637
+ z.object({
658
638
  filter: z.string().optional().default(""),
659
639
  start: z.number().positive({ error: "Start must be positive" }).optional().default(1),
660
640
  limit: z.number().positive({ error: "Limit must be positive" }).optional().default(40)
@@ -818,6 +798,15 @@ const setQuerySchema = z.union([
818
798
  operator: z.enum(["AND", "OR"]).optional(),
819
799
  isNegated: z.boolean().optional().default(false)
820
800
  }).strict(),
801
+ z.object({
802
+ target: z.literal("string"),
803
+ value: z.string(),
804
+ matchMode: z.enum(["includes", "exact"]),
805
+ isCaseSensitive: z.boolean(),
806
+ language: z.string().default("eng"),
807
+ operator: z.enum(["AND", "OR"]).optional(),
808
+ isNegated: z.boolean().optional().default(false)
809
+ }).strict(),
821
810
  z.object({
822
811
  target: z.enum([
823
812
  "title",
@@ -897,11 +886,10 @@ const setItemsParamsSchema = z.object({
897
886
  queries: setQueriesSchema,
898
887
  sort: setItemsSortSchema,
899
888
  page: z.number().min(1, "Page must be positive").default(1),
900
- pageSize: z.number().min(1, "Page size must be positive").default(DEFAULT_PAGE_SIZE)
889
+ pageSize: z.number().min(1, "Page size must be positive").default(48)
901
890
  }).superRefine((value, ctx) => {
902
891
  validateSetQueriesOperators(value.queries, ctx);
903
892
  });
904
-
905
893
  //#endregion
906
894
  //#region src/utils/parse/index.ts
907
895
  const TRAILING_ELLIPSIS_REGEX = /\s*\.{3}$/;
@@ -1977,7 +1965,6 @@ function parseConcept(concept, metadata, persistentUrl, belongsTo) {
1977
1965
  function parseConcepts(concepts) {
1978
1966
  return concepts.map((concept) => parseConcept(concept));
1979
1967
  }
1980
-
1981
1968
  //#endregion
1982
1969
  //#region src/utils/fetchers/gallery.ts
1983
1970
  /**
@@ -2004,7 +1991,7 @@ const galleryParamsSchema = z.object({
2004
1991
  */
2005
1992
  async function fetchGallery(params, options) {
2006
1993
  try {
2007
- const version = options?.version ?? DEFAULT_API_VERSION;
1994
+ const version = options?.version ?? 2;
2008
1995
  const { uuid, filter, page, pageSize } = galleryParamsSchema.parse(params);
2009
1996
  const response = await (options?.fetch ?? fetch)(`${version === 2 ? "https://ochre.lib.uchicago.edu/ochre/v2/ochre.php" : "https://ochre.lib.uchicago.edu/ochre"}?xquery=${encodeURIComponent(`<ochre>{
2010
1997
  for $q in ${version === 2 ? "doc()" : "input()"}/ochre[@uuid='${uuid}']
@@ -2036,7 +2023,6 @@ async function fetchGallery(params, options) {
2036
2023
  };
2037
2024
  }
2038
2025
  }
2039
-
2040
2026
  //#endregion
2041
2027
  //#region src/utils/fetchers/uuid.ts
2042
2028
  /**
@@ -2048,7 +2034,7 @@ async function fetchGallery(params, options) {
2048
2034
  */
2049
2035
  async function fetchByUuid(uuid, options) {
2050
2036
  try {
2051
- const version = options?.version ?? DEFAULT_API_VERSION;
2037
+ const version = options?.version ?? 2;
2052
2038
  const parsedUuid = uuidSchema.parse(uuid);
2053
2039
  const response = await (options?.fetch ?? fetch)(version === 2 ? `https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=${parsedUuid}&format=json&lang="*"` : `https://ochre.lib.uchicago.edu/ochre?uuid=${parsedUuid}&format=json&lang="*"`);
2054
2040
  if (!response.ok) throw new Error("Failed to fetch OCHRE data");
@@ -2059,7 +2045,6 @@ async function fetchByUuid(uuid, options) {
2059
2045
  return [error instanceof Error ? error.message : "Unknown error", null];
2060
2046
  }
2061
2047
  }
2062
-
2063
2048
  //#endregion
2064
2049
  //#region src/utils/fetchers/item.ts
2065
2050
  /**
@@ -2070,7 +2055,7 @@ async function fetchByUuid(uuid, options) {
2070
2055
  */
2071
2056
  async function fetchItem(uuid, category, itemCategories, options) {
2072
2057
  try {
2073
- const version = options?.version ?? DEFAULT_API_VERSION;
2058
+ const version = options?.version ?? 2;
2074
2059
  const [error, data] = await fetchByUuid(uuid, {
2075
2060
  fetch,
2076
2061
  version
@@ -2144,15 +2129,16 @@ async function fetchItem(uuid, category, itemCategories, options) {
2144
2129
  };
2145
2130
  }
2146
2131
  }
2147
-
2148
2132
  //#endregion
2149
2133
  //#region src/utils/fetchers/set/query-helpers.ts
2150
2134
  const CTS_INCLUDES_STOP_WORDS = [
2151
- "of",
2152
- "the",
2153
2135
  "and",
2136
+ "at",
2154
2137
  "in",
2155
- "it"
2138
+ "it",
2139
+ "of",
2140
+ "the",
2141
+ "to"
2156
2142
  ];
2157
2143
  const CTS_INCLUDES_STOP_WORDS_VAR = "$ctsIncludesStopWords";
2158
2144
  /**
@@ -2166,53 +2152,188 @@ function buildRawStringMatchPredicate(params) {
2166
2152
  return `${comparedPath} = ${comparedValueLiteral}`;
2167
2153
  }
2168
2154
  /**
2155
+ * Build a combined raw string match predicate for multiple paths.
2156
+ */
2157
+ function buildCombinedRawStringMatchPredicate(params) {
2158
+ const { paths, value, matchMode, isCaseSensitive } = params;
2159
+ const predicates = [];
2160
+ for (const path of paths) predicates.push(buildRawStringMatchPredicate({
2161
+ path,
2162
+ value,
2163
+ matchMode,
2164
+ isCaseSensitive
2165
+ }));
2166
+ if (predicates.length === 1) return predicates[0] ?? "false()";
2167
+ return `(${predicates.join(" or ")})`;
2168
+ }
2169
+ /**
2169
2170
  * Build CTS word-query options for API v2 includes search.
2170
2171
  */
2171
2172
  function buildCtsQueryOptionsExpression(isCaseSensitive) {
2172
2173
  return `(${[
2173
2174
  isCaseSensitive ? "case-sensitive" : "case-insensitive",
2174
2175
  "diacritic-insensitive",
2175
- "punctuation-insensitive",
2176
- "whitespace-insensitive",
2177
- "stemmed"
2176
+ "punctuation-insensitive"
2178
2177
  ].map((option) => stringLiteral(option)).join(", ")})`;
2179
2178
  }
2180
2179
  /**
2181
- * Build a CTS-backed includes predicate for an XQuery string.
2180
+ * Build a CTS word-query expression for an XQuery term.
2182
2181
  */
2183
- function buildCtsIncludesPredicate(params) {
2184
- const { path, value, isCaseSensitive, queryIndex } = params;
2182
+ function buildCtsWordQueryExpression(params) {
2183
+ const { termExpression, isCaseSensitive } = params;
2184
+ return `cts:word-query(${termExpression}, ${buildCtsQueryOptionsExpression(isCaseSensitive)})`;
2185
+ }
2186
+ /**
2187
+ * Build tokenized search declarations for CTS-backed queries.
2188
+ */
2189
+ function buildTokenizedSearchDeclarations(params) {
2190
+ const { value, isCaseSensitive, queryIndex } = params;
2185
2191
  const searchStringVar = `$query${queryIndex}SearchString`;
2186
2192
  const rawTermsVar = `$query${queryIndex}RawTerms`;
2187
2193
  const termsVar = `$query${queryIndex}Terms`;
2194
+ const tokenSourceExpression = isCaseSensitive ? searchStringVar : `fn:lower-case(${searchStringVar})`;
2195
+ return {
2196
+ declarations: [
2197
+ `let ${searchStringVar} := ${stringLiteral(value)}`,
2198
+ String.raw`let ${rawTermsVar} := fn:tokenize(${tokenSourceExpression}, "\W+")`,
2199
+ `let ${termsVar} :=
2200
+ for $term in ${rawTermsVar}
2201
+ let $normalizedTerm := fn:lower-case($term)
2202
+ where $normalizedTerm ne "" and not($normalizedTerm = ${CTS_INCLUDES_STOP_WORDS_VAR})
2203
+ return $term`
2204
+ ],
2205
+ termsVar
2206
+ };
2207
+ }
2208
+ /**
2209
+ * Build a CTS-backed field includes predicate for an XQuery string.
2210
+ */
2211
+ function buildCtsFieldIncludesPredicate(params) {
2212
+ const { path, value, isCaseSensitive, queryIndex } = params;
2213
+ const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
2214
+ value,
2215
+ isCaseSensitive,
2216
+ queryIndex
2217
+ });
2188
2218
  const ctsQueryVar = `$query${queryIndex}CtsQuery`;
2189
- const ctsOptionsExpression = buildCtsQueryOptionsExpression(isCaseSensitive);
2190
2219
  const fallbackPredicate = buildRawStringMatchPredicate({
2191
2220
  path,
2192
2221
  value,
2193
2222
  matchMode: "includes",
2194
2223
  isCaseSensitive
2195
2224
  });
2225
+ return {
2226
+ declarations: [...tokenizedSearchDeclarations.declarations, `let ${ctsQueryVar} :=
2227
+ if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
2228
+ then ${buildCtsWordQueryExpression({
2229
+ termExpression: `${tokenizedSearchDeclarations.termsVar}[1]`,
2230
+ isCaseSensitive
2231
+ })}
2232
+ else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
2233
+ then cts:and-query((
2234
+ for $term in ${tokenizedSearchDeclarations.termsVar}
2235
+ return ${buildCtsWordQueryExpression({
2236
+ termExpression: "$term",
2237
+ isCaseSensitive
2238
+ })}
2239
+ ))
2240
+ else ()`],
2241
+ predicate: `(if (exists(${ctsQueryVar})) then cts:contains(${path}, ${ctsQueryVar}) else ${fallbackPredicate})`
2242
+ };
2243
+ }
2244
+ /**
2245
+ * Build the raw search paths for item-level string search.
2246
+ */
2247
+ function buildItemStringSearchPaths(language) {
2248
+ return [`string-join(identification/label/content[@xml:lang="${language}"]/string, "")`, `string-join(properties//property/value[not(@inherited="true")]/content[@xml:lang="${language}"]/string, "")`];
2249
+ }
2250
+ /**
2251
+ * Build the identification branch for an item-level CTS string search.
2252
+ */
2253
+ function buildItemStringIdentificationBranch(params) {
2254
+ const { termExpression, isCaseSensitive, language } = params;
2255
+ return `cts:element-query(xs:QName("identification"),
2256
+ cts:and-query((
2257
+ cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
2258
+ ${buildCtsWordQueryExpression({
2259
+ termExpression,
2260
+ isCaseSensitive
2261
+ })}
2262
+ ))
2263
+ )`;
2264
+ }
2265
+ /**
2266
+ * Build the property value branch for an item-level CTS string search.
2267
+ */
2268
+ function buildItemStringPropertyValueBranch(params) {
2269
+ const { termExpression, isCaseSensitive, language } = params;
2270
+ return `cts:element-query(xs:QName("properties"),
2271
+ cts:element-query(xs:QName("property"),
2272
+ cts:element-query(xs:QName("value"),
2273
+ cts:and-query((
2274
+ cts:not-query(cts:element-attribute-value-query(xs:QName("value"), xs:QName("inherited"), "true")),
2275
+ cts:element-query(xs:QName("content"),
2276
+ cts:and-query((
2277
+ cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
2278
+ ${buildCtsWordQueryExpression({
2279
+ termExpression,
2280
+ isCaseSensitive
2281
+ })}
2282
+ ))
2283
+ )
2284
+ ))
2285
+ )
2286
+ )
2287
+ )`;
2288
+ }
2289
+ /**
2290
+ * Build an item-level CTS string search predicate.
2291
+ */
2292
+ function buildItemStringSearchPredicate(params) {
2293
+ const { query, version, queryIndex } = params;
2294
+ const fallbackPredicate = buildCombinedRawStringMatchPredicate({
2295
+ paths: buildItemStringSearchPaths(query.language),
2296
+ value: query.value,
2297
+ matchMode: query.matchMode,
2298
+ isCaseSensitive: query.isCaseSensitive
2299
+ });
2300
+ if (query.matchMode !== "includes" || version !== 2) return {
2301
+ declarations: [],
2302
+ predicate: fallbackPredicate
2303
+ };
2304
+ const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
2305
+ value: query.value,
2306
+ isCaseSensitive: query.isCaseSensitive,
2307
+ queryIndex
2308
+ });
2309
+ const termQueriesVar = `$query${queryIndex}TermQueries`;
2310
+ const ctsQueryVar = `$query${queryIndex}CtsQuery`;
2196
2311
  return {
2197
2312
  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"}`,
2313
+ ...tokenizedSearchDeclarations.declarations,
2314
+ `let ${termQueriesVar} :=
2315
+ for $term in ${tokenizedSearchDeclarations.termsVar}
2316
+ return
2317
+ cts:or-query((
2318
+ ${buildItemStringIdentificationBranch({
2319
+ termExpression: "$term",
2320
+ isCaseSensitive: query.isCaseSensitive,
2321
+ language: query.language
2322
+ })},
2323
+ ${buildItemStringPropertyValueBranch({
2324
+ termExpression: "$term",
2325
+ isCaseSensitive: query.isCaseSensitive,
2326
+ language: query.language
2327
+ })}
2328
+ ))`,
2205
2329
  `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"))
2330
+ if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
2331
+ then ${termQueriesVar}[1]
2332
+ else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
2333
+ then cts:and-query(${termQueriesVar})
2213
2334
  else ()`
2214
2335
  ],
2215
- predicate: `(if (exists(${ctsQueryVar})) then cts:contains(${path}, ${ctsQueryVar}) else ${fallbackPredicate})`
2336
+ predicate: `(if (exists(${ctsQueryVar})) then cts:contains(., ${ctsQueryVar}) else ${fallbackPredicate})`
2216
2337
  };
2217
2338
  }
2218
2339
  /**
@@ -2220,7 +2341,7 @@ function buildCtsIncludesPredicate(params) {
2220
2341
  */
2221
2342
  function buildStringMatchPredicate(params) {
2222
2343
  const { path, value, matchMode, isCaseSensitive, version, queryIndex } = params;
2223
- if (matchMode === "includes" && version === 2) return buildCtsIncludesPredicate({
2344
+ if (matchMode === "includes" && version === 2) return buildCtsFieldIncludesPredicate({
2224
2345
  path,
2225
2346
  value,
2226
2347
  isCaseSensitive,
@@ -2267,7 +2388,7 @@ function buildPropertyValuePredicate(params) {
2267
2388
  predicate: `.//properties//property[value[@rawValue=${stringLiteral(query.value)}]]`
2268
2389
  };
2269
2390
  const compiledStringPredicate = buildStringMatchPredicate({
2270
- path: `string-join(value/content[@xml:lang="${query.language}"]/string, "")`,
2391
+ path: query.matchMode === "includes" && version === 2 ? `string-join(value[not(@inherited="true")]/content[@xml:lang="${query.language}"]/string, "")` : `string-join(value/content[@xml:lang="${query.language}"]/string, "")`,
2271
2392
  value: query.value,
2272
2393
  matchMode: query.matchMode,
2273
2394
  isCaseSensitive: query.isCaseSensitive,
@@ -2285,6 +2406,11 @@ function buildPropertyValuePredicate(params) {
2285
2406
  function buildQueryPredicate(params) {
2286
2407
  const { query, version, queryIndex } = params;
2287
2408
  switch (query.target) {
2409
+ case "string": return buildItemStringSearchPredicate({
2410
+ query,
2411
+ version,
2412
+ queryIndex
2413
+ });
2288
2414
  case "title": return buildStringMatchPredicate({
2289
2415
  path: `string-join(identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2290
2416
  value: query.value,
@@ -2378,7 +2504,6 @@ function buildQueryFilters(params) {
2378
2504
  predicate: predicateParts.join(" ")
2379
2505
  };
2380
2506
  }
2381
-
2382
2507
  //#endregion
2383
2508
  //#region src/utils/fetchers/set/items.ts
2384
2509
  function mapSortDirectionToXQuery(direction) {
@@ -2467,7 +2592,7 @@ function buildOrderedItemsClause(sort) {
2467
2592
  * @returns An XQuery string
2468
2593
  */
2469
2594
  function buildXQuery$1(params, options) {
2470
- const version = options?.version ?? DEFAULT_API_VERSION;
2595
+ const version = options?.version ?? 2;
2471
2596
  const { propertyVariableUuids, queries, sort, setScopeUuids, belongsToCollectionScopeUuids, page, pageSize } = params;
2472
2597
  const startPosition = (page - 1) * pageSize + 1;
2473
2598
  const endPosition = page * pageSize;
@@ -2522,7 +2647,7 @@ function buildXQuery$1(params, options) {
2522
2647
  */
2523
2648
  async function fetchSetItems(params, itemCategories, options) {
2524
2649
  try {
2525
- const version = options?.version ?? DEFAULT_API_VERSION;
2650
+ const version = options?.version ?? 2;
2526
2651
  const { setScopeUuids, belongsToCollectionScopeUuids, propertyVariableUuids, queries, sort, page, pageSize } = setItemsParamsSchema.parse(params);
2527
2652
  const xquery = buildXQuery$1({
2528
2653
  setScopeUuids,
@@ -2533,7 +2658,13 @@ async function fetchSetItems(params, itemCategories, options) {
2533
2658
  page,
2534
2659
  pageSize
2535
2660
  }, { version });
2536
- 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="*"`);
2661
+ let response;
2662
+ if (version === 2) response = await (options?.fetch ?? fetch)("https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery&format=json", {
2663
+ method: "POST",
2664
+ body: xquery,
2665
+ headers: { "Content-Type": "application/xquery" }
2666
+ });
2667
+ else response = await (options?.fetch ?? fetch)(`https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"`);
2537
2668
  if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`);
2538
2669
  const data = await response.json();
2539
2670
  if (Array.isArray(data.result) || Object.keys(data.result).length === 0) throw new Error("No items found");
@@ -2611,7 +2742,6 @@ async function fetchSetItems(params, itemCategories, options) {
2611
2742
  };
2612
2743
  }
2613
2744
  }
2614
-
2615
2745
  //#endregion
2616
2746
  //#region src/utils/fetchers/set/property-values-by-property-variables.ts
2617
2747
  function parsePropertyValueLabel(content) {
@@ -2764,7 +2894,7 @@ const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({
2764
2894
  * @returns An XQuery string
2765
2895
  */
2766
2896
  function buildXQuery(params, options) {
2767
- const version = options?.version ?? DEFAULT_API_VERSION;
2897
+ const version = options?.version ?? 2;
2768
2898
  const { setScopeUuids, belongsToCollectionScopeUuids, propertyVariableUuids, queries, attributes, isLimitedToLeafPropertyValues } = params;
2769
2899
  let setScopeFilter = "/set/items/*";
2770
2900
  if (setScopeUuids.length > 0) setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
@@ -2837,7 +2967,7 @@ return (${returnedSequences.join(", ")})`}}</ochre>`;
2837
2967
  */
2838
2968
  async function fetchSetPropertyValuesByPropertyVariables(params, options) {
2839
2969
  try {
2840
- const version = options?.version ?? DEFAULT_API_VERSION;
2970
+ const version = options?.version ?? 2;
2841
2971
  const { setScopeUuids, belongsToCollectionScopeUuids, propertyVariableUuids, queries, attributes, isLimitedToLeafPropertyValues } = setPropertyValuesByPropertyVariablesParamsSchema.parse(params);
2842
2972
  const xquery = buildXQuery({
2843
2973
  setScopeUuids,
@@ -2847,7 +2977,13 @@ async function fetchSetPropertyValuesByPropertyVariables(params, options) {
2847
2977
  attributes,
2848
2978
  isLimitedToLeafPropertyValues
2849
2979
  }, { version });
2850
- 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="*"`);
2980
+ let response;
2981
+ if (version === 2) response = await (options?.fetch ?? fetch)("https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery&format=json", {
2982
+ method: "POST",
2983
+ body: xquery,
2984
+ headers: { "Content-Type": "application/xquery" }
2985
+ });
2986
+ else response = await (options?.fetch ?? fetch)(`https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"`);
2851
2987
  if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`);
2852
2988
  const data = await response.json();
2853
2989
  const parsedResultRaw = responseSchema.parse(data);
@@ -2899,7 +3035,6 @@ async function fetchSetPropertyValuesByPropertyVariables(params, options) {
2899
3035
  };
2900
3036
  }
2901
3037
  }
2902
-
2903
3038
  //#endregion
2904
3039
  //#region src/utils/getters.ts
2905
3040
  const DEFAULT_OPTIONS = { includeNestedProperties: false };
@@ -3153,7 +3288,6 @@ function filterProperties(property, filter, options = DEFAULT_OPTIONS) {
3153
3288
  }
3154
3289
  return false;
3155
3290
  }
3156
-
3157
3291
  //#endregion
3158
3292
  //#region src/utils/parse/website.ts
3159
3293
  const SEGMENT_UNIQUE_SLUG_PREFIX_REGEX = /^\$[^-]*-/;
@@ -4404,7 +4538,7 @@ function parseContexts(contexts) {
4404
4538
  }
4405
4539
  return contextsParsed;
4406
4540
  }
4407
- function parseWebsite(websiteTree, metadata, belongsTo, { version = DEFAULT_API_VERSION } = {}) {
4541
+ function parseWebsite(websiteTree, metadata, belongsTo, { version = 2 } = {}) {
4408
4542
  if (!websiteTree.properties) throw new Error("Website properties not found");
4409
4543
  if (typeof websiteTree.items === "string" || !("resource" in websiteTree.items)) throw new Error("Website pages not found");
4410
4544
  const resources = ensureArray(websiteTree.items.resource);
@@ -4424,7 +4558,6 @@ function parseWebsite(websiteTree, metadata, belongsTo, { version = DEFAULT_API_
4424
4558
  properties
4425
4559
  };
4426
4560
  }
4427
-
4428
4561
  //#endregion
4429
4562
  //#region src/utils/fetchers/website.ts
4430
4563
  const API_VERSION_SUFFIX_REGEX = /-v\d+$/;
@@ -4437,7 +4570,7 @@ const API_VERSION_SUFFIX_REGEX = /-v\d+$/;
4437
4570
  function parseApiVersionSuffix(abbreviation) {
4438
4571
  if (!API_VERSION_SUFFIX_REGEX.test(abbreviation)) return {
4439
4572
  abbreviation,
4440
- version: DEFAULT_API_VERSION
4573
+ version: 2
4441
4574
  };
4442
4575
  const result = apiVersionSuffixSchema.safeParse(abbreviation.slice(-3));
4443
4576
  if (!result.success) throw new Error("Invalid API version suffix");
@@ -4497,6 +4630,5 @@ async function fetchWebsite(abbreviation, options) {
4497
4630
  };
4498
4631
  }
4499
4632
  }
4500
-
4501
4633
  //#endregion
4502
- export { DEFAULT_API_VERSION, DEFAULT_PAGE_SIZE, fetchGallery, fetchItem, fetchSetItems, fetchSetPropertyValuesByPropertyVariables, fetchWebsite, filterProperties, flattenItemProperties, getLeafPropertyValues, getPropertyByLabel, getPropertyByLabelAndValue, getPropertyByLabelAndValues, getPropertyByUuid, getPropertyValueByLabel, getPropertyValueByUuid, getPropertyValuesByLabel, getPropertyValuesByUuid, getUniqueProperties, getUniquePropertyLabels };
4634
+ export { DEFAULT_API_VERSION, DEFAULT_PAGE_SIZE, fetchGallery, fetchItem, fetchSetItems, fetchSetPropertyValuesByPropertyVariables, fetchWebsite, filterProperties, flattenItemProperties, getLeafPropertyValues, getPropertyByLabel, getPropertyByLabelAndValue, getPropertyByLabelAndValues, getPropertyByUuid, getPropertyValueByLabel, getPropertyValueByUuid, getPropertyValuesByLabel, getPropertyValuesByUuid, getUniqueProperties, getUniquePropertyLabels };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ochre-sdk",
3
- "version": "0.20.21",
3
+ "version": "0.20.23",
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,12 +46,12 @@
46
46
  "zod": "^4.3.6"
47
47
  },
48
48
  "devDependencies": {
49
- "@antfu/eslint-config": "^7.7.0",
49
+ "@antfu/eslint-config": "^7.7.3",
50
50
  "@types/node": "^24.12.0",
51
- "bumpp": "^10.4.1",
52
- "eslint": "^10.0.2",
51
+ "bumpp": "^11.0.1",
52
+ "eslint": "^10.0.3",
53
53
  "prettier": "^3.8.1",
54
- "tsdown": "^0.20.3",
54
+ "tsdown": "^0.21.4",
55
55
  "typescript": "^5.9.3"
56
56
  },
57
57
  "scripts": {