ochre-sdk 0.20.7 → 0.20.11

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
@@ -951,7 +951,7 @@ type WebElementComponent = {
951
951
  isResultsBarDisplayed: boolean;
952
952
  isMapDisplayed: boolean;
953
953
  isInputDisplayed: boolean;
954
- isLimitedToTitleQuery: boolean;
954
+ isLimitedToInputFilter: boolean;
955
955
  isLimitedToLeafPropertyValues: boolean;
956
956
  sidebarSort: "default" | "alphabetical";
957
957
  };
@@ -1231,12 +1231,6 @@ declare function fetchSetItems<U extends Array<DataCategory> = Array<DataCategor
1231
1231
  }>;
1232
1232
  //#endregion
1233
1233
  //#region src/utils/fetchers/set/property-values-by-property-variables.d.ts
1234
- type SetPropertyValuesByPropertyVariablesTitleQueryInput = {
1235
- value: string;
1236
- matchMode: "includes" | "exact";
1237
- isCaseSensitive: boolean;
1238
- language?: string;
1239
- };
1240
1234
  /**
1241
1235
  * Fetches and parses Set property values by property variables from the OCHRE API
1242
1236
  *
@@ -1244,7 +1238,7 @@ type SetPropertyValuesByPropertyVariablesTitleQueryInput = {
1244
1238
  * @param params.setScopeUuids - An array of set scope UUIDs to filter by
1245
1239
  * @param params.belongsToCollectionScopeUuids - The collection scope UUIDs to filter by
1246
1240
  * @param params.propertyVariableUuids - The property variable UUIDs to query by
1247
- * @param params.titleQuery - Title query to filter returned items by item title
1241
+ * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
1248
1242
  * @param params.isLimitedToLeafPropertyValues - Whether to limit the property values to leaf property values
1249
1243
  * @param options - Options for the fetch
1250
1244
  * @param options.fetch - The fetch function to use
@@ -1255,7 +1249,7 @@ declare function fetchSetPropertyValuesByPropertyVariables(params: {
1255
1249
  setScopeUuids: Array<string>;
1256
1250
  belongsToCollectionScopeUuids: Array<string>;
1257
1251
  propertyVariableUuids: Array<string>;
1258
- titleQuery?: SetPropertyValuesByPropertyVariablesTitleQueryInput;
1252
+ queries?: Array<Query>;
1259
1253
  isLimitedToLeafPropertyValues?: boolean;
1260
1254
  }, options?: {
1261
1255
  fetch?: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>;
package/dist/index.mjs CHANGED
@@ -28,6 +28,15 @@ function getStringItemByLanguage(content, language) {
28
28
  return content.find((item) => item.lang === language) ?? null;
29
29
  }
30
30
  /**
31
+ * Transforms a permanent identification URL to a OCHRE API URL
32
+ *
33
+ * @param url - The permanent identification URL to transform
34
+ * @returns The OCHRE API URL
35
+ */
36
+ function transformPermanentIdentificationUrl(url) {
37
+ return url.replace("https://pi.lib.uchicago.edu/1001/org/ochre/", "https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=");
38
+ }
39
+ /**
31
40
  * Parses email addresses in a string into HTML links
32
41
  *
33
42
  * @param string - Input string to parse
@@ -37,7 +46,7 @@ function parseEmail(string) {
37
46
  const splitString = string.split(" ");
38
47
  const returnSplitString = [];
39
48
  for (const string of splitString) {
40
- const cleanString = string.replaceAll(/(?<=\s|^)[([{]+|[)\]}]+(?=\s|$)/g, "").replaceAll(/[!),:;?\]]/g, "").replace(/\.$/, "");
49
+ const cleanString = transformPermanentIdentificationUrl(string).replaceAll(/(?<=\s|^)[([{]+|[)\]}]+(?=\s|$)/g, "").replaceAll(/[!),:;?\]]/g, "").replace(/\.$/, "");
41
50
  const index = string.indexOf(cleanString);
42
51
  const before = string.slice(0, index);
43
52
  const after = string.slice(index + cleanString.length);
@@ -321,9 +330,9 @@ function parseStringDocumentItem(item) {
321
330
  }
322
331
  return applyWhitespaceToResult(linkElement, itemWhitespace);
323
332
  }
324
- case "externalDocument": if (linkResource.publicationDateTime != null) return applyWhitespaceToResult(String.raw`<ExternalLink href="https:\/\/ochre.lib.uchicago.edu/ochre?uuid=${linkResource.uuid}&load" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</ExternalLink>`, itemWhitespace);
333
+ case "externalDocument": if (linkResource.publicationDateTime != null) return applyWhitespaceToResult(String.raw`<ExternalLink href="https:\/\/ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=${linkResource.uuid}&load" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</ExternalLink>`, itemWhitespace);
325
334
  else return applyWhitespaceToResult(`<TooltipSpan${linkContent !== null ? ` content="${linkContent}"` : ""}>${itemString}</TooltipSpan>`, itemWhitespace);
326
- case "webpage": return applyWhitespaceToResult(`<ExternalLink href="${linkResource.href}" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</ExternalLink>`, itemWhitespace);
335
+ case "webpage": return applyWhitespaceToResult(`<ExternalLink href="${linkResource.href != null ? transformPermanentIdentificationUrl(linkResource.href) : "#"}" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</ExternalLink>`, itemWhitespace);
327
336
  default: return "";
328
337
  }
329
338
  } else if ("spatialUnit" in link) {
@@ -760,87 +769,70 @@ const boundsSchema = z.string().transform((str, ctx) => {
760
769
  }
761
770
  }).pipe(z.tuple([z.tuple([z.number(), z.number()]), z.tuple([z.number(), z.number()])], { message: "Must contain exactly 2 coordinate pairs" }));
762
771
  /**
763
- * Schema for validating the parameters for the Set property values by property variables fetching function
772
+ * Shared schema for Set queries
764
773
  * @internal
765
774
  */
