ochre-sdk 1.0.13 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/constants.d.mts +17 -0
  2. package/dist/constants.mjs +85 -0
  3. package/dist/fetchers/gallery.d.mts +38 -0
  4. package/dist/fetchers/gallery.mjs +91 -0
  5. package/dist/fetchers/item-links.d.mts +32 -0
  6. package/dist/fetchers/item-links.mjs +120 -0
  7. package/dist/fetchers/item.d.mts +74 -0
  8. package/dist/fetchers/item.mjs +146 -0
  9. package/dist/fetchers/set/items.d.mts +48 -0
  10. package/dist/fetchers/set/items.mjs +268 -0
  11. package/dist/fetchers/set/property-values.d.mts +46 -0
  12. package/dist/fetchers/set/property-values.mjs +514 -0
  13. package/dist/fetchers/website.d.mts +25 -0
  14. package/dist/fetchers/website.mjs +38 -0
  15. package/dist/getters.d.mts +193 -0
  16. package/dist/getters.mjs +341 -0
  17. package/dist/helpers.d.mts +18 -0
  18. package/dist/helpers.mjs +33 -0
  19. package/dist/index.d.mts +12 -1971
  20. package/dist/index.mjs +9 -7236
  21. package/dist/parsers/helpers.d.mts +27 -0
  22. package/dist/parsers/helpers.mjs +53 -0
  23. package/dist/parsers/index.d.mts +65 -0
  24. package/dist/parsers/index.mjs +1338 -0
  25. package/dist/parsers/mdx.d.mts +4 -0
  26. package/dist/parsers/mdx.mjs +9 -0
  27. package/dist/parsers/multilingual.d.mts +189 -0
  28. package/dist/parsers/multilingual.mjs +410 -0
  29. package/dist/parsers/string.d.mts +29 -0
  30. package/dist/parsers/string.mjs +477 -0
  31. package/dist/parsers/website/index.d.mts +20 -0
  32. package/dist/parsers/website/index.mjs +1245 -0
  33. package/dist/parsers/website/reader.d.mts +29 -0
  34. package/dist/parsers/website/reader.mjs +75 -0
  35. package/dist/query.d.mts +13 -0
  36. package/dist/query.mjs +827 -0
  37. package/dist/schemas.d.mts +84 -0
  38. package/dist/schemas.mjs +232 -0
  39. package/dist/types/index.d.mts +840 -0
  40. package/dist/types/index.mjs +1 -0
  41. package/dist/types/website.d.mts +501 -0
  42. package/dist/types/website.mjs +1 -0
  43. package/dist/utils.d.mts +34 -0
  44. package/dist/utils.mjs +172 -0
  45. package/dist/xml/metadata.d.mts +5 -0
  46. package/dist/xml/metadata.mjs +30 -0
  47. package/dist/xml/schemas.d.mts +13 -0
  48. package/dist/xml/schemas.mjs +849 -0
  49. package/dist/xml/types.d.mts +901 -0
  50. package/dist/xml/types.mjs +1 -0
  51. package/package.json +19 -17
