ochre-sdk 1.0.71 → 1.0.73
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/README.md +22 -0
- package/dist/constants.mjs +0 -5
- package/dist/fetchers/gallery.mjs +11 -5
- package/dist/fetchers/item-children.mjs +4 -2
- package/dist/fetchers/item-links.mjs +10 -5
- package/dist/fetchers/item-ocr-data.d.mts +37 -0
- package/dist/fetchers/item-ocr-data.mjs +166 -0
- package/dist/fetchers/item.mjs +43 -58
- package/dist/fetchers/set/items.mjs +26 -12
- package/dist/fetchers/set/property-values.mjs +24 -14
- package/dist/fetchers/website-metadata.mjs +7 -3
- package/dist/fetchers/website.mjs +20 -3
- package/dist/helpers.d.mts +1 -5
- package/dist/helpers.mjs +1 -5
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +3 -3
- package/dist/parsers/index.d.mts +3 -10
- package/dist/parsers/index.mjs +1 -75
- package/dist/parsers/string.mjs +5 -1
- package/dist/query.d.mts +19 -28
- package/dist/query.mjs +104 -86
- package/dist/schemas.d.mts +7 -8
- package/dist/schemas.mjs +8 -10
- package/dist/types/index.d.mts +27 -57
- package/dist/utilities.d.mts +25 -1
- package/dist/utilities.mjs +41 -1
- package/dist/xml/schemas.d.mts +2 -7
- package/dist/xml/schemas.mjs +9 -43
- package/dist/xml/types.d.mts +13 -49
- package/package.json +3 -3
- package/dist/fetchers/ocr-matches.d.mts +0 -44
- package/dist/fetchers/ocr-matches.mjs +0 -134
package/dist/query.mjs
CHANGED
|
@@ -12,11 +12,7 @@ const CTS_INCLUDES_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
12
12
|
const CTS_INCLUDES_TOKEN_WORD_REGEX = /^\p{L}+$/u;
|
|
13
13
|
const CTS_INCLUDES_TOKEN_REGEX = /[\p{L}\p{N}*?]+/gu;
|
|
14
14
|
const CTS_EXACT_TEXT_TOKEN_REGEX = /[\p{L}\p{N}]+/gu;
|
|
15
|
-
|
|
16
|
-
* Error message for OCR queries nested inside an OR group
|
|
17
|
-
* @internal
|
|
18
|
-
*/
|
|
19
|
-
const OCR_DISJUNCTION_ERROR_MESSAGE = "OCR queries cannot be nested inside an OR group because they are resolved by a document join instead of a CTS query";
|
|
15
|
+
const MAX_OCR_TEXT_CONDITIONS = 4;
|
|
20
16
|
const CONTENT_TARGET_CONTENT_ELEMENT_PATHS = {
|
|
21
17
|
title: [
|
|
22
18
|
"identification",
|
|
@@ -204,9 +200,6 @@ function buildNestedElementQuery(elementNames, queryExpression) {
|
|
|
204
200
|
function buildNotCtsQueryExpression(queryExpression) {
|
|
205
201
|
return `cts:not-query(${queryExpression})`;
|
|
206
202
|
}
|
|
207
|
-
function buildCtsNearQueryExpression(queryExpressions) {
|
|
208
|
-
return `cts:near-query((${queryExpressions.join(", ")}), ${queryExpressions.length - 1}, ("ordered"))`;
|
|
209
|
-
}
|
|
210
203
|
function buildAndCtsQueryExpressionInternal(queryExpressions) {
|
|
211
204
|
if (queryExpressions.length === 0) return "cts:true-query()";
|
|
212
205
|
if (queryExpressions.length === 1) return queryExpressions[0] ?? "cts:true-query()";
|
|
@@ -246,8 +239,7 @@ function buildRichTextContentQueryExpression(parameters) {
|
|
|
246
239
|
const { value, matchMode, isCaseSensitive, language } = parameters;
|
|
247
240
|
return buildAndCtsQueryExpressionInternal([buildContentLanguageQuery(language), matchMode === "exact" ? buildRichTextExactQueryExpression({
|
|
248
241
|
value,
|
|
249
|
-
isCaseSensitive
|
|
250
|
-
language
|
|
242
|
+
isCaseSensitive
|
|
251
243
|
}) : buildCtsWordQueryExpression({
|
|
252
244
|
value,
|
|
253
245
|
matchMode,
|
|
@@ -480,62 +472,83 @@ function buildItemStringQueryExpression(parameters) {
|
|
|
480
472
|
language
|
|
481
473
|
})]);
|
|
482
474
|
}
|
|
483
|
-
function
|
|
484
|
-
const { value, matchMode, isCaseSensitive } =
|
|
485
|
-
const
|
|
486
|
-
const terms = [];
|
|
487
|
-
for (const term of rawTerms) if (getWildcardStrippedValue(term) !== "") terms.push(term);
|
|
488
|
-
return terms;
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Compile one CTS query expression per OCR search term, in word order
|
|
492
|
-
*
|
|
493
|
-
* Highlighting matches each OCR word against these same per-term expressions,
|
|
494
|
-
* so hit locations always agree with what the filter matched.
|
|
495
|
-
* @internal
|
|
496
|
-
*/
|
|
497
|
-
function buildOcrTermQueryExpressions(parameters) {
|
|
498
|
-
const { value, matchMode, isCaseSensitive } = parameters;
|
|
499
|
-
const terms = tokenizeOcrSearchValue({
|
|
475
|
+
function buildOcrTextQueryExpression(query) {
|
|
476
|
+
const { value, matchMode, isCaseSensitive } = query;
|
|
477
|
+
const phraseQueryExpression = buildRichTextPhraseQueryExpression({
|
|
500
478
|
value,
|
|
501
|
-
matchMode,
|
|
502
479
|
isCaseSensitive
|
|
503
480
|
});
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
value: term,
|
|
481
|
+
if (matchMode === "exact") return buildNestedElementQuery(["ocrText"], phraseQueryExpression);
|
|
482
|
+
const terms = tokenizeIncludesSearchValue({
|
|
483
|
+
value,
|
|
508
484
|
isCaseSensitive
|
|
509
|
-
})
|
|
485
|
+
});
|
|
486
|
+
if (terms.length === 0) return "cts:false-query()";
|
|
487
|
+
const tokenizedQueryExpression = buildAndCtsQueryExpressionInternal(Array.from(terms, (term) => buildCtsWordQueryExpression({
|
|
510
488
|
value: term,
|
|
511
489
|
matchMode,
|
|
512
490
|
isCaseSensitive,
|
|
513
491
|
queryFamily: "text"
|
|
514
|
-
}));
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
492
|
+
})));
|
|
493
|
+
return buildNestedElementQuery(["ocrText"], shouldUseFullValueFallbackForIncludes({
|
|
494
|
+
value,
|
|
495
|
+
isCaseSensitive,
|
|
496
|
+
terms
|
|
497
|
+
}) ? buildOrCtsQueryExpressionInternal([phraseQueryExpression, tokenizedQueryExpression]) : tokenizedQueryExpression);
|
|
520
498
|
}
|
|
521
|
-
function
|
|
522
|
-
|
|
499
|
+
function getOcrTextConditionKey(query) {
|
|
500
|
+
return [
|
|
523
501
|
query.value,
|
|
524
502
|
query.matchMode,
|
|
525
503
|
query.isCaseSensitive ? "case-sensitive" : "case-insensitive"
|
|
526
504
|
].join("|");
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
505
|
+
}
|
|
506
|
+
function registerOcrTextCondition(context, query) {
|
|
507
|
+
const key = getOcrTextConditionKey(query);
|
|
508
|
+
if (context.ocrTextConditionIndexesByKey.has(key)) return;
|
|
509
|
+
context.ocrTextConditionIndexesByKey.set(key, context.ocrTextConditions.length);
|
|
510
|
+
context.ocrTextConditions.push({
|
|
511
|
+
variableName: `$ocrTextUuids${context.ocrTextConditions.length + 1}`,
|
|
512
|
+
bindingExpression: `for $ocrTextDocument in cts:search(doc(), ${buildOcrTextQueryExpression(query)})\n return document-uri($ocrTextDocument)`
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
function collectOcrTextConditions(context, query) {
|
|
516
|
+
if (isQueryLeaf(query)) {
|
|
517
|
+
if (query.target === "ocrText") registerOcrTextCondition(context, query);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
for (const childQuery of getQueryGroupChildren(query)) collectOcrTextConditions(context, childQuery);
|
|
521
|
+
}
|
|
522
|
+
function isOcrTextLeafMatched(context, query, ocrTextValues) {
|
|
523
|
+
const conditionIndex = context.ocrTextConditionIndexesByKey.get(getOcrTextConditionKey(query));
|
|
524
|
+
const isMatched = conditionIndex != null && ocrTextValues[conditionIndex] === true;
|
|
525
|
+
return query.isNegated === true ? !isMatched : isMatched;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Enumerate every assignment of "this item is in the OCR text match set" across
|
|
529
|
+
* the compiled conditions, least significant position first
|
|
530
|
+
*/
|
|
531
|
+
function getOcrTextValueCombinations(count) {
|
|
532
|
+
return Array.from({ length: 2 ** count }, (_, index) => Array.from({ length: count }, (_, position) => (index >> position & 1) === 1));
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Resolve a query tree against one OCR text assignment, treating every CTS leaf
|
|
536
|
+
* as unknown. Only a definite `false` is actionable: it means the branch cannot
|
|
537
|
+
* match anything and can be dropped before it costs a `cts:search`.
|
|
538
|
+
*/
|
|
539
|
+
function evaluateOcrTextBranch(context, query, ocrTextValues) {
|
|
540
|
+
if (isQueryLeaf(query)) return query.target === "ocrText" ? isOcrTextLeafMatched(context, query, ocrTextValues) : null;
|
|
541
|
+
const isAndGroup = "and" in query;
|
|
542
|
+
let result = isAndGroup;
|
|
543
|
+
for (const childQuery of getQueryGroupChildren(query)) {
|
|
544
|
+
const childResult = evaluateOcrTextBranch(context, childQuery, ocrTextValues);
|
|
545
|
+
if (childResult === !isAndGroup) return !isAndGroup;
|
|
546
|
+
if (childResult == null) result = null;
|
|
536
547
|
}
|
|
537
|
-
|
|
538
|
-
|
|
548
|
+
return result;
|
|
549
|
+
}
|
|
550
|
+
function buildOcrTextItemPredicates(context, ocrTextValues) {
|
|
551
|
+
return Array.from(context.ocrTextConditions, (condition, index) => ocrTextValues[index] === true ? `[@uuid = ${condition.variableName}]` : `[not(@uuid = ${condition.variableName})]`).join("");
|
|
539
552
|
}
|
|
540
553
|
function getLeafSearchValue(query) {
|
|
541
554
|
switch (query.target) {
|
|
@@ -618,10 +631,8 @@ function createQueryCompilerContext() {
|
|
|
618
631
|
nextHelperSerial: 1,
|
|
619
632
|
helperNamesByKey: /* @__PURE__ */ new Map(),
|
|
620
633
|
helperDeclarations: [],
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
ocrBindings: [],
|
|
624
|
-
itemPredicates: []
|
|
634
|
+
ocrTextConditions: [],
|
|
635
|
+
ocrTextConditionIndexesByKey: /* @__PURE__ */ new Map()
|
|
625
636
|
};
|
|
626
637
|
}
|
|
627
638
|
function registerConstantHelper(parameters) {
|
|
@@ -818,24 +829,11 @@ function getQueryGroupChildren(query) {
|
|
|
818
829
|
function getQueryGroupOperator(query) {
|
|
819
830
|
return "and" in query ? "and" : "or";
|
|
820
831
|
}
|
|
821
|
-
function isDisjunctiveQueryGroup(query) {
|
|
822
|
-
return "or" in query && query.or.length > 1;
|
|
823
|
-
}
|
|
824
|
-
/**
|
|
825
|
-
* Whether a query tree nests an OCR leaf inside an OR group
|
|
826
|
-
* @internal
|
|
827
|
-
*/
|
|
828
|
-
function hasOcrQueryInDisjunction(query, isInDisjunction = false) {
|
|
829
|
-
if (isQueryLeaf(query)) return query.target === "ocr" && isInDisjunction;
|
|
830
|
-
const isChildInDisjunction = isInDisjunction || isDisjunctiveQueryGroup(query);
|
|
831
|
-
for (const childQuery of getQueryGroupChildren(query)) if (hasOcrQueryInDisjunction(childQuery, isChildInDisjunction)) return true;
|
|
832
|
-
return false;
|
|
833
|
-
}
|
|
834
832
|
function getCompatibleIncludesGroupLeaves(query) {
|
|
835
833
|
if (!("or" in query) || query.or.length <= 1) return null;
|
|
836
834
|
const leafQueries = [];
|
|
837
835
|
for (const childQuery of query.or) {
|
|
838
|
-
if (!isQueryLeaf(childQuery) || childQuery.target === "
|
|
836
|
+
if (!isQueryLeaf(childQuery) || childQuery.target === "ocrText") return null;
|
|
839
837
|
leafQueries.push(childQuery);
|
|
840
838
|
}
|
|
841
839
|
const firstQuery = leafQueries[0];
|
|
@@ -896,20 +894,15 @@ function buildIncludesGroupQueryExpression(context, queries) {
|
|
|
896
894
|
bodyExpression: buildOrCtsQueryExpressionInternal(exactMemberHelpers.map((helper) => helper.callExpression))
|
|
897
895
|
}).callExpression, tokenizedQueryExpression]);
|
|
898
896
|
}
|
|
899
|
-
function buildQueryNode(context, query,
|
|
897
|
+
function buildQueryNode(context, query, ocrTextValues) {
|
|
900
898
|
if (isQueryLeaf(query)) {
|
|
901
|
-
if (query.target === "
|
|
902
|
-
if (isInDisjunction) throw new Error(OCR_DISJUNCTION_ERROR_MESSAGE, { cause: query });
|
|
903
|
-
registerOcrItemPredicate(context, query);
|
|
904
|
-
return "cts:true-query()";
|
|
905
|
-
}
|
|
899
|
+
if (query.target === "ocrText") return isOcrTextLeafMatched(context, query, ocrTextValues) ? "cts:true-query()" : "cts:false-query()";
|
|
906
900
|
const queryExpression = buildLeafQueryExpression(context, query);
|
|
907
901
|
return query.isNegated === true ? buildNotCtsQueryExpression(queryExpression) : queryExpression;
|
|
908
902
|
}
|
|
909
903
|
const optimizedIncludesGroupQueries = getCompatibleIncludesGroupLeaves(query);
|
|
910
904
|
if (optimizedIncludesGroupQueries != null) return buildIncludesGroupQueryExpression(context, optimizedIncludesGroupQueries);
|
|
911
|
-
const
|
|
912
|
-
const childQueryExpressions = Array.from(getQueryGroupChildren(query), (childQuery) => buildQueryNode(context, childQuery, isChildInDisjunction));
|
|
905
|
+
const childQueryExpressions = Array.from(getQueryGroupChildren(query), (childQuery) => buildQueryNode(context, childQuery, ocrTextValues));
|
|
913
906
|
return (getQueryGroupOperator(query) === "and" ? buildAndCtsQueryExpressionInternal : buildOrCtsQueryExpressionInternal)(childQueryExpressions);
|
|
914
907
|
}
|
|
915
908
|
function buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids, belongsToCollectionPropertyVariableUuid) {
|
|
@@ -923,22 +916,47 @@ function buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids,
|
|
|
923
916
|
}))))
|
|
924
917
|
});
|
|
925
918
|
}
|
|
919
|
+
/**
|
|
920
|
+
* Compile a query tree into the CTS searches that resolve it
|
|
921
|
+
*
|
|
922
|
+
* OCR text is not carried by Set item projections, so an `ocrText` leaf cannot
|
|
923
|
+
* be a CTS term: it resolves to a document join whose UUID list can only be
|
|
924
|
+
* applied as an item path predicate, and path predicates only ever AND. To keep
|
|
925
|
+
* `ocrText` composable with `or` anyway, the tree is split on each distinct OCR
|
|
926
|
+
* text condition, one branch per assignment of "this item is in that match
|
|
927
|
+
* set". Every branch is a plain CTS search, and their union is the result.
|
|
928
|
+
* Branches that the assignment already rules out are dropped, so a query whose
|
|
929
|
+
* OCR text leaves are all conjunctive still compiles to a single search.
|
|
930
|
+
*/
|
|
926
931
|
function buildQueryPlan(parameters) {
|
|
927
932
|
const { queries } = parameters;
|
|
928
933
|
if (queries == null) return {
|
|
929
934
|
prolog: "",
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
935
|
+
ocrTextBindings: [],
|
|
936
|
+
branches: [{
|
|
937
|
+
itemPredicates: "",
|
|
938
|
+
queryExpression: null
|
|
939
|
+
}]
|
|
933
940
|
};
|
|
934
941
|
const context = createQueryCompilerContext();
|
|
935
|
-
|
|
942
|
+
collectOcrTextConditions(context, queries);
|
|
943
|
+
if (context.ocrTextConditions.length > MAX_OCR_TEXT_CONDITIONS) throw new Error(`A query cannot contain more than ${MAX_OCR_TEXT_CONDITIONS} distinct OCR text searches`, { cause: context.ocrTextConditions.length });
|
|
944
|
+
const branches = [];
|
|
945
|
+
for (const ocrTextValues of getOcrTextValueCombinations(context.ocrTextConditions.length)) {
|
|
946
|
+
if (evaluateOcrTextBranch(context, queries, ocrTextValues) === false) continue;
|
|
947
|
+
branches.push({
|
|
948
|
+
itemPredicates: buildOcrTextItemPredicates(context, ocrTextValues),
|
|
949
|
+
queryExpression: buildQueryNode(context, queries, ocrTextValues)
|
|
950
|
+
});
|
|
951
|
+
}
|
|
936
952
|
return {
|
|
937
953
|
prolog: context.helperDeclarations.join("\n\n"),
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
954
|
+
ocrTextBindings: Array.from(context.ocrTextConditions, (condition) => ({
|
|
955
|
+
name: condition.variableName,
|
|
956
|
+
expression: condition.bindingExpression
|
|
957
|
+
})),
|
|
958
|
+
branches
|
|
941
959
|
};
|
|
942
960
|
}
|
|
943
961
|
//#endregion
|
|
944
|
-
export {
|
|
962
|
+
export { buildAndCtsQueryExpression, buildBelongsToCollectionQueryExpression, buildQueryPlan };
|
package/dist/schemas.d.mts
CHANGED
|
@@ -38,7 +38,7 @@ declare const renderOptionsSchema: v.SchemaWithPipe<readonly [v.StringSchema<und
|
|
|
38
38
|
declare const setPropertyValuesParametersSchema: v.ObjectSchema<{
|
|
39
39
|
readonly setScopeUuids: v.SchemaWithPipe<readonly [v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "Invalid pseudo-UUID">]>, undefined>, v.MinLengthAction<string[], 1, "At least one set scope UUID is required">]>;
|
|
40
40
|
readonly belongsToCollectionScopeUuids: v.OptionalSchema<v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "Invalid pseudo-UUID">]>, undefined>, readonly []>;
|
|
41
|
-
readonly queries: v.OptionalSchema<v.NullableSchema<v.
|
|
41
|
+
readonly queries: v.OptionalSchema<v.NullableSchema<v.GenericSchema<unknown, Query>, undefined>, null>;
|
|
42
42
|
readonly attributes: v.OptionalSchema<v.ObjectSchema<{
|
|
43
43
|
readonly bibliographies: v.GenericSchema<unknown, boolean>;
|
|
44
44
|
readonly periods: v.GenericSchema<unknown, boolean>;
|
|
@@ -49,15 +49,14 @@ declare const setPropertyValuesParametersSchema: v.ObjectSchema<{
|
|
|
49
49
|
readonly isLimitedToLeafPropertyValues: v.GenericSchema<unknown, boolean>;
|
|
50
50
|
}, undefined>;
|
|
51
51
|
/**
|
|
52
|
-
* Schema for validating OCR
|
|
52
|
+
* Schema for validating the parameters for the item OCR data fetching function
|
|
53
53
|
* @internal
|
|
54
54
|
*/
|
|
55
|
-
declare const
|
|
56
|
-
readonly
|
|
57
|
-
readonly value: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.
|
|
55
|
+
declare const itemOcrDataParametersSchema: v.ObjectSchema<{
|
|
56
|
+
readonly uuid: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "Invalid pseudo-UUID">]>;
|
|
57
|
+
readonly value: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "A search value is required">]>;
|
|
58
58
|
readonly matchMode: v.OptionalSchema<v.PicklistSchema<["includes", "exact"], undefined>, "includes">;
|
|
59
59
|
readonly isCaseSensitive: v.GenericSchema<unknown, boolean>;
|
|
60
|
-
readonly maxMatchesPerItem: v.OptionalSchema<v.GenericSchema<unknown, number>, 50>;
|
|
61
60
|
}, undefined>;
|
|
62
61
|
/**
|
|
63
62
|
* Schema for validating Set items parameters
|
|
@@ -66,7 +65,7 @@ declare const ocrMatchesParametersSchema: v.ObjectSchema<{
|
|
|
66
65
|
declare const setItemsParametersSchema: v.ObjectSchema<{
|
|
67
66
|
readonly setScopeUuids: v.SchemaWithPipe<readonly [v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "Invalid pseudo-UUID">]>, undefined>, v.MinLengthAction<string[], 1, "At least one set scope UUID is required">]>;
|
|
68
67
|
readonly belongsToCollectionScopeUuids: v.OptionalSchema<v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "Invalid pseudo-UUID">]>, undefined>, readonly []>;
|
|
69
|
-
readonly queries: v.OptionalSchema<v.NullableSchema<v.
|
|
68
|
+
readonly queries: v.OptionalSchema<v.NullableSchema<v.GenericSchema<unknown, Query>, undefined>, null>;
|
|
70
69
|
readonly sort: v.OptionalSchema<v.VariantSchema<"target", [v.StrictObjectSchema<{
|
|
71
70
|
readonly target: v.LiteralSchema<"none", undefined>;
|
|
72
71
|
}, undefined>, v.StrictObjectSchema<{
|
|
@@ -86,4 +85,4 @@ declare const setItemsParametersSchema: v.ObjectSchema<{
|
|
|
86
85
|
readonly pageSize: v.OptionalSchema<v.GenericSchema<unknown, number>, 48>;
|
|
87
86
|
}, undefined>;
|
|
88
87
|
//#endregion
|
|
89
|
-
export { componentSchema, gallerySchema, iso639_3Schema,
|
|
88
|
+
export { componentSchema, gallerySchema, iso639_3Schema, itemOcrDataParametersSchema, renderOptionsSchema, setItemsParametersSchema, setPropertyValuesParametersSchema, uuidSchema };
|
package/dist/schemas.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isPseudoUuid } from "./utilities.mjs";
|
|
2
2
|
import "./helpers.mjs";
|
|
3
|
-
import { OCR_DISJUNCTION_ERROR_MESSAGE, hasOcrQueryInDisjunction } from "./query.mjs";
|
|
4
3
|
import * as v from "valibot";
|
|
5
4
|
//#region src/schemas.ts
|
|
6
5
|
const positiveNumber = (message) => v.pipe(v.number(), v.minValue(1, message));
|
|
@@ -143,7 +142,7 @@ const setQueryLeafSchema = v.union([
|
|
|
143
142
|
...standardQueryFields
|
|
144
143
|
}),
|
|
145
144
|
v.strictObject({
|
|
146
|
-
target: v.literal("
|
|
145
|
+
target: v.literal("ocrText"),
|
|
147
146
|
value: v.string(),
|
|
148
147
|
matchMode: standardQueryFields.matchMode,
|
|
149
148
|
isCaseSensitive: standardQueryFields.isCaseSensitive,
|
|
@@ -175,7 +174,7 @@ const setQuerySchema = v.lazy(() => v.union([
|
|
|
175
174
|
* Schema for validating Set queries
|
|
176
175
|
* @internal
|
|
177
176
|
*/
|
|
178
|
-
const setQueriesSchema = v.optional(v.nullable(
|
|
177
|
+
const setQueriesSchema = v.optional(v.nullable(setQuerySchema), null);
|
|
179
178
|
/**
|
|
180
179
|
* Schema for validating Set items sort
|
|
181
180
|
* @internal
|
|
@@ -222,15 +221,14 @@ const setPropertyValuesParametersSchema = v.object({
|
|
|
222
221
|
isLimitedToLeafPropertyValues: defaultBoolean(false)
|
|
223
222
|
});
|
|
224
223
|
/**
|
|
225
|
-
* Schema for validating OCR
|
|
224
|
+
* Schema for validating the parameters for the item OCR data fetching function
|
|
226
225
|
* @internal
|
|
227
226
|
*/
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
value: v.pipe(v.string(), v.
|
|
227
|
+
const itemOcrDataParametersSchema = v.object({
|
|
228
|
+
uuid: uuidSchema,
|
|
229
|
+
value: v.pipe(v.string(), v.check((value) => value.trim() !== "", "A search value is required")),
|
|
231
230
|
matchMode: v.optional(v.picklist(["includes", "exact"]), "includes"),
|
|
232
|
-
isCaseSensitive: defaultBoolean(false)
|
|
233
|
-
maxMatchesPerItem: v.optional(positiveNumber("Max matches per item must be positive"), 50)
|
|
231
|
+
isCaseSensitive: defaultBoolean(false)
|
|
234
232
|
});
|
|
235
233
|
/**
|
|
236
234
|
* Schema for validating Set items parameters
|
|
@@ -245,4 +243,4 @@ const setItemsParametersSchema = v.object({
|
|
|
245
243
|
pageSize: v.optional(positiveNumber("Page size must be positive"), 48)
|
|
246
244
|
});
|
|
247
245
|
//#endregion
|
|
248
|
-
export { componentSchema, gallerySchema, iso639_3Schema,
|
|
246
|
+
export { componentSchema, gallerySchema, iso639_3Schema, itemOcrDataParametersSchema, renderOptionsSchema, setItemsParametersSchema, setPropertyValuesParametersSchema, uuidSchema };
|
package/dist/types/index.d.mts
CHANGED
|
@@ -263,58 +263,29 @@ type ImageMap = {
|
|
|
263
263
|
height: number;
|
|
264
264
|
};
|
|
265
265
|
/**
|
|
266
|
-
*
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
y
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
*
|
|
274
|
-
*/
|
|
275
|
-
type OcrWord = {
|
|
276
|
-
content: string;
|
|
277
|
-
x: number;
|
|
278
|
-
y: number;
|
|
279
|
-
width: number;
|
|
280
|
-
height: number;
|
|
281
|
-
vertices: Array<OcrPoint>;
|
|
282
|
-
};
|
|
283
|
-
/**
|
|
284
|
-
* Text line of an OCR text block in OCHRE
|
|
285
|
-
*/
|
|
286
|
-
type OcrTextLine = {
|
|
287
|
-
content: string;
|
|
288
|
-
words: Array<OcrWord>;
|
|
289
|
-
};
|
|
290
|
-
/**
|
|
291
|
-
* Text block of an OCR page in OCHRE
|
|
292
|
-
*/
|
|
293
|
-
type OcrTextBlock = {
|
|
294
|
-
lines: Array<OcrTextLine>;
|
|
295
|
-
};
|
|
296
|
-
/**
|
|
297
|
-
* OCR page of a resource in OCHRE
|
|
298
|
-
*/
|
|
299
|
-
type OcrPage = {
|
|
300
|
-
number: number | null;
|
|
301
|
-
fileName: string | null;
|
|
302
|
-
width: number | null;
|
|
303
|
-
height: number | null;
|
|
304
|
-
blocks: Array<OcrTextBlock>;
|
|
305
|
-
};
|
|
306
|
-
/**
|
|
307
|
-
* A run of adjacent OCR words matching a search value in OCHRE
|
|
266
|
+
* Positioned OCR string in OCHRE
|
|
267
|
+
*
|
|
268
|
+
* OCHRE gives no guarantee about the node hierarchy inside a Resource's
|
|
269
|
+
* `<ocr>` layer, so only `<string>` nodes are parsed, at whatever depth they
|
|
270
|
+
* occur. `x` and `y` come from `HPOS` and `VPOS` and locate the top-left
|
|
271
|
+
* corner of the string's box, while `vertices` is its full bounding polygon,
|
|
272
|
+
* which is not necessarily rectangular. Every geometry attribute is optional
|
|
273
|
+
* in the source, so each one is null when absent or unparseable.
|
|
308
274
|
*
|
|
309
|
-
* `
|
|
310
|
-
*
|
|
275
|
+
* `resourceUuid` is the Resource that owns the OCR layer, which differs from
|
|
276
|
+
* the requested item when the OCR lives on a child Resource.
|
|
311
277
|
*/
|
|
312
|
-
type
|
|
313
|
-
uuid: string;
|
|
278
|
+
type OcrString = {
|
|
314
279
|
resourceUuid: string | null;
|
|
315
|
-
page: Omit<OcrPage, "blocks">;
|
|
316
280
|
content: string;
|
|
317
|
-
|
|
281
|
+
x: number | null;
|
|
282
|
+
y: number | null;
|
|
283
|
+
width: number | null;
|
|
284
|
+
height: number | null;
|
|
285
|
+
vertices: Array<{
|
|
286
|
+
x: number;
|
|
287
|
+
y: number;
|
|
288
|
+
}>;
|
|
318
289
|
};
|
|
319
290
|
/**
|
|
320
291
|
* Note in OCHRE
|
|
@@ -734,7 +705,6 @@ type Resource<T extends LanguageCodes = LanguageCodes, U extends ItemPayloadKind
|
|
|
734
705
|
image: Image<T> | null;
|
|
735
706
|
document: MultilingualString<T> | null;
|
|
736
707
|
imageMap: ImageMap | null;
|
|
737
|
-
ocr: Array<OcrPage>;
|
|
738
708
|
coordinates: Array<Coordinates<T>>;
|
|
739
709
|
periods: Array<Period<T, "embedded">>;
|
|
740
710
|
links: ItemLinks<T>;
|
|
@@ -845,12 +815,12 @@ type SetItemsSort = {
|
|
|
845
815
|
/**
|
|
846
816
|
* Represents a leaf query for Set items
|
|
847
817
|
*
|
|
848
|
-
* The `
|
|
849
|
-
*
|
|
850
|
-
* than a CTS term,
|
|
851
|
-
*
|
|
852
|
-
*
|
|
853
|
-
*
|
|
818
|
+
* The `ocrText` target matches the OCR text layer of Resource items. Because
|
|
819
|
+
* Set item projections do not carry `<ocrText>`, it is resolved by a document
|
|
820
|
+
* join rather than a CTS term, so it composes with `and`, `or`, and `isNegated`
|
|
821
|
+
* at the cost of one extra search branch per distinct OCR text value in a
|
|
822
|
+
* disjunction. OCR text carries no language, so `ocrText` leaves take no
|
|
823
|
+
* `language`.
|
|
854
824
|
*/
|
|
855
825
|
type QueryLeaf = {
|
|
856
826
|
target: "property";
|
|
@@ -918,7 +888,7 @@ type QueryLeaf = {
|
|
|
918
888
|
language: string;
|
|
919
889
|
isNegated?: boolean;
|
|
920
890
|
} | {
|
|
921
|
-
target: "
|
|
891
|
+
target: "ocrText";
|
|
922
892
|
value: string;
|
|
923
893
|
matchMode: "includes" | "exact";
|
|
924
894
|
isCaseSensitive: boolean;
|
|
@@ -944,4 +914,4 @@ type QueryGroup = {
|
|
|
944
914
|
*/
|
|
945
915
|
type Query = QueryLeaf | QueryGroup;
|
|
946
916
|
//#endregion
|
|
947
|
-
export { AnyBibliography, AnyConcept, AnyItem, AnyPeriod, AnyPerson, AnyPropertyValue, AnyPropertyVariable, AnyResource, AnySet, AnySpatialUnit, AnyText, AnyTree, BaseItem, BaseItemLink, BelongsTo, Bibliography, BibliographyEntryInfo, BibliographyItemLink, BibliographySourceDocument, Concept, ConceptItemLink, ContainedItemCategory, ContainedItemCategoryFromOption, ContainedItemCategoryOption, Context, ContextItem, ContextItemCategory, ContextNode, Coordinates, CoordinatesSource, DictionaryUnitItemLink, EmbeddedBibliography, EmbeddedConcept, EmbeddedItem, EmbeddedPeriod, EmbeddedPerson, EmbeddedPropertyValue, EmbeddedPropertyVariable, EmbeddedResource, EmbeddedSet, EmbeddedSpatialUnit, EmbeddedText, EmbeddedTree, Event, Gallery, Heading, HeadingItemCategory, Identification, Image, ImageMap, ImageMapArea, Interpretation, Item, ItemCategory, ItemCategoryFromOption, ItemCategoryOption, ItemCategoryWithEmbeddedItems, ItemContainerCategory, ItemLink, ItemLinkCategory, ItemLinks, ItemPayloadKind, ItemProperty, ItemWithoutEmbeddedItems, LanguageCodes, License, Metadata, Note, Observation,
|
|
917
|
+
export { AnyBibliography, AnyConcept, AnyItem, AnyPeriod, AnyPerson, AnyPropertyValue, AnyPropertyVariable, AnyResource, AnySet, AnySpatialUnit, AnyText, AnyTree, BaseItem, BaseItemLink, BelongsTo, Bibliography, BibliographyEntryInfo, BibliographyItemLink, BibliographySourceDocument, Concept, ConceptItemLink, ContainedItemCategory, ContainedItemCategoryFromOption, ContainedItemCategoryOption, Context, ContextItem, ContextItemCategory, ContextNode, Coordinates, CoordinatesSource, DictionaryUnitItemLink, EmbeddedBibliography, EmbeddedConcept, EmbeddedItem, EmbeddedPeriod, EmbeddedPerson, EmbeddedPropertyValue, EmbeddedPropertyVariable, EmbeddedResource, EmbeddedSet, EmbeddedSpatialUnit, EmbeddedText, EmbeddedTree, Event, Gallery, Heading, HeadingItemCategory, Identification, Image, ImageMap, ImageMapArea, Interpretation, Item, ItemCategory, ItemCategoryFromOption, ItemCategoryOption, ItemCategoryWithEmbeddedItems, ItemContainerCategory, ItemLink, ItemLinkCategory, ItemLinks, ItemPayloadKind, ItemProperty, ItemWithoutEmbeddedItems, LanguageCodes, License, Metadata, Note, Observation, OcrString, Period, PeriodItemLink, Person, PersonItemLink, Prettify, Property, PropertyLike, PropertyRelation, PropertyValue, PropertyValueContent, PropertyValueDataType, PropertyValueItemLink, PropertyValueQueryItem, PropertyVariable, PropertyVariableItemLink, Query, QueryGroup, QueryLeaf, QueryablePropertyValueDataType, RecursiveItemCategory, Resource, ResourceItemLink, Section, Set, SetAttributeValueQueryItem, SetBibliography, SetConcept, SetItem, SetItemCategory, SetItemLink, SetItemProperty, SetItemSimplifiedProperty, SetItemsSort, SetItemsSortDirection, SetPeriod, SetResource, SetSpatialUnit, SetTree, SimplifiedProperty, SpatialUnit, SpatialUnitItemLink, Text, TextItemLink, TopLevelItem, Tree, TreeItemCategory, TreeItemLink };
|
package/dist/utilities.d.mts
CHANGED
|
@@ -20,6 +20,30 @@ declare function isPseudoUuid(value: string): boolean;
|
|
|
20
20
|
* @returns The escaped string literal
|
|
21
21
|
*/
|
|
22
22
|
declare function stringLiteral(value: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* XQuery prolog declaring `local:omit-supplemental`, which drops every element
|
|
25
|
+
* carrying `supplemental="true"` from a node sequence, at any depth.
|
|
26
|
+
*
|
|
27
|
+
* Subtrees without a supplemental descendant are returned by reference, so
|
|
28
|
+
* nodes are only copied along the path leading to an omitted element. The
|
|
29
|
+
* lookahead walks the attribute axis (`//@supplemental`) rather than testing
|
|
30
|
+
* every element, which measures around three times faster on large documents.
|
|
31
|
+
*
|
|
32
|
+
* Must be declared before any query body that calls {@link omitSupplemental}.
|
|
33
|
+
*/
|
|
34
|
+
declare const SUPPLEMENTAL_XQUERY_PROLOG = "declare function local:omit-supplemental($nodes as node()*) as node()* {\n for $node in $nodes\n return\n if ($node instance of element())\n then\n if ($node/@supplemental = \"true\")\n then ()\n else if (empty($node//@supplemental[. = \"true\"]))\n then $node\n else element { node-name($node) } {\n $node/@*,\n local:omit-supplemental($node/node())\n }\n else $node\n};";
|
|
35
|
+
/**
|
|
36
|
+
* Wrap an XQuery node expression so supplemental nodes are omitted from it
|
|
37
|
+
* @param expression - The XQuery expression returning the nodes to filter
|
|
38
|
+
* @returns The wrapped XQuery expression
|
|
39
|
+
*/
|
|
40
|
+
declare function omitSupplemental(expression: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* XQuery predicate keeping only nodes that are neither supplemental themselves
|
|
43
|
+
* nor nested inside a supplemental node. Use it when aggregating over nodes
|
|
44
|
+
* instead of returning them.
|
|
45
|
+
*/
|
|
46
|
+
declare const NOT_SUPPLEMENTAL_PREDICATE = "[not(ancestor-or-self::*[@supplemental = \"true\"])]";
|
|
23
47
|
/**
|
|
24
48
|
* Flatten a properties array
|
|
25
49
|
* @param properties - The properties to flatten
|
|
@@ -28,4 +52,4 @@ declare function stringLiteral(value: string): string;
|
|
|
28
52
|
*/
|
|
29
53
|
declare function flattenProperties<T extends LanguageCodes = LanguageCodes>(properties: ReadonlyArray<Property<T> | SetItemProperty<T>>): Array<SetItemProperty<T>>;
|
|
30
54
|
//#endregion
|
|
31
|
-
export { createSchemaValidationError, flattenProperties, getErrorOutput, isPseudoUuid, stringLiteral };
|
|
55
|
+
export { NOT_SUPPLEMENTAL_PREDICATE, SUPPLEMENTAL_XQUERY_PROLOG, createSchemaValidationError, flattenProperties, getErrorOutput, isPseudoUuid, omitSupplemental, stringLiteral };
|
package/dist/utilities.mjs
CHANGED
|
@@ -151,6 +151,46 @@ function stringLiteral(value) {
|
|
|
151
151
|
return `"${value.replaceAll("\"", "\"\"")}"`;
|
|
152
152
|
}
|
|
153
153
|
/**
|
|
154
|
+
* XQuery prolog declaring `local:omit-supplemental`, which drops every element
|
|
155
|
+
* carrying `supplemental="true"` from a node sequence, at any depth.
|
|
156
|
+
*
|
|
157
|
+
* Subtrees without a supplemental descendant are returned by reference, so
|
|
158
|
+
* nodes are only copied along the path leading to an omitted element. The
|
|
159
|
+
* lookahead walks the attribute axis (`//@supplemental`) rather than testing
|
|
160
|
+
* every element, which measures around three times faster on large documents.
|
|
161
|
+
*
|
|
162
|
+
* Must be declared before any query body that calls {@link omitSupplemental}.
|
|
163
|
+
*/
|
|
164
|
+
const SUPPLEMENTAL_XQUERY_PROLOG = `declare function local:omit-supplemental($nodes as node()*) as node()* {
|
|
165
|
+
for $node in $nodes
|
|
166
|
+
return
|
|
167
|
+
if ($node instance of element())
|
|
168
|
+
then
|
|
169
|
+
if ($node/@supplemental = "true")
|
|
170
|
+
then ()
|
|
171
|
+
else if (empty($node//@supplemental[. = "true"]))
|
|
172
|
+
then $node
|
|
173
|
+
else element { node-name($node) } {
|
|
174
|
+
$node/@*,
|
|
175
|
+
local:omit-supplemental($node/node())
|
|
176
|
+
}
|
|
177
|
+
else $node
|
|
178
|
+
};`;
|
|
179
|
+
/**
|
|
180
|
+
* Wrap an XQuery node expression so supplemental nodes are omitted from it
|
|
181
|
+
* @param expression - The XQuery expression returning the nodes to filter
|
|
182
|
+
* @returns The wrapped XQuery expression
|
|
183
|
+
*/
|
|
184
|
+
function omitSupplemental(expression) {
|
|
185
|
+
return `local:omit-supplemental(${expression})`;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* XQuery predicate keeping only nodes that are neither supplemental themselves
|
|
189
|
+
* nor nested inside a supplemental node. Use it when aggregating over nodes
|
|
190
|
+
* instead of returning them.
|
|
191
|
+
*/
|
|
192
|
+
const NOT_SUPPLEMENTAL_PREDICATE = "[not(ancestor-or-self::*[@supplemental = \"true\"])]";
|
|
193
|
+
/**
|
|
154
194
|
* Flatten a properties array
|
|
155
195
|
* @param properties - The properties to flatten
|
|
156
196
|
* @returns The flattened properties
|
|
@@ -169,4 +209,4 @@ function flattenProperties(properties) {
|
|
|
169
209
|
return result;
|
|
170
210
|
}
|
|
171
211
|
//#endregion
|
|
172
|
-
export { createSchemaValidationError, flattenProperties, getErrorOutput, isPseudoUuid, stringLiteral };
|
|
212
|
+
export { NOT_SUPPLEMENTAL_PREDICATE, SUPPLEMENTAL_XQUERY_PROLOG, createSchemaValidationError, flattenProperties, getErrorOutput, isPseudoUuid, omitSupplemental, stringLiteral };
|
package/dist/xml/schemas.d.mts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import { XMLData as XMLData$1, XMLDataItem as XMLDataItem$1, XMLGalleryData as XMLGalleryData$1, XMLItemLinksData as XMLItemLinksData$1, XMLLink as XMLLink$1,
|
|
1
|
+
import { XMLData as XMLData$1, XMLDataItem as XMLDataItem$1, XMLGalleryData as XMLGalleryData$1, XMLItemLinksData as XMLItemLinksData$1, XMLLink as XMLLink$1, XMLSetItemsData as XMLSetItemsData$1, XMLWebsiteData as XMLWebsiteData$1 } from "./types.mjs";
|
|
2
2
|
import * as v from "valibot";
|
|
3
3
|
//#region src/xml/schemas.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* Schema for validating OCR matches fetched from the OCHRE API
|
|
6
|
-
* @internal
|
|
7
|
-
*/
|
|
8
|
-
declare const XMLOcrMatchesData: v.GenericSchema<unknown, XMLOcrMatchesData$1>;
|
|
9
4
|
declare const XMLLink: v.GenericSchema<unknown, XMLLink$1>;
|
|
10
5
|
declare const XMLDataItem: v.GenericSchema<unknown, XMLDataItem$1>;
|
|
11
6
|
declare const XMLItemLinksData: v.GenericSchema<unknown, XMLItemLinksData$1>;
|
|
@@ -14,4 +9,4 @@ declare const XMLSetItemsData: v.GenericSchema<unknown, XMLSetItemsData$1>;
|
|
|
14
9
|
declare const XMLData: v.GenericSchema<unknown, XMLData$1>;
|
|
15
10
|
declare const XMLWebsiteData: v.GenericSchema<unknown, XMLWebsiteData$1>;
|
|
16
11
|
//#endregion
|
|
17
|
-
export { XMLData, XMLDataItem, XMLGalleryData, XMLItemLinksData, XMLLink,
|
|
12
|
+
export { XMLData, XMLDataItem, XMLGalleryData, XMLItemLinksData, XMLLink, XMLSetItemsData, XMLWebsiteData };
|