766
- const setPropertyValuesByPropertyVariablesParamsSchema = z.object({
767
- setScopeUuids: z.array(uuidSchema).min(1, "At least one set scope UUID is required"),
768
- belongsToCollectionScopeUuids: z.array(uuidSchema).default([]),
769
- propertyVariableUuids: z.array(uuidSchema).min(1, "At least one property variable UUID is required"),
770
- titleQuery: z.object({
775
+ const setQuerySchema = z.union([
776
+ z.object({
777
+ target: z.literal("propertyValue"),
778
+ dataType: z.enum([
779
+ "string",
780
+ "integer",
781
+ "decimal",
782
+ "boolean",
783
+ "time",
784
+ "IDREF"
785
+ ]),
771
786
  value: z.string(),
772
787
  matchMode: z.enum(["includes", "exact"]),
773
788
  isCaseSensitive: z.boolean(),
774
- language: z.string().default("eng")
775
- }).strict().optional(),
776
- isLimitedToLeafPropertyValues: z.boolean().default(false)
777
- });
778
- const setItemsParamsSchema = z.object({
779
- setScopeUuids: z.array(uuidSchema).min(1, "At least one set scope UUID is required"),
780
- belongsToCollectionScopeUuids: z.array(uuidSchema).default([]),
781
- propertyVariableUuids: z.array(uuidSchema).default([]),
782
- queries: z.array(z.union([
783
- z.object({
784
- target: z.literal("propertyValue"),
785
- dataType: z.enum([
786
- "string",
787
- "integer",
788
- "decimal",
789
- "boolean",
790
- "time",
791
- "IDREF"
792
- ]),
793
- value: z.string(),
794
- matchMode: z.enum(["includes", "exact"]),
795
- isCaseSensitive: z.boolean(),
796
- language: z.string().default("eng"),
797
- operator: z.enum(["AND", "OR"]).optional(),
798
- isNegated: z.boolean().optional().default(false)
799
- }).strict(),
800
- z.object({
801
- target: z.literal("propertyValue"),
802
- dataType: z.enum(["date", "dateTime"]),
803
- value: z.string(),
804
- from: z.string(),
805
- to: z.string().optional(),
806
- matchMode: z.enum(["includes", "exact"]),
807
- isCaseSensitive: z.boolean(),
808
- language: z.string().default("eng"),
809
- operator: z.enum(["AND", "OR"]).optional(),
810
- isNegated: z.boolean().optional().default(false)
811
- }).strict(),
812
- z.object({
813
- target: z.literal("propertyValue"),
814
- dataType: z.enum(["date", "dateTime"]),
815
- value: z.string(),
816
- from: z.string().optional(),
817
- to: z.string(),
818
- matchMode: z.enum(["includes", "exact"]),
819
- isCaseSensitive: z.boolean(),
820
- language: z.string().default("eng"),
821
- operator: z.enum(["AND", "OR"]).optional(),
822
- isNegated: z.boolean().optional().default(false)
823
- }).strict(),
824
- z.object({
825
- target: z.enum([
826
- "title",
827
- "description",
828
- "image",
829
- "periods",
830
- "bibliography"
831
- ]),
832
- value: z.string(),
833
- matchMode: z.enum(["includes", "exact"]),
834
- isCaseSensitive: z.boolean(),
835
- language: z.string().default("eng"),
836
- operator: z.enum(["AND", "OR"]).optional(),
837
- isNegated: z.boolean().optional().default(false)
838
- }).strict()
839
- ])).default([]),
840
- page: z.number().min(1, "Page must be positive").default(1),
841
- pageSize: z.number().min(1, "Page size must be positive").default(DEFAULT_PAGE_SIZE)
842
- }).superRefine((value, ctx) => {
843
- for (const [index, query] of value.queries.entries()) {
789
+ language: z.string().default("eng"),
790
+ operator: z.enum(["AND", "OR"]).optional(),
791
+ isNegated: z.boolean().optional().default(false)
792
+ }).strict(),
793
+ z.object({
794
+ target: z.literal("propertyValue"),
795
+ dataType: z.enum(["date", "dateTime"]),
796
+ value: z.string(),
797
+ from: z.string(),
798
+ to: z.string().optional(),
799
+ matchMode: z.enum(["includes", "exact"]),
800
+ isCaseSensitive: z.boolean(),
801
+ language: z.string().default("eng"),
802
+ operator: z.enum(["AND", "OR"]).optional(),
803
+ isNegated: z.boolean().optional().default(false)
804
+ }).strict(),
805
+ z.object({
806
+ target: z.literal("propertyValue"),
807
+ dataType: z.enum(["date", "dateTime"]),
808
+ value: z.string(),
809
+ from: z.string().optional(),
810
+ to: z.string(),
811
+ matchMode: z.enum(["includes", "exact"]),
812
+ isCaseSensitive: z.boolean(),
813
+ language: z.string().default("eng"),
814
+ operator: z.enum(["AND", "OR"]).optional(),
815
+ isNegated: z.boolean().optional().default(false)
816
+ }).strict(),
817
+ z.object({
818
+ target: z.enum([
819
+ "title",
820
+ "description",
821
+ "image",
822
+ "periods",
823
+ "bibliography"
824
+ ]),
825
+ value: z.string(),
826
+ matchMode: z.enum(["includes", "exact"]),
827
+ isCaseSensitive: z.boolean(),
828
+ language: z.string().default("eng"),
829
+ operator: z.enum(["AND", "OR"]).optional(),
830
+ isNegated: z.boolean().optional().default(false)
831
+ }).strict()
832
+ ]);
833
+ const setQueriesSchema = z.array(setQuerySchema).default([]);
834
+ function validateSetQueriesOperators(queries, ctx) {
835
+ for (const [index, query] of queries.entries()) {
844
836
  if (index === 0 && query.operator != null) ctx.addIssue({
845
837
  code: "custom",
846
838
  path: [
@@ -860,6 +852,29 @@ const setItemsParamsSchema = z.object({
860
852
  message: "Query rules after the first must include an operator"
861
853
  });
862
854
  }
855
+ }
856
+ /**
857
+ * Schema for validating the parameters for the Set property values by property variables fetching function
858
+ * @internal
859
+ */
860
+ const setPropertyValuesByPropertyVariablesParamsSchema = z.object({
861
+ setScopeUuids: z.array(uuidSchema).min(1, "At least one set scope UUID is required"),
862
+ belongsToCollectionScopeUuids: z.array(uuidSchema).default([]),
863
+ propertyVariableUuids: z.array(uuidSchema).min(1, "At least one property variable UUID is required"),
864
+ queries: setQueriesSchema,
865
+ isLimitedToLeafPropertyValues: z.boolean().default(false)
866
+ }).superRefine((value, ctx) => {
867
+ validateSetQueriesOperators(value.queries, ctx);
868
+ });
869
+ const setItemsParamsSchema = z.object({
870
+ setScopeUuids: z.array(uuidSchema).min(1, "At least one set scope UUID is required"),
871
+ belongsToCollectionScopeUuids: z.array(uuidSchema).default([]),
872
+ propertyVariableUuids: z.array(uuidSchema).default([]),
873
+ queries: setQueriesSchema,
874
+ page: z.number().min(1, "Page must be positive").default(1),
875
+ pageSize: z.number().min(1, "Page size must be positive").default(DEFAULT_PAGE_SIZE)
876
+ }).superRefine((value, ctx) => {
877
+ validateSetQueriesOperators(value.queries, ctx);
863
878
  });
864
879
 
865
880
  //#endregion
@@ -1069,7 +1084,7 @@ function parseLink(linkRaw) {
1069
1084
  const returnLink = {
1070
1085
  category: categoryKey,
1071
1086
  content: "content" in link && link.content != null ? parseFakeString(link.content) : null,
1072
- href: "href" in link && link.href != null ? link.href : null,
1087
+ href: "href" in link && link.href != null ? transformPermanentIdentificationUrl(link.href) : null,
1073
1088
  fileFormat: "fileFormat" in link && link.fileFormat != null ? link.fileFormat : null,
1074
1089
  fileSize: "fileSize" in link && link.fileSize != null ? link.fileSize : null,
1075
1090
  uuid: link.uuid ?? null,
@@ -1126,9 +1141,9 @@ function parseImage(image) {
1126
1141
  return {
1127
1142
  publicationDateTime: parseOptionalDate(image.publicationDateTime),
1128
1143
  identification: image.identification ? parseIdentification(image.identification) : null,
1129
- url: image.href ?? (image.htmlImgSrcPrefix == null && image.content != null ? parseFakeString(image.content) : null),
1144
+ url: image.href != null ? transformPermanentIdentificationUrl(image.href) : image.htmlImgSrcPrefix == null && image.content != null ? transformPermanentIdentificationUrl(parseFakeString(image.content)) : null,
1130
1145
  htmlPrefix: image.htmlImgSrcPrefix ?? null,
1131
- content: image.htmlImgSrcPrefix != null && image.content != null ? parseFakeString(image.content) : null,
1146
+ content: image.htmlImgSrcPrefix != null && image.content != null ? transformPermanentIdentificationUrl(parseFakeString(image.content)) : null,
1132
1147
  widthPreview: image.widthPreview ?? null,
1133
1148
  heightPreview: image.heightPreview ?? null,
1134
1149
  width: image.width ?? null,
@@ -1363,7 +1378,7 @@ function parseProperty(property, language = "eng") {
1363
1378
  height: value.height ?? null,
1364
1379
  width: value.width ?? null,
1365
1380
  fileSize: value.fileSize ?? null,
1366
- href: value.href ?? null,
1381
+ href: value.href != null ? transformPermanentIdentificationUrl(value.href) : null,
1367
1382
  slug: value.slug ?? null
1368
1383
  };
1369
1384
  }
@@ -1465,7 +1480,7 @@ function parseBibliography(bibliography, metadata, persistentUrl, belongsTo) {
1465
1480
  publicationDateTime: parseISO(resource.publicationDateTime),
1466
1481
  type: resource.type,
1467
1482
  identification: parseIdentification(resource.identification),
1468
- href: resource.href ?? null
1483
+ href: resource.href != null ? transformPermanentIdentificationUrl(resource.href) : null
1469
1484
  })) : [];
1470
1485
  return {
1471
1486
  uuid: bibliography.uuid ?? null,
@@ -1844,7 +1859,7 @@ function parseResource(resource, metadata, persistentUrl, belongsTo) {
1844
1859
  description: resource.description ? parseFakeStringOrContent(resource.description) : "",
1845
1860
  coordinates: resource.coordinates ? parseCoordinates(resource.coordinates) : [],
1846
1861
  document: resource.document && "content" in resource.document ? parseDocument(resource.document.content) : null,
1847
- href: resource.href ?? null,
1862
+ href: resource.href != null ? transformPermanentIdentificationUrl(resource.href) : null,
1848
1863
  imageMap: resource.imagemap ? parseImageMap(resource.imagemap) : null,
1849
1864
  periods: resource.periods ? parsePeriods(ensureArray(resource.periods.period)) : [],
1850
1865
  links: resource.links ? parseLinks(ensureArray(resource.links)) : [],
@@ -2105,17 +2120,11 @@ async function fetchItem(uuid, category, itemCategories, options) {
2105
2120
  }
2106
2121
 
2107
2122
  //#endregion
2108
- //#region src/utils/fetchers/set/items.ts
2123
+ //#region src/utils/fetchers/set/query-helpers.ts
2109
2124
  /**
2110
2125
  * Build a string match predicate for an XQuery string
2111
- * @param params - The parameters for the predicate
2112
- * @param params.path - The path to the string
2113
- * @param params.value - The value to match
2114
- * @param params.matchMode - The match mode (includes or exact)
2115
- * @param params.isCaseSensitive - Whether to match case-sensitively
2116
- * @returns The string match predicate
2117
- */
2118
- function buildStringMatchPredicate$1(params) {
2126
+ */
2127
+ function buildStringMatchPredicate(params) {
2119
2128
  const { path, value, matchMode, isCaseSensitive } = params;
2120
2129
  const comparedPath = isCaseSensitive ? path : `lower-case(${path})`;
2121
2130
  const comparedValueLiteral = stringLiteral(isCaseSensitive ? value : value.toLowerCase());
@@ -2134,8 +2143,6 @@ function buildDateRangePredicate(params) {
2134
2143
  }
2135
2144
  /**
2136
2145
  * Build a property value predicate for an XQuery string
2137
- * @param query - The propertyValue query
2138
- * @returns The property value predicate
2139
2146
  */
2140
2147
  function buildPropertyValuePredicate(query) {
2141
2148
  if (query.dataType === "IDREF") return `.//properties//property[value[@uuid=${stringLiteral(query.value)}]]`;
@@ -2144,7 +2151,7 @@ function buildPropertyValuePredicate(query) {
2144
2151
  to: query.to
2145
2152
  })}]`;
2146
2153
  if (query.dataType === "time" || query.dataType === "integer" || query.dataType === "decimal" || query.dataType === "boolean") return `.//properties//property[value[@rawValue=${stringLiteral(query.value)}]]`;
2147
- return `.//properties//property[${buildStringMatchPredicate$1({
2154
+ return `.//properties//property[${buildStringMatchPredicate({
2148
2155
  path: `string-join(value/content[@xml:lang="${query.language}"]/string, "")`,
2149
2156
  value: query.value,
2150
2157
  matchMode: query.matchMode,
@@ -2153,36 +2160,34 @@ function buildPropertyValuePredicate(query) {
2153
2160
  }
2154
2161
  /**
2155
2162
  * Build a query predicate for an XQuery string
2156
- * @param query - The query to build the predicate for
2157
- * @returns The query predicate
2158
2163
  */
2159
2164
  function buildQueryPredicate(query) {
2160
2165
  switch (query.target) {
2161
- case "title": return buildStringMatchPredicate$1({
2166
+ case "title": return buildStringMatchPredicate({
2162
2167
  path: `string-join(identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2163
2168
  value: query.value,
2164
2169
  matchMode: query.matchMode,
2165
2170
  isCaseSensitive: query.isCaseSensitive
2166
2171
  });
2167
- case "description": return buildStringMatchPredicate$1({
2172
+ case "description": return buildStringMatchPredicate({
2168
2173
  path: `string-join(description/content[@xml:lang="${query.language}"]/string, "")`,
2169
2174
  value: query.value,
2170
2175
  matchMode: query.matchMode,
2171
2176
  isCaseSensitive: query.isCaseSensitive
2172
2177
  });
2173
- case "periods": return buildStringMatchPredicate$1({
2178
+ case "periods": return buildStringMatchPredicate({
2174
2179
  path: `string-join(periods/period/identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2175
2180
  value: query.value,
2176
2181
  matchMode: query.matchMode,
2177
2182
  isCaseSensitive: query.isCaseSensitive
2178
2183
  });
2179
- case "bibliography": return buildStringMatchPredicate$1({
2184
+ case "bibliography": return buildStringMatchPredicate({
2180
2185
  path: `string-join(bibliographies/bibliography/identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2181
2186
  value: query.value,
2182
2187
  matchMode: query.matchMode,
2183
2188
  isCaseSensitive: query.isCaseSensitive
2184
2189
  });
2185
- case "image": return buildStringMatchPredicate$1({
2190
+ case "image": return buildStringMatchPredicate({
2186
2191
  path: `string-join(image/identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2187
2192
  value: query.value,
2188
2193
  matchMode: query.matchMode,
@@ -2199,6 +2204,19 @@ function buildBooleanQueryClause(query) {
2199
2204
  return query.isNegated ? `not(${baseClause})` : baseClause;
2200
2205
  }
2201
2206
  /**
2207
+ * Build query filters for an XQuery string.
2208
+ */
2209
+ function buildQueryFilters(queries) {
2210
+ return queries.map((query, index) => {
2211
+ const clause = buildBooleanQueryClause(query);
2212
+ if (index === 0) return clause;
2213
+ return `${query.operator === "OR" ? "or" : "and"} ${clause}`;
2214
+ }).join(" ");
2215
+ }
2216
+
2217
+ //#endregion
2218
+ //#region src/utils/fetchers/set/items.ts
2219
+ /**
2202
2220
  * Build an XQuery string to fetch Set items from the OCHRE API
2203
2221
  * @param params - The parameters for the fetch
2204
2222
  * @param params.setScopeUuids - An array of Set scope UUIDs to filter by
@@ -2217,11 +2235,7 @@ function buildXQuery$1(params, options) {
2217
2235
  const startPosition = (page - 1) * pageSize + 1;
2218
2236
  const endPosition = page * pageSize;
2219
2237
  const setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
2220
- const queryFilters = queries.map((query, index) => {
2221
- const clause = buildBooleanQueryClause(query);
2222
- if (index === 0) return clause;
2223
- return `${query.operator === "OR" ? "or" : "and"} ${clause}`;
2224
- }).join(" ");
2238
+ const queryFilters = buildQueryFilters(queries);
2225
2239
  const filterPredicates = [];
2226
2240
  if (belongsToCollectionScopeUuids.length > 0) {
2227
2241
  const belongsToCollectionScopeValues = belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
@@ -2397,35 +2411,6 @@ function aggregatePropertyValues(values) {
2397
2411
  });
2398
2412
  }
2399
2413
  /**
2400
- * Build a string match predicate for an XQuery string
2401
- * @param params - The parameters for the predicate
2402
- * @param params.path - The path to the string
2403
- * @param params.value - The value to match
2404
- * @param params.matchMode - The match mode (includes or exact)
2405
- * @param params.isCaseSensitive - Whether to match case-sensitively
2406
- * @returns The string match predicate
2407
- */
2408
- function buildStringMatchPredicate(params) {
2409
- const { path, value, matchMode, isCaseSensitive } = params;
2410
- const comparedPath = isCaseSensitive ? path : `lower-case(${path})`;
2411
- const comparedValueLiteral = stringLiteral(isCaseSensitive ? value : value.toLowerCase());
2412
- if (matchMode === "includes") return `contains(${comparedPath}, ${comparedValueLiteral})`;
2413
- return `${comparedPath} = ${comparedValueLiteral}`;
2414
- }
2415
- /**
2416
- * Build a title predicate for an XQuery string
2417
- * @param titleQuery - The title query
2418
- * @returns The title predicate
2419
- */
2420
- function buildTitlePredicate(titleQuery) {
2421
- return buildStringMatchPredicate({
2422
- path: `string-join(identification/label/content[@xml:lang="${titleQuery.language}"]/string, "")`,
2423
- value: titleQuery.value,
2424
- matchMode: titleQuery.matchMode,
2425
- isCaseSensitive: titleQuery.isCaseSensitive
2426
- });
2427
- }
2428
- /**
2429
2414
  * Schema for a single property value query item in the OCHRE API response
2430
2415
  */
2431
2416
  const propertyValueQueryItemSchema = z.object({
@@ -2482,7 +2467,7 @@ const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({
2482
2467
  * @param params.setScopeUuids - An array of set scope UUIDs to filter by
2483
2468
  * @param params.belongsToCollectionScopeUuids - An array of collection scope UUIDs to filter by
2484
2469
  * @param params.propertyVariableUuids - An array of property variable UUIDs to fetch
2485
- * @param params.titleQuery - Title query to filter returned items by item title
2470
+ * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
2486
2471
  * @param params.isLimitedToLeafPropertyValues - Whether to limit the property values to leaf property values
2487
2472
  * @param options - Options for the fetch
2488
2473
  * @param options.version - The version of the OCHRE API to use
@@ -2490,16 +2475,17 @@ const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({
2490
2475
  */
2491
2476
  function buildXQuery(params, options) {
2492
2477
  const version = options?.version ?? DEFAULT_API_VERSION;
2493
- const { setScopeUuids, belongsToCollectionScopeUuids, propertyVariableUuids, titleQuery, isLimitedToLeafPropertyValues } = params;
2478
+ const { setScopeUuids, belongsToCollectionScopeUuids, propertyVariableUuids, queries, isLimitedToLeafPropertyValues } = params;
2494
2479
  let setScopeFilter = "/set/items/*";
2495
2480
  if (setScopeUuids.length > 0) setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
2496
2481
  const propertyVariableFilters = propertyVariableUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
2482
+ const queryFilters = buildQueryFilters(queries);
2497
2483
  const filterPredicates = [];
2498
2484
  if (belongsToCollectionScopeUuids.length > 0) {
2499
2485
  const belongsToCollectionScopeValues = belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
2500
2486
  filterPredicates.push(`.//properties[property[label/@uuid="${BELONGS_TO_COLLECTION_UUID}" and value/(${belongsToCollectionScopeValues})]]`);
2501
2487
  }
2502
- if (titleQuery != null) filterPredicates.push(buildTitlePredicate(titleQuery));
2488
+ if (queryFilters.length > 0) filterPredicates.push(`(${queryFilters})`);
2503
2489
  const itemFilters = filterPredicates.length > 0 ? `[${filterPredicates.join(" and ")}]` : "";
2504
2490
  return `<ochre>{${`let $items := ${version === 2 ? "doc()" : "input()"}/ochre
2505
2491
  ${setScopeFilter}
@@ -2522,7 +2508,7 @@ function buildXQuery(params, options) {
2522
2508
  * @param params.setScopeUuids - An array of set scope UUIDs to filter by
2523
2509
  * @param params.belongsToCollectionScopeUuids - The collection scope UUIDs to filter by
2524
2510
  * @param params.propertyVariableUuids - The property variable UUIDs to query by
2525
- * @param params.titleQuery - Title query to filter returned items by item title
2511
+ * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
2526
2512
  * @param params.isLimitedToLeafPropertyValues - Whether to limit the property values to leaf property values
2527
2513
  * @param options - Options for the fetch
2528
2514
  * @param options.fetch - The fetch function to use
@@ -2532,12 +2518,12 @@ function buildXQuery(params, options) {
2532
2518
  async function fetchSetPropertyValuesByPropertyVariables(params, options) {
2533
2519
  try {
2534
2520
  const version = options?.version ?? DEFAULT_API_VERSION;
2535
- const { setScopeUuids, belongsToCollectionScopeUuids, propertyVariableUuids, titleQuery, isLimitedToLeafPropertyValues } = setPropertyValuesByPropertyVariablesParamsSchema.parse(params);
2521
+ const { setScopeUuids, belongsToCollectionScopeUuids, propertyVariableUuids, queries, isLimitedToLeafPropertyValues } = setPropertyValuesByPropertyVariablesParamsSchema.parse(params);
2536
2522
  const xquery = buildXQuery({
2537
2523
  setScopeUuids,
2538
2524
  belongsToCollectionScopeUuids,
2539
2525
  propertyVariableUuids,
2540
- titleQuery,
2526
+ queries,
2541
2527
  isLimitedToLeafPropertyValues
2542
2528
  }, { version });
2543
2529
  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="*"`);
@@ -2939,7 +2925,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
2939
2925
  case "advanced-search": {
2940
2926
  const boundElementPropertyUuid = getPropertyByLabel(componentProperty.properties, "bound-element")?.values[0]?.uuid ?? null;
2941
2927
  const linkToProperty = getPropertyByLabel(componentProperty.properties, "link-to");
2942
- const href = linkToProperty?.values[0]?.href ?? linkToProperty?.values[0]?.slug ?? null;
2928
+ const href = linkToProperty?.values[0]?.href != null ? transformPermanentIdentificationUrl(linkToProperty.values[0].href) : linkToProperty?.values[0]?.slug ?? null;
2943
2929
  if (boundElementPropertyUuid == null && href == null) throw new Error(`Bound element or href not found for the following component: “${componentName}”`);
2944
2930
  properties = {
2945
2931
  component: "advanced-search",
@@ -3018,10 +3004,10 @@ function parseWebElementProperties(componentProperty, elementResource) {
3018
3004
  variant ??= "default";
3019
3005
  let isExternal = false;
3020
3006
  const navigateToProperty = getPropertyByLabel(componentProperty.properties, "navigate-to");
3021
- let href = navigateToProperty?.values[0]?.href ?? navigateToProperty?.values[0]?.slug ?? null;
3007
+ let href = navigateToProperty?.values[0]?.href != null ? transformPermanentIdentificationUrl(navigateToProperty.values[0].href) : navigateToProperty?.values[0]?.slug ?? null;
3022
3008
  if (href === null) {
3023
3009
  const linkToProperty = getPropertyByLabel(componentProperty.properties, "link-to");
3024
- href = linkToProperty?.values[0]?.href ?? linkToProperty?.values[0]?.slug ?? null;
3010
+ href = linkToProperty?.values[0]?.href != null ? transformPermanentIdentificationUrl(linkToProperty.values[0].href) : linkToProperty?.values[0]?.slug ?? null;
3025
3011
  if (href === null) throw new Error(`Properties “navigate-to” or “link-to” not found for the following component: “${componentName}”`);
3026
3012
  else isExternal = true;
3027
3013
  }
@@ -3071,8 +3057,8 @@ function parseWebElementProperties(componentProperty, elementResource) {
3071
3057
  isFilterMapDisplayed ??= false;
3072
3058
  let isFilterInputDisplayed = getPropertyValueByLabel(componentProperty.properties, "filter-input-displayed");
3073
3059
  isFilterInputDisplayed ??= false;
3074
- let isFilterLimitedToTitleQuery = getPropertyValueByLabel(componentProperty.properties, "filter-limit-to-title-query");
3075
- isFilterLimitedToTitleQuery ??= false;
3060
+ let isFilterLimitedToInputFilter = getPropertyValueByLabel(componentProperty.properties, "filter-limit-to-input-filter");
3061
+ isFilterLimitedToInputFilter ??= false;
3076
3062
  let isFilterLimitedToLeafPropertyValues = getPropertyValueByLabel(componentProperty.properties, "filter-limit-to-leaf-property-values");
3077
3063
  isFilterLimitedToLeafPropertyValues ??= false;
3078
3064
  let isSortDisplayed = getPropertyValueByLabel(componentProperty.properties, "sort-displayed");
@@ -3122,7 +3108,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
3122
3108
  isResultsBarDisplayed: isFilterResultsBarDisplayed,
3123
3109
  isMapDisplayed: isFilterMapDisplayed,
3124
3110
  isInputDisplayed: isFilterInputDisplayed,
3125
- isLimitedToTitleQuery: isFilterLimitedToTitleQuery,
3111
+ isLimitedToInputFilter: isFilterLimitedToInputFilter,
3126
3112
  isLimitedToLeafPropertyValues: isFilterLimitedToLeafPropertyValues,
3127
3113
  sidebarSort: filterSidebarSort
3128
3114
  },
@@ -3162,7 +3148,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
3162
3148
  const width = getPropertyValueByLabel(componentProperty.properties, "width");
3163
3149
  properties = {
3164
3150
  component: "iframe",
3165
- href,
3151
+ href: transformPermanentIdentificationUrl(href),
3166
3152
  height: height?.toString() ?? null,
3167
3153
  width: width?.toString() ?? null
3168
3154
  };
@@ -3393,7 +3379,7 @@ function parseWebElementProperties(componentProperty, elementResource) {
3393
3379
  queryVariant ??= "submit";
3394
3380
  const boundElementUuid = getPropertyByLabel(componentProperty.properties, "bound-element")?.values[0]?.uuid ?? null;
3395
3381
  const linkToProperty = getPropertyByLabel(componentProperty.properties, "link-to");
3396
- const href = linkToProperty?.values[0]?.href ?? linkToProperty?.values[0]?.slug ?? null;
3382
+ const href = linkToProperty?.values[0]?.href != null ? transformPermanentIdentificationUrl(linkToProperty.values[0].href) : linkToProperty?.values[0]?.slug ?? null;
3397
3383
  if (!boundElementUuid && !href) throw new Error(`Bound element or href not found for the following component: “${componentName}”`);
3398
3384
  let placeholder = getPropertyValueByLabel(componentProperty.properties, "placeholder-text");
3399
3385
  placeholder ??= null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ochre-sdk",
3
- "version": "0.20.7",
3
+ "version": "0.20.11",
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",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "@antfu/eslint-config": "^7.6.1",
50
- "@types/node": "^24.10.14",
50
+ "@types/node": "^24.10.15",
51
51
  "bumpp": "^10.4.1",
52
52
  "eslint": "^10.0.2",
53
53
  "prettier": "^3.8.1",