package/dist/query.mjs ADDED
@@ -0,0 +1,827 @@
1
+ import { stringLiteral } from "./utils.mjs";
2
+ //#region src/query.ts
3
+ const CTS_INCLUDES_STOP_WORDS = new Set([
4
+ "and",
5
+ "at",
6
+ "in",
7
+ "it",
8
+ "of",
9
+ "the",
10
+ "to"
11
+ ]);
12
+ const CTS_INCLUDES_TOKEN_WORD_REGEX = /^\p{L}+$/u;
13
+ const CTS_INCLUDES_TOKEN_REGEX = /[\p{L}\p{N}*?]+/gu;
14
+ const CTS_EXACT_TEXT_TOKEN_REGEX = /[\p{L}\p{N}]+/gu;
15
+ const CONTENT_TARGET_CONTENT_ELEMENT_PATHS = {
16
+ title: [
17
+ "identification",
18
+ "label",
19
+ "content"
20
+ ],
21
+ description: ["description", "content"],
22
+ image: [
23
+ "image",
24
+ "identification",
25
+ "label",
26
+ "content"
27
+ ],
28
+ periods: [
29
+ "periods",
30
+ "period",
31
+ "identification",
32
+ "label",
33
+ "content"
34
+ ],
35
+ bibliography: [
36
+ "bibliographies",
37
+ "bibliography",
38
+ "identification",
39
+ "label",
40
+ "content"
41
+ ]
42
+ };
43
+ function tokenizeIncludesSearchValue(params) {
44
+ const { value, isCaseSensitive } = params;
45
+ const rawTerms = (isCaseSensitive ? value : value.toLowerCase()).match(CTS_INCLUDES_TOKEN_REGEX) ?? [];
46
+ const terms = [];
47
+ for (const term of rawTerms) {
48
+ if (term.includes("*") || term.includes("?")) {
49
+ if (term.replaceAll("*", "").replaceAll("?", "") !== "") terms.push(term);
50
+ continue;
51
+ }
52
+ const normalizedTerm = term.toLowerCase();
53
+ if (normalizedTerm !== "" && !CTS_INCLUDES_STOP_WORDS.has(normalizedTerm)) terms.push(term);
54
+ }
55
+ return terms;
56
+ }
57
+ function tokenizeExactTextSearchValue(params) {
58
+ const { value, isCaseSensitive } = params;
59
+ const rawTerms = (isCaseSensitive ? value : value.toLowerCase()).match(CTS_EXACT_TEXT_TOKEN_REGEX) ?? [];
60
+ const terms = [];
61
+ for (const term of rawTerms) if (term !== "") terms.push(term);
62
+ return terms;
63
+ }
64
+ function hasWildcardCharacters(value) {
65
+ return value.includes("*") || value.includes("?");
66
+ }
67
+ function getWildcardStrippedValue(value) {
68
+ return value.replaceAll("*", "").replaceAll("?", "");
69
+ }
70
+ function shouldUseStemmedTextSearch(value) {
71
+ const wildcardStrippedValue = getWildcardStrippedValue(value);
72
+ return wildcardStrippedValue.length >= 3 && CTS_INCLUDES_TOKEN_WORD_REGEX.test(wildcardStrippedValue);
73
+ }
74
+ function shouldUseFullValueFallbackForIncludes(params) {
75
+ const { value, isCaseSensitive, terms } = params;
76
+ if (terms.length <= 1) return false;
77
+ const tokenSource = isCaseSensitive ? value : value.toLowerCase();
78
+ if (/[^\p{L}\p{N}\s*?]/u.test(tokenSource)) return true;
79
+ const rawSpaceTerms = tokenSource.trim().split(/\s+/u).filter(Boolean);
80
+ if (rawSpaceTerms.length !== terms.length) return true;
81
+ for (const rawTerm of rawSpaceTerms) {
82
+ const wildcardStrippedTerm = getWildcardStrippedValue(rawTerm);
83
+ if (hasWildcardCharacters(rawTerm)) return true;
84
+ if (!CTS_INCLUDES_TOKEN_WORD_REGEX.test(wildcardStrippedTerm)) return true;
85
+ if (CTS_INCLUDES_STOP_WORDS.has(rawTerm.toLowerCase())) return true;
86
+ }
87
+ for (const [index, rawTerm] of rawSpaceTerms.entries()) if (rawTerm !== (terms[index] ?? "")) return true;
88
+ return false;
89
+ }
90
+ function buildWordQueryOptionsExpression(params) {
91
+ const { matchMode, isCaseSensitive, queryFamily, language, isWildcarded } = params;
92
+ const { isStemmed } = params;
93
+ const options = [
94
+ isCaseSensitive ? "case-sensitive" : "case-insensitive",
95
+ matchMode === "exact" ? "diacritic-sensitive" : "diacritic-insensitive",
96
+ matchMode === "exact" ? "punctuation-sensitive" : "punctuation-insensitive",
97
+ matchMode === "exact" ? "whitespace-sensitive" : "whitespace-insensitive"
98
+ ];
99
+ if (matchMode === "exact") options.push("unstemmed", "unwildcarded");
100
+ else if (queryFamily === "text") {
101
+ options.push(isStemmed ? "stemmed" : "unstemmed", isWildcarded ? "wildcarded" : "unwildcarded");
102
+ if (isStemmed && language != null && language !== "") options.push(`lang=${language}`);
103
+ }
104
+ return `(${options.map((option) => stringLiteral(option)).join(", ")})`;
105
+ }
106
+ function buildRichTextPhraseOptionsExpression(params) {
107
+ const { isCaseSensitive } = params;
108
+ return `(${[
109
+ isCaseSensitive ? "case-sensitive" : "case-insensitive",
110
+ "diacritic-sensitive",
111
+ "punctuation-insensitive",
112
+ "whitespace-insensitive",
113
+ "unstemmed",
114
+ "unwildcarded"
115
+ ].map((option) => stringLiteral(option)).join(", ")})`;
116
+ }
117
+ function buildCtsWordQueryExpression(params) {
118
+ const { value, matchMode, isCaseSensitive, queryFamily, language } = params;
119
+ const isWildcarded = matchMode === "includes" && hasWildcardCharacters(value);
120
+ const isStemmed = matchMode === "includes" && queryFamily === "text" && !isWildcarded && shouldUseStemmedTextSearch(value);
121
+ return `cts:word-query(${stringLiteral(value)}, ${buildWordQueryOptionsExpression({
122
+ matchMode,
123
+ isCaseSensitive,
124
+ queryFamily,
125
+ language,
126
+ isWildcarded,
127
+ isStemmed
128
+ })})`;
129
+ }
130
+ function buildRichTextPhraseQueryExpression(params) {
131
+ const { value, isCaseSensitive } = params;
132
+ return `cts:word-query(${stringLiteral(value)}, ${buildRichTextPhraseOptionsExpression({ isCaseSensitive })})`;
133
+ }
134
+ function buildRichTextExactQueryExpression(params) {
135
+ const { value, isCaseSensitive } = params;
136
+ const phraseQuery = buildRichTextPhraseQueryExpression({
137
+ value,
138
+ isCaseSensitive
139
+ });
140
+ const terms = tokenizeExactTextSearchValue({
141
+ value,
142
+ isCaseSensitive
143
+ });
144
+ if (terms.length <= 1) return phraseQuery;
145
+ return buildOrCtsQueryExpressionInternal([phraseQuery, buildAndCtsQueryExpressionInternal(terms.map((term) => buildRichTextPhraseQueryExpression({
146
+ value: term,
147
+ isCaseSensitive
148
+ })))]);
149
+ }
150
+ function buildCtsElementWordQueryExpression(params) {
151
+ const { elementName, value, matchMode, isCaseSensitive, queryFamily, language } = params;
152
+ const isWildcarded = matchMode === "includes" && hasWildcardCharacters(value);
153
+ const isStemmed = matchMode === "includes" && queryFamily === "text" && !isWildcarded && shouldUseStemmedTextSearch(value);
154
+ return `cts:element-word-query(xs:QName("${elementName}"), ${stringLiteral(value)}, ${buildWordQueryOptionsExpression({
155
+ matchMode,
156
+ isCaseSensitive,
157
+ queryFamily,
158
+ language,
159
+ isWildcarded,
160
+ isStemmed
161
+ })})`;
162
+ }
163
+ function buildCtsElementAttributeWordQueryExpression(params) {
164
+ const { elementName, attributeName, value, matchMode, isCaseSensitive, queryFamily, language } = params;
165
+ const isWildcarded = matchMode === "includes" && hasWildcardCharacters(value);
166
+ const isStemmed = matchMode === "includes" && queryFamily === "text" && !isWildcarded && shouldUseStemmedTextSearch(value);
167
+ return `cts:element-attribute-word-query(xs:QName("${elementName}"), xs:QName("${attributeName}"), ${stringLiteral(value)}, ${buildWordQueryOptionsExpression({
168
+ matchMode,
169
+ isCaseSensitive,
170
+ queryFamily,
171
+ language,
172
+ isWildcarded,
173
+ isStemmed
174
+ })})`;
175
+ }
176
+ function buildCtsElementValueQueryExpression(params) {
177
+ const { elementName, value, isCaseSensitive } = params;
178
+ return `cts:element-value-query(xs:QName("${elementName}"), ${stringLiteral(value)}, ${buildWordQueryOptionsExpression({
179
+ matchMode: "exact",
180
+ isCaseSensitive
181
+ })})`;
182
+ }
183
+ function buildCtsElementAttributeValueQueryExpression(params) {
184
+ const { elementName, attributeName, value, isCaseSensitive } = params;
185
+ return `cts:element-attribute-value-query(xs:QName("${elementName}"), xs:QName("${attributeName}"), ${stringLiteral(value)}, ${buildWordQueryOptionsExpression({
186
+ matchMode: "exact",
187
+ isCaseSensitive
188
+ })})`;
189
+ }
190
+ function buildPlainElementAttributeValueQueryExpression(params) {
191
+ const { elementName, attributeName, value } = params;
192
+ return `cts:element-attribute-value-query(xs:QName("${elementName}"), xs:QName("${attributeName}"), ${stringLiteral(value)})`;
193
+ }
194
+ function buildNestedElementQuery(elementNames, queryExpression) {
195
+ let wrappedQueryExpression = queryExpression;
196
+ for (const elementName of elementNames.toReversed()) wrappedQueryExpression = `cts:element-query(xs:QName("${elementName}"), ${wrappedQueryExpression})`;
197
+ return wrappedQueryExpression;
198
+ }
199
+ function buildNotCtsQueryExpression(queryExpression) {
200
+ return `cts:not-query(${queryExpression})`;
201
+ }
202
+ function buildAndCtsQueryExpressionInternal(queryExpressions) {
203
+ if (queryExpressions.length === 0) return "cts:true-query()";
204
+ if (queryExpressions.length === 1) return queryExpressions[0] ?? "cts:true-query()";
205
+ return `cts:and-query((${queryExpressions.join(", ")}))`;
206
+ }
207
+ function buildOrCtsQueryExpressionInternal(queryExpressions) {
208
+ if (queryExpressions.length === 0) return "cts:false-query()";
209
+ if (queryExpressions.length === 1) return queryExpressions[0] ?? "cts:false-query()";
210
+ return `cts:or-query((${queryExpressions.join(", ")}))`;
211
+ }
212
+ function buildAndCtsQueryExpression(queryExpressions) {
213
+ if (queryExpressions.length === 0) return null;
214
+ return buildAndCtsQueryExpressionInternal(queryExpressions);
215
+ }
216
+ function buildContentLanguageQuery(language) {
217
+ return buildPlainElementAttributeValueQueryExpression({
218
+ elementName: "content",
219
+ attributeName: "xml:lang",
220
+ value: language
221
+ });
222
+ }
223
+ function buildPropertyLabelQuery(propertyVariable) {
224
+ return buildPlainElementAttributeValueQueryExpression({
225
+ elementName: "label",
226
+ attributeName: "uuid",
227
+ value: propertyVariable
228
+ });
229
+ }
230
+ function buildValueNotInheritedQuery() {
231
+ return buildNotCtsQueryExpression(buildPlainElementAttributeValueQueryExpression({
232
+ elementName: "value",
233
+ attributeName: "inherited",
234
+ value: "true"
235
+ }));
236
+ }
237
+ function buildValueNotIdRefQuery() {
238
+ return buildNotCtsQueryExpression(buildPlainElementAttributeValueQueryExpression({
239
+ elementName: "value",
240
+ attributeName: "dataType",
241
+ value: "IDREF"
242
+ }));
243
+ }
244
+ function buildRichTextContentQueryExpression(params) {
245
+ const { value, matchMode, isCaseSensitive, language } = params;
246
+ return buildAndCtsQueryExpressionInternal([buildContentLanguageQuery(language), matchMode === "exact" ? buildRichTextExactQueryExpression({
247
+ value,
248
+ isCaseSensitive,
249
+ language
250
+ }) : buildCtsWordQueryExpression({
251
+ value,
252
+ matchMode,
253
+ isCaseSensitive,
254
+ queryFamily: "text",
255
+ language
256
+ })]);
257
+ }
258
+ function buildValueContentInnerQuery(params) {
259
+ const { language, value, matchMode, isCaseSensitive } = params;
260
+ return buildNestedElementQuery(["content"], buildRichTextContentQueryExpression({
261
+ language,
262
+ value,
263
+ matchMode,
264
+ isCaseSensitive
265
+ }));
266
+ }
267
+ function buildValueDirectTextInnerQuery(params) {
268
+ const { value, matchMode, isCaseSensitive } = params;
269
+ const directTextQuery = matchMode === "exact" ? buildCtsElementValueQueryExpression({
270
+ elementName: "value",
271
+ value,
272
+ isCaseSensitive
273
+ }) : buildCtsElementWordQueryExpression({
274
+ elementName: "value",
275
+ value,
276
+ matchMode,
277
+ isCaseSensitive,
278
+ queryFamily: "raw"
279
+ });
280
+ return buildAndCtsQueryExpressionInternal([buildNotCtsQueryExpression(buildNestedElementQuery(["content"], "cts:true-query()")), directTextQuery]);
281
+ }
282
+ function buildValueRawValueInnerQuery(params) {
283
+ const { value, matchMode, isCaseSensitive } = params;
284
+ if (matchMode === "exact") return buildCtsElementAttributeValueQueryExpression({
285
+ elementName: "value",
286
+ attributeName: "rawValue",
287
+ value,
288
+ isCaseSensitive
289
+ });
290
+ return buildCtsElementAttributeWordQueryExpression({
291
+ elementName: "value",
292
+ attributeName: "rawValue",
293
+ value,
294
+ matchMode,
295
+ isCaseSensitive,
296
+ queryFamily: "raw"
297
+ });
298
+ }
299
+ function buildNotesQueryExpression(params) {
300
+ const { value, matchMode, isCaseSensitive, language } = params;
301
+ return buildNestedElementQuery([
302
+ "notes",
303
+ "note",
304
+ "content"
305
+ ], buildRichTextContentQueryExpression({
306
+ value,
307
+ matchMode,
308
+ isCaseSensitive,
309
+ language
310
+ }));
311
+ }
312
+ function buildContentTargetQueryExpression(params) {
313
+ const { target, value, matchMode, isCaseSensitive, language } = params;
314
+ const contentElementPath = CONTENT_TARGET_CONTENT_ELEMENT_PATHS[target];
315
+ return buildNestedElementQuery(contentElementPath, buildRichTextContentQueryExpression({
316
+ value,
317
+ matchMode,
318
+ isCaseSensitive,
319
+ language
320
+ }));
321
+ }
322
+ function buildPropertyQueryExpression(params) {
323
+ const { propertyVariable, queryExpression } = params;
324
+ const propertyQueryExpressions = [queryExpression];
325
+ if (propertyVariable != null) propertyQueryExpressions.unshift(buildPropertyLabelQuery(propertyVariable));
326
+ return buildNestedElementQuery(["properties", "property"], buildAndCtsQueryExpressionInternal(propertyQueryExpressions));
327
+ }
328
+ function buildPropertyTextMatchQueryExpression(params) {
329
+ const { propertyVariable, valueFilters = [], contentQueryExpression, rawValueQueryExpression, bareValueQueryExpression } = params;
330
+ const letBindings = [];
331
+ const valueMatchReferences = [];
332
+ if (contentQueryExpression != null) {
333
+ letBindings.push(`let $contentQuery := ${contentQueryExpression}`);
334
+ valueMatchReferences.push("$contentQuery");
335
+ }
336
+ if (rawValueQueryExpression != null) {
337
+ letBindings.push(`let $rawValueQuery := ${rawValueQueryExpression}`);
338
+ valueMatchReferences.push("$rawValueQuery");
339
+ }
340
+ if (bareValueQueryExpression != null) {
341
+ letBindings.push(`let $bareValueQuery := ${bareValueQueryExpression}`);
342
+ valueMatchReferences.push("$bareValueQuery");
343
+ }
344
+ const valueQueryExpressions = [...valueFilters];
345
+ if (valueMatchReferences.length > 0) valueQueryExpressions.push(buildOrCtsQueryExpressionInternal(valueMatchReferences));
346
+ const propertyQueryExpressions = [];
347
+ if (propertyVariable != null) propertyQueryExpressions.push(buildPropertyLabelQuery(propertyVariable));
348
+ propertyQueryExpressions.push(buildNestedElementQuery(["value"], buildAndCtsQueryExpressionInternal(valueQueryExpressions)));
349
+ const propertyQueryExpression = buildNestedElementQuery(["properties", "property"], buildAndCtsQueryExpressionInternal(propertyQueryExpressions));
350
+ if (letBindings.length === 0) return propertyQueryExpression;
351
+ return `(${letBindings.join("\n ")}\n return ${propertyQueryExpression})`;
352
+ }
353
+ function buildPropertyPresenceQueryExpression(params) {
354
+ return buildPropertyQueryExpression({
355
+ propertyVariable: params.propertyVariable,
356
+ queryExpression: "cts:true-query()"
357
+ });
358
+ }
359
+ function buildPropertyStringQueryExpression(params) {
360
+ const { propertyVariable, value, matchMode, isCaseSensitive, language } = params;
361
+ return buildPropertyTextMatchQueryExpression({
362
+ propertyVariable,
363
+ valueFilters: [buildValueNotInheritedQuery()],
364
+ contentQueryExpression: buildValueContentInnerQuery({
365
+ language,
366
+ value,
367
+ matchMode,
368
+ isCaseSensitive
369
+ }),
370
+ rawValueQueryExpression: buildValueRawValueInnerQuery({
371
+ value,
372
+ matchMode,
373
+ isCaseSensitive
374
+ }),
375
+ bareValueQueryExpression: buildValueDirectTextInnerQuery({
376
+ value,
377
+ matchMode,
378
+ isCaseSensitive
379
+ })
380
+ });
381
+ }
382
+ function buildPropertyScalarQueryExpression(params) {
383
+ const { propertyVariable, value, matchMode, isCaseSensitive } = params;
384
+ return buildPropertyQueryExpression({
385
+ propertyVariable,
386
+ queryExpression: buildNestedElementQuery(["value"], buildOrCtsQueryExpressionInternal([buildValueRawValueInnerQuery({
387
+ value,
388
+ matchMode,
389
+ isCaseSensitive
390
+ }), buildValueDirectTextInnerQuery({
391
+ value,
392
+ matchMode,
393
+ isCaseSensitive
394
+ })]))
395
+ });
396
+ }
397
+ function buildPropertyAllQueryExpression(params) {
398
+ const { query, value, matchMode } = params;
399
+ return buildPropertyTextMatchQueryExpression({
400
+ propertyVariable: query.propertyVariable,
401
+ valueFilters: [buildValueNotIdRefQuery()],
402
+ contentQueryExpression: buildValueContentInnerQuery({
403
+ language: query.language,
404
+ value,
405
+ matchMode,
406
+ isCaseSensitive: query.isCaseSensitive
407
+ }),
408
+ rawValueQueryExpression: buildValueRawValueInnerQuery({
409
+ value,
410
+ matchMode,
411
+ isCaseSensitive: query.isCaseSensitive
412
+ }),
413
+ bareValueQueryExpression: buildValueDirectTextInnerQuery({
414
+ value,
415
+ matchMode,
416
+ isCaseSensitive: query.isCaseSensitive
417
+ })
418
+ });
419
+ }
420
+ function buildPropertyIdRefQueryExpression(params) {
421
+ const { propertyVariable, value } = params;
422
+ return buildPropertyQueryExpression({
423
+ propertyVariable,
424
+ queryExpression: buildNestedElementQuery(["value"], buildPlainElementAttributeValueQueryExpression({
425
+ elementName: "value",
426
+ attributeName: "uuid",
427
+ value
428
+ }))
429
+ });
430
+ }
431
+ function buildPropertyDateRangeQueryExpression(query) {
432
+ const rangeQueryExpressions = [];
433
+ if (query.from != null) rangeQueryExpressions.push(`cts:element-attribute-range-query(xs:QName("value"), xs:QName("rawValue"), ">=", ${stringLiteral(query.from)})`);
434
+ if (query.to != null) rangeQueryExpressions.push(`cts:element-attribute-range-query(xs:QName("value"), xs:QName("rawValue"), "<=", ${stringLiteral(query.to)})`);
435
+ return buildPropertyQueryExpression({
436
+ propertyVariable: query.propertyVariable,
437
+ queryExpression: buildNestedElementQuery(["value"], buildAndCtsQueryExpressionInternal(rangeQueryExpressions))
438
+ });
439
+ }
440
+ function buildItemStringQueryExpression(params) {
441
+ const { value, matchMode, isCaseSensitive, language } = params;
442
+ return buildOrCtsQueryExpressionInternal([buildContentTargetQueryExpression({
443
+ target: "title",
444
+ value,
445
+ matchMode,
446
+ isCaseSensitive,
447
+ language
448
+ }), buildPropertyStringQueryExpression({
449
+ value,
450
+ matchMode,
451
+ isCaseSensitive,
452
+ language
453
+ })]);
454
+ }
455
+ function getLeafSearchValue(query) {
456
+ switch (query.target) {
457
+ case "string":
458
+ case "title":
459
+ case "description":
460
+ case "image":
461
+ case "periods":
462
+ case "bibliography":
463
+ case "notes": return query.value;
464
+ case "property": return "value" in query && query.value != null ? query.value : null;
465
+ }
466
+ }
467
+ function buildLeafValueQueryExpression(params) {
468
+ const { query, value, matchMode } = params;
469
+ switch (query.target) {
470
+ case "string": return buildItemStringQueryExpression({
471
+ value,
472
+ matchMode,
473
+ isCaseSensitive: query.isCaseSensitive,
474
+ language: query.language
475
+ });
476
+ case "notes": return buildNotesQueryExpression({
477
+ value,
478
+ matchMode,
479
+ isCaseSensitive: query.isCaseSensitive,
480
+ language: query.language
481
+ });
482
+ case "title":
483
+ case "description":
484
+ case "image":
485
+ case "periods":
486
+ case "bibliography": return buildContentTargetQueryExpression({
487
+ target: query.target,
488
+ value,
489
+ matchMode,
490
+ isCaseSensitive: query.isCaseSensitive,
491
+ language: query.language
492
+ });
493
+ case "property": switch (query.dataType) {
494
+ case "all": return buildPropertyAllQueryExpression({
495
+ query,
496
+ value,
497
+ matchMode
498
+ });
499
+ case "IDREF": return buildPropertyIdRefQueryExpression({
500
+ propertyVariable: query.propertyVariable,
501
+ value
502
+ });
503
+ case "string": return buildPropertyStringQueryExpression({
504
+ propertyVariable: query.propertyVariable,
505
+ value,
506
+ matchMode,
507
+ isCaseSensitive: query.isCaseSensitive,
508
+ language: query.language
509
+ });
510
+ case "integer":
511
+ case "decimal":
512
+ case "time":
513
+ case "boolean":
514
+ case "date":
515
+ case "dateTime": return buildPropertyScalarQueryExpression({
516
+ propertyVariable: query.propertyVariable,
517
+ value,
518
+ matchMode,
519
+ isCaseSensitive: query.isCaseSensitive
520
+ });
521
+ }
522
+ }
523
+ }
524
+ function indentBlock(value, spaces) {
525
+ const prefix = " ".repeat(spaces);
526
+ return value.split("\n").map((line) => line === "" ? line : `${prefix}${line}`).join("\n");
527
+ }
528
+ function createQueryCompilerContext() {
529
+ return {
530
+ nextHelperSerial: 1,
531
+ helperNamesByKey: /* @__PURE__ */ new Map(),
532
+ helperDeclarations: []
533
+ };
534
+ }
535
+ function registerConstantHelper(params) {
536
+ const { context, key, bodyExpression } = params;
537
+ const existingName = context.helperNamesByKey.get(key);
538
+ if (existingName != null) return {
539
+ name: existingName,
540
+ callExpression: `${existingName}()`
541
+ };
542
+ const helperName = `local:queryHelper${context.nextHelperSerial}`;
543
+ context.nextHelperSerial += 1;
544
+ context.helperNamesByKey.set(key, helperName);
545
+ context.helperDeclarations.push(`declare function ${helperName}() as cts:query {\n${indentBlock(bodyExpression, 2)}\n};`);
546
+ return {
547
+ name: helperName,
548
+ callExpression: `${helperName}()`
549
+ };
550
+ }
551
+ function replaceSampleValueLiteral(expression, sampleValue, valueReference) {
552
+ return expression.replaceAll(stringLiteral(sampleValue), valueReference);
553
+ }
554
+ function registerParameterizedHelper(params) {
555
+ const { context, key, bodyExpression } = params;
556
+ const existingName = context.helperNamesByKey.get(key);
557
+ if (existingName != null) return {
558
+ name: existingName,
559
+ call: (valueExpression) => `${existingName}(${valueExpression})`
560
+ };
561
+ const helperName = `local:queryHelper${context.nextHelperSerial}`;
562
+ context.nextHelperSerial += 1;
563
+ context.helperNamesByKey.set(key, helperName);
564
+ context.helperDeclarations.push(`declare function ${helperName}($value as xs:string) as cts:query {\n${indentBlock(bodyExpression, 2)}\n};`);
565
+ return {
566
+ name: helperName,
567
+ call: (valueExpression) => `${helperName}(${valueExpression})`
568
+ };
569
+ }
570
+ function getLeafHelperKey(params) {
571
+ const { query, matchMode, value } = params;
572
+ switch (query.target) {
573
+ case "string":
574
+ case "title":
575
+ case "description":
576
+ case "image":
577
+ case "periods":
578
+ case "bibliography":
579
+ case "notes": return [
580
+ "leaf",
581
+ matchMode,
582
+ query.target,
583
+ value,
584
+ query.isCaseSensitive ? "case-sensitive" : "case-insensitive",
585
+ query.language
586
+ ].join("|");
587
+ case "property": return [
588
+ "leaf",
589
+ matchMode,
590
+ query.target,
591
+ query.dataType,
592
+ query.propertyVariable ?? "",
593
+ value,
594
+ query.isCaseSensitive ? "case-sensitive" : "case-insensitive",
595
+ query.language
596
+ ].join("|");
597
+ }
598
+ }
599
+ function registerLeafHelper(params) {
600
+ const { context, query, matchMode, value } = params;
601
+ return registerConstantHelper({
602
+ context,
603
+ key: getLeafHelperKey({
604
+ query,
605
+ matchMode,
606
+ value
607
+ }),
608
+ bodyExpression: buildLeafValueQueryExpression({
609
+ query,
610
+ value,
611
+ matchMode
612
+ })
613
+ });
614
+ }
615
+ function getIncludesLeafHelperKey(params) {
616
+ const { query, value } = params;
617
+ const isWildcarded = hasWildcardCharacters(value);
618
+ const isStemmed = !isWildcarded && shouldUseStemmedTextSearch(value);
619
+ switch (query.target) {
620
+ case "string":
621
+ case "title":
622
+ case "description":
623
+ case "image":
624
+ case "periods":
625
+ case "bibliography":
626
+ case "notes": return [
627
+ "includes-helper",
628
+ query.target,
629
+ query.isCaseSensitive ? "case-sensitive" : "case-insensitive",
630
+ query.language,
631
+ isWildcarded ? "wildcarded" : "unwildcarded",
632
+ isStemmed ? "stemmed" : "unstemmed"
633
+ ].join("|");
634
+ case "property": return [
635
+ "includes-helper",
636
+ query.target,
637
+ query.dataType,
638
+ query.propertyVariable ?? "",
639
+ query.isCaseSensitive ? "case-sensitive" : "case-insensitive",
640
+ query.language,
641
+ isWildcarded ? "wildcarded" : "unwildcarded",
642
+ isStemmed ? "stemmed" : "unstemmed"
643
+ ].join("|");
644
+ }
645
+ }
646
+ function registerIncludesLeafHelper(params) {
647
+ const { context, query, sampleValue } = params;
648
+ return registerParameterizedHelper({
649
+ context,
650
+ key: getIncludesLeafHelperKey({
651
+ query,
652
+ value: sampleValue
653
+ }),
654
+ bodyExpression: replaceSampleValueLiteral(buildLeafValueQueryExpression({
655
+ query,
656
+ value: sampleValue,
657
+ matchMode: "includes"
658
+ }), sampleValue, "$value")
659
+ });
660
+ }
661
+ function buildLeafQueryExpression(context, query) {
662
+ if (query.target === "property" && query.dataType !== "date" && query.dataType !== "dateTime" && !("value" in query) && query.propertyVariable != null) return buildPropertyPresenceQueryExpression({ propertyVariable: query.propertyVariable });
663
+ if (query.target === "property" && (query.dataType === "date" || query.dataType === "dateTime") && query.value == null) return buildPropertyDateRangeQueryExpression(query);
664
+ const searchValue = getLeafSearchValue(query);
665
+ if (searchValue == null) throw new Error("Missing searchable value for query leaf", { cause: query });
666
+ const exactHelper = registerLeafHelper({
667
+ context,
668
+ query,
669
+ matchMode: "exact",
670
+ value: searchValue
671
+ });
672
+ if (query.matchMode === "exact") return exactHelper.callExpression;
673
+ const terms = tokenizeIncludesSearchValue({
674
+ value: searchValue,
675
+ isCaseSensitive: query.isCaseSensitive
676
+ });
677
+ if (terms.length === 0) return "cts:false-query()";
678
+ const includesHelper = registerIncludesLeafHelper({
679
+ context,
680
+ query,
681
+ sampleValue: terms[0] ?? ""
682
+ });
683
+ const tokenizedHelperCalls = [];
684
+ for (const term of terms) {
685
+ const termHelper = term === (terms[0] ?? "") ? includesHelper : registerIncludesLeafHelper({
686
+ context,
687
+ query,
688
+ sampleValue: term
689
+ });
690
+ tokenizedHelperCalls.push(termHelper.call(stringLiteral(term)));
691
+ }
692
+ const tokenizedQueryExpression = buildAndCtsQueryExpressionInternal(tokenizedHelperCalls);
693
+ if (!shouldUseFullValueFallbackForIncludes({
694
+ value: searchValue,
695
+ isCaseSensitive: query.isCaseSensitive,
696
+ terms
697
+ })) return tokenizedQueryExpression;
698
+ return buildOrCtsQueryExpressionInternal([exactHelper.callExpression, tokenizedQueryExpression]);
699
+ }
700
+ function getGroupableIncludesValue(query) {
701
+ if (query.matchMode !== "includes" || query.isNegated === true) return null;
702
+ switch (query.target) {
703
+ case "string":
704
+ case "title":
705
+ case "description":
706
+ case "image":
707
+ case "periods":
708
+ case "bibliography":
709
+ case "notes": return query.value;
710
+ case "property":
711
+ if (!("value" in query) || query.value == null || query.dataType === "IDREF") return null;
712
+ return query.value;
713
+ }
714
+ }
715
+ function isQueryLeaf(query) {
716
+ return "target" in query;
717
+ }
718
+ function getQueryGroupChildren(query) {
719
+ return "and" in query ? query.and : query.or;
720
+ }
721
+ function getQueryGroupOperator(query) {
722
+ return "and" in query ? "and" : "or";
723
+ }
724
+ function getCompatibleIncludesGroupLeaves(query) {
725
+ if (!("or" in query) || query.or.length <= 1) return null;
726
+ const leafQueries = [];
727
+ for (const childQuery of query.or) {
728
+ if (!isQueryLeaf(childQuery)) return null;
729
+ leafQueries.push(childQuery);
730
+ }
731
+ const firstQuery = leafQueries[0];
732
+ if (firstQuery == null) return null;
733
+ const groupValue = getGroupableIncludesValue(firstQuery);
734
+ if (groupValue == null) return null;
735
+ for (const leafQuery of leafQueries) if (getGroupableIncludesValue(leafQuery) !== groupValue || leafQuery.isCaseSensitive !== firstQuery.isCaseSensitive || leafQuery.language !== firstQuery.language) return null;
736
+ return leafQueries;
737
+ }
738
+ function buildIncludesGroupQueryExpression(context, queries) {
739
+ const firstQuery = queries[0];
740
+ if (firstQuery == null) throw new Error("Cannot build an includes group without queries", { cause: queries });
741
+ const groupValue = getGroupableIncludesValue(firstQuery);
742
+ if (groupValue == null) throw new Error("Cannot build an includes group without a search value", { cause: firstQuery });
743
+ const terms = tokenizeIncludesSearchValue({
744
+ value: groupValue,
745
+ isCaseSensitive: firstQuery.isCaseSensitive
746
+ });
747
+ if (terms.length === 0) return "cts:false-query()";
748
+ const tokenizedHelperCalls = [];
749
+ for (const term of terms) {
750
+ const memberHelpers = queries.map((query) => registerIncludesLeafHelper({
751
+ context,
752
+ query,
753
+ sampleValue: term
754
+ }));
755
+ const termGroupHelper = registerParameterizedHelper({
756
+ context,
757
+ key: [
758
+ "group",
759
+ "includes",
760
+ ...memberHelpers.map((helper) => helper.name)
761
+ ].join("|"),
762
+ bodyExpression: buildOrCtsQueryExpressionInternal(memberHelpers.map((helper) => helper.call("$value")))
763
+ });
764
+ tokenizedHelperCalls.push(termGroupHelper.call(stringLiteral(term)));
765
+ }
766
+ const tokenizedQueryExpression = buildAndCtsQueryExpressionInternal(tokenizedHelperCalls);
767
+ if (!shouldUseFullValueFallbackForIncludes({
768
+ value: groupValue,
769
+ isCaseSensitive: firstQuery.isCaseSensitive,
770
+ terms
771
+ })) return tokenizedQueryExpression;
772
+ const exactMemberHelpers = queries.map((query) => registerLeafHelper({
773
+ context,
774
+ query,
775
+ matchMode: "exact",
776
+ value: groupValue
777
+ }));
778
+ return buildOrCtsQueryExpressionInternal([registerConstantHelper({
779
+ context,
780
+ key: [
781
+ "group",
782
+ "exact",
783
+ groupValue,
784
+ ...exactMemberHelpers.map((helper) => helper.name)
785
+ ].join("|"),
786
+ bodyExpression: buildOrCtsQueryExpressionInternal(exactMemberHelpers.map((helper) => helper.callExpression))
787
+ }).callExpression, tokenizedQueryExpression]);
788
+ }
789
+ function buildQueryNode(context, query) {
790
+ if (isQueryLeaf(query)) {
791
+ const queryExpression = buildLeafQueryExpression(context, query);
792
+ return query.isNegated === true ? buildNotCtsQueryExpression(queryExpression) : queryExpression;
793
+ }
794
+ const optimizedIncludesGroupQueries = getCompatibleIncludesGroupLeaves(query);
795
+ if (optimizedIncludesGroupQueries != null) return buildIncludesGroupQueryExpression(context, optimizedIncludesGroupQueries);
796
+ const childQueryExpressions = [];
797
+ for (const childQuery of getQueryGroupChildren(query)) childQueryExpressions.push(buildQueryNode(context, childQuery));
798
+ return getQueryGroupOperator(query) === "and" ? buildAndCtsQueryExpressionInternal(childQueryExpressions) : buildOrCtsQueryExpressionInternal(childQueryExpressions);
799
+ }
800
+ function buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids, belongsToCollectionPropertyVariableUuid) {
801
+ if (belongsToCollectionScopeUuids.length === 0) return null;
802
+ const collectionValueQueryExpressions = [];
803
+ for (const uuid of belongsToCollectionScopeUuids) collectionValueQueryExpressions.push(buildPlainElementAttributeValueQueryExpression({
804
+ elementName: "value",
805
+ attributeName: "uuid",
806
+ value: uuid
807
+ }));
808
+ return buildPropertyQueryExpression({
809
+ propertyVariable: belongsToCollectionPropertyVariableUuid,
810
+ queryExpression: buildNestedElementQuery(["value"], buildOrCtsQueryExpressionInternal(collectionValueQueryExpressions))
811
+ });
812
+ }
813
+ function buildQueryPlan(params) {
814
+ const { queries } = params;
815
+ if (queries == null) return {
816
+ prolog: "",
817
+ queryExpression: null
818
+ };
819
+ const context = createQueryCompilerContext();
820
+ const queryExpression = buildQueryNode(context, queries);
821
+ return {
822
+ prolog: context.helperDeclarations.join("\n\n"),
823
+ queryExpression
824
+ };
825
+ }
826
+ //#endregion
827
+ export { buildAndCtsQueryExpression, buildBelongsToCollectionQueryExpression, buildQueryPlan };