ochre-sdk 0.20.22 → 0.20.24

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
@@ -674,9 +674,10 @@ type SetItemsSort = {
674
674
  * Represents a query for Set items
675
675
  */
676
676
  type Query = {
677
- target: "propertyValue";
677
+ target: "property";
678
+ propertyVariables: Array<string>;
678
679
  dataType: Exclude<Exclude<PropertyValueContentType, "coordinate">, "date" | "dateTime">;
679
- value: string;
680
+ propertyValues?: Array<string>;
680
681
  from?: never;
681
682
  to?: never;
682
683
  matchMode: "includes" | "exact";
@@ -685,9 +686,10 @@ type Query = {
685
686
  operator?: "AND" | "OR";
686
687
  isNegated?: boolean;
687
688
  } | {
688
- target: "propertyValue";
689
+ target: "property";
690
+ propertyVariables: Array<string>;
689
691
  dataType: "date" | "dateTime";
690
- value: string;
692
+ propertyValues?: never;
691
693
  from: string;
692
694
  to?: string;
693
695
  matchMode: "includes" | "exact";
@@ -696,9 +698,10 @@ type Query = {
696
698
  operator?: "AND" | "OR";
697
699
  isNegated?: boolean;
698
700
  } | {
699
- target: "propertyValue";
701
+ target: "property";
702
+ propertyVariables: Array<string>;
700
703
  dataType: "date" | "dateTime";
701
- value: string;
704
+ propertyValues?: never;
702
705
  from?: string;
703
706
  to: string;
704
707
  matchMode: "includes" | "exact";
@@ -706,6 +709,14 @@ type Query = {
706
709
  language: string;
707
710
  operator?: "AND" | "OR";
708
711
  isNegated?: boolean;
712
+ } | {
713
+ target: "string";
714
+ value: string;
715
+ matchMode: "includes" | "exact";
716
+ isCaseSensitive: boolean;
717
+ language: string;
718
+ operator?: "AND" | "OR";
719
+ isNegated?: boolean;
709
720
  } | {
710
721
  target: "title" | "description" | "image" | "periods" | "bibliography";
711
722
  value: string;
@@ -1223,7 +1234,6 @@ declare function fetchItem<T extends DataCategory = DataCategory, U extends Data
1223
1234
  *
1224
1235
  * @param params - The parameters for the fetch
1225
1236
  * @param params.setScopeUuids - The Set scope UUIDs to filter by
1226
- * @param params.propertyVariableUuids - The property variable UUIDs to filter by
1227
1237
  * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
1228
1238
  * @param params.sort - Optional sorting configuration applied before pagination.
1229
1239
  * For propertyValue sorting, dataType is required and the sort key uses the first valid leaf value (value[not(@i)]).
@@ -1237,7 +1247,6 @@ declare function fetchItem<T extends DataCategory = DataCategory, U extends Data
1237
1247
  */
1238
1248
  declare function fetchSetItems<U extends Array<DataCategory> = Array<DataCategory>>(params: {
1239
1249
  setScopeUuids: Array<string>;
1240
- propertyVariableUuids: Array<string>;
1241
1250
  queries: Array<Query>;
1242
1251
  sort?: SetItemsSort;
1243
1252
  page: number;
@@ -1265,7 +1274,6 @@ declare function fetchSetItems<U extends Array<DataCategory> = Array<DataCategor
1265
1274
  *
1266
1275
  * @param params - The parameters for the fetch
1267
1276
  * @param params.setScopeUuids - An array of set scope UUIDs to filter by
1268
- * @param params.propertyVariableUuids - The property variable UUIDs to query by
1269
1277
  * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
1270
1278
  * @param params.attributes - Whether to return values for bibliographies and periods
1271
1279
  * @param params.attributes.bibliographies - Whether to return values for bibliographies
@@ -1279,7 +1287,6 @@ declare function fetchSetItems<U extends Array<DataCategory> = Array<DataCategor
1279
1287
  */
1280
1288
  declare function fetchSetPropertyValuesByPropertyVariables(params: {
1281
1289
  setScopeUuids: Array<string>;
1282
- propertyVariableUuids: Array<string>;
1283
1290
  queries?: Array<Query>;
1284
1291
  attributes?: {
1285
1292
  bibliographies: boolean;
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)
@@ -778,7 +758,8 @@ const boundsSchema = z.string().transform((str, ctx) => {
778
758
  */
779
759
  const setQuerySchema = z.union([
780
760
  z.object({
781
- target: z.literal("propertyValue"),
761
+ target: z.literal("property"),
762
+ propertyVariables: z.array(uuidSchema).min(1, "At least one property variable UUID is required"),
782
763
  dataType: z.enum([
783
764
  "string",
784
765
  "integer",
@@ -787,7 +768,7 @@ const setQuerySchema = z.union([
787
768
  "time",
788
769
  "IDREF"
789
770
  ]),
790
- value: z.string(),
771
+ propertyValues: z.array(z.string()).min(1, "At least one property value is required").optional(),
791
772
  matchMode: z.enum(["includes", "exact"]),
792
773
  isCaseSensitive: z.boolean(),
793
774
  language: z.string().default("eng"),
@@ -795,9 +776,9 @@ const setQuerySchema = z.union([
795
776
  isNegated: z.boolean().optional().default(false)
796
777
  }).strict(),
797
778
  z.object({
798
- target: z.literal("propertyValue"),
779
+ target: z.literal("property"),
780
+ propertyVariables: z.array(uuidSchema).min(1, "At least one property variable UUID is required"),
799
781
  dataType: z.enum(["date", "dateTime"]),
800
- value: z.string(),
801
782
  from: z.string(),
802
783
  to: z.string().optional(),
803
784
  matchMode: z.enum(["includes", "exact"]),
@@ -807,9 +788,9 @@ const setQuerySchema = z.union([
807
788
  isNegated: z.boolean().optional().default(false)
808
789
  }).strict(),
809
790
  z.object({
810
- target: z.literal("propertyValue"),
791
+ target: z.literal("property"),
792
+ propertyVariables: z.array(uuidSchema).min(1, "At least one property variable UUID is required"),
811
793
  dataType: z.enum(["date", "dateTime"]),
812
- value: z.string(),
813
794
  from: z.string().optional(),
814
795
  to: z.string(),
815
796
  matchMode: z.enum(["includes", "exact"]),
@@ -818,6 +799,15 @@ const setQuerySchema = z.union([
818
799
  operator: z.enum(["AND", "OR"]).optional(),
819
800
  isNegated: z.boolean().optional().default(false)
820
801
  }).strict(),
802
+ z.object({
803
+ target: z.literal("string"),
804
+ value: z.string(),
805
+ matchMode: z.enum(["includes", "exact"]),
806
+ isCaseSensitive: z.boolean(),
807
+ language: z.string().default("eng"),
808
+ operator: z.enum(["AND", "OR"]).optional(),
809
+ isNegated: z.boolean().optional().default(false)
810
+ }).strict(),
821
811
  z.object({
822
812
  target: z.enum([
823
813
  "title",
@@ -877,7 +867,6 @@ function validateSetQueriesOperators(queries, ctx) {
877
867
  const setPropertyValuesByPropertyVariablesParamsSchema = z.object({
878
868
  setScopeUuids: z.array(uuidSchema).min(1, "At least one set scope UUID is required"),
879
869
  belongsToCollectionScopeUuids: z.array(uuidSchema).default([]),
880
- propertyVariableUuids: z.array(uuidSchema).min(1, "At least one property variable UUID is required"),
881
870
  queries: setQueriesSchema,
882
871
  attributes: z.object({
883
872
  bibliographies: z.boolean().default(false),
@@ -889,19 +878,22 @@ const setPropertyValuesByPropertyVariablesParamsSchema = z.object({
889
878
  isLimitedToLeafPropertyValues: z.boolean().default(false)
890
879
  }).superRefine((value, ctx) => {
891
880
  validateSetQueriesOperators(value.queries, ctx);
881
+ if (!value.queries.some((query) => query.target === "property")) ctx.addIssue({
882
+ code: "custom",
883
+ path: ["queries"],
884
+ message: "At least one property query is required"
885
+ });
892
886
  });
893
887
  const setItemsParamsSchema = z.object({
894
888
  setScopeUuids: z.array(uuidSchema).min(1, "At least one set scope UUID is required"),
895
889
  belongsToCollectionScopeUuids: z.array(uuidSchema).default([]),
896
- propertyVariableUuids: z.array(uuidSchema).default([]),
897
890
  queries: setQueriesSchema,
898
891
  sort: setItemsSortSchema,
899
892
  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)
893
+ pageSize: z.number().min(1, "Page size must be positive").default(48)
901
894
  }).superRefine((value, ctx) => {
902
895
  validateSetQueriesOperators(value.queries, ctx);
903
896
  });
904
-
905
897
  //#endregion
906
898
  //#region src/utils/parse/index.ts
907
899
  const TRAILING_ELLIPSIS_REGEX = /\s*\.{3}$/;
@@ -1977,7 +1969,6 @@ function parseConcept(concept, metadata, persistentUrl, belongsTo) {
1977
1969
  function parseConcepts(concepts) {
1978
1970
  return concepts.map((concept) => parseConcept(concept));
1979
1971
  }
1980
-
1981
1972
  //#endregion
1982
1973
  //#region src/utils/fetchers/gallery.ts
1983
1974
  /**
@@ -2004,7 +1995,7 @@ const galleryParamsSchema = z.object({
2004
1995
  */
2005
1996
  async function fetchGallery(params, options) {
2006
1997
  try {
2007
- const version = options?.version ?? DEFAULT_API_VERSION;
1998
+ const version = options?.version ?? 2;
2008
1999
  const { uuid, filter, page, pageSize } = galleryParamsSchema.parse(params);
2009
2000
  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
2001
  for $q in ${version === 2 ? "doc()" : "input()"}/ochre[@uuid='${uuid}']
@@ -2036,7 +2027,6 @@ async function fetchGallery(params, options) {
2036
2027
  };
2037
2028
  }
2038
2029
  }
2039
-
2040
2030
  //#endregion
2041
2031
  //#region src/utils/fetchers/uuid.ts
2042
2032
  /**
@@ -2048,7 +2038,7 @@ async function fetchGallery(params, options) {
2048
2038
  */
2049
2039
  async function fetchByUuid(uuid, options) {
2050
2040
  try {
2051
- const version = options?.version ?? DEFAULT_API_VERSION;
2041
+ const version = options?.version ?? 2;
2052
2042
  const parsedUuid = uuidSchema.parse(uuid);
2053
2043
  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
2044
  if (!response.ok) throw new Error("Failed to fetch OCHRE data");
@@ -2059,7 +2049,6 @@ async function fetchByUuid(uuid, options) {
2059
2049
  return [error instanceof Error ? error.message : "Unknown error", null];
2060
2050
  }
2061
2051
  }
2062
-
2063
2052
  //#endregion
2064
2053
  //#region src/utils/fetchers/item.ts
2065
2054
  /**
@@ -2070,7 +2059,7 @@ async function fetchByUuid(uuid, options) {
2070
2059
  */
2071
2060
  async function fetchItem(uuid, category, itemCategories, options) {
2072
2061
  try {
2073
- const version = options?.version ?? DEFAULT_API_VERSION;
2062
+ const version = options?.version ?? 2;
2074
2063
  const [error, data] = await fetchByUuid(uuid, {
2075
2064
  fetch,
2076
2065
  version
@@ -2144,26 +2133,49 @@ async function fetchItem(uuid, category, itemCategories, options) {
2144
2133
  };
2145
2134
  }
2146
2135
  }
2147
-
2148
2136
  //#endregion
2149
2137
  //#region src/utils/fetchers/set/query-helpers.ts
2150
2138
  const CTS_INCLUDES_STOP_WORDS = [
2151
- "of",
2152
- "the",
2153
2139
  "and",
2140
+ "at",
2154
2141
  "in",
2155
- "it"
2142
+ "it",
2143
+ "of",
2144
+ "the",
2145
+ "to"
2156
2146
  ];
2157
2147
  const CTS_INCLUDES_STOP_WORDS_VAR = "$ctsIncludesStopWords";
2148
+ function buildFlattenedContentValuesExpression(contentNodesExpression) {
2149
+ return `for $content in ${contentNodesExpression}
2150
+ return string-join($content//text(), "")`;
2151
+ }
2158
2152
  /**
2159
2153
  * Build a string match predicate for an XQuery string.
2160
2154
  */
2161
2155
  function buildRawStringMatchPredicate(params) {
2162
- const { path, value, matchMode, isCaseSensitive } = params;
2163
- const comparedPath = isCaseSensitive ? path : `lower-case(${path})`;
2156
+ const { valueExpression, value, matchMode, isCaseSensitive } = params;
2164
2157
  const comparedValueLiteral = stringLiteral(isCaseSensitive ? value : value.toLowerCase());
2165
- if (matchMode === "includes") return `contains(${comparedPath}, ${comparedValueLiteral})`;
2166
- return `${comparedPath} = ${comparedValueLiteral}`;
2158
+ const candidateVar = "$candidate";
2159
+ const comparedCandidate = isCaseSensitive ? candidateVar : `lower-case(${candidateVar})`;
2160
+ if (matchMode === "includes") return `some ${candidateVar} in (${valueExpression})
2161
+ satisfies contains(${comparedCandidate}, ${comparedValueLiteral})`;
2162
+ return `some ${candidateVar} in (${valueExpression})
2163
+ satisfies ${comparedCandidate} = ${comparedValueLiteral}`;
2164
+ }
2165
+ /**
2166
+ * Build a combined raw string match predicate for multiple paths.
2167
+ */
2168
+ function buildCombinedRawStringMatchPredicate(params) {
2169
+ const { valueExpressions, value, matchMode, isCaseSensitive } = params;
2170
+ const predicates = [];
2171
+ for (const valueExpression of valueExpressions) predicates.push(buildRawStringMatchPredicate({
2172
+ valueExpression,
2173
+ value,
2174
+ matchMode,
2175
+ isCaseSensitive
2176
+ }));
2177
+ if (predicates.length === 1) return predicates[0] ?? "false()";
2178
+ return `(${predicates.join(" or ")})`;
2167
2179
  }
2168
2180
  /**
2169
2181
  * Build CTS word-query options for API v2 includes search.
@@ -2172,70 +2184,204 @@ function buildCtsQueryOptionsExpression(isCaseSensitive) {
2172
2184
  return `(${[
2173
2185
  isCaseSensitive ? "case-sensitive" : "case-insensitive",
2174
2186
  "diacritic-insensitive",
2175
- "punctuation-insensitive",
2176
- "whitespace-insensitive",
2177
- "stemmed"
2187
+ "punctuation-insensitive"
2178
2188
  ].map((option) => stringLiteral(option)).join(", ")})`;
2179
2189
  }
2180
2190
  /**
2181
- * Build a CTS-backed includes predicate for an XQuery string.
2191
+ * Build a CTS word-query expression for an XQuery term.
2182
2192
  */
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
- });
2193
+ function buildCtsWordQueryExpression(params) {
2194
+ const { termExpression, isCaseSensitive } = params;
2195
+ return `cts:word-query(${termExpression}, ${buildCtsQueryOptionsExpression(isCaseSensitive)})`;
2196
+ }
2197
+ /**
2198
+ * Build tokenized search declarations for CTS-backed queries.
2199
+ */
2200
+ function buildTokenizedSearchDeclarations(params) {
2201
+ const { value, isCaseSensitive, queryKey } = params;
2202
+ const searchStringVar = `$query${queryKey}SearchString`;
2203
+ const rawTermsVar = `$query${queryKey}RawTerms`;
2204
+ const termsVar = `$query${queryKey}Terms`;
2205
+ const tokenSourceExpression = isCaseSensitive ? searchStringVar : `fn:lower-case(${searchStringVar})`;
2196
2206
  return {
2197
2207
  declarations: [
2198
2208
  `let ${searchStringVar} := ${stringLiteral(value)}`,
2199
- String.raw`let ${rawTermsVar} := fn:tokenize(${searchStringVar}, "\W+")`,
2209
+ String.raw`let ${rawTermsVar} := fn:tokenize(${tokenSourceExpression}, "\W+")`,
2200
2210
  `let ${termsVar} :=
2201
2211
  for $term in ${rawTermsVar}
2202
2212
  let $normalizedTerm := fn:lower-case($term)
2203
2213
  where $normalizedTerm ne "" and not($normalizedTerm = ${CTS_INCLUDES_STOP_WORDS_VAR})
2204
- return ${isCaseSensitive ? "$term" : "$normalizedTerm"}`,
2214
+ return $term`
2215
+ ],
2216
+ termsVar
2217
+ };
2218
+ }
2219
+ /**
2220
+ * Build a CTS-backed field includes predicate for an XQuery string.
2221
+ */
2222
+ function buildCtsFieldIncludesPredicate(params) {
2223
+ const { contentNodesExpression, valueExpression, value, isCaseSensitive, queryKey } = params;
2224
+ const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
2225
+ value,
2226
+ isCaseSensitive,
2227
+ queryKey
2228
+ });
2229
+ const ctsQueryVar = `$query${queryKey}CtsQuery`;
2230
+ const contentNodeVar = `$query${queryKey}ContentNode`;
2231
+ const fallbackPredicate = buildRawStringMatchPredicate({
2232
+ valueExpression,
2233
+ value,
2234
+ matchMode: "includes",
2235
+ isCaseSensitive
2236
+ });
2237
+ return {
2238
+ declarations: [...tokenizedSearchDeclarations.declarations, `let ${ctsQueryVar} :=
2239
+ if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
2240
+ then ${buildCtsWordQueryExpression({
2241
+ termExpression: `${tokenizedSearchDeclarations.termsVar}[1]`,
2242
+ isCaseSensitive
2243
+ })}
2244
+ else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
2245
+ then cts:and-query((
2246
+ for $term in ${tokenizedSearchDeclarations.termsVar}
2247
+ return ${buildCtsWordQueryExpression({
2248
+ termExpression: "$term",
2249
+ isCaseSensitive
2250
+ })}
2251
+ ))
2252
+ else ()`],
2253
+ predicate: `(if (exists(${ctsQueryVar}))
2254
+ then some ${contentNodeVar} in (${contentNodesExpression})
2255
+ satisfies cts:contains(${contentNodeVar}, ${ctsQueryVar})
2256
+ else ${fallbackPredicate})`
2257
+ };
2258
+ }
2259
+ /**
2260
+ * Build the raw search paths for item-level string search.
2261
+ */
2262
+ function buildItemStringSearchPaths(language) {
2263
+ return [buildFlattenedContentValuesExpression(`identification/label/content[@xml:lang="${language}"]`), buildFlattenedContentValuesExpression(`properties//property/value[not(@inherited="true")]/content[@xml:lang="${language}"]`)];
2264
+ }
2265
+ /**
2266
+ * Build the identification branch for an item-level CTS string search.
2267
+ */
2268
+ function buildItemStringIdentificationBranch(params) {
2269
+ const { termExpression, isCaseSensitive, language } = params;
2270
+ return `cts:element-query(xs:QName("identification"),
2271
+ cts:and-query((
2272
+ cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
2273
+ ${buildCtsWordQueryExpression({
2274
+ termExpression,
2275
+ isCaseSensitive
2276
+ })}
2277
+ ))
2278
+ )`;
2279
+ }
2280
+ /**
2281
+ * Build the property value branch for an item-level CTS string search.
2282
+ */
2283
+ function buildItemStringPropertyValueBranch(params) {
2284
+ const { termExpression, isCaseSensitive, language } = params;
2285
+ return `cts:element-query(xs:QName("properties"),
2286
+ cts:element-query(xs:QName("property"),
2287
+ cts:element-query(xs:QName("value"),
2288
+ cts:and-query((
2289
+ cts:not-query(cts:element-attribute-value-query(xs:QName("value"), xs:QName("inherited"), "true")),
2290
+ cts:element-query(xs:QName("content"),
2291
+ cts:and-query((
2292
+ cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
2293
+ ${buildCtsWordQueryExpression({
2294
+ termExpression,
2295
+ isCaseSensitive
2296
+ })}
2297
+ ))
2298
+ )
2299
+ ))
2300
+ )
2301
+ )
2302
+ )`;
2303
+ }
2304
+ /**
2305
+ * Build an item-level CTS string search predicate.
2306
+ */
2307
+ function buildItemStringSearchPredicate(params) {
2308
+ const { query, version, queryKey } = params;
2309
+ const fallbackPredicate = buildCombinedRawStringMatchPredicate({
2310
+ valueExpressions: buildItemStringSearchPaths(query.language),
2311
+ value: query.value,
2312
+ matchMode: query.matchMode,
2313
+ isCaseSensitive: query.isCaseSensitive
2314
+ });
2315
+ if (query.matchMode !== "includes" || version !== 2) return {
2316
+ declarations: [],
2317
+ predicate: fallbackPredicate
2318
+ };
2319
+ const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
2320
+ value: query.value,
2321
+ isCaseSensitive: query.isCaseSensitive,
2322
+ queryKey
2323
+ });
2324
+ const termQueriesVar = `$query${queryKey}TermQueries`;
2325
+ const ctsQueryVar = `$query${queryKey}CtsQuery`;
2326
+ return {
2327
+ declarations: [
2328
+ ...tokenizedSearchDeclarations.declarations,
2329
+ `let ${termQueriesVar} :=
2330
+ for $term in ${tokenizedSearchDeclarations.termsVar}
2331
+ return
2332
+ cts:or-query((
2333
+ ${buildItemStringIdentificationBranch({
2334
+ termExpression: "$term",
2335
+ isCaseSensitive: query.isCaseSensitive,
2336
+ language: query.language
2337
+ })},
2338
+ ${buildItemStringPropertyValueBranch({
2339
+ termExpression: "$term",
2340
+ isCaseSensitive: query.isCaseSensitive,
2341
+ language: query.language
2342
+ })}
2343
+ ))`,
2205
2344
  `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"))
2345
+ if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
2346
+ then ${termQueriesVar}[1]
2347
+ else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
2348
+ then cts:and-query(${termQueriesVar})
2213
2349
  else ()`
2214
2350
  ],
2215
- predicate: `(if (exists(${ctsQueryVar})) then cts:contains(${path}, ${ctsQueryVar}) else ${fallbackPredicate})`
2351
+ predicate: `(if (exists(${ctsQueryVar})) then cts:contains(., ${ctsQueryVar}) else ${fallbackPredicate})`
2216
2352
  };
2217
2353
  }
2218
2354
  /**
2219
2355
  * Build a string match predicate for an XQuery string.
2220
2356
  */
2221
2357
  function buildStringMatchPredicate(params) {
2222
- const { path, value, matchMode, isCaseSensitive, version, queryIndex } = params;
2223
- if (matchMode === "includes" && version === 2) return buildCtsIncludesPredicate({
2224
- path,
2358
+ const { contentNodesExpression, value, matchMode, isCaseSensitive, version, queryKey } = params;
2359
+ const valueExpression = buildFlattenedContentValuesExpression(contentNodesExpression);
2360
+ if (matchMode === "includes" && version === 2) return buildCtsFieldIncludesPredicate({
2361
+ contentNodesExpression,
2362
+ valueExpression,
2225
2363
  value,
2226
2364
  isCaseSensitive,
2227
- queryIndex
2365
+ queryKey
2228
2366
  });
2229
2367
  return {
2230
2368
  declarations: [],
2231
2369
  predicate: buildRawStringMatchPredicate({
2232
- path,
2370
+ valueExpression,
2233
2371
  value,
2234
2372
  matchMode,
2235
2373
  isCaseSensitive
2236
2374
  })
2237
2375
  };
2238
2376
  }
2377
+ function buildOrPredicate(predicates) {
2378
+ if (predicates.length === 1) return predicates[0] ?? "false()";
2379
+ return `(${predicates.join(" or ")})`;
2380
+ }
2381
+ function buildAndPredicate(predicates) {
2382
+ if (predicates.length === 1) return predicates[0] ?? "false()";
2383
+ return `(${predicates.join(" and ")})`;
2384
+ }
2239
2385
  /**
2240
2386
  * Build a date/dateTime range predicate for an XQuery string.
2241
2387
  */
@@ -2247,88 +2393,139 @@ function buildDateRangePredicate(params) {
2247
2393
  return conditions.join(" and ");
2248
2394
  }
2249
2395
  /**
2250
- * Build a property value predicate for an XQuery string.
2396
+ * Build a property label predicate for an XQuery string.
2251
2397
  */
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)}]]`
2398
+ function buildPropertyLabelPredicate(propertyVariables) {
2399
+ const labelPredicates = [];
2400
+ for (const propertyVariable of propertyVariables) labelPredicates.push(`@uuid=${stringLiteral(propertyVariable)}`);
2401
+ return `label[${buildOrPredicate(labelPredicates)}]`;
2402
+ }
2403
+ function buildPropertyValueAttributePredicate(params) {
2404
+ const { propertyValues, attributeName } = params;
2405
+ const valuePredicates = [];
2406
+ for (const propertyValue of propertyValues) valuePredicates.push(`value[@${attributeName}=${stringLiteral(propertyValue)}]`);
2407
+ return buildOrPredicate(valuePredicates);
2408
+ }
2409
+ function buildPropertyStringValuePredicate(params) {
2410
+ const { query, version, queryKey } = params;
2411
+ const propertyContentNodesExpression = query.matchMode === "includes" && version === 2 ? `value[not(@inherited="true")]/content[@xml:lang="${query.language}"]` : `value/content[@xml:lang="${query.language}"]`;
2412
+ const declarations = [];
2413
+ const valuePredicates = [];
2414
+ for (const [propertyValueIndex, propertyValue] of query.propertyValues.entries()) {
2415
+ const compiledStringPredicate = buildStringMatchPredicate({
2416
+ contentNodesExpression: propertyContentNodesExpression,
2417
+ value: propertyValue,
2418
+ matchMode: query.matchMode,
2419
+ isCaseSensitive: query.isCaseSensitive,
2420
+ version,
2421
+ queryKey: `${queryKey}_${propertyValueIndex + 1}`
2422
+ });
2423
+ declarations.push(...compiledStringPredicate.declarations);
2424
+ valuePredicates.push(compiledStringPredicate.predicate);
2425
+ }
2426
+ return {
2427
+ declarations,
2428
+ predicate: buildOrPredicate(valuePredicates)
2268
2429
  };
2269
- const compiledStringPredicate = buildStringMatchPredicate({
2270
- path: `string-join(value/content[@xml:lang="${query.language}"]/string, "")`,
2271
- value: query.value,
2272
- matchMode: query.matchMode,
2273
- isCaseSensitive: query.isCaseSensitive,
2274
- version,
2275
- queryIndex
2276
- });
2430
+ }
2431
+ /**
2432
+ * Build a property predicate for an XQuery string.
2433
+ */
2434
+ function buildPropertyPredicate(params) {
2435
+ const { query, version, queryKey } = params;
2436
+ const predicateParts = [buildPropertyLabelPredicate(query.propertyVariables)];
2437
+ const declarations = [];
2438
+ if (query.dataType === "date" || query.dataType === "dateTime") predicateParts.push(buildDateRangePredicate({
2439
+ from: query.from,
2440
+ to: query.to
2441
+ }));
2442
+ else if (query.propertyValues != null) switch (query.dataType) {
2443
+ case "IDREF":
2444
+ predicateParts.push(buildPropertyValueAttributePredicate({
2445
+ propertyValues: query.propertyValues,
2446
+ attributeName: "uuid"
2447
+ }));
2448
+ break;
2449
+ case "integer":
2450
+ case "decimal":
2451
+ case "time":
2452
+ case "boolean":
2453
+ predicateParts.push(buildPropertyValueAttributePredicate({
2454
+ propertyValues: query.propertyValues,
2455
+ attributeName: "rawValue"
2456
+ }));
2457
+ break;
2458
+ case "string": {
2459
+ const compiledStringPredicate = buildPropertyStringValuePredicate({
2460
+ query,
2461
+ version,
2462
+ queryKey
2463
+ });
2464
+ declarations.push(...compiledStringPredicate.declarations);
2465
+ predicateParts.push(compiledStringPredicate.predicate);
2466
+ break;
2467
+ }
2468
+ }
2277
2469
  return {
2278
- declarations: compiledStringPredicate.declarations,
2279
- predicate: `.//properties//property[${compiledStringPredicate.predicate}]`
2470
+ declarations,
2471
+ predicate: `.//properties//property[${buildAndPredicate(predicateParts)}]`
2280
2472
  };
2281
2473
  }
2282
2474
  /**
2283
2475
  * Build a query predicate for an XQuery string.
2284
2476
  */
2285
2477
  function buildQueryPredicate(params) {
2286
- const { query, version, queryIndex } = params;
2478
+ const { query, version, queryKey } = params;
2287
2479
  switch (query.target) {
2480
+ case "string": return buildItemStringSearchPredicate({
2481
+ query,
2482
+ version,
2483
+ queryKey
2484
+ });
2288
2485
  case "title": return buildStringMatchPredicate({
2289
- path: `string-join(identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2486
+ contentNodesExpression: `identification/label/content[@xml:lang="${query.language}"]`,
2290
2487
  value: query.value,
2291
2488
  matchMode: query.matchMode,
2292
2489
  isCaseSensitive: query.isCaseSensitive,
2293
2490
  version,
2294
- queryIndex
2491
+ queryKey
2295
2492
  });
2296
2493
  case "description": return buildStringMatchPredicate({
2297
- path: `string-join(description/content[@xml:lang="${query.language}"]/string, "")`,
2494
+ contentNodesExpression: `description/content[@xml:lang="${query.language}"]`,
2298
2495
  value: query.value,
2299
2496
  matchMode: query.matchMode,
2300
2497
  isCaseSensitive: query.isCaseSensitive,
2301
2498
  version,
2302
- queryIndex
2499
+ queryKey
2303
2500
  });
2304
2501
  case "periods": return buildStringMatchPredicate({
2305
- path: `string-join(periods/period/identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2502
+ contentNodesExpression: `periods/period/identification/label/content[@xml:lang="${query.language}"]`,
2306
2503
  value: query.value,
2307
2504
  matchMode: query.matchMode,
2308
2505
  isCaseSensitive: query.isCaseSensitive,
2309
2506
  version,
2310
- queryIndex
2507
+ queryKey
2311
2508
  });
2312
2509
  case "bibliography": return buildStringMatchPredicate({
2313
- path: `string-join(bibliographies/bibliography/identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2510
+ contentNodesExpression: `bibliographies/bibliography/identification/label/content[@xml:lang="${query.language}"]`,
2314
2511
  value: query.value,
2315
2512
  matchMode: query.matchMode,
2316
2513
  isCaseSensitive: query.isCaseSensitive,
2317
2514
  version,
2318
- queryIndex
2515
+ queryKey
2319
2516
  });
2320
2517
  case "image": return buildStringMatchPredicate({
2321
- path: `string-join(image/identification/label/content[@xml:lang="${query.language}"]/string, "")`,
2518
+ contentNodesExpression: `image/identification/label/content[@xml:lang="${query.language}"]`,
2322
2519
  value: query.value,
2323
2520
  matchMode: query.matchMode,
2324
2521
  isCaseSensitive: query.isCaseSensitive,
2325
2522
  version,
2326
- queryIndex
2523
+ queryKey
2327
2524
  });
2328
- case "propertyValue": return buildPropertyValuePredicate({
2525
+ case "property": return buildPropertyPredicate({
2329
2526
  query,
2330
2527
  version,
2331
- queryIndex
2528
+ queryKey
2332
2529
  });
2333
2530
  }
2334
2531
  }
@@ -2336,11 +2533,11 @@ function buildQueryPredicate(params) {
2336
2533
  * Build a boolean query clause for an XQuery string.
2337
2534
  */
2338
2535
  function buildBooleanQueryClause(params) {
2339
- const { query, version, queryIndex } = params;
2536
+ const { query, version, queryKey } = params;
2340
2537
  const compiledQueryPredicate = buildQueryPredicate({
2341
2538
  query,
2342
2539
  version,
2343
- queryIndex
2540
+ queryKey
2344
2541
  });
2345
2542
  const baseClause = `(${compiledQueryPredicate.predicate})`;
2346
2543
  return {
@@ -2360,7 +2557,7 @@ function buildQueryFilters(params) {
2360
2557
  const compiledClause = buildBooleanQueryClause({
2361
2558
  query,
2362
2559
  version,
2363
- queryIndex: index + 1
2560
+ queryKey: `${index + 1}`
2364
2561
  });
2365
2562
  if (compiledClause.declarations.length > 0) {
2366
2563
  hasCtsIncludesClauses = true;
@@ -2378,7 +2575,6 @@ function buildQueryFilters(params) {
2378
2575
  predicate: predicateParts.join(" ")
2379
2576
  };
2380
2577
  }
2381
-
2382
2578
  //#endregion
2383
2579
  //#region src/utils/fetchers/set/items.ts
2384
2580
  function mapSortDirectionToXQuery(direction) {
@@ -2456,7 +2652,6 @@ function buildOrderedItemsClause(sort) {
2456
2652
  * @param params - The parameters for the fetch
2457
2653
  * @param params.setScopeUuids - An array of Set scope UUIDs to filter by
2458
2654
  * @param params.belongsToCollectionScopeUuids - An array of collection scope UUIDs to filter by
2459
- * @param params.propertyVariableUuids - An array of property variable UUIDs to filter by
2460
2655
  * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
2461
2656
  * @param params.sort - Optional sorting configuration applied before pagination.
2462
2657
  * For propertyValue sorting, dataType is required and the sort key uses the first valid leaf value (value[not(@i)]).
@@ -2467,8 +2662,8 @@ function buildOrderedItemsClause(sort) {
2467
2662
  * @returns An XQuery string
2468
2663
  */
2469
2664
  function buildXQuery$1(params, options) {
2470
- const version = options?.version ?? DEFAULT_API_VERSION;
2471
- const { propertyVariableUuids, queries, sort, setScopeUuids, belongsToCollectionScopeUuids, page, pageSize } = params;
2665
+ const version = options?.version ?? 2;
2666
+ const { queries, sort, setScopeUuids, belongsToCollectionScopeUuids, page, pageSize } = params;
2472
2667
  const startPosition = (page - 1) * pageSize + 1;
2473
2668
  const endPosition = page * pageSize;
2474
2669
  const setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
@@ -2482,10 +2677,6 @@ function buildXQuery$1(params, options) {
2482
2677
  const belongsToCollectionScopeValues = belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
2483
2678
  filterPredicates.push(`.//properties[property[label/@uuid="${BELONGS_TO_COLLECTION_UUID}" and value/(${belongsToCollectionScopeValues})]]`);
2484
2679
  }
2485
- if (propertyVariableUuids.length > 0) {
2486
- const propertyVariables = propertyVariableUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
2487
- filterPredicates.push(`.//properties//property[label[${propertyVariables}]]`);
2488
- }
2489
2680
  if (compiledQueryFilters.predicate.length > 0) filterPredicates.push(`(${compiledQueryFilters.predicate})`);
2490
2681
  const itemFilters = filterPredicates.length > 0 ? `[${filterPredicates.join(" and ")}]` : "";
2491
2682
  const orderedItemsClause = buildOrderedItemsClause(sort);
@@ -2508,7 +2699,6 @@ function buildXQuery$1(params, options) {
2508
2699
  *
2509
2700
  * @param params - The parameters for the fetch
2510
2701
  * @param params.setScopeUuids - The Set scope UUIDs to filter by
2511
- * @param params.propertyVariableUuids - The property variable UUIDs to filter by
2512
2702
  * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
2513
2703
  * @param params.sort - Optional sorting configuration applied before pagination.
2514
2704
  * For propertyValue sorting, dataType is required and the sort key uses the first valid leaf value (value[not(@i)]).
@@ -2522,12 +2712,11 @@ function buildXQuery$1(params, options) {
2522
2712
  */
2523
2713
  async function fetchSetItems(params, itemCategories, options) {
2524
2714
  try {
2525
- const version = options?.version ?? DEFAULT_API_VERSION;
2526
- const { setScopeUuids, belongsToCollectionScopeUuids, propertyVariableUuids, queries, sort, page, pageSize } = setItemsParamsSchema.parse(params);
2715
+ const version = options?.version ?? 2;
2716
+ const { setScopeUuids, belongsToCollectionScopeUuids, queries, sort, page, pageSize } = setItemsParamsSchema.parse(params);
2527
2717
  const xquery = buildXQuery$1({
2528
2718
  setScopeUuids,
2529
2719
  belongsToCollectionScopeUuids,
2530
- propertyVariableUuids,
2531
2720
  queries,
2532
2721
  sort,
2533
2722
  page,
@@ -2617,7 +2806,6 @@ async function fetchSetItems(params, itemCategories, options) {
2617
2806
  };
2618
2807
  }
2619
2808
  }
2620
-
2621
2809
  //#endregion
2622
2810
  //#region src/utils/fetchers/set/property-values-by-property-variables.ts
2623
2811
  function parsePropertyValueLabel(content) {
@@ -2691,6 +2879,14 @@ function aggregateAttributeValues(values) {
2691
2879
  return a.content.localeCompare(b.content);
2692
2880
  });
2693
2881
  }
2882
+ function getPropertyVariableUuidsFromQueries(queries) {
2883
+ const propertyVariableUuids = /* @__PURE__ */ new Set();
2884
+ for (const query of queries) {
2885
+ if (query.target !== "property") continue;
2886
+ for (const propertyVariableUuid of query.propertyVariables) propertyVariableUuids.add(propertyVariableUuid);
2887
+ }
2888
+ return [...propertyVariableUuids];
2889
+ }
2694
2890
  /**
2695
2891
  * Schema for a single property value query item in the OCHRE API response
2696
2892
  */
@@ -2759,7 +2955,6 @@ const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({
2759
2955
  * @param params - The parameters for the fetch
2760
2956
  * @param params.setScopeUuids - An array of set scope UUIDs to filter by
2761
2957
  * @param params.belongsToCollectionScopeUuids - An array of collection scope UUIDs to filter by
2762
- * @param params.propertyVariableUuids - An array of property variable UUIDs to fetch
2763
2958
  * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
2764
2959
  * @param params.attributes - Whether to return values for bibliographies and periods
2765
2960
  * @param params.attributes.bibliographies - Whether to return values for bibliographies
@@ -2770,11 +2965,11 @@ const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({
2770
2965
  * @returns An XQuery string
2771
2966
  */
2772
2967
  function buildXQuery(params, options) {
2773
- const version = options?.version ?? DEFAULT_API_VERSION;
2774
- const { setScopeUuids, belongsToCollectionScopeUuids, propertyVariableUuids, queries, attributes, isLimitedToLeafPropertyValues } = params;
2968
+ const version = options?.version ?? 2;
2969
+ const { setScopeUuids, belongsToCollectionScopeUuids, queries, attributes, isLimitedToLeafPropertyValues } = params;
2775
2970
  let setScopeFilter = "/set/items/*";
2776
2971
  if (setScopeUuids.length > 0) setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
2777
- const propertyVariableFilters = propertyVariableUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
2972
+ const propertyVariableFilters = getPropertyVariableUuidsFromQueries(queries).map((uuid) => `@uuid="${uuid}"`).join(" or ");
2778
2973
  const compiledQueryFilters = buildQueryFilters({
2779
2974
  queries,
2780
2975
  version
@@ -2795,14 +2990,14 @@ let $property-values :=
2795
2990
  let $item-uuid := $v/ancestor::*[parent::items]/@uuid
2796
2991
  let $variable-uuid := $p/label/@uuid
2797
2992
  return <propertyValue uuid="{$v/@uuid}" rawValue="{$v/@rawValue}" dataType="{$v/@dataType}" itemUuid="{$item-uuid}" variableUuid="{$variable-uuid}">{
2798
- if ($v/content) then string-join($v/content[@xml:lang="eng"]/string, "") else $v/text()
2993
+ if ($v/content) then string-join($v/content[@xml:lang="eng"]//text(), "") else $v/text()
2799
2994
  }</propertyValue>`];
2800
2995
  const returnedSequences = ["$property-values"];
2801
2996
  if (attributes.bibliographies) {
2802
2997
  queryBlocks.push(`let $bibliography-values :=
2803
2998
  for $item in $items
2804
2999
  for $bibliography in $item/bibliographies/bibliography
2805
- let $label := string-join($bibliography/identification/label/content[@xml:lang="eng"]/string, "")
3000
+ let $label := string-join($bibliography/identification/label/content[@xml:lang="eng"]//text(), "")
2806
3001
  where string-length($label) gt 0
2807
3002
  return <attributeValue attributeType="bibliographies" itemUuid="{$item/@uuid}" content="{$label}" />`);
2808
3003
  returnedSequences.push("$bibliography-values");
@@ -2811,7 +3006,7 @@ let $property-values :=
2811
3006
  queryBlocks.push(`let $period-values :=
2812
3007
  for $item in $items
2813
3008
  for $period in $item/periods/period
2814
- let $label := string-join($period/identification/label/content[@xml:lang="eng"]/string, "")
3009
+ let $label := string-join($period/identification/label/content[@xml:lang="eng"]//text(), "")
2815
3010
  where string-length($label) gt 0
2816
3011
  return <attributeValue attributeType="periods" itemUuid="{$item/@uuid}" content="{$label}" />`);
2817
3012
  returnedSequences.push("$period-values");
@@ -2829,7 +3024,6 @@ return (${returnedSequences.join(", ")})`}}</ochre>`;
2829
3024
  *
2830
3025
  * @param params - The parameters for the fetch
2831
3026
  * @param params.setScopeUuids - An array of set scope UUIDs to filter by
2832
- * @param params.propertyVariableUuids - The property variable UUIDs to query by
2833
3027
  * @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
2834
3028
  * @param params.attributes - Whether to return values for bibliographies and periods
2835
3029
  * @param params.attributes.bibliographies - Whether to return values for bibliographies
@@ -2843,12 +3037,11 @@ return (${returnedSequences.join(", ")})`}}</ochre>`;
2843
3037
  */
2844
3038
  async function fetchSetPropertyValuesByPropertyVariables(params, options) {
2845
3039
  try {
2846
- const version = options?.version ?? DEFAULT_API_VERSION;
2847
- const { setScopeUuids, belongsToCollectionScopeUuids, propertyVariableUuids, queries, attributes, isLimitedToLeafPropertyValues } = setPropertyValuesByPropertyVariablesParamsSchema.parse(params);
3040
+ const version = options?.version ?? 2;
3041
+ const { setScopeUuids, belongsToCollectionScopeUuids, queries, attributes, isLimitedToLeafPropertyValues } = setPropertyValuesByPropertyVariablesParamsSchema.parse(params);
2848
3042
  const xquery = buildXQuery({
2849
3043
  setScopeUuids,
2850
3044
  belongsToCollectionScopeUuids,
2851
- propertyVariableUuids,
2852
3045
  queries,
2853
3046
  attributes,
2854
3047
  isLimitedToLeafPropertyValues
@@ -2911,7 +3104,6 @@ async function fetchSetPropertyValuesByPropertyVariables(params, options) {
2911
3104
  };
2912
3105
  }
2913
3106
  }
2914
-
2915
3107
  //#endregion
2916
3108
  //#region src/utils/getters.ts
2917
3109
  const DEFAULT_OPTIONS = { includeNestedProperties: false };
@@ -3165,7 +3357,6 @@ function filterProperties(property, filter, options = DEFAULT_OPTIONS) {
3165
3357
  }
3166
3358
  return false;
3167
3359
  }
3168
-
3169
3360
  //#endregion
3170
3361
  //#region src/utils/parse/website.ts
3171
3362
  const SEGMENT_UNIQUE_SLUG_PREFIX_REGEX = /^\$[^-]*-/;
@@ -4416,7 +4607,7 @@ function parseContexts(contexts) {
4416
4607
  }
4417
4608
  return contextsParsed;
4418
4609
  }
4419
- function parseWebsite(websiteTree, metadata, belongsTo, { version = DEFAULT_API_VERSION } = {}) {
4610
+ function parseWebsite(websiteTree, metadata, belongsTo, { version = 2 } = {}) {
4420
4611
  if (!websiteTree.properties) throw new Error("Website properties not found");
4421
4612
  if (typeof websiteTree.items === "string" || !("resource" in websiteTree.items)) throw new Error("Website pages not found");
4422
4613
  const resources = ensureArray(websiteTree.items.resource);
@@ -4436,7 +4627,6 @@ function parseWebsite(websiteTree, metadata, belongsTo, { version = DEFAULT_API_
4436
4627
  properties
4437
4628
  };
4438
4629
  }
4439
-
4440
4630
  //#endregion
4441
4631
  //#region src/utils/fetchers/website.ts
4442
4632
  const API_VERSION_SUFFIX_REGEX = /-v\d+$/;
@@ -4449,7 +4639,7 @@ const API_VERSION_SUFFIX_REGEX = /-v\d+$/;
4449
4639
  function parseApiVersionSuffix(abbreviation) {
4450
4640
  if (!API_VERSION_SUFFIX_REGEX.test(abbreviation)) return {
4451
4641
  abbreviation,
4452
- version: DEFAULT_API_VERSION
4642
+ version: 2
4453
4643
  };
4454
4644
  const result = apiVersionSuffixSchema.safeParse(abbreviation.slice(-3));
4455
4645
  if (!result.success) throw new Error("Invalid API version suffix");
@@ -4509,6 +4699,5 @@ async function fetchWebsite(abbreviation, options) {
4509
4699
  };
4510
4700
  }
4511
4701
  }
4512
-
4513
4702
  //#endregion
4514
- 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 };
4703
+ 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.22",
3
+ "version": "0.20.24",
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.1.0",
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": {